From 04d067f37259479885e0ac8f033a1ad42c6e968c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 17:50:34 +0100 Subject: [PATCH 01/85] Cleverbot reenabled --- .../Modules/Games/Commands/PlantAndPickCommands.cs | 8 ++++---- src/NadekoBot/Modules/Utility/Utility.cs | 2 -- src/NadekoBot/Services/CommandHandler.cs | 11 ++++++++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 560f9f45..d7fb96cc 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -46,9 +46,9 @@ namespace NadekoBot.Modules.Games { _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); -#if !GLOBAL_NADEKO + NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; -#endif + using (var uow = DbHandler.UnitOfWork()) { var conf = uow.BotConfig.GetOrCreate(); @@ -101,7 +101,7 @@ namespace NadekoBot.Modules.Games } catch { } } -#if !GLOBAL_NADEKO + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Pick() @@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Games } plantedFlowers.AddOrUpdate(Context.Channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); } -#endif + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 27f8d25e..e2d17029 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -200,9 +200,7 @@ namespace NadekoBot.Modules.Utility .AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)) -#if !GLOBAL_NADEKO .WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued.")) -#endif ); } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 60196598..47c7e770 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -123,17 +123,22 @@ namespace NadekoBot.Services { return; } -#if !GLOBAL_NADEKO + try { var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg); if (cleverbotExecuted) + { + _log.Info($@"CleverBot Executed + Server: {guild.Name} [{guild.Id}] + Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}] + UserId: {usrMsg.Author} [{usrMsg.Author.Id}] + Message: {usrMsg.Content}"); return; + } } catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); } - -#endif try { // maybe this message is a custom reaction From 8ee66817b4b3a2fb9b4d2e24ff408c0d32960dc5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 18:36:33 +0100 Subject: [PATCH 02/85] Mistyped --- docs/Custom Reactions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Custom Reactions.md b/docs/Custom Reactions.md index 3d99cb23..8d41afca 100644 --- a/docs/Custom Reactions.md +++ b/docs/Custom Reactions.md @@ -40,7 +40,7 @@ There are currently three different placeholders which we will look at, with mor | Placeholder | Description | Example Usage | Usage | |:-----------:|-------------|---------------|-------| -|`%mention`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"| +|`%mention%`|The `%mention%` placeholder is triggered when you type `@BotName` - It's important to note that if you've given the bot a custom nickname, this trigger won't work!|```.acr "Hello %mention%" I, %mention%, also say hello!```|Input: "Hello @BotName" Output: "I, @BotName, also say hello!"| |`%user%`|The `%user%` placeholder mentions the person who said the command|`.acr "Who am I?" You are %user%!`|Input: "Who am I?" Output: "You are @Username!"| |`%rng%`|The `%rng%` placeholder generates a random number between 0 and 10. You can also specify a custom range (%rng1-100%) even with negative numbers: `%rng-9--1%` (from -9 to -1) . |`.acr "Random number" %rng%`|Input: "Random number" Output: "2"| |`%rnduser%`|The `%rnduser%` placeholder mentions a random user from the server. |`.acr "Random user" %rnduser%`|Input: "Random number" Output: @SomeUser| From 96ede41e52c3bb222a6daf0d1b258123939184d5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 18:48:06 +0100 Subject: [PATCH 03/85] Disabled cleverot and flower generation on public bot --- src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs | 3 ++- src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs | 3 +++ src/NadekoBot/Services/CommandHandler.cs | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index 9ec59147..a65787fb 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Games } return true; } - +#if !GLOBAL_NADEKO [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(ChannelPermission.ManageMessages)] @@ -120,6 +120,7 @@ namespace NadekoBot.Modules.Games await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Enabled cleverbot on this server.").ConfigureAwait(false); } +#endif } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index d7fb96cc..6841c089 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -47,7 +47,10 @@ namespace NadekoBot.Modules.Games _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); + +#if !GLOBAL_NADEKO NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; +#endif using (var uow = DbHandler.UnitOfWork()) { diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 47c7e770..9c899d8c 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -124,10 +124,10 @@ namespace NadekoBot.Services return; } +#if !GLOBAL_NADEKO try { var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg); - if (cleverbotExecuted) { _log.Info($@"CleverBot Executed @@ -139,6 +139,7 @@ namespace NadekoBot.Services } } catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); } +#endif try { // maybe this message is a custom reaction From 0a517495f39a7ba1ece396bee7e949acd4436247 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 19:23:53 +0100 Subject: [PATCH 04/85] Disabled >plant and pick again --- src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 6841c089..6d17dd76 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -104,7 +104,7 @@ namespace NadekoBot.Modules.Games } catch { } } - +#if !GLOBAL_NADEKO [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Pick() @@ -162,6 +162,7 @@ namespace NadekoBot.Modules.Games } plantedFlowers.AddOrUpdate(Context.Channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); } +#endif [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] From 0a26444975aaaff78a218d1eeaa0d4c03f934b40 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 19:45:01 +0100 Subject: [PATCH 05/85] dnd roll now works properly --- .../Modules/Gambling/Commands/DiceRollCommand.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 90c54e2d..01283c83 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -164,12 +164,15 @@ namespace NadekoBot.Modules.Gambling var arr = new int[n1]; for (int i = 0; i < n1; i++) { - arr[i] = rng.Next(1, n2 + 1) + add - sub; + arr[i] = rng.Next(1, n2 + 1); } - var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}` +`{add}` -`{sub}`") - .AddField(efb => efb.WithName(Format.Bold("Result")) - .WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString()))))); + var sum = arr.Sum(); + var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}`") + .AddField(efb => efb.WithName(Format.Bold("Rolls")) + .WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString()))))) + .AddField(efb => efb.WithName(Format.Bold("Sum")) + .WithValue(sum + " + " + add + " - " + sub + " = " + (sum + add - sub))); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } } From fc454d3c3f1956a9cd0f91dcbb734225b00acd1d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 19:56:39 +0100 Subject: [PATCH 06/85] Fixed hours on songs --- src/NadekoBot/Modules/Music/Classes/Song.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index 6a614eec..0ee23fd4 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -62,7 +62,15 @@ namespace NadekoBot.Modules.Music.Classes else if (TotalTime == TimeSpan.MaxValue) return "∞"; else - return TotalTime.ToString(@"mm\:ss"); + { + var time = TotalTime.ToString(@"mm\:ss"); + var hrs = (int)TotalTime.TotalHours; + + if (hrs > 0) + return hrs + ":" + time; + else + return time; + } } } From 42dc85010560b0095dc978227e4abc58d93569cd Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 21:01:22 +0100 Subject: [PATCH 07/85] Permission guide fix --- docs/Permissions System.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/docs/Permissions System.md b/docs/Permissions System.md index 11c32daf..99f97e04 100644 --- a/docs/Permissions System.md +++ b/docs/Permissions System.md @@ -43,7 +43,7 @@ Commonly Asked Questions --------------- ###How do I create a music DJ? -To allow users to only see the current song and have a DJ role for queuing follow these five steps: +To allow users to only see the current song and have a DJ role for queuing follow these steps: 1. `;sm Music disable` @@ -53,17 +53,13 @@ To allow users to only see the current song and have a DJ role for queuing follo * Enables the "nowplaying" command for everyone -3. `;sc !!getlink enable` - - * Enables the "getlink" command for everyone - -4. `;sc !!listqueue enable` +3. `;sc !!listqueue enable` * Enables the "listqueue" command for everyone -5. `;rm Music enable DJ` +4. `;rm Music enable DJ` - * Enables all the music commands only for the DJ role + * Enables all music commands only for the DJ role ###How do I create a NSFW channel? From 980a4a10d093c0cdedab72a64dc0f5310dd2ec49 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 21:12:21 +0100 Subject: [PATCH 08/85] Better error in !imdb --- src/NadekoBot/Modules/Searches/Commands/OMDB/OmdbProvider.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Searches/Commands/OMDB/OmdbProvider.cs b/src/NadekoBot/Modules/Searches/Commands/OMDB/OmdbProvider.cs index 892f8fab..a3d56fcf 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OMDB/OmdbProvider.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OMDB/OmdbProvider.cs @@ -18,7 +18,8 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB { var res = await http.GetStringAsync(String.Format(queryUrl,name.Trim().Replace(' ','+'))).ConfigureAwait(false); var movie = JsonConvert.DeserializeObject(res); - + if (movie?.Title == null) + return null; movie.Poster = await NadekoBot.Google.ShortenUrl(movie.Poster); return movie; } From c7c555d010b393c16916d89a8d4af086c98fba11 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 22:38:22 +0100 Subject: [PATCH 09/85] ~img and ~rimg reworked --- src/NadekoBot/Modules/Searches/Searches.cs | 110 ++++++++++-------- .../Resources/CommandStrings.Designer.cs | 108 ++++++++--------- src/NadekoBot/Resources/CommandStrings.resx | 22 ++-- src/NadekoBot/Services/CommandHandler.cs | 5 +- 4 files changed, 129 insertions(+), 116 deletions(-) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index b919335c..643ea0e0 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -109,61 +109,75 @@ namespace NadekoBot.Modules.Searches } [NadekoCommand, Usage, Description, Aliases] - public async Task I([Remainder] string query = null) + public async Task Image([Remainder] string terms = null) { - if (string.IsNullOrWhiteSpace(query)) + terms = terms?.Trim(); + if (string.IsNullOrWhiteSpace(terms)) return; - try - { - using (var http = new HttpClient()) - { - var reqString = $"https://www.googleapis.com/customsearch/v1?q={Uri.EscapeDataString(query)}&cx=018084019232060951019%3Ahs5piey28-e&num=1&searchType=image&fields=items%2Flink&key={NadekoBot.Credentials.GoogleApiKey}"; - var obj = JObject.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false)); - await Context.Channel.SendMessageAsync(obj["items"][0]["link"].ToString()).ConfigureAwait(false); - } - } - catch (HttpRequestException exception) - { - if (exception.Message.Contains("403 (Forbidden)")) - { - await Context.Channel.SendErrorAsync("Daily limit reached!"); - } - else - { - await Context.Channel.SendErrorAsync("Something went wrong."); - _log.Error(exception); - } - } + + terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); + + var fullQueryLink = $"http://imgur.com/search?q={ terms }"; + var config = Configuration.Default.WithDefaultLoader(); + var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink); + + var elems = document.QuerySelectorAll("a.image-list-link"); + + if (!elems.Any()) + return; + + var img = (elems.FirstOrDefault()?.Children?.FirstOrDefault() as IHtmlImageElement); + + if (img?.Source == null) + return; + + var source = img.Source.Replace("b.", "."); + + var embed = new EmbedBuilder() + .WithOkColor() + .WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50)) + .WithUrl(fullQueryLink) + .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) + .WithDescription(source) + .WithImageUrl(source) + .WithTitle(Context.User.Mention); + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - public async Task Ir([Remainder] string query = null) + public async Task RandomImage([Remainder] string terms = null) { - if (string.IsNullOrWhiteSpace(query)) + terms = terms?.Trim(); + if (string.IsNullOrWhiteSpace(terms)) return; - try - { - using (var http = new HttpClient()) - { - var rng = new NadekoRandom(); - var reqString = $"https://www.googleapis.com/customsearch/v1?q={Uri.EscapeDataString(query)}&cx=018084019232060951019%3Ahs5piey28-e&num=1&searchType=image&start={ rng.Next(1, 50) }&fields=items%2Flink&key={NadekoBot.Credentials.GoogleApiKey}"; - var obj = JObject.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false)); - var items = obj["items"] as JArray; - await Context.Channel.SendMessageAsync(items[0]["link"].ToString()).ConfigureAwait(false); - } - } - catch (HttpRequestException exception) - { - if (exception.Message.Contains("403 (Forbidden)")) - { - await Context.Channel.SendErrorAsync("Daily limit reached!"); - } - else - { - await Context.Channel.SendErrorAsync("Something went wrong."); - _log.Error(exception); - } - } + + terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); + + var fullQueryLink = $"http://imgur.com/search?q={ terms }"; + var config = Configuration.Default.WithDefaultLoader(); + var document = await BrowsingContext.New(config).OpenAsync(fullQueryLink); + + var elems = document.QuerySelectorAll("a.image-list-link").ToList(); + + if (!elems.Any()) + return; + + var img = (elems.ElementAtOrDefault(new NadekoRandom().Next(0, elems.Count))?.Children?.FirstOrDefault() as IHtmlImageElement); + + if (img?.Source == null) + return; + + var source = img.Source.Replace("b.", "."); + + var embed = new EmbedBuilder() + .WithOkColor() + .WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50)) + .WithUrl(fullQueryLink) + .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) + .WithDescription(source) + .WithImageUrl(source) + .WithTitle(Context.User.Mention); + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 70259971..431ca335 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -3245,33 +3245,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to img i. - /// - public static string i_cmd { - get { - return ResourceManager.GetString("i_cmd", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pulls the first image found using a search parameter. Use {0}ir for different results.. - /// - public static string i_desc { - get { - return ResourceManager.GetString("i_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `{0}i cute kitten`. - /// - public static string i_usage { - get { - return ResourceManager.GetString("i_usage", resourceCulture); - } - } - /// /// Looks up a localized string similar to iam. /// @@ -3326,6 +3299,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to image img. + /// + public static string image_cmd { + get { + return ResourceManager.GetString("image_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pulls the first image found using a search parameter. Use {0}imgr for different results.. + /// + public static string image_desc { + get { + return ResourceManager.GetString("image_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}img cute kitten`. + /// + public static string image_usage { + get { + return ResourceManager.GetString("image_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to imdb omdb. /// @@ -3380,33 +3380,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to ir. - /// - public static string ir_cmd { - get { - return ResourceManager.GetString("ir_cmd", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Pulls a random image using a search parameter.. - /// - public static string ir_desc { - get { - return ResourceManager.GetString("ir_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `{0}ir cute kitten`. - /// - public static string ir_usage { - get { - return ResourceManager.GetString("ir_usage", resourceCulture); - } - } - /// /// Looks up a localized string similar to jcsc. /// @@ -5405,6 +5378,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to randomimage rimg. + /// + public static string randomimage_cmd { + get { + return ResourceManager.GetString("randomimage_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Pulls a random image using a search parameter.. + /// + public static string randomimage_desc { + get { + return ResourceManager.GetString("randomimage_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}rimg cute kitten`. + /// + public static string randomimage_usage { + get { + return ResourceManager.GetString("randomimage_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to remind. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index fe484754..104128cb 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1926,23 +1926,23 @@ `{0}woof` - - img i + + image img - - Pulls the first image found using a search parameter. Use {0}ir for different results. + + Pulls the first image found using a search parameter. Use {0}imgr for different results. - - `{0}i cute kitten` + + `{0}img cute kitten` - - ir + + randomimage rimg - + Pulls a random image using a search parameter. - - `{0}ir cute kitten` + + `{0}rimg cute kitten` lmgtfy diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 9c899d8c..d99930b5 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -66,13 +66,12 @@ namespace NadekoBot.Services { try { - var usrMsg = msg as SocketUserMessage; if (usrMsg == null) return; - if (!usrMsg.IsAuthor()) - UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); + //if (!usrMsg.IsAuthor()) + // UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); if (msg.Author.IsBot || !NadekoBot.Ready) //no bots return; From 703dc7b3b2e0fcc79f2d5dc2783a5eb5652a5083 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 3 Jan 2017 22:49:39 +0100 Subject: [PATCH 10/85] More attempts to stabilize --- src/NadekoBot/ShardedDiscordClient.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 8a341197..2c6037b0 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -65,6 +65,13 @@ namespace NadekoBot client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); + + client.Disconnected += (ex) => + { + _log.Error("Shard #{0} disconnected", i); + _log.Error(ex); + return Task.CompletedTask; + }; } Clients = clientList.AsReadOnly(); @@ -85,8 +92,14 @@ namespace NadekoBot public Task GetDMChannelAsync(ulong channelId) => Clients[0].GetDMChannelAsync(channelId); - internal Task LoginAsync(TokenType tokenType, string token) => - Task.WhenAll(Clients.Select(async c => { await c.LoginAsync(tokenType, token).ConfigureAwait(false); _log.Info($"Shard #{c.ShardId} logged in."); })); + internal async Task LoginAsync(TokenType tokenType, string token) + { + foreach (var c in Clients) + { + await c.LoginAsync(tokenType, token).ConfigureAwait(false); + _log.Info($"Shard #{c.ShardId} logged in."); + } + } internal async Task ConnectAsync() { From 8ea62fd8075f364330e0b1a7ef012e0f2c6f5244 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 00:38:11 +0100 Subject: [PATCH 11/85] Better logging reconnect on disconnect? --- src/NadekoBot/ShardedDiscordClient.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 2c6037b0..b84cb63c 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -66,11 +66,18 @@ namespace NadekoBot _log.Info($"Shard #{i} initialized."); - client.Disconnected += (ex) => + var j = i; + client.Disconnected += async (ex) => { - _log.Error("Shard #{0} disconnected", i); - _log.Error(ex); - return Task.CompletedTask; + try + { + _log.Error("Shard #{0} disconnected", j); + _log.Error(ex, ex?.Message ?? "No error"); + + try { await client.DisconnectAsync().ConfigureAwait(false); } catch { } + await client.ConnectAsync().ConfigureAwait(false); + } + catch { } }; } From d2f4cbeec198688e364827adb8e1c7d6e95a6833 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 01:16:56 +0100 Subject: [PATCH 12/85] Fix --- src/NadekoBot/ShardedDiscordClient.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index b84cb63c..aee2af7b 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -67,17 +67,11 @@ namespace NadekoBot _log.Info($"Shard #{i} initialized."); var j = i; - client.Disconnected += async (ex) => + client.Disconnected += (ex) => { - try - { - _log.Error("Shard #{0} disconnected", j); - _log.Error(ex, ex?.Message ?? "No error"); - - try { await client.DisconnectAsync().ConfigureAwait(false); } catch { } - await client.ConnectAsync().ConfigureAwait(false); - } - catch { } + _log.Error("Shard #{0} disconnected", j); + _log.Error(ex, ex?.Message ?? "No error"); + return Task.CompletedTask; }; } From f7300267440b7f9cc469cff7bc6b7a5ca405a885 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 13:23:30 +0100 Subject: [PATCH 13/85] !!d fixed --- src/NadekoBot/Modules/Music/Music.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 7ecef7c5..8b175557 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -101,14 +101,14 @@ namespace NadekoBot.Modules.Music [RequireContext(ContextType.Guild)] public async Task Destroy() { - await Context.Channel.SendErrorAsync("This command is temporarily disabled.").ConfigureAwait(false); + //await Context.Channel.SendErrorAsync("This command is temporarily disabled.").ConfigureAwait(false); - /*MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(channel.Guild.Id, out musicPlayer)) return Task.CompletedTask; - if (((IGuildUser)umsg.Author).VoiceChannel == musicPlayer.PlaybackVoiceChannel) - if(MusicPlayers.TryRemove(channel.Guild.Id, out musicPlayer)) + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return; + if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel) + if (MusicPlayers.TryRemove(Context.Guild.Id, out musicPlayer)) musicPlayer.Destroy(); - return Task.CompletedTask;*/ + } [NadekoCommand, Usage, Description, Aliases] From 3a2ce9d3520a8af25b0003e430798528e6713ef8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 13:35:54 +0100 Subject: [PATCH 14/85] .b fix, no logner waits 2 secs if no reason is specified --- .../Modules/Administration/Administration.cs | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 91c952a6..f2d6956d 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Administration NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; DeleteMessagesOnCommand = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); - + } private static async Task DelMsgOnCmd_Handler(SocketUserMessage msg, CommandInfo cmd) @@ -202,7 +202,7 @@ namespace NadekoBot.Modules.Administration return; } var roleName = args[0].ToUpperInvariant(); - var role = Context.Guild.Roles.Where(r=>r.Name.ToUpperInvariant() == roleName).FirstOrDefault(); + var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault(); if (role == null) { @@ -217,7 +217,7 @@ namespace NadekoBot.Modules.Administration var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16)); var green = Convert.ToByte(rgb ? int.Parse(args[2]) : Convert.ToInt32(arg1.Substring(2, 2), 16)); var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16)); - + await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false); await Context.Channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false); } @@ -236,18 +236,22 @@ namespace NadekoBot.Modules.Administration { msg = "❗️No reason provided."; } - if (Context.User.Id != user.Guild.OwnerId && ((IGuildUser)Context.User).GetRoles().Select(r=>r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()) + if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())) { await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.").ConfigureAwait(false); return; } - try + if (!string.IsNullOrWhiteSpace(msg)) { - await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{Context.Guild.Name}` server.**\n" + - $"⚖ *Reason:* {msg}").ConfigureAwait(false); - await Task.Delay(2000).ConfigureAwait(false); + try + { + await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{Context.Guild.Name}` server.**\n" + + $"⚖ *Reason:* {msg}").ConfigureAwait(false); + await Task.Delay(2000).ConfigureAwait(false); + + } + catch { } } - catch { } try { await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); @@ -275,13 +279,18 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); return; } - try + + if (!string.IsNullOrWhiteSpace(msg)) { - await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{Context.Guild.Name}` server.**\n" + - $"⚖ *Reason:* {msg}").ConfigureAwait(false); - await Task.Delay(2000).ConfigureAwait(false); + try + { + await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{Context.Guild.Name}` server.**\n" + + $"⚖ *Reason:* {msg}").ConfigureAwait(false); + await Task.Delay(2000).ConfigureAwait(false); + } + catch { } } - catch { } + try { await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); @@ -439,7 +448,7 @@ namespace NadekoBot.Modules.Administration public async Task Prune() { var user = await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false); - + var enumerable = (await Context.Channel.GetMessagesAsync().Flatten()).AsEnumerable(); enumerable = enumerable.Where(x => x.Author.Id == user.Id); await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); @@ -511,7 +520,7 @@ title, title).ConfigureAwait(false); foreach (var role in roles) { send += $"\n**{role.Name}**\n"; - send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Distinct().Select(u=>u.Mention)); + send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Distinct().Select(u => u.Mention)); } while (send.Length > 2000) @@ -536,7 +545,7 @@ title, title).ConfigureAwait(false); donatorsOrdered = uow.Donators.GetDonatorsOrdered(); } await Context.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); - + nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); if (nadekoSupportServer == null) From 26cc51ebde4901bd4aaa6be99634c5f1a1f2b791 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 13:37:34 +0100 Subject: [PATCH 15/85] Fixed ~img description, thx doge --- src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 431ca335..368dc408 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -3309,7 +3309,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Pulls the first image found using a search parameter. Use {0}imgr for different results.. + /// Looks up a localized string similar to Pulls the first image found using a search parameter. Use {0}rimg for different results.. /// public static string image_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 104128cb..3eb5f456 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1930,7 +1930,7 @@ image img - Pulls the first image found using a search parameter. Use {0}imgr for different results. + Pulls the first image found using a search parameter. Use {0}rimg for different results. `{0}img cute kitten` From 9e2482ad04ff8eae6785a36a87f82d7aaf7604cc Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 13:38:53 +0100 Subject: [PATCH 16/85] Updated commandlist --- docs/Commands List.md | 646 ++++++++++++++++++++++++++++++------------ 1 file changed, 470 insertions(+), 176 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index 68e59981..1fbbf94b 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -2,24 +2,73 @@ You can support the project on patreon: or paypa ##Table Of Contents - [Help](#help) +- [Acropobia](#acropobia) - [Administration](#administration) +- [AnimalRacing](#animalracing) +- [AnimeSearchCommands](#animesearchcommands) +- [AntiRaidCommands](#antiraidcommands) +- [AutoAssignRoleCommands](#autoassignrolecommands) +- [BlacklistCommands](#blacklistcommands) +- [CalcCommands](#calccommands) - [ClashOfClans](#clashofclans) +- [CleverBotCommands](#cleverbotcommands) +- [CmdCdsCommands](#cmdcdscommands) +- [CrossServerTextChannel](#crossservertextchannel) - [CustomReactions](#customreactions) +- [DMForwardCommands](#dmforwardcommands) +- [DrawCommands](#drawcommands) +- [DriceRollCommands](#dricerollcommands) +- [FilterCommands](#filtercommands) +- [FlipCoinCommands](#flipcoincommands) - [Gambling](#gambling) - [Games](#games) +- [HangmanCommands](#hangmancommands) +- [InfoCommands](#infocommands) +- [JokeCommands](#jokecommands) +- [LogCommands](#logcommands) +- [Migration](#migration) - [Music](#music) +- [MuteCommands](#mutecommands) - [NSFW](#nsfw) +- [OsuCommands](#osucommands) +- [OverwatchCommands](#overwatchcommands) - [Permissions](#permissions) +- [PlaceCommands](#placecommands) +- [PlantPickCommands](#plantpickcommands) +- [PlayingRotateCommands](#playingrotatecommands) - [Pokemon](#pokemon) +- [PokemonSearchCommands](#pokemonsearchcommands) +- [PollCommands](#pollcommands) +- [QuoteCommands](#quotecommands) +- [RatelimitCommand](#ratelimitcommand) +- [RemindCommands](#remindcommands) +- [RepeatCommands](#repeatcommands) - [Searches](#searches) +- [SelfAssignedRolesCommands](#selfassignedrolescommands) +- [SelfCommands](#selfcommands) +- [ServerGreetCommands](#servergreetcommands) +- [SpeedTypingCommands](#speedtypingcommands) +- [StreamNotificationCommands](#streamnotificationcommands) +- [TranslateCommands](#translatecommands) +- [TriviaCommands](#triviacommands) +- [UnitConverterCommands](#unitconvertercommands) - [Utility](#utility) +- [VoicePlusTextCommands](#voiceplustextcommands) +- [XkcdCommands](#xkcdcommands) +### Acropobia +Command and aliases | Description | Usage +----------------|--------------|------- +`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` + +###### [Back to TOC](#table-of-contents) + ### Administration Command and aliases | Description | Usage ----------------|--------------|------- -`.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms` -`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd` +`.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms` +`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd` `.setrole` `.sr` | Sets a role for a given user. **Requires ManageRoles server permission.** | `.sr @User Guest` `.removerole` `.rr` | Removes a role from a given user. **Requires ManageRoles server permission.** | `.rr @User Admin` `.renamerole` `.renr` | Renames a role. Roles you are renaming must be lower than bot's highest role. **Requires ManageRoles server permission.** | `.renr "First role" SecondRole` @@ -38,62 +87,58 @@ Command and aliases | Description | Usage `.settopic` `.st` | Sets a topic on the current channel. **Requires ManageChannels server permission.** | `.st My new topic` `.setchanlname` `.schn` | Changes the name of the current channel. **Requires ManageChannels server permission.** | `.schn NewName` `.prune` `.clr` | `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X` -`.die` | Shuts the bot down. **Bot Owner only.** | `.die` -`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` -`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` -`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` -`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` -`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message` -`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam` -`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner only.** | `.savechat 150` +`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner only.** | `.savechat 150` `.mentionrole` `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. **Requires MentionEveryone server permission.** | `.menro RoleName` -`.donators` | List of lovely people who donated to keep this project alive. | `.donators` -`.donadd` | Add a donator to the database. **Bot Owner only.** | `.donadd Donate Amount` -`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick` -`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban` +`.donators` | List of lovely people who donated to keep this project alive. | `.donators` +`.donadd` | Add a donator to the database. **Bot Owner only.** | `.donadd Donate Amount` + +###### [Back to TOC](#table-of-contents) + +### AnimalRacing +Command and aliases | Description | Usage +----------------|--------------|------- +`$race` | Starts a new animal race. | `$race` +`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` + +###### [Back to TOC](#table-of-contents) + +### AnimeSearchCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol` +`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin` + +###### [Back to TOC](#table-of-contents) + +### AntiRaidCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick` +`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban` + +###### [Back to TOC](#table-of-contents) + +### AutoAssignRoleCommands +Command and aliases | Description | Usage +----------------|--------------|------- `.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable -`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc` -`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere` -`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` -`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` -`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` -`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` -`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` -`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` -`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned` -`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` -`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` -`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` -`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced` -`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone` -`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone` -`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone` -`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone` -`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` -`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` -`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl` -`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl` -`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl` -`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl` -`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode` -`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm` -`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer` -`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar` -`.lsar` | Lists all self-assignable roles. | `.lsar` -`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar` -`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer` -`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer` -`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` -`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30` -`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet` -`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.` -`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm` -`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`. -`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye` -`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` -`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30` -`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text` -`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t` + +###### [Back to TOC](#table-of-contents) + +### BlacklistCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` +`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312` +`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer` + +###### [Back to TOC](#table-of-contents) + +### CalcCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` +`.calcops` | Shows all available operations in .calc command | `.calcops` ###### [Back to TOC](#table-of-contents) @@ -112,6 +157,30 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) +### CleverBotCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages channel permission.** | `>cleverbot` + +###### [Back to TOC](#table-of-contents) + +### CmdCdsCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` +`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` + +###### [Back to TOC](#table-of-contents) + +### CrossServerTextChannel +Command and aliases | Description | Usage +----------------|--------------|------- +`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc` +`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere` +`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` + +###### [Back to TOC](#table-of-contents) + ### CustomReactions Command and aliases | Description | Usage ----------------|--------------|------- @@ -120,59 +189,85 @@ Command and aliases | Description | Usage `.listcustreactg` `.lcrg` | Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. | `.lcrg 1` `.showcustreact` `.scr` | Shows a custom reaction's response on a given ID. | `.scr 1` `.delcustreact` `.dcr` | Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. | `.dcr 5` -`.crstatsclear` | Resets the counters on `.crstats`. You can specify a trigger to clear stats only for that trigger. | `.crstatsclear` or `.crstatsclear rng` -`.crstats` | Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `.crstatsclear` to reset the counters. | `.crstats` or `.crstats 3` +`.crstatsclear` | Resets the counters on `.crstats`. You can specify a trigger to clear stats only for that trigger. **Bot Owner only.** | `.crstatsclear` or `.crstatsclear rng` +`.crstats` | Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `.crstatsclear` to reset the counters. | `.crstats` or `.crstats 3` + +###### [Back to TOC](#table-of-contents) + +### DMForwardCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` +`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` + +###### [Back to TOC](#table-of-contents) + +### DrawCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` +`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh` + +###### [Back to TOC](#table-of-contents) + +### DriceRollCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` +`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` +`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` + +###### [Back to TOC](#table-of-contents) + +### FilterCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi` +`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi` +`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw` +`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` +`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` +`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` + +###### [Back to TOC](#table-of-contents) + +### FlipCoinCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` +`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t` ###### [Back to TOC](#table-of-contents) ### Gambling Command and aliases | Description | Usage ----------------|--------------|------- -`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName` +`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName` `$cash` `$$$` | Check how much currency a person has. (Defaults to yourself) | `$$$` or `$$$ @SomeGuy` -`$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"` -`$award` | Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. **Bot Owner only.** | `$award 100 @person` or `$award 5 Role Of Gamblers` -`$take` | Takes a certain amount of currency from someone. **Bot Owner only.** | `$take 1 "@someguy"` +`$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"` +`$award` | Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. **Bot Owner only.** | `$award 100 @person` or `$award 5 Role Of Gamblers` +`$take` | Takes a certain amount of currency from someone. **Bot Owner only.** | `$take 1 "@someguy"` `$betroll` `$br` | Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10. | `$br 5` `$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb` -`$race` | Starts a new animal race. | `$race` -`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` -`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` -`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` -`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` -`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` -`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh` -`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` -`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t` ###### [Back to TOC](#table-of-contents) ### Games Command and aliases | Description | Usage ----------------|--------------|------- -`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more` -`>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something` -`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors` -`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows` -`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `>leet 3 Hello` -`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` -`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` -`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` -`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` -`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot` -`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` -`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` -`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` -`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` -`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` -`>typestart` | Starts a typing contest. | `>typestart` -`>typestop` | Stops a typing contest on the current channel. | `>typestop` -`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords` -`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3` -`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` -`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint` -`>tl` | Shows a current trivia leaderboard. | `>tl` -`>tq` | Quits current trivia after current question. | `>tq` +`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more` +`>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something` +`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors` +`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows` +`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `>leet 3 Hello` + +###### [Back to TOC](#table-of-contents) + +### HangmanCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` +`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` ###### [Back to TOC](#table-of-contents) @@ -182,9 +277,46 @@ Command and aliases | Description | Usage `-modules` `-mdls` | Lists all bot modules. | `-modules` `-commands` `-cmds` | List all of the bot's commands from a certain module. You can either specify full, or only first few letters of the module name. | `-commands Administration` or `-cmds Admin` `-help` `-h` | Either shows a help for a single command, or DMs you help link if no arguments are specified. | `-h !!q` or `-h` -`-hgit` | Generates the commandlist.md file. **Bot Owner only.** | `-hgit` +`-hgit` | Generates the commandlist.md file. **Bot Owner only.** | `-hgit` `-readme` `-guide` | Sends a readme and a guide links to the channel. | `-readme` or `-guide` -`-donate` | Instructions for helping the project financially. | `-donate` +`-donate` | Instructions for helping the project financially. | `-donate` + +###### [Back to TOC](#table-of-contents) + +### InfoCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server` +`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel` +`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser` + +###### [Back to TOC](#table-of-contents) + +### JokeCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~yomama` `~ym` | Shows a random joke from | `~ym` +`~randjoke` `~rj` | Shows a random joke from | `~rj` +`~chucknorris` `~cn` | Shows a random chucknorris joke from | `~cn` +`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke` +`~magicitem` `~mi` | Shows a random magicitem from | `~mi` + +###### [Back to TOC](#table-of-contents) + +### LogCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` +`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` +`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` +`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned` + +###### [Back to TOC](#table-of-contents) + +### Migration +Command and aliases | Description | Usage +----------------|--------------|------- +`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` ###### [Back to TOC](#table-of-contents) @@ -215,33 +347,62 @@ Command and aliases | Description | Usage `!!setmaxplaytime` `!!smp` | Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. | `!!smp 0` or `!!smp 270` `!!reptcursong` `!!rcs` | Toggles repeat of current song. | `!!rcs` `!!rpeatplaylst` `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!rpl` -`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1` -`!!load` | Loads a saved playlist using it's ID. Use `!!pls` to list all saved playlists and !!save to save new ones. | `!!load 5` +`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1` +`!!load` | Loads a saved playlist using it's ID. Use `!!pls` to list all saved playlists and !!save to save new ones. | `!!load 5` `!!playlists` `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1` `!!deleteplaylist` `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5` -`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30` +`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30` `!!autoplay` `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `!!ap` ###### [Back to TOC](#table-of-contents) +### MuteCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced` +`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone` +`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone` +`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone` +`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone` +`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` +`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` + +###### [Back to TOC](#table-of-contents) + ### NSFW Command and aliases | Description | Usage ----------------|--------------|------- -`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri` -`~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. | `~autohentai 30 yuri|tail|long_hair` or `~autohentai` -`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri` -`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing` -`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2` -`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri` -`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing` -`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing` -`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. | `~e621 yuri kissing` -`~cp` | We all know where this will lead you to. | `~cp` -`~boobs` | Real adult content. | `~boobs` +`~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri` +`~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. | `~autohentai 30 yuri|tail|long_hair` or `~autohentai` +`~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri` +`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing` +`~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2` +`~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri` +`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing` +`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing` +`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. | `~e621 yuri kissing` +`~cp` | We all know where this will lead you to. | `~cp` +`~boobs` | Real adult content. | `~boobs` `~butts` `~ass` `~butt` | Real adult content. | `~butts` or `~ass` ###### [Back to TOC](#table-of-contents) +### OsuCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` +`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712` +`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` + +###### [Back to TOC](#table-of-contents) + +### OverwatchCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016` + +###### [Back to TOC](#table-of-contents) + ### Permissions Command and aliases | Description | Usage ----------------|--------------|------- @@ -262,28 +423,95 @@ Command and aliases | Description | Usage `;allrolemdls` `;arm` | Enable or disable all modules for a specific role. | `;arm [enable/disable] MyRole` `;allusrmdls` `;aum` | Enable or disable all modules for a specific user. | `;aum enable @someone` `;allsrvrmdls` `;asm` | Enable or disable all modules for your server. | `;asm [enable/disable]` -`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` -`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312` -`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer` -`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` -`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` -`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi` -`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi` -`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw` -`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` -`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` -`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` + +###### [Back to TOC](#table-of-contents) + +### PlaceCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist` +`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400` + +###### [Back to TOC](#table-of-contents) + +### PlantPickCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` +`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` +`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` + +###### [Back to TOC](#table-of-contents) + +### PlayingRotateCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl` +`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl` +`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl` +`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl` ###### [Back to TOC](#table-of-contents) ### Pokemon Command and aliases | Description | Usage ----------------|--------------|------- -`>attack` | Attacks a target with the given move. Use `>movelist` to see a list of moves your type can use. | `>attack "vine whip" @someguy` +`>attack` | Attacks a target with the given move. Use `>movelist` to see a list of moves your type can use. | `>attack "vine whip" @someguy` `>movelist` `>ml` | Lists the moves you are able to use | `>ml` -`>heal` | Heals someone. Revives those who fainted. Costs a NadekoFlower | `>heal @someone` -`>type` | Get the poketype of the target. | `>type @someone` -`>settype` | Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. | `>settype fire` or `>settype` +`>heal` | Heals someone. Revives those who fainted. Costs a NadekoFlower | `>heal @someone` +`>type` | Get the poketype of the target. | `>type @someone` +`>settype` | Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. | `>settype fire` or `>settype` + +###### [Back to TOC](#table-of-contents) + +### PokemonSearchCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon` +`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow` + +###### [Back to TOC](#table-of-contents) + +### PollCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` +`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` +`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` + +###### [Back to TOC](#table-of-contents) + +### QuoteCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`...` | Shows a random quote with a specified name. | `... abc` +`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` +`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` +`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` + +###### [Back to TOC](#table-of-contents) + +### RatelimitCommand +Command and aliases | Description | Usage +----------------|--------------|------- +`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode` + +###### [Back to TOC](#table-of-contents) + +### RemindCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` +`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!` + +###### [Back to TOC](#table-of-contents) + +### RepeatCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` +`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` ###### [Back to TOC](#table-of-contents) @@ -295,57 +523,122 @@ Command and aliases | Description | Usage `~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman` `~randomcat` `~meow` | Shows a random cat image. | `~meow` `~randomdog` `~woof` | Shows a random dog image. | `~woof` -`~img` `~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | `~i cute kitten` -`~ir` | Pulls a random image using a search parameter. | `~ir cute kitten` -`~lmgtfy` | Google something for an idiot. | `~lmgtfy query` -`~shorten` | Attempts to shorten an URL, if it fails, returns the input URL. | `~shorten https://google.com` +`~image` `~img` | Pulls the first image found using a search parameter. Use ~rimg for different results. | `~img cute kitten` +`~randomimage` `~rimg` | Pulls a random image using a search parameter. | `~rimg cute kitten` +`~lmgtfy` | Google something for an idiot. | `~lmgtfy query` +`~shorten` | Attempts to shorten an URL, if it fails, returns the input URL. | `~shorten https://google.com` `~google` `~g` | Get a google search link for some terms. | `~google query` `~magicthegathering` `~mtg` | Searches for a Magic The Gathering card. | `~magicthegathering about face` or `~mtg about face` `~hearthstone` `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera` `~yodify` `~yoda` | Translates your normal sentences into Yoda styled sentences! | ~yodify I was once an adventurer like you` or `~yoda my feelings hurt` `~urbandict` `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple` `~define` `~def` | Finds a definition of a word. | `~def heresy` -`~#` | Searches Tagdef.com for a hashtag. | `~# ff` -`~catfact` | Shows a random catfact from | `~catfact` -`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"` -`~revimg` | Returns a google reverse image search for an image from a link. | `~revimg Image link` -`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~safebooru yuri+kissing` +`~#` | Searches Tagdef.com for a hashtag. | `~# ff` +`~catfact` | Shows a random catfact from | `~catfact` +`~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"` +`~revimg` | Returns a google reverse image search for an image from a link. | `~revimg Image link` +`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~safebooru yuri+kissing` `~wikipedia` `~wiki` | Gives you back a wikipedia link | `~wiki query` `~color` `~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00` -`~videocall` | Creates a private video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `~videocall "@SomeGuy"` +`~videocall` | Creates a private video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `~videocall "@SomeGuy"` `~avatar` `~av` | Shows a mentioned person's avatar. | `~av "@SomeGuy"` -`~wikia` | Gives you back a wikia link | `~wikia mtg Vigilance` or `~wikia mlp Dashy` +`~wikia` | Gives you back a wikia link | `~wikia mtg Vigilance` or `~wikia mlp Dashy` `~minecraftping` `~mcping` | Pings a minecraft server. | `~mcping 127.0.0.1:25565` `~minecraftquery` `~mcq` | Finds information about a minecraft server. | `~mcq server:ip` -`~lolban` | Shows top banned champions ordered by ban rate. | `~lolban` -`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist` -`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` -`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol` -`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin` -`~yomama` `~ym` | Shows a random joke from | `~ym` -`~randjoke` `~rj` | Shows a random joke from | `~rj` -`~chucknorris` `~cn` | Shows a random chucknorris joke from | `~cn` -`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke` -`~magicitem` `~mi` | Shows a random magicitem from | `~mi` -`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` -`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712` -`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` -`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016` -`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist` -`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400` -`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon` -`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow` +`~lolban` | Shows top banned champions ordered by ban rate. | `~lolban` +`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist` +`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` + +###### [Back to TOC](#table-of-contents) + +### SelfAssignedRolesCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm` +`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer` +`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar` +`.lsar` | Lists all self-assignable roles. | `.lsar` +`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar` +`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer` +`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer` + +###### [Back to TOC](#table-of-contents) + +### SelfCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` +`.die` | Shuts the bot down. **Bot Owner only.** | `.die` +`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` +`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` +`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` +`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` +`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message` +`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam` + +###### [Back to TOC](#table-of-contents) + +### ServerGreetCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30` +`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet` +`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.` +`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm` +`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`. +`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye` +`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` +`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30` + +###### [Back to TOC](#table-of-contents) + +### SpeedTypingCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`>typestart` | Starts a typing contest. | `>typestart` +`>typestop` | Stops a typing contest on the current channel. | `>typestop` +`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords` +`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3` +`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` + +###### [Back to TOC](#table-of-contents) + +### StreamNotificationCommands +Command and aliases | Description | Usage +----------------|--------------|------- `~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer` `~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer` `~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer` `~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls` `~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy` `~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer` + +###### [Back to TOC](#table-of-contents) + +### TranslateCommands +Command and aliases | Description | Usage +----------------|--------------|------- `~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello` `~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del` `~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value. -`~translangs` | Lists the valid languages for translation. | `~translangs` -`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest` +`~translangs` | Lists the valid languages for translation. | `~translangs` + +###### [Back to TOC](#table-of-contents) + +### TriviaCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint` +`>tl` | Shows a current trivia leaderboard. | `>tl` +`>tq` | Quits current trivia after current question. | `>tq` + +###### [Back to TOC](#table-of-contents) + +### UnitConverterCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist` +`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000` ###### [Back to TOC](#table-of-contents) @@ -354,28 +647,29 @@ Command and aliases | Description | Usage ----------------|--------------|------- `.togethertube` `.totube` | Creates a new room on and shows the link in the chat. | `.totube` `.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch` -`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role` -`.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms` +`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role` +`.checkmyperms` | Checks your user-specific permissions on this channel. | `.checkmyperms` `.userid` `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy"` `.channelid` `.cid` | Shows current channel ID. | `.cid` `.serverid` `.sid` | Shows current server ID. | `.sid` -`.roles` | List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. | `.roles 2` or `.roles @Someone` +`.roles` | List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. | `.roles 2` or `.roles @Someone` `.channeltopic` `.ct` | Sends current channel's topic as a message. | `.ct` -`.stats` | Shows some basic stats for Nadeko. | `.stats` +`.stats` | Shows some basic stats for Nadeko. | `.stats` `.showemojis` `.se` | Shows a name and a link to every SPECIAL emoji in the message. | `.se A message full of SPECIAL emojis` -`.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3` -`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` -`.calcops` | Shows all available operations in .calc command | `.calcops` -`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server` -`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel` -`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser` -`.activity` | Checks for spammers. **Bot Owner only.** | `.activity` -`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. -`...` | Shows a random quote with a specified name. | `... abc` -`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` -`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` -`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` -`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` -`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!` -`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist` -`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000` +`.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3` +`.activity` | Checks for spammers. **Bot Owner only.** | `.activity` + +###### [Back to TOC](#table-of-contents) + +### VoicePlusTextCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text` +`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t` + +###### [Back to TOC](#table-of-contents) + +### XkcdCommands +Command and aliases | Description | Usage +----------------|--------------|------- +`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest` From bdfe3f1b362dec636e58aad5cbaac2a52e577133 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 13:47:36 +0100 Subject: [PATCH 17/85] -hgit fixed, updated commandlsit --- docs/Commands List.md | 508 ++++++++--------------------- src/NadekoBot/Modules/Help/Help.cs | 15 +- 2 files changed, 136 insertions(+), 387 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index 1fbbf94b..727d53f3 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -57,16 +57,61 @@ You can support the project on patreon: or paypa - [XkcdCommands](#xkcdcommands) -### Acropobia -Command and aliases | Description | Usage -----------------|--------------|------- -`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` - -###### [Back to TOC](#table-of-contents) - ### Administration Command and aliases | Description | Usage ----------------|--------------|------- +`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text` +`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t` +`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30` +`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet` +`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.` +`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm` +`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`. +`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye` +`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` +`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30` +`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` +`.die` | Shuts the bot down. **Bot Owner only.** | `.die` +`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` +`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` +`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` +`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` +`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message` +`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam` +`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm` +`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer` +`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar` +`.lsar` | Lists all self-assignable roles. | `.lsar` +`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar` +`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer` +`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer` +`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode` +`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl` +`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl` +`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl` +`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl` +`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced` +`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone` +`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone` +`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone` +`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone` +`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` +`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` +`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` +`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` +`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` +`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` +`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` +`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` +`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned` +`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` +`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` +`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc` +`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere` +`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` +`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable +`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick` +`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban` `.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms` `.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd` `.setrole` `.sr` | Sets a role for a given user. **Requires ManageRoles server permission.** | `.sr @User Guest` @@ -94,54 +139,6 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### AnimalRacing -Command and aliases | Description | Usage -----------------|--------------|------- -`$race` | Starts a new animal race. | `$race` -`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` - -###### [Back to TOC](#table-of-contents) - -### AnimeSearchCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol` -`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin` - -###### [Back to TOC](#table-of-contents) - -### AntiRaidCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick` -`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban` - -###### [Back to TOC](#table-of-contents) - -### AutoAssignRoleCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable - -###### [Back to TOC](#table-of-contents) - -### BlacklistCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` -`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312` -`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer` - -###### [Back to TOC](#table-of-contents) - -### CalcCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` -`.calcops` | Shows all available operations in .calc command | `.calcops` - -###### [Back to TOC](#table-of-contents) - ### ClashOfClans Command and aliases | Description | Usage ----------------|--------------|------- @@ -157,30 +154,6 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### CleverBotCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages channel permission.** | `>cleverbot` - -###### [Back to TOC](#table-of-contents) - -### CmdCdsCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` -`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` - -###### [Back to TOC](#table-of-contents) - -### CrossServerTextChannel -Command and aliases | Description | Usage -----------------|--------------|------- -`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc` -`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere` -`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` - -###### [Back to TOC](#table-of-contents) - ### CustomReactions Command and aliases | Description | Usage ----------------|--------------|------- @@ -194,54 +167,18 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### DMForwardCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` -`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` - -###### [Back to TOC](#table-of-contents) - -### DrawCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` -`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh` - -###### [Back to TOC](#table-of-contents) - -### DriceRollCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` -`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` -`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` - -###### [Back to TOC](#table-of-contents) - -### FilterCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi` -`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi` -`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw` -`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` -`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` -`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` - -###### [Back to TOC](#table-of-contents) - -### FlipCoinCommands +### Gambling Command and aliases | Description | Usage ----------------|--------------|------- `$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` `$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t` - -###### [Back to TOC](#table-of-contents) - -### Gambling -Command and aliases | Description | Usage -----------------|--------------|------- +`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` +`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh` +`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` +`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` +`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` +`$race` | Starts a new animal race. | `$race` +`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` `$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName` `$cash` `$$$` | Check how much currency a person has. (Defaults to yourself) | `$$$` or `$$$ @SomeGuy` `$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"` @@ -255,6 +192,24 @@ Command and aliases | Description | Usage ### Games Command and aliases | Description | Usage ----------------|--------------|------- +`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint` +`>tl` | Shows a current trivia leaderboard. | `>tl` +`>tq` | Quits current trivia after current question. | `>tq` +`>typestart` | Starts a typing contest. | `>typestart` +`>typestop` | Stops a typing contest on the current channel. | `>typestop` +`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords` +`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3` +`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` +`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` +`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` +`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` +`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` +`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` +`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` +`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` +`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` +`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages channel permission.** | `>cleverbot` +`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` `>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more` `>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something` `>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors` @@ -263,14 +218,6 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### HangmanCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` -`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` - -###### [Back to TOC](#table-of-contents) - ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -283,43 +230,6 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### InfoCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server` -`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel` -`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser` - -###### [Back to TOC](#table-of-contents) - -### JokeCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~yomama` `~ym` | Shows a random joke from | `~ym` -`~randjoke` `~rj` | Shows a random joke from | `~rj` -`~chucknorris` `~cn` | Shows a random chucknorris joke from | `~cn` -`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke` -`~magicitem` `~mi` | Shows a random magicitem from | `~mi` - -###### [Back to TOC](#table-of-contents) - -### LogCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` -`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` -`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` -`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned` - -###### [Back to TOC](#table-of-contents) - -### Migration -Command and aliases | Description | Usage -----------------|--------------|------- -`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` - -###### [Back to TOC](#table-of-contents) - ### Music Command and aliases | Description | Usage ----------------|--------------|------- @@ -356,19 +266,6 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### MuteCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced` -`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone` -`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone` -`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone` -`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone` -`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` -`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` - -###### [Back to TOC](#table-of-contents) - ### NSFW Command and aliases | Description | Usage ----------------|--------------|------- @@ -387,25 +284,20 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### OsuCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` -`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712` -`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` - -###### [Back to TOC](#table-of-contents) - -### OverwatchCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016` - -###### [Back to TOC](#table-of-contents) - ### Permissions Command and aliases | Description | Usage ----------------|--------------|------- +`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi` +`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi` +`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw` +`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` +`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` +`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` +`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` +`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` +`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` +`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312` +`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer` `;verbose` `;v` | Sets whether to show when a command/module is blocked. | `;verbose true` `;permrole` `;pr` | Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `;pr role` `;listperms` `;lp` | Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. | `;lp` or `;lp 3` @@ -426,33 +318,6 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### PlaceCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist` -`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400` - -###### [Back to TOC](#table-of-contents) - -### PlantPickCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` -`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` -`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` - -###### [Back to TOC](#table-of-contents) - -### PlayingRotateCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl` -`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl` -`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl` -`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl` - -###### [Back to TOC](#table-of-contents) - ### Pokemon Command and aliases | Description | Usage ----------------|--------------|------- @@ -464,60 +329,35 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### PokemonSearchCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon` -`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow` - -###### [Back to TOC](#table-of-contents) - -### PollCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` -`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` -`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` - -###### [Back to TOC](#table-of-contents) - -### QuoteCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. -`...` | Shows a random quote with a specified name. | `... abc` -`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` -`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` -`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` - -###### [Back to TOC](#table-of-contents) - -### RatelimitCommand -Command and aliases | Description | Usage -----------------|--------------|------- -`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode` - -###### [Back to TOC](#table-of-contents) - -### RemindCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` -`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!` - -###### [Back to TOC](#table-of-contents) - -### RepeatCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` -`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` - -###### [Back to TOC](#table-of-contents) - ### Searches Command and aliases | Description | Usage ----------------|--------------|------- +`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest` +`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello` +`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del` +`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value. +`~translangs` | Lists the valid languages for translation. | `~translangs` +`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer` +`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer` +`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer` +`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls` +`~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy` +`~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer` +`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon` +`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow` +`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist` +`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400` +`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016` +`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` +`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712` +`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` +`~yomama` `~ym` | Shows a random joke from | `~ym` +`~randjoke` `~rj` | Shows a random joke from | `~rj` +`~chucknorris` `~cn` | Shows a random chucknorris joke from | `~cn` +`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke` +`~magicitem` `~mi` | Shows a random magicitem from | `~mi` +`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol` +`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin` `~weather` `~we` | Shows weather data for a specified city. You can also specify a country after a comma. | `~we Moscow, RU` `~youtube` `~yt` | Searches youtubes and shows the first result | `~yt query` `~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman` @@ -551,100 +391,23 @@ Command and aliases | Description | Usage ###### [Back to TOC](#table-of-contents) -### SelfAssignedRolesCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm` -`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer` -`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar` -`.lsar` | Lists all self-assignable roles. | `.lsar` -`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar` -`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer` -`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer` - -###### [Back to TOC](#table-of-contents) - -### SelfCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` -`.die` | Shuts the bot down. **Bot Owner only.** | `.die` -`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` -`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` -`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` -`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` -`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message` -`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam` - -###### [Back to TOC](#table-of-contents) - -### ServerGreetCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30` -`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet` -`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.` -`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm` -`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`. -`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye` -`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` -`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30` - -###### [Back to TOC](#table-of-contents) - -### SpeedTypingCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`>typestart` | Starts a typing contest. | `>typestart` -`>typestop` | Stops a typing contest on the current channel. | `>typestop` -`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords` -`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3` -`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` - -###### [Back to TOC](#table-of-contents) - -### StreamNotificationCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer` -`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer` -`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer` -`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls` -`~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy` -`~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer` - -###### [Back to TOC](#table-of-contents) - -### TranslateCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello` -`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del` -`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value. -`~translangs` | Lists the valid languages for translation. | `~translangs` - -###### [Back to TOC](#table-of-contents) - -### TriviaCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint` -`>tl` | Shows a current trivia leaderboard. | `>tl` -`>tq` | Quits current trivia after current question. | `>tq` - -###### [Back to TOC](#table-of-contents) - -### UnitConverterCommands +### Utility Command and aliases | Description | Usage ----------------|--------------|------- `.convertlist` | List of the convertible dimensions and currencies. | `.convertlist` `.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000` - -###### [Back to TOC](#table-of-contents) - -### Utility -Command and aliases | Description | Usage -----------------|--------------|------- +`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` +`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!` +`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`...` | Shows a random quote with a specified name. | `... abc` +`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` +`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` +`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` +`.serverinfo` `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | `.sinfo Some Server` +`.channelinfo` `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | `.cinfo #some-channel` +`.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser` +`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` +`.calcops` | Shows all available operations in .calc command | `.calcops` `.togethertube` `.totube` | Creates a new room on and shows the link in the chat. | `.totube` `.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch` `.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role` @@ -658,18 +421,3 @@ Command and aliases | Description | Usage `.showemojis` `.se` | Shows a name and a link to every SPECIAL emoji in the message. | `.se A message full of SPECIAL emojis` `.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3` `.activity` | Checks for spammers. **Bot Owner only.** | `.activity` - -###### [Back to TOC](#table-of-contents) - -### VoicePlusTextCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text` -`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t` - -###### [Back to TOC](#table-of-contents) - -### XkcdCommands -Command and aliases | Description | Usage -----------------|--------------|------- -`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest` diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 3d32eaca..f4e4136a 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -122,17 +122,18 @@ namespace NadekoBot.Modules.Help [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [OwnerOnly] - public Task Hgit() + public async Task Hgit() { var helpstr = new StringBuilder(); helpstr.AppendLine("You can support the project on patreon: or paypal: \n"); helpstr.AppendLine("##Table Of Contents"); - helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.Name.ToLowerInvariant() != "help").OrderBy(m => m.Name).Prepend(NadekoBot.CommandService.Modules.FirstOrDefault(m=>m.Name.ToLowerInvariant()=="help")).Select(m => $"- [{m.Name}](#{m.Name.ToLowerInvariant()})"))); + helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help").OrderBy(m => m.Name).Prepend(NadekoBot.CommandService.Modules.FirstOrDefault(m=>m.Name.ToLowerInvariant()=="help")).Select(m => $"- [{m.Name}](#{m.Name.ToLowerInvariant()})"))); helpstr.AppendLine(); string lastModule = null; - foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.Name).GroupBy(c => c.Aliases.First()).Select(g => g.First())) + foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First())) { - if (com.Module.Name != lastModule) + var module = com.Module.GetTopLevelModule(); + if (module.Name != lastModule) { if (lastModule != null) { @@ -140,16 +141,16 @@ namespace NadekoBot.Modules.Help helpstr.AppendLine("###### [Back to TOC](#table-of-contents)"); } helpstr.AppendLine(); - helpstr.AppendLine("### " + com.Module.Name + " "); + helpstr.AppendLine("### " + module.Name + " "); helpstr.AppendLine("Command and aliases | Description | Usage"); helpstr.AppendLine("----------------|--------------|-------"); - lastModule = com.Module.Name; + lastModule = module.Name; } helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} | {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.GetPrefix())}"); } helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser().Username , "@BotName"); File.WriteAllText("../../docs/Commands List.md", helpstr.ToString()); - return Task.CompletedTask; + await Context.Channel.SendMessageAsync("").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From 6a38192a8795c4eae2bdba9e25a8bd9200faf58e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 14:31:17 +0100 Subject: [PATCH 18/85] More fixes to -hgit and commandlist --- docs/Commands List.md | 42 ------------------------------ src/NadekoBot/Modules/Help/Help.cs | 9 +++++-- 2 files changed, 7 insertions(+), 44 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index 727d53f3..f435ab6d 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -2,59 +2,17 @@ You can support the project on patreon: or paypa ##Table Of Contents - [Help](#help) -- [Acropobia](#acropobia) - [Administration](#administration) -- [AnimalRacing](#animalracing) -- [AnimeSearchCommands](#animesearchcommands) -- [AntiRaidCommands](#antiraidcommands) -- [AutoAssignRoleCommands](#autoassignrolecommands) -- [BlacklistCommands](#blacklistcommands) -- [CalcCommands](#calccommands) - [ClashOfClans](#clashofclans) -- [CleverBotCommands](#cleverbotcommands) -- [CmdCdsCommands](#cmdcdscommands) -- [CrossServerTextChannel](#crossservertextchannel) - [CustomReactions](#customreactions) -- [DMForwardCommands](#dmforwardcommands) -- [DrawCommands](#drawcommands) -- [DriceRollCommands](#dricerollcommands) -- [FilterCommands](#filtercommands) -- [FlipCoinCommands](#flipcoincommands) - [Gambling](#gambling) - [Games](#games) -- [HangmanCommands](#hangmancommands) -- [InfoCommands](#infocommands) -- [JokeCommands](#jokecommands) -- [LogCommands](#logcommands) -- [Migration](#migration) - [Music](#music) -- [MuteCommands](#mutecommands) - [NSFW](#nsfw) -- [OsuCommands](#osucommands) -- [OverwatchCommands](#overwatchcommands) - [Permissions](#permissions) -- [PlaceCommands](#placecommands) -- [PlantPickCommands](#plantpickcommands) -- [PlayingRotateCommands](#playingrotatecommands) - [Pokemon](#pokemon) -- [PokemonSearchCommands](#pokemonsearchcommands) -- [PollCommands](#pollcommands) -- [QuoteCommands](#quotecommands) -- [RatelimitCommand](#ratelimitcommand) -- [RemindCommands](#remindcommands) -- [RepeatCommands](#repeatcommands) - [Searches](#searches) -- [SelfAssignedRolesCommands](#selfassignedrolescommands) -- [SelfCommands](#selfcommands) -- [ServerGreetCommands](#servergreetcommands) -- [SpeedTypingCommands](#speedtypingcommands) -- [StreamNotificationCommands](#streamnotificationcommands) -- [TranslateCommands](#translatecommands) -- [TriviaCommands](#triviacommands) -- [UnitConverterCommands](#unitconvertercommands) - [Utility](#utility) -- [VoicePlusTextCommands](#voiceplustextcommands) -- [XkcdCommands](#xkcdcommands) ### Administration diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index f4e4136a..87f31de0 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -127,7 +127,12 @@ namespace NadekoBot.Modules.Help var helpstr = new StringBuilder(); helpstr.AppendLine("You can support the project on patreon: or paypal: \n"); helpstr.AppendLine("##Table Of Contents"); - helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help").OrderBy(m => m.Name).Prepend(NadekoBot.CommandService.Modules.FirstOrDefault(m=>m.Name.ToLowerInvariant()=="help")).Select(m => $"- [{m.Name}](#{m.Name.ToLowerInvariant()})"))); + helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help") + .Select(m => m.GetTopLevelModule().Name) + .Distinct() + .OrderBy(m => m) + .Prepend("Help") + .Select(m => $"- [{m}](#{m.ToLowerInvariant()})"))); helpstr.AppendLine(); string lastModule = null; foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First())) @@ -150,7 +155,7 @@ namespace NadekoBot.Modules.Help } helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser().Username , "@BotName"); File.WriteAllText("../../docs/Commands List.md", helpstr.ToString()); - await Context.Channel.SendMessageAsync("").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync("Commandlist Regenerated").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From 1dad858a659866c024d58fd35c2e79d45b531f8c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 16:04:21 +0100 Subject: [PATCH 19/85] .savechat improved/fixed --- .../Modules/Administration/Administration.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index f2d6956d..4532a9f5 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -483,25 +483,11 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task SaveChat(int cnt) { - ulong? lastmsgId = null; var sb = new StringBuilder(); var msgs = new List(cnt); - while (cnt > 0) - { - var dlcnt = cnt < 100 ? cnt : 100; - IEnumerable dledMsgs; - if (lastmsgId == null) - dledMsgs = await Context.Channel.GetMessagesAsync(cnt).Flatten().ConfigureAwait(false); - else - dledMsgs = await Context.Channel.GetMessagesAsync(lastmsgId.Value, Direction.Before, dlcnt).Flatten().ConfigureAwait(false); - if (!dledMsgs.Any()) - break; + await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false); - msgs.AddRange(dledMsgs); - lastmsgId = msgs[msgs.Count - 1].Id; - cnt -= 100; - } var title = $"Chatlog-{Context.Guild.Name}/#{Context.Channel.Name}-{DateTime.Now}.txt"; var grouping = msgs.GroupBy(x => $"{x.CreatedAt.Date:dd.MM.yyyy}") .Select(g => new { date = g.Key, messages = g.OrderBy(x => x.CreatedAt).Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) }); From c99d2a9bba112bc79943d80cc5e5781e1ce152fa Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 16:53:39 +0100 Subject: [PATCH 20/85] Added .setstatus --- .../Modules/Administration/Administration.cs | 1 - .../Administration/Commands/SelfCommands.cs | 11 +++++++- .../Resources/CommandStrings.Designer.cs | 27 +++++++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 +++++++ src/NadekoBot/ShardedDiscordClient.cs | 27 +++++++++++++++++++ 5 files changed, 73 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 4532a9f5..5b97b69a 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -485,7 +485,6 @@ namespace NadekoBot.Modules.Administration { var sb = new StringBuilder(); var msgs = new List(cnt); - await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false); var title = $"Chatlog-{Context.Guild.Name}/#{Context.Channel.Name}-{DateTime.Now}.txt"; diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 589fa49a..be2643a3 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -59,7 +59,16 @@ namespace NadekoBot.Modules.Administration await NadekoBot.Client.CurrentUser().ModifyAsync(u => u.Username = newName).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"ℹ️ Successfully changed name to **{newName}**").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Bot name changed to **{newName}**").ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task SetStatus([Remainder] SettableUserStatus status) + { + await NadekoBot.Client.SetStatus(status); + + await Context.Channel.SendConfirmAsync($"Bot status changed to **{status}**").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 368dc408..4858f768 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6566,6 +6566,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to setstatus. + /// + public static string setstatus_cmd { + get { + return ResourceManager.GetString("setstatus_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets the bot's status. (Online/Idle/Invisible). + /// + public static string setstatus_desc { + get { + return ResourceManager.GetString("setstatus_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}setstatus Idle`. + /// + public static string setstatus_usage { + get { + return ResourceManager.GetString("setstatus_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to setstream. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 3eb5f456..8c9ee0bf 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2853,4 +2853,13 @@ `{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai` + + setstatus + + + Sets the bot's status. (Online/Idle/Invisible) + + + `{0}setstatus Idle` + \ No newline at end of file diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index aee2af7b..4948eb92 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -140,5 +140,32 @@ namespace NadekoBot public Task SetStream(string name, string url) => Task.WhenAll(Clients.Select(ms => ms.SetGameAsync(name, url, StreamType.NotStreaming))); + + public Task SetStatus(SettableUserStatus status) => Task.WhenAll(Clients.Select(ms => ms.SetStatusAsync(SettableUserStatusToUserStatus(status)))); + + private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) + { + switch (sus) + { + case SettableUserStatus.Online: + return UserStatus.Online; + case SettableUserStatus.Invisible: + return UserStatus.Invisible; + case SettableUserStatus.Idle: + return UserStatus.AFK; + } + + return UserStatus.Online; + } + } + + public enum SettableUserStatus + { + Online = 1, + On = 1, + Invisible = 2, + Invis = 2, + Idle = 3, + Afk = 3 } } \ No newline at end of file From a496467585ecc64546f877d2dc5fdc99ba4eb611 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 17:06:22 +0100 Subject: [PATCH 21/85] no more everyone role in permissions --- src/NadekoBot/Modules/Permissions/Permissions.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index d2b282aa..33d8a27e 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -73,6 +73,9 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task PermRole([Remainder] IRole role = null) { + if (role != null && role == role.Guild.EveryoneRole) + return; + using (var uow = DbHandler.UnitOfWork()) { var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); @@ -379,6 +382,9 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task RoleCmd(CommandInfo command, PermissionAction action, [Remainder] IRole role) { + if (role == role.Guild.EveryoneRole) + return; + using (var uow = DbHandler.UnitOfWork()) { var newPerm = new Permission @@ -405,6 +411,9 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task RoleMdl(ModuleInfo module, PermissionAction action, [Remainder] IRole role) { + if (role == role.Guild.EveryoneRole) + return; + using (var uow = DbHandler.UnitOfWork()) { var newPerm = new Permission @@ -515,6 +524,9 @@ namespace NadekoBot.Modules.Permissions [RequireContext(ContextType.Guild)] public async Task AllRoleMdls(PermissionAction action, [Remainder] IRole role) { + if (role == role.Guild.EveryoneRole) + return; + using (var uow = DbHandler.UnitOfWork()) { var newPerm = new Permission From ce0532335d322c321ce873f13f7d792abe5d9189 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 17:09:27 +0100 Subject: [PATCH 22/85] .setstatus now accepts 'dnd' too --- src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- src/NadekoBot/ShardedDiscordClient.cs | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 4858f768..473a9183 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6576,7 +6576,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets the bot's status. (Online/Idle/Invisible). + /// Looks up a localized string similar to Sets the bot's status. (Online/Idle/Dnd/Invisible). /// public static string setstatus_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 8c9ee0bf..9f6e7b62 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2857,7 +2857,7 @@ setstatus - Sets the bot's status. (Online/Idle/Invisible) + Sets the bot's status. (Online/Idle/Dnd/Invisible) `{0}setstatus Idle` diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 4948eb92..535e4c2e 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -153,6 +153,8 @@ namespace NadekoBot return UserStatus.Invisible; case SettableUserStatus.Idle: return UserStatus.AFK; + case SettableUserStatus.Dnd: + return UserStatus.DoNotDisturb; } return UserStatus.Online; @@ -166,6 +168,9 @@ namespace NadekoBot Invisible = 2, Invis = 2, Idle = 3, - Afk = 3 + Afk = 3, + Dnd = 4, + DoNotDisturb = 4, + Busy = 4, } } \ No newline at end of file From 3bcd4c2374d7a7beda8be99183b676422a6bd451 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 22:08:25 +0100 Subject: [PATCH 23/85] Fixed removed song message --- src/NadekoBot/Modules/Music/Classes/MusicControls.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index 4b15153b..be726c84 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -137,7 +137,7 @@ namespace NadekoBot.Modules.Music.Classes var index = playlist.IndexOf(CurrentSong); if (index != -1) - RemoveSongAt(index); + RemoveSongAt(index, true); OnStarted(this, CurrentSong); await CurrentSong.Play(audioClient, cancelToken); @@ -273,14 +273,14 @@ namespace NadekoBot.Modules.Music.Classes }); } - public void RemoveSongAt(int index) + public void RemoveSongAt(int index, bool silent = false) { actionQueue.Enqueue(() => { if (index < 0 || index >= playlist.Count) return; var song = playlist.ElementAtOrDefault(index); - if (playlist.Remove(song)) + if (playlist.Remove(song) && !silent) { SongRemoved(song); } From 8d26471748a508fe2a2f425eae4edb6dcf2e0111 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 4 Jan 2017 22:45:36 +0100 Subject: [PATCH 24/85] Logs --- src/NadekoBot/ShardedDiscordClient.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 535e4c2e..259fe88f 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -65,7 +65,7 @@ namespace NadekoBot client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); - + client.Log += Client_Log; var j = i; client.Disconnected += (ex) => { @@ -78,6 +78,12 @@ namespace NadekoBot Clients = clientList.AsReadOnly(); } + private Task Client_Log(LogMessage arg) + { + _log.Warn(arg.Exception, arg.Message); + return Task.CompletedTask; + } + public DiscordSocketClient MainClient => Clients[0]; From 74b6ef5e7b983579a8f43596de54a983d2a7e960 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 10:33:20 +0100 Subject: [PATCH 25/85] .setgame should now disable playing status --- src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index be2643a3..915b6373 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -97,8 +97,6 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task SetGame([Remainder] string game = null) { - game = game ?? ""; - await NadekoBot.Client.SetGame(game).ConfigureAwait(false); await Context.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false); From d9e6bc467693e131036b5d07a357a437eb9230ac Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 11:04:05 +0100 Subject: [PATCH 26/85] Music paused is now once again removed after 10 seconds --- src/NadekoBot/Modules/Music/Music.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 8b175557..9751c938 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -857,10 +857,14 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota { try { + IUserMessage msg; if (paused) - await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); + msg = await textCh.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); else - await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); + msg = await textCh.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); + + if (msg != null) + msg.DeleteAfter(10); } catch { } }; From 58ff017bf8f2cd21812082f2eb0cce1f1c60aaa0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 11:21:38 +0100 Subject: [PATCH 27/85] >pick has no cooldown on self-hosted bots --- .../Modules/Games/Commands/PlantAndPickCommands.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 6d17dd76..69fbc862 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -111,9 +111,12 @@ namespace NadekoBot.Modules.Games { var channel = (ITextChannel)Context.Channel; - if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages || !usersRecentlyPicked.Add(Context.User.Id)) + if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages) return; - +#if GLOBAL_NADEKO + if (!usersRecentlyPicked.Add(Context.User.Id)) + return; +#endif try { @@ -131,8 +134,10 @@ namespace NadekoBot.Modules.Games } finally { +#if GLOBAL_NADEKO await Task.Delay(60000); usersRecentlyPicked.TryRemove(Context.User.Id); +#endif } } From c5037a2c1f81dd1baec02ed819687d2c365f8553 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 13:49:50 +0100 Subject: [PATCH 28/85] .rotaterolecolor added. closes #936 --- NadekoBot.sln | 8 --- src/NadekoBot/Modules/Searches/Searches.cs | 6 +- .../Modules/Utility/Commands/Remind.cs | 1 + src/NadekoBot/Modules/Utility/Utility.cs | 70 +++++++++++++++++++ .../Resources/CommandStrings.Designer.cs | 27 +++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 +++ .../Services/Impl/GoogleApiService.cs | 2 +- 7 files changed, 109 insertions(+), 14 deletions(-) diff --git a/NadekoBot.sln b/NadekoBot.sln index e0ebea17..7551e778 100644 --- a/NadekoBot.sln +++ b/NadekoBot.sln @@ -12,8 +12,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "NadekoBot", "src\NadekoBot\NadekoBot.xproj", "{45EC1473-C678-4857-A544-07DFE0D0B478}" EndProject -Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.API", "discord.net\src\Discord.Net.API\Discord.Net.API.xproj", "{834C70DF-1230-4AAA-9C13-48AB232E8D76}" -EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Commands", "discord.net\src\Discord.Net.Commands\Discord.Net.Commands.xproj", "{078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}" EndProject Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Discord.Net.Core", "discord.net\src\Discord.Net.Core\Discord.Net.Core.xproj", "{E5F4786F-58F3-469E-8C87-1908A95436B7}" @@ -35,12 +33,6 @@ Global {45EC1473-C678-4857-A544-07DFE0D0B478}.GlobalNadeko|Any CPU.Build.0 = Release|Any CPU {45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.ActiveCfg = Release|Any CPU {45EC1473-C678-4857-A544-07DFE0D0B478}.Release|Any CPU.Build.0 = Release|Any CPU - {834C70DF-1230-4AAA-9C13-48AB232E8D76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {834C70DF-1230-4AAA-9C13-48AB232E8D76}.Debug|Any CPU.Build.0 = Debug|Any CPU - {834C70DF-1230-4AAA-9C13-48AB232E8D76}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU - {834C70DF-1230-4AAA-9C13-48AB232E8D76}.GlobalNadeko|Any CPU.Build.0 = Debug|Any CPU - {834C70DF-1230-4AAA-9C13-48AB232E8D76}.Release|Any CPU.ActiveCfg = Release|Any CPU - {834C70DF-1230-4AAA-9C13-48AB232E8D76}.Release|Any CPU.Build.0 = Release|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {078DD7E6-943D-4D09-AFC2-D2BA58B76C9C}.GlobalNadeko|Any CPU.ActiveCfg = Debug|Any CPU diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 643ea0e0..16b67e18 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -582,14 +582,10 @@ namespace NadekoBot.Modules.Searches public async Task Color([Remainder] string color = null) { color = color?.Trim().Replace("#", ""); - if (string.IsNullOrWhiteSpace((string)color)) + if (string.IsNullOrWhiteSpace(color)) return; var img = new ImageSharp.Image(50, 50); - var red = Convert.ToInt32(color.Substring(0, 2), 16); - var green = Convert.ToInt32(color.Substring(2, 2), 16); - var blue = Convert.ToInt32(color.Substring(4, 2), 16); - img.BackgroundColor(new ImageSharp.Color(color)); await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png"); diff --git a/src/NadekoBot/Modules/Utility/Commands/Remind.cs b/src/NadekoBot/Modules/Utility/Commands/Remind.cs index 6cb3965f..58660bce 100644 --- a/src/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/src/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Utility diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index e2d17029..3abf4e04 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -10,12 +10,82 @@ using System.Text.RegularExpressions; using System.Reflection; using NadekoBot.Services.Impl; using System.Net.Http; +using System.Collections.Concurrent; +using System.Threading; +using ImageSharp; namespace NadekoBot.Modules.Utility { [NadekoModule("Utility", ".")] public partial class Utility : DiscordModule { + private static ConcurrentDictionary rotatingRoleColors = new ConcurrentDictionary(); + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [OwnerOnly] + public async Task RotateRoleColor(int timeout, IRole role, params string[] hexes) + { + var channel = (ITextChannel)Context.Channel; + + if (timeout < 0 || timeout > 3600) + return; + + Timer t; + if (timeout == 0 || hexes.Length == 0) + { + if (rotatingRoleColors.TryRemove(role.Id, out t)) + { + t.Change(Timeout.Infinite, Timeout.Infinite); + await channel.SendConfirmAsync($"Stopped rotating colors for the **{role.Name}** role").ConfigureAwait(false); + } + return; + } + + var hexColors = hexes.Select(hex => + { + try { return (ImageSharp.Color?)new ImageSharp.Color(hex.Replace("#", "")); } catch { return null; } + }) + .Where(c => c != null) + .Select(c => c.Value) + .ToArray(); + + if (!hexColors.Any()) + { + await channel.SendMessageAsync("No colors are in the correct format. Use `#00ff00` for example.").ConfigureAwait(false); + return; + } + + var images = hexColors.Select(color => + { + var img = new ImageSharp.Image(50, 50); + img.BackgroundColor(color); + return img; + }).Merge().ToStream(); + + var i = 0; + t = new Timer(async (_) => + { + try + { + var color = hexColors[i]; + await role.ModifyAsync(r => r.Color = new Discord.Color(color.R, color.G, color.B)).ConfigureAwait(false); + ++i; + if (i >= hexColors.Length) + i = 0; + } + catch { } + }, null, 0, timeout * 1000); + + rotatingRoleColors.AddOrUpdate(role.Id, t, (key, old) => + { + old.Change(Timeout.Infinite, Timeout.Infinite); + return t; + }); + + await channel.SendFileAsync(images, "magicalgirl.jpg", $"Rotating **{role.Name}** role's color.").ConfigureAwait(false); + } + [NadekoCommand, Usage, Description, Aliases] public async Task TogetherTube() { diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 473a9183..17754f51 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6053,6 +6053,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to rotaterolecolor rrc. + /// + public static string rotaterolecolor_cmd { + get { + return ResourceManager.GetString("rotaterolecolor_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds. Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable.. + /// + public static string rotaterolecolor_desc { + get { + return ResourceManager.GetString("rotaterolecolor_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}rrc 1 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole`. + /// + public static string rotaterolecolor_usage { + get { + return ResourceManager.GetString("rotaterolecolor_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to rps. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 9f6e7b62..c6e2bd15 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2862,4 +2862,13 @@ `{0}setstatus Idle` + + rotaterolecolor rrc + + + Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds. Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. + + + `{0}rrc 1 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` + \ No newline at end of file diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index 6e5727f7..56a40965 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -149,7 +149,7 @@ namespace NadekoBot.Services.Impl return toReturn; } - + //todo AsyncEnumerable public async Task> GetVideoDurationsAsync(IEnumerable videoIds) { var videoIdsList = videoIds as List ?? videoIds.ToList(); From 725d844131c4c68c31734454eeacba5e3be36011 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 19:13:53 +0100 Subject: [PATCH 29/85] .rrc reduced to a minimum of 60 seconds. b1nzy wasn't happy about this --- src/NadekoBot/Modules/Utility/Utility.cs | 2 +- src/NadekoBot/Resources/CommandStrings.Designer.cs | 4 ++-- src/NadekoBot/Resources/CommandStrings.resx | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 3abf4e04..eb7c7404 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Utility { var channel = (ITextChannel)Context.Channel; - if (timeout < 0 || timeout > 3600) + if ((timeout < 60 && timeout != 0) || timeout > 3600) return; Timer t; diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 17754f51..950eeaad 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6063,7 +6063,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds. Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable.. + /// Looks up a localized string similar to Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable.. /// public static string rotaterolecolor_desc { get { @@ -6072,7 +6072,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to `{0}rrc 1 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole`. + /// Looks up a localized string similar to `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole`. /// public static string rotaterolecolor_usage { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index c6e2bd15..c540455d 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2866,9 +2866,9 @@ rotaterolecolor rrc - Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds. Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. + Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. - `{0}rrc 1 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` + `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` \ No newline at end of file From 2d1cfad07f8edf09d3e097d2ad113d411ce23bb1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 21:44:31 +0100 Subject: [PATCH 30/85] Error logging --- .../Modules/Music/Classes/MusicControls.cs | 4 ++++ .../Modules/Music/Classes/SongHandler.cs | 19 ++++++++++++------- src/NadekoBot/Modules/Music/Music.cs | 5 +++-- src/NadekoBot/ShardedDiscordClient.cs | 3 ++- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index be726c84..257b319f 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -152,6 +152,10 @@ namespace NadekoBot.Modules.Music.Classes } catch (OperationCanceledException) { } + catch (TimeoutException) { + Console.WriteLine("Bot couldn't connect to the channel. Most likely insufficient permissions. Fix permissions, then queue songs again"); + ClearQueue(); + } catch (Exception ex) { Console.WriteLine("Music thread almost crashed."); diff --git a/src/NadekoBot/Modules/Music/Classes/SongHandler.cs b/src/NadekoBot/Modules/Music/Classes/SongHandler.cs index 7892e278..496de92c 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongHandler.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongHandler.cs @@ -76,16 +76,21 @@ namespace NadekoBot.Modules.Music.Classes }) { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) }; } - + Console.WriteLine("Getting video id"); var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault(); + Console.WriteLine("Done getting video id"); if (string.IsNullOrWhiteSpace(link)) throw new OperationCanceledException("Not a valid youtube query."); - var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); - var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); - var video = videos - .Where(v => v.AudioBitrate < 256) - .OrderByDescending(v => v.AudioBitrate) - .FirstOrDefault(); + //var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetVideoAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); + //var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); + //var video = videos + // .Where(v => v.AudioBitrate < 256) + // .OrderByDescending(v => v.AudioBitrate) + // .FirstOrDefault(); + + Console.WriteLine("Getting video itself"); + var video = await YouTube.Default.GetVideoAsync(link).ConfigureAwait(false); + Console.WriteLine("Done getting video"); if (video == null) // do something with this error throw new Exception("Could not load any video elements based on the query."); diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 9751c938..a3b15204 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -325,7 +325,7 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota while (ids.Any() && !cancelSource.IsCancellationRequested) { - var tasks = Task.WhenAll(ids.Take(5).Select(async id => + var tasks = Task.WhenAll(ids.Take(3).Select(async id => { if (cancelSource.Token.IsCancellationRequested) return; @@ -874,8 +874,9 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota try { musicPlayer.ThrowIfQueueFull(); + Console.WriteLine("Resolving"); resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false); - + Console.WriteLine("Resolved"); if (resolvedSong == null) throw new SongNotFoundException(); diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 259fe88f..c2300bd0 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -80,7 +80,8 @@ namespace NadekoBot private Task Client_Log(LogMessage arg) { - _log.Warn(arg.Exception, arg.Message); + _log.Warn(arg.Message); + _log.Error(arg.Exception); return Task.CompletedTask; } From 3e693ea2301ade5883ceafbaefc579151f99dd5a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 21:45:40 +0100 Subject: [PATCH 31/85] Revert "Error logging" This reverts commit 2d1cfad07f8edf09d3e097d2ad113d411ce23bb1. --- .../Modules/Music/Classes/MusicControls.cs | 4 ---- .../Modules/Music/Classes/SongHandler.cs | 19 +++++++------------ src/NadekoBot/Modules/Music/Music.cs | 5 ++--- src/NadekoBot/ShardedDiscordClient.cs | 3 +-- 4 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index 257b319f..be726c84 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -152,10 +152,6 @@ namespace NadekoBot.Modules.Music.Classes } catch (OperationCanceledException) { } - catch (TimeoutException) { - Console.WriteLine("Bot couldn't connect to the channel. Most likely insufficient permissions. Fix permissions, then queue songs again"); - ClearQueue(); - } catch (Exception ex) { Console.WriteLine("Music thread almost crashed."); diff --git a/src/NadekoBot/Modules/Music/Classes/SongHandler.cs b/src/NadekoBot/Modules/Music/Classes/SongHandler.cs index 496de92c..7892e278 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongHandler.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongHandler.cs @@ -76,21 +76,16 @@ namespace NadekoBot.Modules.Music.Classes }) { TotalTime = TimeSpan.FromMilliseconds(svideo.Duration) }; } - Console.WriteLine("Getting video id"); + var link = (await NadekoBot.Google.GetVideosByKeywordsAsync(query).ConfigureAwait(false)).FirstOrDefault(); - Console.WriteLine("Done getting video id"); if (string.IsNullOrWhiteSpace(link)) throw new OperationCanceledException("Not a valid youtube query."); - //var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetVideoAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); - //var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); - //var video = videos - // .Where(v => v.AudioBitrate < 256) - // .OrderByDescending(v => v.AudioBitrate) - // .FirstOrDefault(); - - Console.WriteLine("Getting video itself"); - var video = await YouTube.Default.GetVideoAsync(link).ConfigureAwait(false); - Console.WriteLine("Done getting video"); + var allVideos = await Task.Run(async () => { try { return await YouTube.Default.GetAllVideosAsync(link).ConfigureAwait(false); } catch { return Enumerable.Empty(); } }).ConfigureAwait(false); + var videos = allVideos.Where(v => v.AdaptiveKind == AdaptiveKind.Audio); + var video = videos + .Where(v => v.AudioBitrate < 256) + .OrderByDescending(v => v.AudioBitrate) + .FirstOrDefault(); if (video == null) // do something with this error throw new Exception("Could not load any video elements based on the query."); diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index a3b15204..9751c938 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -325,7 +325,7 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota while (ids.Any() && !cancelSource.IsCancellationRequested) { - var tasks = Task.WhenAll(ids.Take(3).Select(async id => + var tasks = Task.WhenAll(ids.Take(5).Select(async id => { if (cancelSource.Token.IsCancellationRequested) return; @@ -874,9 +874,8 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota try { musicPlayer.ThrowIfQueueFull(); - Console.WriteLine("Resolving"); resolvedSong = await SongHandler.ResolveSong(query, musicType).ConfigureAwait(false); - Console.WriteLine("Resolved"); + if (resolvedSong == null) throw new SongNotFoundException(); diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index c2300bd0..259fe88f 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -80,8 +80,7 @@ namespace NadekoBot private Task Client_Log(LogMessage arg) { - _log.Warn(arg.Message); - _log.Error(arg.Exception); + _log.Warn(arg.Exception, arg.Message); return Task.CompletedTask; } From 517bfc2294e49fbb128c01723374901964247416 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 21:49:32 +0100 Subject: [PATCH 32/85] logging+ --- src/NadekoBot/ShardedDiscordClient.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 259fe88f..c2300bd0 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -80,7 +80,8 @@ namespace NadekoBot private Task Client_Log(LogMessage arg) { - _log.Warn(arg.Exception, arg.Message); + _log.Warn(arg.Message); + _log.Error(arg.Exception); return Task.CompletedTask; } From 871089bf26846dba07589f250806ec4d5f74cb28 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 21:54:49 +0100 Subject: [PATCH 33/85] Fixed !!rm spam? --- .../Modules/Music/Classes/MusicControls.cs | 4 +-- src/NadekoBot/Modules/Music/Music.cs | 33 ++++++++++--------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index be726c84..1e822f0a 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -77,7 +77,7 @@ namespace NadekoBot.Modules.Music.Classes public string PrettyVolume => $"🔉 {(int)(Volume * 100)}%"; - public event Action SongRemoved = delegate { }; + public event Action SongRemoved = delegate { }; public MusicPlayer(IVoiceChannel startingVoiceChannel, float? defaultVolume) { @@ -282,7 +282,7 @@ namespace NadekoBot.Modules.Music.Classes var song = playlist.ElementAtOrDefault(index); if (playlist.Remove(song) && !silent) { - SongRemoved(song); + SongRemoved(song, index); } }); diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 9751c938..165ed935 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -466,22 +466,6 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) return; - musicPlayer.SongRemoved += async (song) => - { - try - { - var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName("Removed song #" + num).WithMusicIcon()) - .WithDescription(song.PrettyName) - .WithFooter(ef => ef.WithText(song.PrettyInfo)) - .WithErrorColor(); - - await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); - - } - catch { } - }; - musicPlayer.RemoveSongAt(num - 1); } @@ -868,6 +852,23 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota } catch { } }; + + + mp.SongRemoved += async (song, index) => + { + try + { + var embed = new EmbedBuilder() + .WithAuthor(eab => eab.WithName("Removed song #" + (index + 1)).WithMusicIcon()) + .WithDescription(song.PrettyName) + .WithFooter(ef => ef.WithText(song.PrettyInfo)) + .WithErrorColor(); + + await textCh.EmbedAsync(embed).ConfigureAwait(false); + + } + catch { } + }; return mp; }); Song resolvedSong; From 22bad86d379c5761ce6e45c09d44e0138b423aeb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 5 Jan 2017 21:58:11 +0100 Subject: [PATCH 34/85] Fixed music WARNINGS, not ERRORS - WARNINGS --- src/NadekoBot/Modules/Music/Music.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 165ed935..536a00ae 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -99,16 +99,16 @@ namespace NadekoBot.Modules.Music [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task Destroy() + public Task Destroy() { - //await Context.Channel.SendErrorAsync("This command is temporarily disabled.").ConfigureAwait(false); - MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return; + if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return Task.CompletedTask; if (((IGuildUser)Context.User).VoiceChannel == musicPlayer.PlaybackVoiceChannel) if (MusicPlayers.TryRemove(Context.Guild.Id, out musicPlayer)) musicPlayer.Destroy(); + return Task.CompletedTask; + } [NadekoCommand, Usage, Description, Aliases] @@ -458,15 +458,16 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [Priority(0)] - public async Task Remove(int num) + public Task Remove(int num) { MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) - return; + return Task.CompletedTask; if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) - return; + return Task.CompletedTask; musicPlayer.RemoveSongAt(num - 1); + return Task.CompletedTask; } [NadekoCommand, Usage, Description, Aliases] @@ -474,8 +475,6 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota [Priority(1)] public async Task Remove(string all) { - - if (all.Trim().ToUpperInvariant() != "ALL") return; MusicPlayer musicPlayer; From beda2bf83c1c8ba85d30db7742631e110ec3c2b3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 6 Jan 2017 21:56:47 +0100 Subject: [PATCH 35/85] Cleverbot reenabled --- src/NadekoBot/Services/CommandHandler.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index d99930b5..f196e20e 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -123,7 +123,6 @@ namespace NadekoBot.Services return; } -#if !GLOBAL_NADEKO try { var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg); @@ -138,7 +137,7 @@ namespace NadekoBot.Services } } catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); } -#endif + try { // maybe this message is a custom reaction From 29f1f98c6a482d7ef6b2b39ae6ecf85314bf14a5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 6 Jan 2017 23:26:04 +0100 Subject: [PATCH 36/85] Hangman disabled on public bot --- src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index eba11193..33170986 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Games { await Context.Channel.SendConfirmAsync(typesStr); } - +#if !GLOBAL_NADEKO [NadekoCommand, Usage, Description, Aliases] public async Task Hangman(HangmanTermPool.HangmanTermType type = HangmanTermPool.HangmanTermType.All) { @@ -52,6 +52,7 @@ namespace NadekoBot.Modules.Games await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord); } +#endif } } } From 8c8aad8ae24e6a30772c18b0ea372a39f5f5de21 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 6 Jan 2017 23:58:06 +0100 Subject: [PATCH 37/85] Some random thing --- src/NadekoBot/_Extensions/Extensions.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index f9a8157b..3994e54f 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -1,6 +1,8 @@ using Discord; using Discord.Commands; +using Discord.WebSocket; using ImageSharp; +using NadekoBot.Services.Discord; using Newtonsoft.Json; using System; using System.Collections.Concurrent; @@ -16,6 +18,13 @@ namespace NadekoBot.Extensions { public static class Extensions { + public static ReactionEventWrapper OnReactionAdded(this SocketMessage msg, Action reactionAdded) + { + var wrap = new ReactionEventWrapper(msg); + wrap.OnReactionAdded += reactionAdded; + return wrap; + } + public static void AddFakeHeaders(this HttpClient http) { http.DefaultRequestHeaders.Clear(); From c39d65dffd4c89511ab38b7a73b97f8481ffbcea Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 00:00:55 +0100 Subject: [PATCH 38/85] Some logging fix --- .../Discord/SocketMessageEventWrapper.cs | 66 +++++++++++++++++++ src/NadekoBot/ShardedDiscordClient.cs | 2 + 2 files changed, 68 insertions(+) create mode 100644 src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs diff --git a/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs b/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs new file mode 100644 index 00000000..7c24b564 --- /dev/null +++ b/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs @@ -0,0 +1,66 @@ +using Discord; +using Discord.WebSocket; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Discord +{ + public class ReactionEventWrapper : IDisposable + { + public SocketMessage Message { get; } + public event Action OnReactionAdded = delegate { }; + public event Action OnReactionRemoved = delegate { }; + public event Action OnReactionsCleared = delegate { }; + + public ReactionEventWrapper(SocketMessage msg) + { + if (msg == null) + throw new ArgumentNullException(nameof(msg)); + Message = msg; + + msg.Discord.ReactionAdded += Discord_ReactionAdded; + msg.Discord.ReactionRemoved += Discord_ReactionRemoved; + msg.Discord.ReactionsCleared += Discord_ReactionsCleared; + } + + private Task Discord_ReactionsCleared(ulong messageId, Optional reaction) + { + if (messageId == Message.Id) + OnReactionsCleared?.Invoke(); + return Task.CompletedTask; + } + + private Task Discord_ReactionRemoved(ulong messageId, Optional arg2, SocketReaction reaction) + { + if (messageId == Message.Id) + OnReactionRemoved?.Invoke(reaction); + return Task.CompletedTask; + } + + private Task Discord_ReactionAdded(ulong messageId, Optional message, SocketReaction reaction) + { + if(messageId == Message.Id) + OnReactionAdded?.Invoke(reaction); + return Task.CompletedTask; + } + + public void UnsubAll() + { + Message.Discord.ReactionAdded -= Discord_ReactionAdded; + Message.Discord.ReactionRemoved -= Discord_ReactionRemoved; + Message.Discord.ReactionsCleared -= Discord_ReactionsCleared; + } + + private bool disposing = false; + public void Dispose() + { + if (disposing) + return; + disposing = true; + UnsubAll(); + } + } +} diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index c2300bd0..580f0e8d 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -65,7 +65,9 @@ namespace NadekoBot client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); +#if GLOBAL_NADEKO client.Log += Client_Log; +#endif var j = i; client.Disconnected += (ex) => { From f052bcf5a015ca8b306ca7f1092a0988658e9653 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 00:46:07 +0100 Subject: [PATCH 39/85] Many random things --- src/NadekoBot/Modules/Utility/Utility.cs | 4 +++- src/NadekoBot/Services/CommandHandler.cs | 16 ++++++++++++++-- src/NadekoBot/Services/Impl/StatsService.cs | 7 ++++++- src/NadekoBot/ShardedDiscordClient.cs | 3 +++ 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index eb7c7404..5e875219 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -269,8 +269,10 @@ namespace NadekoBot.Modules.Utility .AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(stats.OwnerIds).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuilds().Count} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)) + .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildsCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)) +#if !GLOBAL_NADEKO .WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued.")) +#endif ); } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index f196e20e..e22249e9 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -17,6 +17,7 @@ using static NadekoBot.Modules.Administration.Administration; using NadekoBot.Modules.CustomReactions; using NadekoBot.Modules.Games; using System.Collections.Concurrent; +using System.Threading; namespace NadekoBot.Services { @@ -39,11 +40,19 @@ namespace NadekoBot.Services //userid/msg count public ConcurrentDictionary UserMessagesSent { get; } = new ConcurrentDictionary(); + public ConcurrentHashSet UsersOnShortCooldown { get; } = new ConcurrentHashSet(); + private Timer clearUsersOnShortCooldown { get; } + public CommandHandler(ShardedDiscordClient client, CommandService commandService) { _client = client; _commandService = commandService; _log = LogManager.GetCurrentClassLogger(); + + clearUsersOnShortCooldown = new Timer((_) => + { + UsersOnShortCooldown.Clear(); + }, null, 1500, 1500); } public async Task StartHandling() { @@ -70,8 +79,11 @@ namespace NadekoBot.Services if (usrMsg == null) return; - //if (!usrMsg.IsAuthor()) - // UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); + if (!usrMsg.IsAuthor()) + UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); + + if (!UsersOnShortCooldown.Add(usrMsg.Author.Id)) + return; if (msg.Author.IsBot || !NadekoBot.Ready) //no bots return; diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index c767ecec..3331edeb 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -20,7 +20,12 @@ namespace NadekoBot.Services.Impl public string Library => "Discord.Net"; public int MessageCounter { get; private set; } = 0; public int CommandsRan { get; private set; } = 0; - public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); + public string Heap => +#if !GLOBAL_NADEKO + Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); +#else + "a lot :)"; +#endif public double MessagesPerSecond => MessageCounter / (double)GetUptime().TotalSeconds; public int TextChannels => client.GetGuilds().SelectMany(g => g.Channels.Where(c => c is ITextChannel)).Count(); public int VoiceChannels => client.GetGuilds().SelectMany(g => g.Channels.Where(c => c is IVoiceChannel)).Count(); diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 580f0e8d..29630eaf 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -96,6 +96,9 @@ namespace NadekoBot public IReadOnlyCollection GetGuilds() => Clients.SelectMany(c => c.Guilds).ToList(); + public int GetGuildsCount() => + Clients.Sum(c => c.Guilds.Count); + public SocketGuild GetGuild(ulong id) => Clients.Select(c => c.GetGuild(id)).FirstOrDefault(g => g != null); From ea41d6126c4b61ce5691c8992421e5ae74d53a33 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 00:56:31 +0100 Subject: [PATCH 40/85] Fixed weather pressure --- .../Modules/Searches/Commands/Models/WeatherModels.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Searches/Commands/Models/WeatherModels.cs b/src/NadekoBot/Modules/Searches/Commands/Models/WeatherModels.cs index d3c5742f..371b2065 100644 --- a/src/NadekoBot/Modules/Searches/Commands/Models/WeatherModels.cs +++ b/src/NadekoBot/Modules/Searches/Commands/Models/WeatherModels.cs @@ -23,8 +23,8 @@ namespace NadekoBot.Modules.Searches.Commands.Models public class Main { public double temp { get; set; } - public int pressure { get; set; } - public int humidity { get; set; } + public float pressure { get; set; } + public float humidity { get; set; } public double temp_min { get; set; } public double temp_max { get; set; } } From 0ddf0a1b512f353d3c1b28dc5c3d2323cc891702 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 13:29:22 +0100 Subject: [PATCH 41/85] Woopsie, cleverbot can now be toggled on public bot --- src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index a65787fb..9ec59147 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Games } return true; } -#if !GLOBAL_NADEKO + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(ChannelPermission.ManageMessages)] @@ -120,7 +120,6 @@ namespace NadekoBot.Modules.Games await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Enabled cleverbot on this server.").ConfigureAwait(false); } -#endif } } } \ No newline at end of file From 04ad50ada8f8fd8839177d76c6a86e2ec8645985 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 16:33:24 +0100 Subject: [PATCH 42/85] info commands now ignore everyone role --- src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index ca2ab738..37d80ba6 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Utility .AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Roles**").WithValue(guild.Roles.Count().ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true)) .WithImageUrl(guild.IconUrl) .WithColor(NadekoBot.OkColor); if (guild.Emojis.Count() > 0) @@ -92,8 +92,7 @@ namespace NadekoBot.Modules.Utility embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Current Game**").WithValue($"{(user.Game?.Name == null ? "-" : user.Game.Value.Name)}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count})** - {string.Join(", ", user.GetRoles().Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) + .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count})** - {string.Join(", ", user.GetRoles().Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) .WithThumbnailUrl(user.AvatarUrl) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); From 0425c52332964667fd165ffb2c97871b80ade45d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 16:33:37 +0100 Subject: [PATCH 43/85] Added .crinv command --- Discord.Net | 2 +- src/NadekoBot/Modules/Utility/Utility.cs | 11 ++++++++ .../Resources/CommandStrings.Designer.cs | 27 +++++++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 +++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/Discord.Net b/Discord.Net index fa2568bc..b9f76733 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit fa2568bc312ba35f1518e47601c62fccdb949731 +Subproject commit b9f767337d2b7c07ed76eb83c3bc5030109d5238 diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 5e875219..5c743db5 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -251,6 +251,17 @@ namespace NadekoBot.Modules.Utility await channel.SendConfirmAsync("Channel topic", topic); } + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireBotPermission(ChannelPermission.CreateInstantInvite)] + [RequireUserPermission(ChannelPermission.CreateInstantInvite)] + public async Task CreateInvite() + { + var invite = await ((ITextChannel)Context.Channel).CreateInviteAsync(0, null, isUnique: true); + + await Context.Channel.SendConfirmAsync($"{Context.User.Mention} https://discord.gg/{invite.Code}"); + } + [NadekoCommand, Usage, Description, Aliases] public async Task Stats() { diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 950eeaad..0f4a12b0 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -1841,6 +1841,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to createinvite crinv. + /// + public static string createinvite_cmd { + get { + return ResourceManager.GetString("createinvite_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Creates a new invite which has infinite max uses and never expires.. + /// + public static string createinvite_desc { + get { + return ResourceManager.GetString("createinvite_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}crinv`. + /// + public static string createinvite_usage { + get { + return ResourceManager.GetString("createinvite_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to createrole cr. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index c540455d..4db2061c 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2871,4 +2871,13 @@ `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` + + createinvite crinv + + + Creates a new invite which has infinite max uses and never expires. + + + `{0}crinv` + \ No newline at end of file From 21c6c5fc9ad29186c78e8c6849d22a9acd48a515 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 18:32:33 +0100 Subject: [PATCH 44/85] Some .stats fixes --- .../Commands/PlayingRotateCommands.cs | 4 +- src/NadekoBot/Services/Impl/StatsService.cs | 48 ++++++++++++++----- src/NadekoBot/ShardedDiscordClient.cs | 12 ++++- 3 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs index c9c49f60..b2e8115f 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs @@ -71,8 +71,8 @@ namespace NadekoBot.Modules.Administration public static Dictionary> PlayingPlaceholders { get; } = new Dictionary> { - {"%servers%", () => NadekoBot.Client.GetGuilds().Count().ToString()}, - {"%users%", () => NadekoBot.Client.GetGuilds().Select(s => s.Users.Count).Sum().ToString()}, + {"%servers%", () => NadekoBot.Client.GetGuildsCount().ToString()}, + {"%users%", () => NadekoBot.Client.GetGuilds().Sum(s => s.Users.Count).ToString()}, {"%playing%", () => { var cnt = Music.Music.MusicPlayers.Count(kvp => kvp.Value.CurrentSong != null); if (cnt != 1) return cnt.ToString(); diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 3331edeb..35bd77e8 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -21,16 +21,14 @@ namespace NadekoBot.Services.Impl public int MessageCounter { get; private set; } = 0; public int CommandsRan { get; private set; } = 0; public string Heap => -#if !GLOBAL_NADEKO Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); -#else - "a lot :)"; -#endif public double MessagesPerSecond => MessageCounter / (double)GetUptime().TotalSeconds; - public int TextChannels => client.GetGuilds().SelectMany(g => g.Channels.Where(c => c is ITextChannel)).Count(); - public int VoiceChannels => client.GetGuilds().SelectMany(g => g.Channels.Where(c => c is IVoiceChannel)).Count(); + private uint _textChannels = 0; + public uint TextChannels => _textChannels; + private uint _voiceChannels = 0; + public uint VoiceChannels => _voiceChannels; public string OwnerIds => string.Join(", ", NadekoBot.Credentials.OwnerIds); - + Timer carbonitexTimer { get; } public StatsService(ShardedDiscordClient client, CommandHandler cmdHandler) @@ -44,17 +42,45 @@ namespace NadekoBot.Services.Impl this.client.Disconnected += _ => Reset(); + var guilds = this.client.GetGuilds(); + var _textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count()); + var _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; + + this.client.ChannelCreated += (c) => + { + if (c is ITextChannel) + ++_textChannels; + else if (c is IVoiceChannel) + ++_voiceChannels; + }; + + this.client.ChannelDestroyed += (c) => + { + if (c is ITextChannel) + --_textChannels; + else if (c is IVoiceChannel) + --_voiceChannels; + }; + + this.client.JoinedGuild += (g) => + { + var tc = g.Channels.Where(cx => cx is ITextChannel).Count(); + var vc = g.Channels.Count - tc; + _textChannels += tc; + _voiceChannels += vc; + }; + this.carbonitexTimer = new Timer(async (state) => { - if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey)) - return; + if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey)) + return; try { using (var http = new HttpClient()) { using (var content = new FormUrlEncodedContent( new Dictionary { - { "servercount", this.client.GetGuilds().Count.ToString() }, + { "servercount", this.client.GetGuildsCount().ToString() }, { "key", NadekoBot.Credentials.CarbonKey }})) { content.Headers.Clear(); @@ -76,7 +102,7 @@ Bot Version: [{BotVersion}] Bot ID: {curUser.Id} Owner ID(s): {OwnerIds} Uptime: {GetUptimeString()} -Servers: {client.GetGuilds().Count} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels} +Servers: {client.GetGuildsCount()} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels} Commands Ran this session: {CommandsRan} Messages: {MessageCounter} [{MessagesPerSecond:F2}/sec] Heap: [{Heap} MB]"); } diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 29630eaf..e7f2604c 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -18,6 +18,7 @@ namespace NadekoBot public event Action MessageReceived = delegate { }; public event Action UserLeft = delegate { }; public event Action UserUpdated = delegate { }; + public event Action GuildUserUpdated = delegate { }; public event Action, SocketMessage> MessageUpdated = delegate { }; public event Action> MessageDeleted = delegate { }; public event Action UserBanned = delegate { }; @@ -27,6 +28,10 @@ namespace NadekoBot public event Action ChannelCreated = delegate { }; public event Action ChannelDestroyed = delegate { }; public event Action ChannelUpdated = delegate { }; + + public event Action JoinedGuild = delegate { }; + public event Action LeftGuild = delegate { }; + public event Action Disconnected = delegate { }; private uint _connectedCount = 0; @@ -54,6 +59,7 @@ namespace NadekoBot }; client.UserLeft += arg1 => { UserLeft(arg1); return Task.CompletedTask; }; client.UserUpdated += (arg1, gu2) => { UserUpdated(arg1, gu2); return Task.CompletedTask; }; + client.GuildMemberUpdated += (arg1, arg2) => { GuildUserUpdated(arg1, arg2); return Task.CompletedTask; }; client.MessageUpdated += (arg1, m2) => { MessageUpdated(arg1, m2); return Task.CompletedTask; }; client.MessageDeleted += (arg1, arg2) => { MessageDeleted(arg1, arg2); return Task.CompletedTask; }; client.UserBanned += (arg1, arg2) => { UserBanned(arg1, arg2); return Task.CompletedTask; }; @@ -63,6 +69,8 @@ namespace NadekoBot client.ChannelCreated += arg => { ChannelCreated(arg); return Task.CompletedTask; }; client.ChannelDestroyed += arg => { ChannelDestroyed(arg); return Task.CompletedTask; }; client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; + client.JoinedGuild += (arg1) => { JoinedGuild(arg1); return Task.CompletedTask; }; + client.LeftGuild += (arg1) => { LeftGuild(arg1); return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); #if GLOBAL_NADEKO @@ -93,8 +101,8 @@ namespace NadekoBot public SocketSelfUser CurrentUser() => Clients[0].CurrentUser; - public IReadOnlyCollection GetGuilds() => - Clients.SelectMany(c => c.Guilds).ToList(); + public IEnumerable GetGuilds() => + Clients.SelectMany(c => c.Guilds); public int GetGuildsCount() => Clients.Sum(c => c.Guilds.Count); From 37d462712c684eb3fc9f39cfbfc19ba498ea1ffc Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 18:41:10 +0100 Subject: [PATCH 45/85] Fixes to .stats --- src/NadekoBot/Services/Impl/StatsService.cs | 25 +++++++++++++++------ src/NadekoBot/ShardedDiscordClient.cs | 2 ++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 35bd77e8..ffda1dae 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -23,10 +23,10 @@ namespace NadekoBot.Services.Impl public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); public double MessagesPerSecond => MessageCounter / (double)GetUptime().TotalSeconds; - private uint _textChannels = 0; - public uint TextChannels => _textChannels; - private uint _voiceChannels = 0; - public uint VoiceChannels => _voiceChannels; + private int _textChannels = 0; + public int TextChannels => _textChannels; + private int _voiceChannels = 0; + public int VoiceChannels => _voiceChannels; public string OwnerIds => string.Join(", ", NadekoBot.Credentials.OwnerIds); Timer carbonitexTimer { get; } @@ -42,9 +42,12 @@ namespace NadekoBot.Services.Impl this.client.Disconnected += _ => Reset(); - var guilds = this.client.GetGuilds(); - var _textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count()); - var _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; + this.client.Connected += () => + { + var guilds = this.client.GetGuilds(); + _textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count()); + _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; + }; this.client.ChannelCreated += (c) => { @@ -70,6 +73,14 @@ namespace NadekoBot.Services.Impl _voiceChannels += vc; }; + this.client.LeftGuild += (g) => + { + var tc = g.Channels.Where(cx => cx is ITextChannel).Count(); + var vc = g.Channels.Count - tc; + _textChannels -= tc; + _voiceChannels -= vc; + }; + this.carbonitexTimer = new Timer(async (state) => { if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey)) diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index e7f2604c..af6fc045 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -33,6 +33,7 @@ namespace NadekoBot public event Action LeftGuild = delegate { }; public event Action Disconnected = delegate { }; + public event Action Connected = delegate { }; private uint _connectedCount = 0; private uint _downloadedCount = 0; @@ -145,6 +146,7 @@ namespace NadekoBot } } } + Connected(); } internal Task DownloadAllUsersAsync() => From 55fd2c69bce714807297a05cced83693806690b4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 19:56:44 +0100 Subject: [PATCH 46/85] Nickname, role changes are logged. Slightly prettier --- .../Administration/Commands/LogCommand.cs | 70 ++++++++++++------- .../Administration/Commands/SelfCommands.cs | 2 +- .../Games/Commands/CleverBotCommands.cs | 2 +- src/NadekoBot/Modules/Utility/Utility.cs | 2 +- src/NadekoBot/ShardedDiscordClient.cs | 21 ++++-- 5 files changed, 64 insertions(+), 33 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 04bde59b..d3e2ff35 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -78,7 +78,10 @@ namespace NadekoBot.Modules.Administration _client.UserPresenceUpdated += _client_UserPresenceUpdated; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; + _client.GuildUserUpdated += _client_GuildUserUpdated; +#if !GLOBAL_NADEKO _client.UserUpdated += _client_UserUpdated; +#endif _client.ChannelCreated += _client_ChannelCreated; _client.ChannelDestroyed += _client_ChannelDestroyed; @@ -88,6 +91,35 @@ namespace NadekoBot.Modules.Administration MuteCommands.UserUnmuted += MuteCommands_UserUnmuted; } + private static async void _client_UserUpdated(SocketUser before, SocketUser after) + { + try + { + var str = ""; + if (before.Username != after.Username) + str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**"; + else if (before.AvatarUrl != after.AvatarUrl) + str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await NadekoBot.Google.ShortenUrl(before.AvatarUrl)} `=>` {await NadekoBot.Google.ShortenUrl(after.AvatarUrl)}"; + + var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList(); + foreach (var g in guildsMemberOf) + { + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(g.Id, out logSetting) + || (logSetting.UserUpdatedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.VoicePresenceTTS)) == null) + return; + + try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { } + } + } + catch + { } + } + private static async void _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) { try @@ -228,17 +260,10 @@ namespace NadekoBot.Modules.Administration catch (Exception ex) { _log.Warn(ex); } } - private static async void _client_UserUpdated(SocketUser uBefore, SocketUser uAfter) + private static async void _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) { try { - var before = uBefore as SocketGuildUser; - if (before == null) - return; - var after = uAfter as SocketGuildUser; - if (after == null) - return; - LogSetting logSetting; if (!GuildLogSettings.TryGetValue(before.Guild.Id, out logSetting) || (logSetting.UserUpdatedId == null)) @@ -247,30 +272,25 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null) return; - string str = $"🕔`{prettyCurrentTime}`"; - - if (before.Username != after.Username) - str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**"; - else if (before.Nickname != after.Nickname) - str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}#{before.Discriminator}**\n\t\t`New:` **{after.Nickname}#{after.Discriminator}**"; - else if (before.AvatarUrl != after.AvatarUrl) - str += $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await NadekoBot.Google.ShortenUrl(before.AvatarUrl)} `=>` {await NadekoBot.Google.ShortenUrl(after.AvatarUrl)}"; + var str = ""; + if (before.Nickname != after.Nickname) + str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}**\n\t\t`New:` **{after.Nickname}**"; else if (!before.RoleIds.SequenceEqual(after.RoleIds)) { if (before.RoleIds.Count < after.RoleIds.Count) { var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => "**" + before.Guild.GetRole(r).Name + "**"); - str += $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔"; + str = $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔"; } else if (before.RoleIds.Count > after.RoleIds.Count) { var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => "**" + before.Guild.GetRole(r).Name + "**"); - str += $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔"; + str = $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔"; } } else return; - try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try { await logChannel.SendMessageAsync("🕔`{prettyCurrentTime}` " + str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } catch (Exception ex) { _log.Warn(ex); } } @@ -745,9 +765,9 @@ namespace NadekoBot.Modules.Administration await uow.CompleteAsync().ConfigureAwait(false); } if (action.Value) - await channel.SendMessageAsync("✅ Logging all events on this channel.").ConfigureAwait(false); + await channel.SendConfirmAsync("Logging all events in this channel.").ConfigureAwait(false); else - await channel.SendMessageAsync("ℹ️ Logging disabled.").ConfigureAwait(false); + await channel.SendConfirmAsync("Logging disabled.").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -774,9 +794,9 @@ namespace NadekoBot.Modules.Administration } if (removed == 0) - await channel.SendMessageAsync($"🆗 Logging will **now ignore** #⃣ `{channel.Name} ({channel.Id})`").ConfigureAwait(false); + await channel.SendConfirmAsync($"Logging will IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false); else - await channel.SendMessageAsync($"ℹ️ Logging will **no longer ignore** #⃣ `{channel.Name} ({channel.Id})`").ConfigureAwait(false); + await channel.SendConfirmAsync($"Logging will NOT IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -853,9 +873,9 @@ namespace NadekoBot.Modules.Administration } if (channelId != null) - await channel.SendMessageAsync($"✅ Logging `{type}` event in #⃣ `{channel.Name} ({channel.Id})`").ConfigureAwait(false); + await channel.SendConfirmAsync($"Logging **{type}** event in this channel.").ConfigureAwait(false); else - await channel.SendMessageAsync($"ℹ️ Stopped logging `{type}` event.").ConfigureAwait(false); + await channel.SendConfirmAsync($"Stopped logging **{type}** event.").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 915b6373..753f85f5 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Administration public async Task Leave([Remainder] string guildStr) { guildStr = guildStr.Trim().ToUpperInvariant(); - var server = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Id.ToString().Trim().ToUpperInvariant() == guildStr) ?? + var server = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Id.ToString() == guildStr) ?? NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr); if (server == null) diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index 9ec59147..8197035b 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Games [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [RequireUserPermission(ChannelPermission.ManageMessages)] + [RequireUserPermission(GuildPermission.ManageMessages)] public async Task Cleverbot() { var channel = (ITextChannel)Context.Channel; diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 5c743db5..543b94d3 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -311,7 +311,7 @@ namespace NadekoBot.Modules.Utility if (page < 0) return; - var guilds = NadekoBot.Client.GetGuilds().OrderBy(g => g.Name).Skip((page - 1) * 15).Take(15); + var guilds = await Task.Run(() => NadekoBot.Client.GetGuilds().OrderBy(g => g.Name).Skip((page - 1) * 15).Take(15)).ConfigureAwait(false); if (!guilds.Any()) { diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index af6fc045..7a538192 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -38,6 +38,8 @@ namespace NadekoBot private uint _connectedCount = 0; private uint _downloadedCount = 0; + private int _guildCount = 0; + private IReadOnlyList Clients { get; } public ShardedDiscordClient(DiscordSocketConfig discordSocketConfig) @@ -70,8 +72,8 @@ namespace NadekoBot client.ChannelCreated += arg => { ChannelCreated(arg); return Task.CompletedTask; }; client.ChannelDestroyed += arg => { ChannelDestroyed(arg); return Task.CompletedTask; }; client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; - client.JoinedGuild += (arg1) => { JoinedGuild(arg1); return Task.CompletedTask; }; - client.LeftGuild += (arg1) => { LeftGuild(arg1); return Task.CompletedTask; }; + client.JoinedGuild += (arg1) => { JoinedGuild(arg1); ++_guildCount; return Task.CompletedTask; }; + client.LeftGuild += (arg1) => { LeftGuild(arg1); --_guildCount; return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); #if GLOBAL_NADEKO @@ -106,10 +108,18 @@ namespace NadekoBot Clients.SelectMany(c => c.Guilds); public int GetGuildsCount() => - Clients.Sum(c => c.Guilds.Count); + _guildCount; - public SocketGuild GetGuild(ulong id) => - Clients.Select(c => c.GetGuild(id)).FirstOrDefault(g => g != null); + public SocketGuild GetGuild(ulong id) + { + foreach (var c in Clients) + { + var g = c.GetGuild(id); + if (g != null) + return g; + } + return null; + } public Task GetDMChannelAsync(ulong channelId) => Clients[0].GetDMChannelAsync(channelId); @@ -134,6 +144,7 @@ namespace NadekoBot await c.ConnectAsync().ConfigureAwait(false); sw.Stop(); _log.Info($"Shard #{c.ShardId} connected after {sw.Elapsed.TotalSeconds:F2}s ({++_connectedCount}/{Clients.Count})"); + _guildCount += c.Guilds.Count; } catch { From 8c2a94aeb8f8a034f1ce4cf96da80f874a797a09 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 20:40:10 +0100 Subject: [PATCH 47/85] Name/avatar changes are ready to work, waiting for lib fix --- .../Modules/Administration/Commands/LogCommand.cs | 5 ++++- src/NadekoBot/Services/CommandHandler.cs | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index d3e2ff35..98fee2e3 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -101,6 +101,9 @@ namespace NadekoBot.Modules.Administration else if (before.AvatarUrl != after.AvatarUrl) str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await NadekoBot.Google.ShortenUrl(before.AvatarUrl)} `=>` {await NadekoBot.Google.ShortenUrl(after.AvatarUrl)}"; + if (string.IsNullOrWhiteSpace(str)) + return; + var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList(); foreach (var g in guildsMemberOf) { @@ -110,7 +113,7 @@ namespace NadekoBot.Modules.Administration return; ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.VoicePresenceTTS)) == null) + if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) return; try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index e22249e9..9175f363 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -223,9 +223,13 @@ namespace NadekoBot.Services } catch (Exception ex) { - _log.Warn(ex, "Error in CommandHandler"); + _log.Warn("Error in CommandHandler"); + _log.Warn(ex); if (ex.InnerException != null) - _log.Warn(ex.InnerException, "Inner Exception of the error in CommandHandler"); + { + _log.Warn("Inner Exception of the error in CommandHandler"); + _log.Warn(ex.InnerException); + } } return; From 0427642af28bc8e855bf36b56ea83a46bc8e38cf Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 22:31:42 +0100 Subject: [PATCH 48/85] CommandHandler refactor --- .../Permissions/Commands/BlacklistCommands.cs | 35 ++- src/NadekoBot/Services/CommandHandler.cs | 282 ++++++++++-------- 2 files changed, 183 insertions(+), 134 deletions(-) diff --git a/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs index 72c41bc3..e02dc895 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs @@ -23,13 +23,18 @@ namespace NadekoBot.Modules.Permissions [Group] public class BlacklistCommands : ModuleBase { - public static ConcurrentHashSet BlacklistedItems { get; set; } = new ConcurrentHashSet(); + public static ConcurrentHashSet BlacklistedUsers { get; set; } = new ConcurrentHashSet(); + public static ConcurrentHashSet BlacklistedGuilds { get; set; } = new ConcurrentHashSet(); + public static ConcurrentHashSet BlacklistedChannels { get; set; } = new ConcurrentHashSet(); static BlacklistCommands() { using (var uow = DbHandler.UnitOfWork()) { - BlacklistedItems = new ConcurrentHashSet(uow.BotConfig.GetOrCreate().Blacklist); + var blacklist = uow.BotConfig.GetOrCreate().Blacklist; + BlacklistedUsers = new ConcurrentHashSet(blacklist.Where(bi => bi.Type == BlacklistType.User).Select(c => c.ItemId)); + BlacklistedGuilds = new ConcurrentHashSet(blacklist.Where(bi => bi.Type == BlacklistType.Server).Select(c => c.ItemId)); + BlacklistedChannels = new ConcurrentHashSet(blacklist.Where(bi => bi.Type == BlacklistType.Channel).Select(c => c.ItemId)); } } @@ -66,12 +71,34 @@ namespace NadekoBot.Modules.Permissions { var item = new BlacklistItem { ItemId = id, Type = type }; uow.BotConfig.GetOrCreate().Blacklist.Add(item); - BlacklistedItems.Add(item); + if (type == BlacklistType.Server) + { + BlacklistedGuilds.Add(id); + } + else if (type == BlacklistType.Channel) + { + BlacklistedChannels.Add(id); + } + else if (type == BlacklistType.User) + { + BlacklistedUsers.Add(id); + } } else { uow.BotConfig.GetOrCreate().Blacklist.RemoveWhere(bi => bi.ItemId == id && bi.Type == type); - BlacklistedItems.RemoveWhere(bi => bi.ItemId == id && bi.Type == type); + if (type == BlacklistType.Server) + { + BlacklistedGuilds.TryRemove(id); + } + else if (type == BlacklistType.Channel) + { + BlacklistedChannels.TryRemove(id); + } + else if (type == BlacklistType.User) + { + BlacklistedUsers.TryRemove(id); + } } await uow.CompleteAsync().ConfigureAwait(false); } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 9175f363..e4e30c4d 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -18,6 +18,7 @@ using NadekoBot.Modules.CustomReactions; using NadekoBot.Modules.Games; using System.Collections.Concurrent; using System.Threading; +using NadekoBot.DataStructures; namespace NadekoBot.Services { @@ -29,6 +30,8 @@ namespace NadekoBot.Services } public class CommandHandler { + public const int GlobalCommandsCooldown = 1500; + private ShardedDiscordClient _client; private CommandService _commandService; private Logger _log; @@ -52,7 +55,7 @@ namespace NadekoBot.Services clearUsersOnShortCooldown = new Timer((_) => { UsersOnShortCooldown.Clear(); - }, null, 1500, 1500); + }, null, GlobalCommandsCooldown, GlobalCommandsCooldown); } public async Task StartHandling() { @@ -71,153 +74,187 @@ namespace NadekoBot.Services _client.MessageReceived += MessageReceivedHandler; } - private async void MessageReceivedHandler(SocketMessage msg) + private async Task TryRunCleverbot(SocketUserMessage usrMsg, IGuild guild) { + if (guild == null) + return false; try { - var usrMsg = msg as SocketUserMessage; - if (usrMsg == null) - return; - - if (!usrMsg.IsAuthor()) - UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); - - if (!UsersOnShortCooldown.Add(usrMsg.Author.Id)) - return; - - if (msg.Author.IsBot || !NadekoBot.Ready) //no bots - return; - - var guild = (msg.Channel as SocketTextChannel)?.Guild; - - if (guild != null && guild.OwnerId != msg.Author.Id) + var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg).ConfigureAwait(false); + if (cleverbotExecuted) { - //todo split checks into their own modules - if (Permissions.FilterCommands.InviteFilteringChannels.Contains(msg.Channel.Id) || - Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id)) - { - if (usrMsg.Content.IsDiscordInvite()) - { - try - { - await usrMsg.DeleteAsync().ConfigureAwait(false); - return; - } - catch (HttpException ex) - { - _log.Warn("I do not have permission to filter invites in channel with id " + msg.Channel.Id, ex); - } - } - } - - var filteredWords = Permissions.FilterCommands.FilteredWordsForChannel(msg.Channel.Id, guild.Id).Concat(Permissions.FilterCommands.FilteredWordsForServer(guild.Id)); - var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' '); - if (filteredWords.Any(w => wordsInMessage.Contains(w))) - { - try - { - await usrMsg.DeleteAsync().ConfigureAwait(false); - return; - } - catch (HttpException ex) - { - _log.Warn("I do not have permission to filter words in channel with id " + msg.Channel.Id, ex); - } - } - } - - BlacklistItem blacklistedItem; - if ((blacklistedItem = Permissions.BlacklistCommands.BlacklistedItems.FirstOrDefault(bi => - (bi.Type == BlacklistItem.BlacklistType.Server && bi.ItemId == guild?.Id) || - (bi.Type == BlacklistItem.BlacklistType.Channel && bi.ItemId == msg.Channel.Id) || - (bi.Type == BlacklistItem.BlacklistType.User && bi.ItemId == msg.Author.Id))) != null) - { - return; - } - - try - { - var cleverbotExecuted = await Games.CleverBotCommands.TryAsk(usrMsg); - if (cleverbotExecuted) - { - _log.Info($@"CleverBot Executed + _log.Info($@"CleverBot Executed Server: {guild.Name} [{guild.Id}] Channel: {usrMsg.Channel?.Name} [{usrMsg.Channel?.Id}] UserId: {usrMsg.Author} [{usrMsg.Author.Id}] Message: {usrMsg.Content}"); - return; - } + return true; } - catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); } + } + catch (Exception ex) { _log.Warn(ex, "Error in cleverbot"); } + return false; + } - try - { - // maybe this message is a custom reaction - var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false); + private bool IsBlacklisted(IGuild guild, SocketUserMessage usrMsg) => + (guild != null && BlacklistCommands.BlacklistedGuilds.Contains(guild.Id)) || + BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) || + BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id); - //if it was, don't execute the command - if (crExecuted) - return; - } - catch { } - string messageContent = usrMsg.Content; + private async Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, Stopwatch sw) + { + await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); + _log.Info("Command Executed after {4}s\n\t" + + "User: {0}\n\t" + + "Server: {1}\n\t" + + "Channel: {2}\n\t" + + "Message: {3}", + usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0} + (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} + (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} + usrMsg.Content, // {3} + sw.Elapsed.TotalSeconds); + } - var sw = new Stopwatch(); - sw.Start(); - var exec = await ExecuteCommand(new CommandContext(_client.MainClient, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best); - var command = exec.CommandInfo; - var permCache = exec.PermissionCache; - var result = exec.Result; - sw.Stop(); - var channel = (msg.Channel as ITextChannel); - if (result.IsSuccess) - { - await CommandExecuted(usrMsg, command); - _log.Info("Command Executed after {4}s\n\t" + - "User: {0}\n\t" + - "Server: {1}\n\t" + - "Channel: {2}\n\t" + - "Message: {3}", - msg.Author + " [" + msg.Author.Id + "]", // {0} - (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} - (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} - usrMsg.Content, // {3} - sw.Elapsed.TotalSeconds // {4} - ); - } - else if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) - { - _log.Warn("Command Errored after {5}s\n\t" + + private void LogErroredExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, Stopwatch sw) + { + _log.Warn("Command Errored after {5}s\n\t" + "User: {0}\n\t" + "Server: {1}\n\t" + "Channel: {2}\n\t" + "Message: {3}\n\t" + "Error: {4}", - msg.Author + " [" + msg.Author.Id + "]", // {0} + usrMsg.Author + " [" + usrMsg.Author.Id + "]", // {0} (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content,// {3} - result.ErrorReason, // {4} + exec.Result.ErrorReason, // {4} sw.Elapsed.TotalSeconds // {5} ); - if (guild != null && command != null && result.Error == CommandError.Exception) + } + + private async Task InviteFiltered(IGuild guild, SocketUserMessage usrMsg) + { + if ((Permissions.FilterCommands.InviteFilteringChannels.Contains(usrMsg.Channel.Id) || + Permissions.FilterCommands.InviteFilteringServers.Contains(guild.Id)) && + usrMsg.Content.IsDiscordInvite()) + { + try + { + await usrMsg.DeleteAsync().ConfigureAwait(false); + return true; + } + catch (HttpException ex) + { + _log.Warn("I do not have permission to filter invites in channel with id " + usrMsg.Channel.Id, ex); + return true; + } + } + return false; + } + + private async Task WordFiltered(IGuild guild, SocketUserMessage usrMsg) + { + var filteredChannelWords = Permissions.FilterCommands.FilteredWordsForChannel(usrMsg.Channel.Id, guild.Id); + var filteredServerWords = Permissions.FilterCommands.FilteredWordsForServer(guild.Id); + var wordsInMessage = usrMsg.Content.ToLowerInvariant().Split(' '); + if (filteredChannelWords.Count != 0 || filteredServerWords.Count != 0) + { + foreach (var word in wordsInMessage) + { + if (filteredChannelWords.Contains(word) || + filteredServerWords.Contains(word)) { - if (permCache != null && permCache.Verbose) - try { await msg.Channel.SendMessageAsync("⚠️ " + result.ErrorReason).ConfigureAwait(false); } catch { } + try + { + await usrMsg.DeleteAsync().ConfigureAwait(false); + } + catch (HttpException ex) + { + _log.Warn("I do not have permission to filter words in channel with id " + usrMsg.Channel.Id, ex); + } + return true; + } + } + } + return false; + } + + private async void MessageReceivedHandler(SocketMessage msg) + { + try + { + if (msg.Author.IsBot || !NadekoBot.Ready) //no bots, wait until bot connected and initialized + return; + + var usrMsg = msg as SocketUserMessage; + if (usrMsg == null) //has to be an user message, not system/other messages. + return; + + // track how many messagges each user is sending + UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); + + // Bot will ignore commands which are ran more often than what specified by + // GlobalCommandsCooldown constant (miliseconds) + if (!UsersOnShortCooldown.Add(usrMsg.Author.Id)) + return; + + var channel = msg.Channel as SocketTextChannel; + var guild = channel?.Guild; + + if (guild != null && guild.OwnerId != msg.Author.Id) + { + if (await InviteFiltered(guild, usrMsg).ConfigureAwait(false)) + return; + + if (await WordFiltered(guild, usrMsg).ConfigureAwait(false)) + return; + } + + if (IsBlacklisted(guild, usrMsg)) + return; + + var cleverBotRan = await TryRunCleverbot(usrMsg, guild).ConfigureAwait(false); + if (cleverBotRan) + return; + + // maybe this message is a custom reaction + var crExecuted = await CustomReactions.TryExecuteCustomReaction(usrMsg).ConfigureAwait(false); + if (crExecuted) //if it was, don't execute the command + return; + + string messageContent = usrMsg.Content; + + // execute the command and measure the time it took + var sw = Stopwatch.StartNew(); + var exec = await ExecuteCommand(new CommandContext(_client.MainClient, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best); + sw.Stop(); + + if (exec.Result.IsSuccess) + { + await LogSuccessfulExecution(usrMsg, exec, channel, sw).ConfigureAwait(false); + } + else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) + { + LogErroredExecution(usrMsg, exec, channel, sw); + if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception) + { + if (exec.PermissionCache != null && exec.PermissionCache.Verbose) + try { await msg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { } } } else { if (msg.Channel is IPrivateChannel) { - //rofl, gotta do this to prevent this message from occuring on polls + // rofl, gotta do this to prevent dm help message being sent to + // users who are voting on private polls (sending a number in a DM) int vote; if (int.TryParse(msg.Content, out vote)) return; - + await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); - await DMForwardCommands.HandleDMForwarding(msg, ownerChannels); + await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false); } } } @@ -231,9 +268,8 @@ namespace NadekoBot.Services _log.Warn(ex.InnerException); } } - - return; } + public Task ExecuteCommandAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) => ExecuteCommand(context, context.Message.Content.Substring(argPos), dependencyMap, multiMatchHandling); @@ -328,19 +364,5 @@ namespace NadekoBot.Services return new ExecuteCommandResult(null, null, SearchResult.FromError(CommandError.UnknownCommand, "This input does not match any overload.")); } - - public struct ExecuteCommandResult - { - public readonly CommandInfo CommandInfo; - public readonly PermissionCache PermissionCache; - public readonly IResult Result; - - public ExecuteCommandResult(CommandInfo commandInfo, PermissionCache cache, IResult result) - { - this.CommandInfo = commandInfo; - this.PermissionCache = cache; - this.Result = result; - } - } } } \ No newline at end of file From 09dbdf83ade8f33b2457b10f95176087ec4aae9c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 22:34:34 +0100 Subject: [PATCH 49/85] Fix --- .../DataStructures/ExecuteCommandResult.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/NadekoBot/DataStructures/ExecuteCommandResult.cs diff --git a/src/NadekoBot/DataStructures/ExecuteCommandResult.cs b/src/NadekoBot/DataStructures/ExecuteCommandResult.cs new file mode 100644 index 00000000..c8568c43 --- /dev/null +++ b/src/NadekoBot/DataStructures/ExecuteCommandResult.cs @@ -0,0 +1,24 @@ +using Discord.Commands; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using static NadekoBot.Modules.Permissions.Permissions; + +namespace NadekoBot.DataStructures +{ + public struct ExecuteCommandResult + { + public readonly CommandInfo CommandInfo; + public readonly PermissionCache PermissionCache; + public readonly IResult Result; + + public ExecuteCommandResult(CommandInfo commandInfo, PermissionCache cache, IResult result) + { + this.CommandInfo = commandInfo; + this.PermissionCache = cache; + this.Result = result; + } + } +} From 6cb0587a1beda6f9b70b952d84c1cff450a09a60 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 7 Jan 2017 22:44:42 +0100 Subject: [PATCH 50/85] Not use old api --- src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs | 6 +++--- .../Modules/Games/Commands/PlantAndPickCommands.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 3e8c01a5..c37dae67 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -26,9 +26,9 @@ namespace NadekoBot.Modules.Gambling if (count == 1) { if (rng.Next(0, 2) == 1) - await Context.Channel.SendFileAsync(headsPath, $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); else - await Context.Channel.SendFileAsync(tailsPath, $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); return; } if (count > 10 || count < 1) @@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Gambling str = $"{Context.User.Mention}`Better luck next time.`"; } - await Context.Channel.SendFileAsync(imgPathToSend, str).ConfigureAwait(false); + await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), str).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 69fbc862..d85bbb75 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -94,7 +94,7 @@ namespace NadekoBot.Modules.Games lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); var sent = await channel.SendFileAsync( - GetRandomCurrencyImagePath(), + File.Open(GetRandomCurrencyImagePath(), FileMode.OpenOrCreate), $"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") .ConfigureAwait(false); plantedFlowers.AddOrUpdate(channel.Id, new List() { sent }, (id, old) => { old.Add(sent); return old; }); @@ -163,7 +163,7 @@ namespace NadekoBot.Modules.Games } else { - msg = await Context.Channel.SendFileAsync(file, msgToSend).ConfigureAwait(false); + msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), msgToSend).ConfigureAwait(false); } plantedFlowers.AddOrUpdate(Context.Channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); } From fa2b9788dd466ec9eb9c7e65e160a6d1933b6548 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 08:56:28 +0100 Subject: [PATCH 51/85] Fixed images --- src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs | 6 +++--- .../Modules/Games/Commands/PlantAndPickCommands.cs | 2 +- src/NadekoBot/Services/Impl/StatsService.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index c37dae67..389c7927 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -26,9 +26,9 @@ namespace NadekoBot.Modules.Gambling if (count == 1) { if (rng.Next(0, 2) == 1) - await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); else - await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); return; } if (count > 10 || count < 1) @@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Gambling str = $"{Context.User.Mention}`Better luck next time.`"; } - await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), str).ConfigureAwait(false); + await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), "coin.jpg", str).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index d85bbb75..aee98cdf 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -163,7 +163,7 @@ namespace NadekoBot.Modules.Games } else { - msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), msgToSend).ConfigureAwait(false); + msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), "plant.jpg", msgToSend).ConfigureAwait(false); } plantedFlowers.AddOrUpdate(Context.Channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); } diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index ffda1dae..558feed7 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Services.Impl public int CommandsRan { get; private set; } = 0; public string Heap => Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); - public double MessagesPerSecond => MessageCounter / (double)GetUptime().TotalSeconds; + public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds; private int _textChannels = 0; public int TextChannels => _textChannels; private int _voiceChannels = 0; From 511db159f7567285f35196ed5a36ce12588d082c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 08:57:26 +0100 Subject: [PATCH 52/85] Hangman back --- src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index 33170986..eba11193 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Games { await Context.Channel.SendConfirmAsync(typesStr); } -#if !GLOBAL_NADEKO + [NadekoCommand, Usage, Description, Aliases] public async Task Hangman(HangmanTermPool.HangmanTermType type = HangmanTermPool.HangmanTermType.All) { @@ -52,7 +52,6 @@ namespace NadekoBot.Modules.Games await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord); } -#endif } } } From 42977c7941f7a5bfd7d8f752681325c93466d26b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 09:02:44 +0100 Subject: [PATCH 53/85] Download users again --- src/NadekoBot/NadekoBot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 8b7bba44..9685b510 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -91,7 +91,7 @@ namespace NadekoBot //connect await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false); await Client.ConnectAsync().ConfigureAwait(false); - //await Client.DownloadAllUsersAsync().ConfigureAwait(false); + await Client.DownloadAllUsersAsync().ConfigureAwait(false); _log.Info("Connected"); From 1c46d0442177527774f1e7cf1fd0a7e95bdd744a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 09:48:35 +0100 Subject: [PATCH 54/85] You can now edit hangman.json to add more categories --- .../Games/Commands/Hangman/HangmanGame.cs | 65 +++++++------------ .../Modules/Games/Commands/HangmanCommands.cs | 13 +++- 2 files changed, 35 insertions(+), 43 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs index d096b452..58071453 100644 --- a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs @@ -12,37 +12,15 @@ using System.Threading.Tasks; namespace NadekoBot.Modules.Games.Commands.Hangman { - public class HangmanModel - { - public List All { get; set; } - public List Animals { get; set; } - public List Countries { get; set; } - public List Movies { get; set; } - public List Things { get; set; } - } - public class HangmanTermPool { - public enum HangmanTermType - { - All, - Animals, - Countries, - Movies, - Things - } - const string termsPath = "data/hangman.json"; - public static HangmanModel data { get; } + public static IReadOnlyDictionary data { get; } static HangmanTermPool() { try { - data = JsonConvert.DeserializeObject(File.ReadAllText(termsPath)); - data.All = data.Animals.Concat(data.Countries) - .Concat(data.Movies) - .Concat(data.Things) - .ToList(); + data = JsonConvert.DeserializeObject>(File.ReadAllText(termsPath)); } catch (Exception ex) { @@ -50,23 +28,27 @@ namespace NadekoBot.Modules.Games.Commands.Hangman } } - public static HangmanObject GetTerm(HangmanTermType type) + public static HangmanObject GetTerm(string type) { + if (string.IsNullOrWhiteSpace(type)) + throw new ArgumentNullException(nameof(type)); + + type = type.Trim(); + var rng = new NadekoRandom(); - switch (type) - { - case HangmanTermType.Animals: - return data.Animals[rng.Next(0, data.Animals.Count)]; - case HangmanTermType.Countries: - return data.Countries[rng.Next(0, data.Countries.Count)]; - case HangmanTermType.Movies: - return data.Movies[rng.Next(0, data.Movies.Count)]; - case HangmanTermType.Things: - return data.Things[rng.Next(0, data.Things.Count)]; - default: - return data.All[rng.Next(0, data.All.Count)]; + + if (type == "All") { + var keys = data.Keys.ToArray(); + type = keys[rng.Next(0, keys.Length)]; } + HangmanObject[] termTypes; + data.TryGetValue(type, out termTypes); + + if (termTypes.Length == 0) + return null; + + return termTypes[rng.Next(0, termTypes.Length)]; } } @@ -95,20 +77,23 @@ namespace NadekoBot.Modules.Games.Commands.Hangman public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant() .Where(c => char.IsLetter(c) || char.IsDigit(c))); - public HangmanTermPool.HangmanTermType TermType { get; } + public string TermType { get; } public event Action OnEnded; - public HangmanGame(IMessageChannel channel, HangmanTermPool.HangmanTermType type) + public HangmanGame(IMessageChannel channel, string type) { _log = LogManager.GetCurrentClassLogger(); this.GameChannel = channel; - this.TermType = type; + this.TermType = type.ToTitleCase(); } public void Start() { this.Term = HangmanTermPool.GetTerm(TermType); + + if (this.Term == null) + throw new KeyNotFoundException("Can't find a term with that type. Use hangmanlist command."); // start listening for answers when game starts NadekoBot.Client.MessageReceived += PotentialGuess; } diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index eba11193..0827ab94 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Games static HangmanCommands() { _log = LogManager.GetCurrentClassLogger(); - typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", Enum.GetNames(typeof(HangmanTermPool.HangmanTermType))); + typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys); } [NadekoCommand, Usage, Description, Aliases] @@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Games } [NadekoCommand, Usage, Description, Aliases] - public async Task Hangman(HangmanTermPool.HangmanTermType type = HangmanTermPool.HangmanTermType.All) + public async Task Hangman([Remainder]string type = "All") { var hm = new HangmanGame(Context.Channel, type); @@ -48,7 +48,14 @@ namespace NadekoBot.Modules.Games HangmanGame throwaway; HangmanGames.TryRemove(g.GameChannel.Id, out throwaway); }; - hm.Start(); + try + { + hm.Start(); + } + catch (Exception ex) { + try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { } + return; + } await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman() + "\n" + hm.ScrambledWord); } From ca0a16d813e74e9baae1893d265d69620c641e5c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 12:42:48 +0100 Subject: [PATCH 55/85] cleverbot change for public bot, role changes now show time --- .../Modules/Administration/Commands/LogCommand.cs | 2 +- src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs | 8 +++++++- src/NadekoBot/Services/CleverBotApi/Cleverbot.cs | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 98fee2e3..856d729d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -293,7 +293,7 @@ namespace NadekoBot.Modules.Administration } else return; - try { await logChannel.SendMessageAsync("🕔`{prettyCurrentTime}` " + str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try { await logChannel.SendMessageAsync($"🕔`{prettyCurrentTime}` " + str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } catch (Exception ex) { _log.Warn(ex); } } diff --git a/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs b/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs index 36589fe6..7b952b1c 100644 --- a/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs +++ b/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs @@ -29,10 +29,16 @@ namespace Services.CleverBotApi public static ChatterBot Create(ChatterBotType type, object arg) { +#if GLOBAL_NADEKO + var url = "http://www.cleverbot.com/webservicemin?uc=321&botapi=nadekobot"; +#else + var url = "http://www.cleverbot.com/webservicemin?uc=321"; +#endif + switch (type) { case ChatterBotType.CLEVERBOT: - return new Cleverbot("http://www.cleverbot.com/", "http://www.cleverbot.com/webservicemin?uc=321", 26); + return new Cleverbot("http://www.cleverbot.com/", url, 26); case ChatterBotType.JABBERWACKY: return new Cleverbot("http://jabberwacky.com", "http://jabberwacky.com/webservicemin", 20); case ChatterBotType.PANDORABOTS: diff --git a/src/NadekoBot/Services/CleverBotApi/Cleverbot.cs b/src/NadekoBot/Services/CleverBotApi/Cleverbot.cs index 5599d7b0..45109863 100644 --- a/src/NadekoBot/Services/CleverBotApi/Cleverbot.cs +++ b/src/NadekoBot/Services/CleverBotApi/Cleverbot.cs @@ -46,7 +46,7 @@ namespace Services.CleverBotApi private readonly int endIndex; private readonly string url; private readonly IDictionary vars; - private readonly CookieCollection cookies; + private readonly CookieCollection cookies; public CleverbotSession(string baseUrl, string url, int endIndex) { @@ -60,7 +60,7 @@ namespace Services.CleverBotApi //vars["fno"] = "0"; //vars["sub"] = "Say"; //vars["cleanslate"] = "false"; - cookies = Utils.GetCookies(baseUrl); + cookies = Utils.GetCookies(baseUrl); } public async Task Think(ChatterBotThought thought) From 3ad2b58ca1a40f268455ab896407d22946491a79 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 14:53:14 +0100 Subject: [PATCH 56/85] ~autohentai won't post no reuslts found anymore --- src/NadekoBot/Modules/NSFW/NSFW.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 7c692f0b..4db4c1b3 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -49,7 +49,7 @@ namespace NadekoBot.Modules.NSFW var link = await provider.ConfigureAwait(false); if (string.IsNullOrWhiteSpace(link)) { - if (noError) + if (!noError) await channel.SendErrorAsync("No results found.").ConfigureAwait(false); return; } From 41c610c04f4c00f957dec8ccbe98ed545c06ff37 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 15:37:12 +0100 Subject: [PATCH 57/85] Fixed random currency generation image --- src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index aee98cdf..c50b9ac1 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -95,6 +95,7 @@ namespace NadekoBot.Modules.Games var sent = await channel.SendFileAsync( File.Open(GetRandomCurrencyImagePath(), FileMode.OpenOrCreate), + "RandomFlower.jpg", $"❗ A random { Gambling.Gambling.CurrencyName } appeared! Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") .ConfigureAwait(false); plantedFlowers.AddOrUpdate(channel.Id, new List() { sent }, (id, old) => { old.Add(sent); return old; }); From 8a76d201974394be5d14ad99fa19fb26ba002657 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 17:22:06 +0100 Subject: [PATCH 58/85] Pick/plant back, not downloading users on public bot --- src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs | 3 +-- src/NadekoBot/NadekoBot.cs | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index c50b9ac1..078b9183 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Games } catch { } } -#if !GLOBAL_NADEKO + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Pick() @@ -168,7 +168,6 @@ namespace NadekoBot.Modules.Games } plantedFlowers.AddOrUpdate(Context.Channel.Id, new List() { msg }, (id, old) => { old.Add(msg); return old; }); } -#endif [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 9685b510..9f72a07f 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -91,7 +91,9 @@ namespace NadekoBot //connect await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false); await Client.ConnectAsync().ConfigureAwait(false); +#if !GLOBAL_NADEKO await Client.DownloadAllUsersAsync().ConfigureAwait(false); +#endif _log.Info("Connected"); From 812e9656a175649424ba3b0d59ae538e716d0b36 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 18:08:57 +0100 Subject: [PATCH 59/85] >pollstats added --- .../Modules/Games/Commands/PollCommands.cs | 97 ++++++++++++------- .../Resources/CommandStrings.Designer.cs | 27 ++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 ++ 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index f7321a72..01d0477c 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -17,7 +18,7 @@ namespace NadekoBot.Modules.Games [Group] public class PollCommands : ModuleBase { - public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); + public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); [NadekoCommand, Usage, Description, Aliases] [RequireUserPermission(GuildPermission.ManageMessages)] @@ -31,6 +32,18 @@ namespace NadekoBot.Modules.Games public Task PublicPoll([Remainder] string arg = null) => InternalStartPoll(arg, isPublic: true); + [NadekoCommand, Usage, Description, Aliases] + [RequireUserPermission(GuildPermission.ManageMessages)] + [RequireContext(ContextType.Guild)] + public async Task PollStats() + { + Games.Poll poll; + if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll)) + return; + + await Context.Channel.EmbedAsync(poll.GetStats("Current Poll Results")); + } + private async Task InternalStartPoll(string arg, bool isPublic = false) { var channel = (ITextChannel)Context.Channel; @@ -44,7 +57,7 @@ namespace NadekoBot.Modules.Games return; var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic); - if (ActivePolls.TryAdd(channel.Guild, poll)) + if (ActivePolls.TryAdd(channel.Guild.Id, poll)) { await poll.StartPoll().ConfigureAwait(false); } @@ -60,7 +73,7 @@ namespace NadekoBot.Modules.Games var channel = (ITextChannel)Context.Channel; Poll poll; - ActivePolls.TryRemove(channel.Guild, out poll); + ActivePolls.TryRemove(channel.Guild.Id, out poll); await poll.StopPoll().ConfigureAwait(false); } } @@ -69,20 +82,55 @@ namespace NadekoBot.Modules.Games { private readonly IUserMessage originalMessage; private readonly IGuild guild; - private readonly string[] answers; + private string[] Answers { get; } private ConcurrentDictionary participants = new ConcurrentDictionary(); private readonly string question; private DateTime started; private CancellationTokenSource pollCancellationSource = new CancellationTokenSource(); - private readonly bool isPublic; + public bool IsPublic { get; } public Poll(IUserMessage umsg, string question, IEnumerable enumerable, bool isPublic = false) { this.originalMessage = umsg; this.guild = ((ITextChannel)umsg.Channel).Guild; this.question = question; - this.answers = enumerable as string[] ?? enumerable.ToArray(); - this.isPublic = isPublic; + this.Answers = enumerable as string[] ?? enumerable.ToArray(); + this.IsPublic = isPublic; + } + + public EmbedBuilder GetStats(string title) + { + var results = participants.GroupBy(kvp => kvp.Value) + .ToDictionary(x => x.Key, x => x.Sum(kvp => 1)) + .OrderByDescending(kvp => kvp.Value) + .ToArray(); + + var eb = new EmbedBuilder().WithTitle(title); + + var sb = new StringBuilder() + .AppendLine(Format.Bold(question)) + .AppendLine(); + + var totalVotesCast = 0; + if (results.Length == 0) + { + sb.AppendLine("No votes cast."); + } + else + { + for (int i = 0; i < results.Length; i++) + { + var result = results[i]; + sb.AppendLine($"`{i + 1}.` {Format.Bold(Answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes."); + totalVotesCast += result.Value; + } + } + + + eb.WithDescription(sb.ToString()) + .WithFooter(efb => efb.WithText(totalVotesCast + " total votes cast.")); + + return eb; } public async Task StartPoll() @@ -91,8 +139,8 @@ namespace NadekoBot.Modules.Games NadekoBot.Client.MessageReceived += Vote; var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n"; var num = 1; - msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); - if (!isPublic) + msgToSend = Answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); + if (!IsPublic) msgToSend += "\n**Private Message me with the corresponding number of the answer.**"; else msgToSend += "\n**Send a Message here with the corresponding number of the answer.**"; @@ -102,30 +150,7 @@ namespace NadekoBot.Modules.Games public async Task StopPoll() { NadekoBot.Client.MessageReceived -= Vote; - try - { - var results = participants.GroupBy(kvp => kvp.Value) - .ToDictionary(x => x.Key, x => x.Sum(kvp => 1)) - .OrderByDescending(kvp => kvp.Value); - - var totalVotesCast = results.Sum(kvp => kvp.Value); - if (totalVotesCast == 0) - { - await originalMessage.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false); - return; - } - var closeMessage = $"--------------**POLL CLOSED**--------------\n" + - $"📄 , here are the results:\n"; - closeMessage = results.Aggregate(closeMessage, (current, kvp) => current + $"`{kvp.Key}.` **[{answers[kvp.Key - 1]}]**" + - $" has {kvp.Value} votes." + - $"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n"); - - await originalMessage.Channel.SendConfirmAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false); - } - catch (Exception ex) - { - Console.WriteLine($"Error in poll game {ex}"); - } + await originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false); } private async void Vote(SocketMessage imsg) @@ -141,11 +166,11 @@ namespace NadekoBot.Modules.Games int vote; if (!int.TryParse(imsg.Content, out vote)) return; - if (vote < 1 || vote > answers.Length) + if (vote < 1 || vote > Answers.Length) return; IMessageChannel ch; - if (isPublic) + if (IsPublic) { //if public, channel must be the same the poll started in if (originalMessage.Channel.Id != imsg.Channel.Id) @@ -167,7 +192,7 @@ namespace NadekoBot.Modules.Games //user can vote only once if (participants.TryAdd(msg.Author.Id, vote)) { - if (!isPublic) + if (!IsPublic) { await ch.SendConfirmAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 0f4a12b0..58126412 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -5162,6 +5162,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to pollstats. + /// + public static string pollstats_cmd { + get { + return ResourceManager.GetString("pollstats_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shows the poll results without stopping the poll on this server.. + /// + public static string pollstats_desc { + get { + return ResourceManager.GetString("pollstats_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}pollstats`. + /// + public static string pollstats_usage { + get { + return ResourceManager.GetString("pollstats_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to prune clr. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 4db2061c..3b13300e 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2880,4 +2880,13 @@ `{0}crinv` + + pollstats + + + Shows the poll results without stopping the poll on this server. + + + `{0}pollstats` + \ No newline at end of file From 9d7a0bff68b2692fb2eea762bdc9fbc1c0226044 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 8 Jan 2017 18:14:10 +0100 Subject: [PATCH 60/85] You can now use .ct #somechannel --- src/NadekoBot/Modules/Utility/Utility.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 543b94d3..3c1150f3 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -240,15 +240,16 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task ChannelTopic() + public async Task ChannelTopic([Remainder]ITextChannel channel = null) { - var channel = (ITextChannel)Context.Channel; + if (channel == null) + channel = (ITextChannel)Context.Channel; var topic = channel.Topic; if (string.IsNullOrWhiteSpace(topic)) - await channel.SendErrorAsync("No topic set."); + await channel.SendErrorAsync("No topic set.").ConfigureAwait(false); else - await channel.SendConfirmAsync("Channel topic", topic); + await channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From 018f43e50cb79fae484bc55f52b21a0ded1f5ac6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 9 Jan 2017 00:33:42 +0100 Subject: [PATCH 61/85] Logging updated --- .../Administration/Commands/LogCommand.cs | 262 +++++++++++++----- 1 file changed, 188 insertions(+), 74 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 856d729d..a0eda9d0 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -23,14 +23,17 @@ namespace NadekoBot.Modules.Administration [Group] public class LogCommands : ModuleBase { + private const string clockEmojiUrl = "https://cdn.discordapp.com/attachments/155726317222887425/258309524966866945/clock.png"; + private static ShardedDiscordClient _client { get; } private static Logger _log { get; } private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; + private static string currentTime = $"{DateTime.Now:HH:mm:ss}"; public static ConcurrentDictionary GuildLogSettings { get; } - private static ConcurrentDictionary> UserPresenceUpdates { get; } = new ConcurrentDictionary>(); + private static ConcurrentDictionary> PresenceUpdates { get; } = new ConcurrentDictionary>(); private static Timer timerReference { get; } private IGoogleApiService _google { get; } @@ -50,20 +53,20 @@ namespace NadekoBot.Modules.Administration { try { - var keys = UserPresenceUpdates.Keys.ToList(); + var keys = PresenceUpdates.Keys.ToList(); await Task.WhenAll(keys.Select(async key => { List messages; - if (UserPresenceUpdates.TryRemove(key, out messages)) - try { await key.SendMessageAsync(string.Join(Environment.NewLine, messages)); } catch { } + if (PresenceUpdates.TryRemove(key, out messages)) + try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } catch { } })); } catch (Exception ex) { _log.Warn(ex); } - }, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); + }, null, TimeSpan.FromSeconds(15), TimeSpan.FromSeconds(15)); sw.Stop(); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); @@ -91,33 +94,68 @@ namespace NadekoBot.Modules.Administration MuteCommands.UserUnmuted += MuteCommands_UserUnmuted; } - private static async void _client_UserUpdated(SocketUser before, SocketUser after) + private static async void _client_UserUpdated(SocketUser before, SocketUser uAfter) { try { - var str = ""; - if (before.Username != after.Username) - str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Name Changed |** 🆔 `{before.Id}`\n\t\t`New:` **{after.ToString()}**"; - else if (before.AvatarUrl != after.AvatarUrl) - str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Avatar Changed |** 🆔 `{before.Id}`\n\t🖼 {await NadekoBot.Google.ShortenUrl(before.AvatarUrl)} `=>` {await NadekoBot.Google.ShortenUrl(after.AvatarUrl)}"; + var after = uAfter as SocketGuildUser; - if (string.IsNullOrWhiteSpace(str)) + if (after == null) return; - var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList(); - foreach (var g in guildsMemberOf) + var g = after.Guild; + + LogSetting logSetting; + if (!GuildLogSettings.TryGetValue(g.Id, out logSetting) + || (logSetting.UserUpdatedId == null)) + return; + + ITextChannel logChannel; + if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) + return; + + var embed = new EmbedBuilder(); + + + if (before.Username != after.Username) { - LogSetting logSetting; - if (!GuildLogSettings.TryGetValue(g.Id, out logSetting) - || (logSetting.UserUpdatedId == null)) - return; - - ITextChannel logChannel; - if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) - return; - - try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { } + embed.WithAuthor(eab => eab.WithName("Username Changed")) + .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") + .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true)) + .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true)) + .WithFooter(fb => fb.WithText(currentTime)) + .WithOkColor(); } + else if (before.AvatarUrl != after.AvatarUrl) + { + embed.WithAuthor(eab => eab.WithName("Avatar Changed")) + .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") + .WithThumbnailUrl(before.AvatarUrl) + .WithImageUrl(after.AvatarUrl) + .WithFooter(fb => fb.WithText(currentTime)) + .WithOkColor(); + } + else + { + return; + } + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); + + //var guildsMemberOf = NadekoBot.Client.GetGuilds().Where(g => g.Users.Select(u => u.Id).Contains(before.Id)).ToList(); + //foreach (var g in guildsMemberOf) + //{ + // LogSetting logSetting; + // if (!GuildLogSettings.TryGetValue(g.Id, out logSetting) + // || (logSetting.UserUpdatedId == null)) + // return; + + // ITextChannel logChannel; + // if ((logChannel = await TryGetLogChannel(g, logSetting, LogType.UserUpdated)) == null) + // return; + + // try { await logChannel.SendMessageAsync(str).ConfigureAwait(false); } catch { } + //} } catch { } @@ -146,7 +184,7 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.VoicePresenceTTS)) == null) return; - string str = null; + var str = ""; if (beforeVch?.Guild == afterVch?.Guild) { str = $"{usr.Username} moved from {beforeVch.Name} to {afterVch.Name}"; @@ -190,9 +228,15 @@ namespace NadekoBot.Modules.Administration mutes = "text and voice chat"; break; } - await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🔇 **| User muted from the {mutes}. |** 🆔 `{usr.Id}`").ConfigureAwait(false); + + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("User Muted from " + mutes)) + .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") + .WithFooter(fb => fb.WithText(currentTime)) + .WithOkColor(); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType) @@ -221,9 +265,15 @@ namespace NadekoBot.Modules.Administration mutes = "text and voice chat"; break; } - await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🔊 **| User unmuted from the {mutes}. |** 🆔 `{usr.Id}`").ConfigureAwait(false); + + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("User Unmuted from " + mutes)) + .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") + .WithFooter(fb => fb.WithText($"{currentTime}")) + .WithOkColor(); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection) @@ -245,22 +295,25 @@ namespace NadekoBot.Modules.Administration if (action == PunishmentAction.Mute) { punishment = "🔇 MUTED"; - //punishment = "MUTED"; } else if (action == PunishmentAction.Kick) { punishment = "☣ SOFT-BANNED (KICKED)"; - //punishment = "KICKED"; } else if (action == PunishmentAction.Ban) { punishment = "⛔️ BANNED"; - //punishment = "BANNED"; } - await logChannel.SendMessageAsync(String.Join("\n", users.Select(user => $"‼️ {Format.Bold(user.ToString())} got **{punishment}** due to __**{protection}**__ protection on **{user.Guild.Name}** server."))) - .ConfigureAwait(false); + + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"Anti-{protection}")) + .WithTitle($"Users " + punishment) + .WithDescription(String.Join("\n", users.Select(u => u.ToString()))) + .WithFooter(fb => fb.WithText($"{currentTime}")) + .WithOkColor(); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } private static async void _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) @@ -275,27 +328,35 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.UserUpdated)) == null) return; - var str = ""; + var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText(currentTime)) + .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); if (before.Nickname != after.Nickname) - str = $"👤__**{before.Username}#{before.Discriminator}**__ **| Nickname Changed |** 🆔 `{before.Id}`\n\t\t`Old:` **{before.Nickname}**\n\t\t`New:` **{after.Nickname}**"; + { + embed.WithAuthor(eab => eab.WithName("Nickname Changed")) + + .AddField(efb => efb.WithName("Old Nickname").WithValue($"{before.Nickname}#{before.Discriminator}")) + .AddField(efb => efb.WithName("New Nickname").WithValue($"{after.Nickname}#{after.Discriminator}")); + } else if (!before.RoleIds.SequenceEqual(after.RoleIds)) { if (before.RoleIds.Count < after.RoleIds.Count) { - var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => "**" + before.Guild.GetRole(r).Name + "**"); - str = $"👤__**{before.ToString()}**__ **| User's Role Added |** 🆔 `{before.Id}`\n\t✅ {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔"; + var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); + embed.WithAuthor(eab => eab.WithName("User's Role Added")) + .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); } else if (before.RoleIds.Count > after.RoleIds.Count) { - var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => "**" + before.Guild.GetRole(r).Name + "**"); - str = $"👤__**{before.ToString()}**__ **| User's Role Removed |** 🆔 `{before.Id}`\n\t🚮 {string.Join(", ", diffRoles).SanitizeMentions()}\n\t\t⚔ **`{string.Join(", ", after.GetRoles().Select(r => r.Name)).SanitizeMentions()}`** ⚔"; + var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); + embed.WithAuthor(eab => eab.WithName("User's Role Removed")) + .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); } } else return; - try { await logChannel.SendMessageAsync($"🕔`{prettyCurrentTime}` " + str).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } private static async void _client_ChannelUpdated(IChannel cbefore, IChannel cafter) @@ -316,16 +377,30 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(before.Guild, logSetting, LogType.ChannelUpdated)) == null) return; + + var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText(currentTime)); + + var beforeTextChannel = cbefore as ITextChannel; + var afterTextChannel = cafter as ITextChannel; + if (before.Name != after.Name) - await logChannel.SendMessageAsync($@"🕓`{prettyCurrentTime}`ℹ️ **| Channel Name Changed |** #⃣ `{after.Name} ({after.Id})` - `Old:` {before.Name} - **`New:`** {after.Name}").ConfigureAwait(false); - else if ((before as ITextChannel).Topic != (after as ITextChannel).Topic) - await logChannel.SendMessageAsync($@"🕘`{prettyCurrentTime}`ℹ️ **| Channel Topic Changed |** #⃣ `{after.Name} ({after.Id})` - `Old:` {((ITextChannel)before).Topic} - **`New:`** {((ITextChannel)after).Topic}").ConfigureAwait(false); + { + embed.WithTitle("Channel Name Changed") + .AddField(efb => efb.WithName("Id").WithValue(before.Id.ToString())) + .AddField(efb => efb.WithName("Old Name").WithValue(before.Name)) + .AddField(efb => efb.WithName("New name").WithValue(after.Name)); + } + else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) + { + embed.WithTitle("Channel Topic Changed") + .AddField(efb => efb.WithName("Id").WithValue(before.Id.ToString())) + .AddField(efb => efb.WithName("Old Topic").WithValue(beforeTextChannel.Topic)) + .AddField(efb => efb.WithName("New Topic").WithValue(afterTextChannel.Topic)); + } + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } private static async void _client_ChannelDestroyed(IChannel ich) @@ -346,9 +421,13 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null) return; - await logChannel.SendMessageAsync($"🕕`{prettyCurrentTime}`🗑 **| {(ch is IVoiceChannel ? "Voice" : "Text")} Channel Deleted #⃣ {ch.Name}** `({ch.Id})`").ConfigureAwait(false); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle((ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed") + .WithDescription($"{ch.Name} | {ch.Id}") + .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } private static async void _client_ChannelCreated(IChannel ich) @@ -368,7 +447,11 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null) return; - await logChannel.SendMessageAsync($"🕓`{prettyCurrentTime}`🆕 **| {(ch is IVoiceChannel ? "Voice" : "Text")} Channel Created: #⃣ {ch.Name}** `({ch.Id})`").ConfigureAwait(false); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle((ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created") + .WithDescription($"{ch.Name} | {ch.Id}") + .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } @@ -410,12 +493,9 @@ namespace NadekoBot.Modules.Administration str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel."; } if (str != null) - UserPresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); - } - catch (Exception ex) - { - _log.Warn(ex); + PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); } + catch { } } private static async void _client_UserPresenceUpdated(Optional optGuild, SocketUser usr, SocketPresence before, SocketPresence after) @@ -438,11 +518,11 @@ namespace NadekoBot.Modules.Administration return; string str; if (before.Status != after.Status) - str = $"🔵`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**."; + str = $"🎭`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**."; else str = $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game}**."; - UserPresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); } catch { } } @@ -459,7 +539,13 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserLeft)) == null) return; - await logChannel.SendMessageAsync($"❗️🕛`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__❌ **| USER LEFT |** 🆔 `{usr.Id}`").ConfigureAwait(false); + + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("User Left") + .WithThumbnailUrl(usr.AvatarUrl) + .WithDescription($"{usr} | {usr.Id}") + .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch { } } @@ -477,7 +563,12 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserJoined)) == null) return; - await logChannel.SendMessageAsync($"❕🕓`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__✅ **| USER JOINED |** 🆔 `{usr.Id}`").ConfigureAwait(false); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("User Joined") + .WithThumbnailUrl(usr.AvatarUrl) + .WithDescription($"{usr} | {usr.Id}") + .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } @@ -495,7 +586,11 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserUnbanned)) == null) return; - await logChannel.SendMessageAsync($"❕🕘`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__♻️ **| USER UN-BANNED |** 🆔 `{usr.Id}`").ConfigureAwait(false); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("User Unbanned") + .WithDescription($"{usr} | {usr.Id}") + .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } @@ -512,7 +607,11 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserBanned)) == null) return; - await logChannel.SendMessageAsync($"‼️🕕`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__🚫 **| USER BANNED |** 🆔 `{usr.Id}`").ConfigureAwait(false); + await logChannel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("User Banned") + .WithDescription($"{usr} | {usr.Id}") + .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } @@ -539,13 +638,19 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageDeleted)) == null || logChannel.Id == msg.Id) return; - var str = $@"🕔`{prettyCurrentTime}`👤__**{msg.Author.Username}#{msg.Author.Discriminator}**__ **| Deleted Message |** 🆔 `{msg.Author.Id}` #⃣ `{channel.Name}` -🗑 {msg.Resolve(userHandling: TagHandling.FullName)}"; + var embed = new EmbedBuilder() + .WithOkColor() + .WithAuthor(eab => eab.WithName("Message Deleted")) + .WithTitle($"{msg.Author}") + .WithDescription($"{msg.Resolve(userHandling: TagHandling.FullName)}") + .AddField(efb => efb.WithName("Message Id").WithValue(msg.Id.ToString()).WithIsInline(false)) + .WithFooter(efb => efb.WithText(currentTime)); if (msg.Attachments.Any()) - str += $"{Environment.NewLine}📎 {string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))}"; - await logChannel.SendMessageAsync(str.SanitizeMentions()).ConfigureAwait(false); + embed.AddField(efb => efb.WithName("Attachments").WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false)); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } private static async void _client_MessageUpdated(Optional optmsg, SocketMessage imsg2) @@ -576,11 +681,19 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(channel.Guild, logSetting, LogType.MessageUpdated)) == null || logChannel.Id == after.Channel.Id) return; - await logChannel.SendMessageAsync($@"🕔`{prettyCurrentTime}`👤__**{before.Author.Username}#{before.Author.Discriminator}**__ **| 📝 Edited Message |** 🆔 `{before.Author.Id}` #⃣ `{channel.Name}` - `Old:` {before.Resolve(userHandling: TagHandling.FullName).SanitizeMentions()} - **`New:`** {after.Resolve(userHandling: TagHandling.FullName).SanitizeMentions()}").ConfigureAwait(false); + + var embed = new EmbedBuilder() + .WithOkColor() + .WithAuthor(eab => eab.WithName("Message Updated")) + .WithTitle($"{after.Author}") + .AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName("Message Id").WithValue(after.Id.ToString()).WithIsInline(false)) + .WithFooter(efb => efb.WithText(currentTime)); + + await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch { } } public enum LogType @@ -763,6 +876,7 @@ namespace NadekoBot.Modules.Administration logSetting.ChannelUpdatedId = logSetting.LogUserPresenceId = logSetting.LogVoicePresenceId = + logSetting.UserMutedId = logSetting.LogVoicePresenceTTSId = (action.Value ? channel.Id : (ulong?)null); await uow.CompleteAsync().ConfigureAwait(false); From a97900add6f91d27004809df09d78dcf23f4b4e4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 9 Jan 2017 11:56:40 +0100 Subject: [PATCH 62/85] added emojis to logcommands, thx to Manuel --- .../Administration/Commands/LogCommand.cs | 83 +++++++++++-------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index a0eda9d0..e4541afc 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -119,8 +119,8 @@ namespace NadekoBot.Modules.Administration if (before.Username != after.Username) { - embed.WithAuthor(eab => eab.WithName("Username Changed")) - .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") + embed.WithTitle("👥 Username Changed") + .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true)) .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true)) .WithFooter(fb => fb.WithText(currentTime)) @@ -128,7 +128,8 @@ namespace NadekoBot.Modules.Administration } else if (before.AvatarUrl != after.AvatarUrl) { - embed.WithAuthor(eab => eab.WithName("Avatar Changed")) + embed.WithTitle("👥 Avatar Changed") + .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") .WithThumbnailUrl(before.AvatarUrl) .WithImageUrl(after.AvatarUrl) @@ -229,7 +230,7 @@ namespace NadekoBot.Modules.Administration break; } - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("User Muted from " + mutes)) + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔇 User Muted from " + mutes)) .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithFooter(fb => fb.WithText(currentTime)) .WithOkColor(); @@ -266,7 +267,7 @@ namespace NadekoBot.Modules.Administration break; } - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("User Unmuted from " + mutes)) + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔊 User Unmuted from " + mutes)) .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithFooter(fb => fb.WithText($"{currentTime}")) .WithOkColor(); @@ -305,7 +306,7 @@ namespace NadekoBot.Modules.Administration punishment = "⛔️ BANNED"; } - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"Anti-{protection}")) + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}")) .WithTitle($"Users " + punishment) .WithDescription(String.Join("\n", users.Select(u => u.ToString()))) .WithFooter(fb => fb.WithText($"{currentTime}")) @@ -332,7 +333,7 @@ namespace NadekoBot.Modules.Administration .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); if (before.Nickname != after.Nickname) { - embed.WithAuthor(eab => eab.WithName("Nickname Changed")) + embed.WithAuthor(eab => eab.WithName("👥 Nickname Changed")) .AddField(efb => efb.WithName("Old Nickname").WithValue($"{before.Nickname}#{before.Discriminator}")) .AddField(efb => efb.WithName("New Nickname").WithValue($"{after.Nickname}#{after.Discriminator}")); @@ -342,13 +343,13 @@ namespace NadekoBot.Modules.Administration if (before.RoleIds.Count < after.RoleIds.Count) { var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); - embed.WithAuthor(eab => eab.WithName("User's Role Added")) + embed.WithAuthor(eab => eab.WithName("⚔ User's Role Added")) .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); } else if (before.RoleIds.Count > after.RoleIds.Count) { var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); - embed.WithAuthor(eab => eab.WithName("User's Role Removed")) + embed.WithAuthor(eab => eab.WithName("⚔ User's Role Removed")) .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); } } @@ -385,15 +386,14 @@ namespace NadekoBot.Modules.Administration if (before.Name != after.Name) { - embed.WithTitle("Channel Name Changed") - .AddField(efb => efb.WithName("Id").WithValue(before.Id.ToString())) - .AddField(efb => efb.WithName("Old Name").WithValue(before.Name)) - .AddField(efb => efb.WithName("New name").WithValue(after.Name)); + embed.WithTitle("ℹ️ Channel Name Changed") + .WithDescription($"{after} | {after.Id}") + .AddField(efb => efb.WithName("Old Name").WithValue(before.Name)); } else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) { - embed.WithTitle("Channel Topic Changed") - .AddField(efb => efb.WithName("Id").WithValue(before.Id.ToString())) + embed.WithTitle("ℹ️ Channel Topic Changed") + .WithDescription($"{after} | {after.Id}") .AddField(efb => efb.WithName("Old Topic").WithValue(beforeTextChannel.Topic)) .AddField(efb => efb.WithName("New Topic").WithValue(afterTextChannel.Topic)); } @@ -423,7 +423,7 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle((ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed") + .WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed") .WithDescription($"{ch.Name} | {ch.Id}") .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } @@ -449,7 +449,7 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle((ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created") + .WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created") .WithDescription($"{ch.Name} | {ch.Id}") .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } @@ -502,7 +502,7 @@ namespace NadekoBot.Modules.Administration { try { - var guild = optGuild.IsSpecified ? optGuild.Value : null; + var guild = optGuild.GetValueOrDefault() ?? (usr as SocketGuildUser)?.Guild; if (guild == null) return; @@ -516,11 +516,16 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(guild, logSetting, LogType.UserPresence)) == null) return; - string str; + string str = ""; if (before.Status != after.Status) str = $"🎭`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**."; - else - str = $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game}**."; + + //if (before.Game?.Name != after.Game?.Name) + //{ + // if (str != "") + // str += "\n"; + // str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**."; + //} PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); } @@ -542,9 +547,10 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("User Left") + .WithTitle("❌ User Left") .WithThumbnailUrl(usr.AvatarUrl) - .WithDescription($"{usr} | {usr.Id}") + .WithDescription(usr.ToString()) + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch { } @@ -565,9 +571,10 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("User Joined") + .WithTitle("✅ User Joined") .WithThumbnailUrl(usr.AvatarUrl) - .WithDescription($"{usr} | {usr.Id}") + .WithDescription($"{usr}") + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } @@ -588,8 +595,10 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("User Unbanned") - .WithDescription($"{usr} | {usr.Id}") + .WithTitle("♻️ User Unbanned") + .WithThumbnailUrl(usr.AvatarUrl) + .WithDescription(usr.ToString()) + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } @@ -609,8 +618,10 @@ namespace NadekoBot.Modules.Administration return; await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("User Banned") - .WithDescription($"{usr} | {usr.Id}") + .WithTitle("🚫 User Banned") + .WithThumbnailUrl(usr.AvatarUrl) + .WithDescription(usr.ToString()) + .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } @@ -640,10 +651,10 @@ namespace NadekoBot.Modules.Administration return; var embed = new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithName("Message Deleted")) - .WithTitle($"{msg.Author}") - .WithDescription($"{msg.Resolve(userHandling: TagHandling.FullName)}") - .AddField(efb => efb.WithName("Message Id").WithValue(msg.Id.ToString()).WithIsInline(false)) + .WithTitle("🗑 Message Deleted") + .WithDescription($"{msg.Author}") + .AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); if (msg.Attachments.Any()) embed.AddField(efb => efb.WithName("Attachments").WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false)); @@ -684,11 +695,11 @@ namespace NadekoBot.Modules.Administration var embed = new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithName("Message Updated")) - .WithTitle($"{after.Author}") + .WithTitle("📝 Message Updated") + .WithDescription(after.Author.ToString()) .AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) - .AddField(efb => efb.WithName("Message Id").WithValue(after.Id.ToString()).WithIsInline(false)) + .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); await logChannel.EmbedAsync(embed).ConfigureAwait(false); From 00978c293cb21e4d58181b2fe53d1908a0c45bd5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 9 Jan 2017 19:58:52 +0100 Subject: [PATCH 63/85] .activity fix, MessageUpdated and messageDeleted show a channel, .ct fix --- src/NadekoBot/Modules/Administration/Commands/LogCommand.cs | 4 ++-- src/NadekoBot/Modules/Gambling/Gambling.cs | 2 +- src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs | 4 ++-- src/NadekoBot/Modules/Utility/Utility.cs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index e4541afc..c7e5d513 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -651,7 +651,7 @@ namespace NadekoBot.Modules.Administration return; var embed = new EmbedBuilder() .WithOkColor() - .WithTitle("🗑 Message Deleted") + .WithTitle($"🗑 Message Deleted in {((ITextChannel)msg.Channel).Mention}") .WithDescription($"{msg.Author}") .AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) @@ -695,7 +695,7 @@ namespace NadekoBot.Modules.Administration var embed = new EmbedBuilder() .WithOkColor() - .WithTitle("📝 Message Updated") + .WithTitle($"📝 Message Updated in {((ITextChannel)after.Channel).Mention}") .WithDescription(after.Author.ToString()) .AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index b3bbe1bd..bbff2239 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -99,7 +99,7 @@ namespace NadekoBot.Modules.Gambling await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully awarded {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount} {(amount == 1 ? Gambling.CurrencyName : Gambling.CurrencyPluralName)} to <@{usrId}>!").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 37d80ba6..8814c50c 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [OwnerOnly] - public async Task Activity(IUserMessage imsg, int page = 1) + public async Task Activity(int page = 1) { const int activityPerPage = 15; page -= 1; @@ -118,7 +118,7 @@ namespace NadekoBot.Modules.Utility str.AppendLine($"`{++startCount}.` **{kvp.Key}** [{kvp.Value/NadekoBot.Stats.GetUptime().TotalSeconds:F2}/s] - {kvp.Value} total"); } - await imsg.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}") + await Context.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}") .WithOkColor() .WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total.")) .WithDescription(str.ToString())); diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 3c1150f3..b7d437d0 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -247,9 +247,9 @@ namespace NadekoBot.Modules.Utility var topic = channel.Topic; if (string.IsNullOrWhiteSpace(topic)) - await channel.SendErrorAsync("No topic set.").ConfigureAwait(false); + await Context.Channel.SendErrorAsync("No topic set.").ConfigureAwait(false); else - await channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From c2d4b43a12551b93b45977376c3e824706918257 Mon Sep 17 00:00:00 2001 From: miraai Date: Mon, 9 Jan 2017 20:34:22 +0100 Subject: [PATCH 64/85] Added 64bit warning --- docs/index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/index.md b/docs/index.md index 872e2e40..80bd42a7 100644 --- a/docs/index.md +++ b/docs/index.md @@ -11,6 +11,9 @@ NadekoBot is an open source project, and it can be found on our [GitHub][GitHub] Here you can read current [Issues][Issues]. If you want to contribute, be sure to PR on the **[dev][dev]** branch. + +**NadekoBot 1.1 release is currently only for 64bit PCs.** + ##Content - [About](about.md) - Guides From 8885afb77a16a48df570a9b37a3f08bc2389ead4 Mon Sep 17 00:00:00 2001 From: miraai Date: Mon, 9 Jan 2017 20:40:22 +0100 Subject: [PATCH 65/85] GG Mirai --- docs/index.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/docs/index.md b/docs/index.md index 80bd42a7..c2dbfa0a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -12,7 +12,7 @@ Here you can read current [Issues][Issues]. If you want to contribute, be sure to PR on the **[dev][dev]** branch. -**NadekoBot 1.1 release is currently only for 64bit PCs.** +**NadekoBot 1.1 release currently does not support x86/32bit architecture.** ##Content - [About](about.md) @@ -37,14 +37,3 @@ If you want to contribute, be sure to PR on the **[dev][dev]** branch. [GitHub]: https://github.com/Kwoth/NadekoBot [Issues]: https://github.com/Kwoth/NadekoBot/issues [dev]: https://github.com/Kwoth/NadekoBot/tree/dev - -[Italian]: http://i.imgur.com/SsaTwOF.png?1 -[Russian]: http://i.imgur.com/wf9bc5G.png?1 -[German]: http://i.imgur.com/EM5qPzf.png?1 -[Chinese]: http://i.imgur.com/MVCNOjT.png?1 -[English]: http://i.imgur.com/jHTyZFS.png?1 -[Spanish]: http://i.imgur.com/9BsusB6.png?1 -[French]: http://i.imgur.com/g2ARPF6.png?1 -[Dutch]: http://i.imgur.com/SadddLj.png?1 -[Norwegian]: http://i.imgur.com/TCVa0V8.png?1 -[Serbian]: http://i.imgur.com/5evoUbU.png From a48fdd1fdce01c95261c61352f76ec28b01df4da Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 00:52:25 +0100 Subject: [PATCH 66/85] .repeat, .repinv, .replst, .reprm added/improved --- .../Commands/MessageRepeater.cs | 149 ++++++++++++------ .../Resources/CommandStrings.Designer.cs | 60 ++++++- src/NadekoBot/Resources/CommandStrings.resx | 24 ++- .../Services/Database/Models/GuildConfig.cs | 1 + 4 files changed, 179 insertions(+), 55 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index 8d50a80e..2e06347a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -1,14 +1,18 @@ using Discord; using Discord.Commands; +using Microsoft.EntityFrameworkCore; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; +using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; @@ -19,7 +23,8 @@ namespace NadekoBot.Modules.Administration [Group] public class RepeatCommands : ModuleBase { - public static ConcurrentDictionary repeaters { get; } + //guildid/RepeatRunners + public static ConcurrentDictionary> repeaters { get; } public class RepeatRunner { @@ -83,7 +88,10 @@ namespace NadekoBot.Modules.Administration var sw = Stopwatch.StartNew(); using (var uow = DbHandler.UnitOfWork()) { - repeaters = new ConcurrentDictionary(uow.Repeaters.GetAll().Select(r => new RepeatRunner(r)).Where(r => r != null).ToDictionary(r => r.Repeater.ChannelId)); + repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs + .ToDictionary(gc => gc.GuildId, + gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) + .Where(gr => gr.Channel != null)))); } sw.Stop(); @@ -93,41 +101,70 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] - public async Task RepeatInvoke() + public async Task RepeatInvoke(int index) { - RepeatRunner rep; + index -= 1; + ConcurrentQueue rep; if (!repeaters.TryGetValue(Context.Channel.Id, out rep)) { await Context.Channel.SendErrorAsync("ℹ️ **No repeating message found on this server.**").ConfigureAwait(false); return; } - rep.Reset(); - await Context.Channel.SendMessageAsync("🔄 " + rep.Repeater.Message).ConfigureAwait(false); - } - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - [RequireUserPermission(GuildPermission.ManageMessages)] - public async Task Repeat() - { - RepeatRunner rep; - if (repeaters.TryRemove(Context.Channel.Id, out rep)) + var repList = rep.ToList(); + + if (index >= repList.Count) { - using (var uow = DbHandler.UnitOfWork()) - { - uow.Repeaters.Remove(rep.Repeater); - await uow.CompleteAsync(); - } - rep.Stop(); - await Context.Channel.SendConfirmAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false); + await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false); + return; } - else - await Context.Channel.SendConfirmAsync("ℹ️ **No message is repeating.**").ConfigureAwait(false); + var repeater = repList[index].Repeater; + + await Context.Channel.SendMessageAsync("🔄 " + repeater.Message).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageMessages)] + [Priority(0)] + public async Task RepeatRemove(int index) + { + if (index < 1) + return; + index -= 1; + + ConcurrentQueue rep; + if (!repeaters.TryGetValue(Context.Guild.Id, out rep)) + return; + + var repeaterList = rep.ToList(); + + if (index >= repeaterList.Count) + { + await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false); + return; + } + + var repeater = repeaterList[index]; + repeater.Stop(); + repeaterList.RemoveAt(index); + + using (var uow = DbHandler.UnitOfWork()) + { + var guildConfig = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(gc => gc.GuildRepeaters)); + + guildConfig.GuildRepeaters.RemoveWhere(r=>r.Id == repeater.Repeater.Id); + await uow.CompleteAsync().ConfigureAwait(false); + } + + if (repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue(repeaterList), rep)) + await Context.Channel.SendConfirmAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.ManageMessages)] + [Priority(1)] public async Task Repeat(int minutes, [Remainder] string message) { if (minutes < 1 || minutes > 10080) @@ -136,38 +173,52 @@ namespace NadekoBot.Modules.Administration if (string.IsNullOrWhiteSpace(message)) return; - RepeatRunner rep; + var toAdd = new Repeater() + { + ChannelId = Context.Channel.Id, + GuildId = Context.Guild.Id, + Interval = TimeSpan.FromMinutes(minutes), + Message = message + }; - rep = repeaters.AddOrUpdate(Context.Channel.Id, (cid) => + var rep = new RepeatRunner(toAdd, (ITextChannel)Context.Channel); + + repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue(new[] { rep }), (key, old) => { - using (var uow = DbHandler.UnitOfWork()) - { - var localRep = new Repeater - { - ChannelId = Context.Channel.Id, - GuildId = Context.Guild.Id, - Interval = TimeSpan.FromMinutes(minutes), - Message = message, - }; - uow.Repeaters.Add(localRep); - uow.Complete(); - return new RepeatRunner(localRep, (ITextChannel)Context.Channel); - } - }, (cid, old) => - { - using (var uow = DbHandler.UnitOfWork()) - { - old.Repeater.Message = message; - old.Repeater.Interval = TimeSpan.FromMinutes(minutes); - uow.Repeaters.Update(old.Repeater); - uow.Complete(); - } - old.Reset(); + old.Enqueue(rep); return old; }); - + await Context.Channel.SendConfirmAsync($"🔁 Repeating **\"{rep.Repeater.Message}\"** every `{rep.Repeater.Interval.Days} day(s), {rep.Repeater.Interval.Hours} hour(s) and {rep.Repeater.Interval.Minutes} minute(s)`.").ConfigureAwait(false); } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.ManageMessages)] + public async Task RepeatList() + { + ConcurrentQueue repRunners; + if (!repeaters.TryGetValue(Context.Guild.Id, out repRunners)) + { + await Context.Channel.SendConfirmAsync("No repeaters running on this server.").ConfigureAwait(false); + return; + } + + var replist = repRunners.ToList(); + var sb = new StringBuilder(); + + for (int i = 0; i < replist.Count; i++) + { + var rep = replist[i]; + + sb.AppendLine($"`{i + 1}.` {rep.Channel.Mention} | {(int)rep.Repeater.Interval.TotalHours}:{rep.Repeater.Interval:mm} | {rep.Repeater.Message.TrimTo(20)}"); + } + + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle("List Of Repeaters") + .WithDescription(sb.ToString())) + .ConfigureAwait(false); + } } } } diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 58126412..fc8637da 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -5712,7 +5712,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Repeat a message every X minutes. If no parameters are specified, repeat is disabled.. + /// Looks up a localized string similar to Repeat a message every X minutes in the current channel.. /// public static string repeat_desc { get { @@ -5739,7 +5739,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Immediately shows the repeat message and restarts the timer.. + /// Looks up a localized string similar to Immediately shows the repeat message on a certain index and restarts its timer.. /// public static string repeatinvoke_desc { get { @@ -5748,7 +5748,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to `{0}repinv`. + /// Looks up a localized string similar to `{0}repinv 1`. /// public static string repeatinvoke_usage { get { @@ -5756,6 +5756,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to repeatlist replst. + /// + public static string repeatlist_cmd { + get { + return ResourceManager.GetString("repeatlist_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shows currently repeating messages and their indexes.. + /// + public static string repeatlist_desc { + get { + return ResourceManager.GetString("repeatlist_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}repeatlist`. + /// + public static string repeatlist_usage { + get { + return ResourceManager.GetString("repeatlist_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to rpeatplaylst rpl. /// @@ -5783,6 +5810,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to repeatremove reprm. + /// + public static string repeatremove_cmd { + get { + return ResourceManager.GetString("repeatremove_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removes a repeating message on a specified index. Use `{0}repeatlist` to see indexes.. + /// + public static string repeatremove_desc { + get { + return ResourceManager.GetString("repeatremove_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}reprm 2`. + /// + public static string repeatremove_usage { + get { + return ResourceManager.GetString("repeatremove_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to reptcursong rcs. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 3b13300e..cc144d2d 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -265,16 +265,16 @@ repeatinvoke repinv - Immediately shows the repeat message and restarts the timer. + Immediately shows the repeat message on a certain index and restarts its timer. - `{0}repinv` + `{0}repinv 1` repeat - Repeat a message every X minutes. If no parameters are specified, repeat is disabled. + Repeat a message every X minutes in the current channel. `{0}repeat 5 Hello there` @@ -2889,4 +2889,22 @@ `{0}pollstats` + + repeatlist replst + + + Shows currently repeating messages and their indexes. + + + `{0}repeatlist` + + + repeatremove reprm + + + Removes a repeating message on a specified index. Use `{0}repeatlist` to see indexes. + + + `{0}reprm 2` + \ No newline at end of file diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 99608eff..e4ed7c97 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -58,6 +58,7 @@ namespace NadekoBot.Services.Database.Models public string MuteRoleName { get; set; } public bool CleverbotEnabled { get; set; } + public HashSet GuildRepeaters { get; set; } } public class FilterChannelId : DbEntity From f2df011d636b6c303f4acc6097ba3184e253ae76 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 13:37:35 +0100 Subject: [PATCH 67/85] Migration and fixes for .repeat commands --- .../20170110111159_repeater-drop.Designer.cs | 834 +++++++++++++++++ .../20170110111159_repeater-drop.cs | 40 + .../20170110111302_repeater-new.Designer.cs | 863 ++++++++++++++++++ .../Migrations/20170110111302_repeater-new.cs | 46 + .../NadekoSqliteContextModelSnapshot.cs | 53 +- .../Commands/MessageRepeater.cs | 34 +- .../Services/Database/IUnitOfWork.cs | 1 - .../Services/Database/Models/GuildConfig.cs | 2 +- .../Services/Database/Models/Repeater.cs | 7 +- .../Services/Database/NadekoContext.cs | 12 +- .../Repositories/IRepeaterRepository.cs | 9 - .../Impl/GuildConfigRepository.cs | 1 + .../Repositories/Impl/RepeaterRepository.cs | 12 - src/NadekoBot/Services/Database/UnitOfWork.cs | 3 - 14 files changed, 1848 insertions(+), 69 deletions(-) create mode 100644 src/NadekoBot/Migrations/20170110111159_repeater-drop.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170110111159_repeater-drop.cs create mode 100644 src/NadekoBot/Migrations/20170110111302_repeater-new.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170110111302_repeater-new.cs delete mode 100644 src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs delete mode 100644 src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs diff --git a/src/NadekoBot/Migrations/20170110111159_repeater-drop.Designer.cs b/src/NadekoBot/Migrations/20170110111159_repeater-drop.Designer.cs new file mode 100644 index 00000000..db95f728 --- /dev/null +++ b/src/NadekoBot/Migrations/20170110111159_repeater-drop.Designer.cs @@ -0,0 +1,834 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170110111159_repeater-drop")] + partial class repeaterdrop + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ItemId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("BlacklistItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BufferSize"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("MigrationVersion"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.HasKey("Id"); + + b.ToTable("BotConfig"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BaseDestroyed"); + + b.Property("CallUser"); + + b.Property("ClashWarId"); + + b.Property("SequenceNumber"); + + b.Property("Stars"); + + b.Property("TimeAdded"); + + b.HasKey("Id"); + + b.HasIndex("ClashWarId"); + + b.ToTable("ClashCallers"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("EnemyClan"); + + b.Property("GuildId"); + + b.Property("Size"); + + b.Property("StartedAt"); + + b.Property("WarState"); + + b.HasKey("Id"); + + b.ToTable("ClashOfClans"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommandName"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("IsRegex"); + + b.Property("OwnerOnly"); + + b.Property("Response"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.ToTable("CustomReactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Name"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Donators"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildConfigId1"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.HasIndex("GuildConfigId1"); + + b.ToTable("FilterChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("Word"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FilteredWord"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GCChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoAssignRoleId"); + + b.Property("AutoDeleteByeMessages"); + + b.Property("AutoDeleteByeMessagesTimer"); + + b.Property("AutoDeleteGreetMessages"); + + b.Property("AutoDeleteGreetMessagesTimer"); + + b.Property("AutoDeleteSelfAssignedRoleMessages"); + + b.Property("ByeMessageChannelId"); + + b.Property("ChannelByeMessageText"); + + b.Property("ChannelGreetMessageText"); + + b.Property("CleverbotEnabled"); + + b.Property("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.HasIndex("LogSettingId"); + + b.HasIndex("RootPermissionId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelCreatedId"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelDestroyedId"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("ChannelUpdatedId"); + + b.Property("IsLogging"); + + b.Property("LogOtherId"); + + b.Property("LogUserPresence"); + + b.Property("LogUserPresenceId"); + + b.Property("LogVoicePresence"); + + b.Property("LogVoicePresenceId"); + + b.Property("LogVoicePresenceTTSId"); + + b.Property("MessageDeleted"); + + b.Property("MessageDeletedId"); + + b.Property("MessageUpdated"); + + b.Property("MessageUpdatedId"); + + b.Property("UserBanned"); + + b.Property("UserBannedId"); + + b.Property("UserJoined"); + + b.Property("UserJoinedId"); + + b.Property("UserLeft"); + + b.Property("UserLeftId"); + + b.Property("UserMutedId"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUnbannedId"); + + b.Property("UserUpdated"); + + b.Property("UserUpdatedId"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ModuleName"); + + b.Property("Prefix"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("ModulePrefixes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("AuthorId"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("MutedUserId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("NextId"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("NextId") + .IsUnique(); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Status"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("PlayingStatus"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("MusicPlaylistId"); + + b.Property("Provider"); + + b.Property("ProviderType"); + + b.Property("Query"); + + b.Property("Title"); + + b.Property("Uri"); + + b.HasKey("Id"); + + b.HasIndex("MusicPlaylistId"); + + b.ToTable("PlaylistSong"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("AuthorName") + .IsRequired(); + + b.Property("GuildId"); + + b.Property("Keyword") + .IsRequired(); + + b.Property("Text") + .IsRequired(); + + b.HasKey("Id"); + + b.ToTable("Quotes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Icon"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("RaceAnimals"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("IsPrivate"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("UserId"); + + b.Property("When"); + + b.HasKey("Id"); + + b.ToTable("Reminders"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildId", "RoleId") + .IsUnique(); + + b.ToTable("SelfAssignableRoles"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("Blacklist") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar") + .WithMany("Bases") + .HasForeignKey("ClashWarId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandCooldowns") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("EightBallResponses") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterInvitesChannelIds") + .HasForeignKey("GuildConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterWordsChannelIds") + .HasForeignKey("GuildConfigId1"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilteredWords") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GenerateCurrencyChannelIds") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + + b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission") + .WithMany() + .HasForeignKey("RootPermissionId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("ModulePrefixes") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("MutedUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") + .WithOne("Previous") + .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RotatingStatusMessages") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist") + .WithMany("Songs") + .HasForeignKey("MusicPlaylistId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RaceAnimals") + .HasForeignKey("BotConfigId"); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170110111159_repeater-drop.cs b/src/NadekoBot/Migrations/20170110111159_repeater-drop.cs new file mode 100644 index 00000000..d05e6672 --- /dev/null +++ b/src/NadekoBot/Migrations/20170110111159_repeater-drop.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class repeaterdrop : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Repeaters"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Repeaters", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ChannelId = table.Column(nullable: false), + GuildId = table.Column(nullable: false), + Interval = table.Column(nullable: false), + Message = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Repeaters", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Repeaters_ChannelId", + table: "Repeaters", + column: "ChannelId", + unique: true); + } + } +} diff --git a/src/NadekoBot/Migrations/20170110111302_repeater-new.Designer.cs b/src/NadekoBot/Migrations/20170110111302_repeater-new.Designer.cs new file mode 100644 index 00000000..09bee367 --- /dev/null +++ b/src/NadekoBot/Migrations/20170110111302_repeater-new.Designer.cs @@ -0,0 +1,863 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170110111302_repeater-new")] + partial class repeaternew + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ItemId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("BlacklistItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BufferSize"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("MigrationVersion"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.HasKey("Id"); + + b.ToTable("BotConfig"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BaseDestroyed"); + + b.Property("CallUser"); + + b.Property("ClashWarId"); + + b.Property("SequenceNumber"); + + b.Property("Stars"); + + b.Property("TimeAdded"); + + b.HasKey("Id"); + + b.HasIndex("ClashWarId"); + + b.ToTable("ClashCallers"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("EnemyClan"); + + b.Property("GuildId"); + + b.Property("Size"); + + b.Property("StartedAt"); + + b.Property("WarState"); + + b.HasKey("Id"); + + b.ToTable("ClashOfClans"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommandName"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("IsRegex"); + + b.Property("OwnerOnly"); + + b.Property("Response"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.ToTable("CustomReactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Name"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Donators"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildConfigId1"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.HasIndex("GuildConfigId1"); + + b.ToTable("FilterChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("Word"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FilteredWord"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GCChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoAssignRoleId"); + + b.Property("AutoDeleteByeMessages"); + + b.Property("AutoDeleteByeMessagesTimer"); + + b.Property("AutoDeleteGreetMessages"); + + b.Property("AutoDeleteGreetMessagesTimer"); + + b.Property("AutoDeleteSelfAssignedRoleMessages"); + + b.Property("ByeMessageChannelId"); + + b.Property("ChannelByeMessageText"); + + b.Property("ChannelGreetMessageText"); + + b.Property("CleverbotEnabled"); + + b.Property("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.HasIndex("LogSettingId"); + + b.HasIndex("RootPermissionId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GuildRepeater"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelCreatedId"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelDestroyedId"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("ChannelUpdatedId"); + + b.Property("IsLogging"); + + b.Property("LogOtherId"); + + b.Property("LogUserPresence"); + + b.Property("LogUserPresenceId"); + + b.Property("LogVoicePresence"); + + b.Property("LogVoicePresenceId"); + + b.Property("LogVoicePresenceTTSId"); + + b.Property("MessageDeleted"); + + b.Property("MessageDeletedId"); + + b.Property("MessageUpdated"); + + b.Property("MessageUpdatedId"); + + b.Property("UserBanned"); + + b.Property("UserBannedId"); + + b.Property("UserJoined"); + + b.Property("UserJoinedId"); + + b.Property("UserLeft"); + + b.Property("UserLeftId"); + + b.Property("UserMutedId"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUnbannedId"); + + b.Property("UserUpdated"); + + b.Property("UserUpdatedId"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ModuleName"); + + b.Property("Prefix"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("ModulePrefixes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("AuthorId"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("MutedUserId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("NextId"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("NextId") + .IsUnique(); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Status"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("PlayingStatus"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("MusicPlaylistId"); + + b.Property("Provider"); + + b.Property("ProviderType"); + + b.Property("Query"); + + b.Property("Title"); + + b.Property("Uri"); + + b.HasKey("Id"); + + b.HasIndex("MusicPlaylistId"); + + b.ToTable("PlaylistSong"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("AuthorName") + .IsRequired(); + + b.Property("GuildId"); + + b.Property("Keyword") + .IsRequired(); + + b.Property("Text") + .IsRequired(); + + b.HasKey("Id"); + + b.ToTable("Quotes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Icon"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("RaceAnimals"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("IsPrivate"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("UserId"); + + b.Property("When"); + + b.HasKey("Id"); + + b.ToTable("Reminders"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildId", "RoleId") + .IsUnique(); + + b.ToTable("SelfAssignableRoles"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("Blacklist") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar") + .WithMany("Bases") + .HasForeignKey("ClashWarId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandCooldowns") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("EightBallResponses") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterInvitesChannelIds") + .HasForeignKey("GuildConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterWordsChannelIds") + .HasForeignKey("GuildConfigId1"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilteredWords") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GenerateCurrencyChannelIds") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + + b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission") + .WithMany() + .HasForeignKey("RootPermissionId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GuildRepeaters") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("ModulePrefixes") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("MutedUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") + .WithOne("Previous") + .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RotatingStatusMessages") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist") + .WithMany("Songs") + .HasForeignKey("MusicPlaylistId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RaceAnimals") + .HasForeignKey("BotConfigId"); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170110111302_repeater-new.cs b/src/NadekoBot/Migrations/20170110111302_repeater-new.cs new file mode 100644 index 00000000..0decfad8 --- /dev/null +++ b/src/NadekoBot/Migrations/20170110111302_repeater-new.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class repeaternew : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "GuildRepeater", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + ChannelId = table.Column(nullable: false), + GuildConfigId = table.Column(nullable: true), + GuildId = table.Column(nullable: false), + Interval = table.Column(nullable: false), + Message = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_GuildRepeater", x => x.Id); + table.ForeignKey( + name: "FK_GuildRepeater_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_GuildRepeater_GuildConfigId", + table: "GuildRepeater", + column: "GuildConfigId"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "GuildRepeater"); + } + } +} diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index c9b359e1..8038530d 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -2,7 +2,10 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; namespace NadekoBot.Migrations { @@ -381,6 +384,28 @@ namespace NadekoBot.Migrations b.ToTable("GuildConfigs"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GuildRepeater"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => { b.Property("Id") @@ -663,27 +688,6 @@ namespace NadekoBot.Migrations b.ToTable("Reminders"); }); - modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b => - { - b.Property("Id") - .ValueGeneratedOnAdd(); - - b.Property("ChannelId"); - - b.Property("GuildId"); - - b.Property("Interval"); - - b.Property("Message"); - - b.HasKey("Id"); - - b.HasIndex("ChannelId") - .IsUnique(); - - b.ToTable("Repeaters"); - }); - modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => { b.Property("Id") @@ -790,6 +794,13 @@ namespace NadekoBot.Migrations .HasForeignKey("RootPermissionId"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GuildRepeaters") + .HasForeignKey("GuildConfigId"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => { b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index 2e06347a..da737ce4 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -58,9 +58,9 @@ namespace NadekoBot.Modules.Administration var toSend = "🔄 " + Repeater.Message; await Task.Delay(Repeater.Interval, token).ConfigureAwait(false); - //var lastMsgInChannel = (await Channel.GetMessagesAsync(2)).FirstOrDefault(); - // if (lastMsgInChannel.Id == oldMsg?.Id) //don't send if it's the same message in the channel - // continue; + //var lastMsgInChannel = (await Channel.GetMessagesAsync(2)).FirstOrDefault(); + // if (lastMsgInChannel.Id == oldMsg?.Id) //don't send if it's the same message in the channel + // continue; if (oldMsg != null) try { await oldMsg.DeleteAsync(); } catch { } @@ -80,6 +80,11 @@ namespace NadekoBot.Modules.Administration { source.Cancel(); } + + public override string ToString() + { + return $"{this.Channel.Mention} | {(int)this.Repeater.Interval.TotalHours}:{this.Repeater.Interval:mm} | {this.Repeater.Message.TrimTo(33)}"; + } } static RepeatCommands() @@ -105,7 +110,7 @@ namespace NadekoBot.Modules.Administration { index -= 1; ConcurrentQueue rep; - if (!repeaters.TryGetValue(Context.Channel.Id, out rep)) + if (!repeaters.TryGetValue(Context.Guild.Id, out rep)) { await Context.Channel.SendErrorAsync("ℹ️ **No repeating message found on this server.**").ConfigureAwait(false); return; @@ -153,12 +158,12 @@ namespace NadekoBot.Modules.Administration { var guildConfig = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(gc => gc.GuildRepeaters)); - guildConfig.GuildRepeaters.RemoveWhere(r=>r.Id == repeater.Repeater.Id); + guildConfig.GuildRepeaters.RemoveWhere(r => r.Id == repeater.Repeater.Id); await uow.CompleteAsync().ConfigureAwait(false); } if (repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue(repeaterList), rep)) - await Context.Channel.SendConfirmAsync("✅ **Stopped repeating a message.**").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync("Message Repeater",$"#{index+1} stopped.\n\n{repeater.ToString()}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -173,7 +178,7 @@ namespace NadekoBot.Modules.Administration if (string.IsNullOrWhiteSpace(message)) return; - var toAdd = new Repeater() + var toAdd = new GuildRepeater() { ChannelId = Context.Channel.Id, GuildId = Context.Guild.Id, @@ -181,6 +186,15 @@ namespace NadekoBot.Modules.Administration Message = message }; + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.GuildRepeaters)); + + gc.GuildRepeaters.Add(toAdd); + + await uow.CompleteAsync().ConfigureAwait(false); + } + var rep = new RepeatRunner(toAdd, (ITextChannel)Context.Channel); repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue(new[] { rep }), (key, old) => @@ -188,7 +202,7 @@ namespace NadekoBot.Modules.Administration old.Enqueue(rep); return old; }); - + await Context.Channel.SendConfirmAsync($"🔁 Repeating **\"{rep.Repeater.Message}\"** every `{rep.Repeater.Interval.Days} day(s), {rep.Repeater.Interval.Hours} hour(s) and {rep.Repeater.Interval.Minutes} minute(s)`.").ConfigureAwait(false); } @@ -211,7 +225,7 @@ namespace NadekoBot.Modules.Administration { var rep = replist[i]; - sb.AppendLine($"`{i + 1}.` {rep.Channel.Mention} | {(int)rep.Repeater.Interval.TotalHours}:{rep.Repeater.Interval:mm} | {rep.Repeater.Message.TrimTo(20)}"); + sb.AppendLine($"`{i + 1}.` {rep.ToString()}"); } await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() @@ -221,4 +235,4 @@ namespace NadekoBot.Modules.Administration } } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Database/IUnitOfWork.cs b/src/NadekoBot/Services/Database/IUnitOfWork.cs index c90d9736..f17e1f6a 100644 --- a/src/NadekoBot/Services/Database/IUnitOfWork.cs +++ b/src/NadekoBot/Services/Database/IUnitOfWork.cs @@ -15,7 +15,6 @@ namespace NadekoBot.Services.Database IReminderRepository Reminders { get; } ISelfAssignedRolesRepository SelfAssignedRoles { get; } IBotConfigRepository BotConfig { get; } - IRepeaterRepository Repeaters { get; } IUnitConverterRepository ConverterUnits { get; } ICustomReactionRepository CustomReactions { get; } ICurrencyRepository Currency { get; } diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index e4ed7c97..2dc13474 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -58,7 +58,7 @@ namespace NadekoBot.Services.Database.Models public string MuteRoleName { get; set; } public bool CleverbotEnabled { get; set; } - public HashSet GuildRepeaters { get; set; } + public HashSet GuildRepeaters { get; set; } = new HashSet(); } public class FilterChannelId : DbEntity diff --git a/src/NadekoBot/Services/Database/Models/Repeater.cs b/src/NadekoBot/Services/Database/Models/Repeater.cs index cf887b43..f8c07bfe 100644 --- a/src/NadekoBot/Services/Database/Models/Repeater.cs +++ b/src/NadekoBot/Services/Database/Models/Repeater.cs @@ -2,11 +2,16 @@ namespace NadekoBot.Services.Database.Models { - public class Repeater :DbEntity + public class Repeater : DbEntity { public ulong GuildId { get; set; } public ulong ChannelId { get; set; } public string Message { get; set; } public TimeSpan Interval { get; set; } } + + public class GuildRepeater : Repeater + { + + } } diff --git a/src/NadekoBot/Services/Database/NadekoContext.cs b/src/NadekoBot/Services/Database/NadekoContext.cs index ec7c2288..dcf73c5e 100644 --- a/src/NadekoBot/Services/Database/NadekoContext.cs +++ b/src/NadekoBot/Services/Database/NadekoContext.cs @@ -16,7 +16,6 @@ namespace NadekoBot.Services.Database public DbSet Reminders { get; set; } public DbSet SelfAssignableRoles { get; set; } public DbSet BotConfig { get; set; } - public DbSet Repeaters { get; set; } public DbSet Currency { get; set; } public DbSet ConversionUnits { get; set; } public DbSet MusicPlaylists { get; set; } @@ -44,6 +43,7 @@ namespace NadekoBot.Services.Database this.Database.Migrate(); EnsureSeedData(); } + ////Uncomment this to db initialisation with dotnet ef migration add [module] //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) //{ @@ -172,16 +172,6 @@ namespace NadekoBot.Services.Database #endregion - #region Repeater - - var repeaterEntity = modelBuilder.Entity(); - - repeaterEntity - .HasIndex(r => r.ChannelId) - .IsUnique(); - - #endregion - #region Currency var currencyEntity = modelBuilder.Entity(); diff --git a/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs b/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs deleted file mode 100644 index 2446c275..00000000 --- a/src/NadekoBot/Services/Database/Repositories/IRepeaterRepository.cs +++ /dev/null @@ -1,9 +0,0 @@ -using NadekoBot.Services.Database.Models; - -namespace NadekoBot.Services.Database.Repositories -{ - public interface IRepeaterRepository : IRepository - { - - } -} diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs index 0720e8ad..b2cce5a0 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs @@ -26,6 +26,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl .Include(gc => gc.FilterWordsChannelIds) .Include(gc => gc.FilteredWords) .Include(gc => gc.CommandCooldowns) + .Include(gc => gc.GuildRepeaters) .ToList(); /// diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs deleted file mode 100644 index 94827f95..00000000 --- a/src/NadekoBot/Services/Database/Repositories/Impl/RepeaterRepository.cs +++ /dev/null @@ -1,12 +0,0 @@ -using NadekoBot.Services.Database.Models; -using Microsoft.EntityFrameworkCore; - -namespace NadekoBot.Services.Database.Repositories.Impl -{ - public class RepeaterRepository : Repository, IRepeaterRepository - { - public RepeaterRepository(DbContext context) : base(context) - { - } - } -} diff --git a/src/NadekoBot/Services/Database/UnitOfWork.cs b/src/NadekoBot/Services/Database/UnitOfWork.cs index 9601145d..88231d6b 100644 --- a/src/NadekoBot/Services/Database/UnitOfWork.cs +++ b/src/NadekoBot/Services/Database/UnitOfWork.cs @@ -30,9 +30,6 @@ namespace NadekoBot.Services.Database private IBotConfigRepository _botConfig; public IBotConfigRepository BotConfig => _botConfig ?? (_botConfig = new BotConfigRepository(_context)); - private IRepeaterRepository _repeaters; - public IRepeaterRepository Repeaters => _repeaters ?? (_repeaters = new RepeaterRepository(_context)); - private ICurrencyRepository _currency; public ICurrencyRepository Currency => _currency ?? (_currency = new CurrencyRepository(_context)); From c705c0494c9a09e9c9cfecd5550da7793152331a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 13:44:26 +0100 Subject: [PATCH 68/85] Commandlist updated --- docs/Commands List.md | 12 +++++++++--- src/NadekoBot/Modules/Music/Music.cs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index f435ab6d..049143f8 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -31,6 +31,7 @@ Command and aliases | Description | Usage `.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` `.die` | Shuts the bot down. **Bot Owner only.** | `.die` `.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` +`.setstatus` | Sets the bot's status. (Online/Idle/Dnd/Invisible) **Bot Owner only.** | `.setstatus Idle` `.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` `.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` `.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` @@ -56,8 +57,10 @@ Command and aliases | Description | Usage `.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` `.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` `.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` -`.repeatinvoke` `.repinv` | Immediately shows the repeat message and restarts the timer. **Requires ManageMessages server permission.** | `.repinv` -`.repeat` | Repeat a message every X minutes. If no parameters are specified, repeat is disabled. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` +`.repeatinvoke` `.repinv` | Immediately shows the repeat message on a certain index and restarts its timer. **Requires ManageMessages server permission.** | `.repinv 1` +`.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` +`.repeat` | Repeat a message every X minutes in the current channel. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` +`.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` `.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` `.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` `.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` @@ -160,13 +163,14 @@ Command and aliases | Description | Usage `>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` `>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` `>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` +`>pollstats` | Shows the poll results without stopping the poll on this server. **Requires ManageMessages server permission.** | `>pollstats` `>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` `>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` `>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` `>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` `>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` `>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` -`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages channel permission.** | `>cleverbot` +`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot` `>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` `>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more` `>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something` @@ -366,6 +370,7 @@ Command and aliases | Description | Usage `.userinfo` `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | `.uinfo @SomeUser` `.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` `.calcops` | Shows all available operations in .calc command | `.calcops` +`.rotaterolecolor` `.rrc` | Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. **Bot Owner only.** | `.rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `.rrc 0 MyLsdRole` `.togethertube` `.totube` | Creates a new room on and shows the link in the chat. | `.totube` `.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch` `.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role` @@ -375,6 +380,7 @@ Command and aliases | Description | Usage `.serverid` `.sid` | Shows current server ID. | `.sid` `.roles` | List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. | `.roles 2` or `.roles @Someone` `.channeltopic` `.ct` | Sends current channel's topic as a message. | `.ct` +`.createinvite` `.crinv` | Creates a new invite which has infinite max uses and never expires. **Requires CreateInstantInvite channel permission.** | `.crinv` `.stats` | Shows some basic stats for Nadeko. | `.stats` `.showemojis` `.se` | Shows a name and a link to every SPECIAL emoji in the message. | `.se A message full of SPECIAL emojis` `.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3` diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 536a00ae..3ffb104c 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -300,7 +300,7 @@ $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {tota return; if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild) { - await Context.Channel.SendErrorAsync("💢 You need to be in a **voice channel** on this server.\n If you are already in a voice (ITextChannel)Context.Channel, try rejoining it.").ConfigureAwait(false); + await Context.Channel.SendErrorAsync($"💢 You need to be in a **voice channel** on this server.").ConfigureAwait(false); return; } var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault(); From 74d05a54a90585ce42dc3309510ff9f6c474010e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 20:17:53 +0100 Subject: [PATCH 69/85] .prune fix (now will delete 1 more message than before) --- src/NadekoBot/Modules/Administration/Administration.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 5b97b69a..e0a9bf0b 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -460,6 +460,9 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(ChannelPermission.ManageMessages)] public async Task Prune(int count) { + if (count < 1) + return; + count += 1; await Context.Message.DeleteAsync().ConfigureAwait(false); int limit = (count < 100) ? count : 100; var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten().ConfigureAwait(false)); @@ -472,6 +475,11 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(ChannelPermission.ManageMessages)] public async Task Prune(IGuildUser user, int count = 100) { + if (count < 1) + return; + + if (user.Id == Context.User.Id) + count += 1; int limit = (count < 100) ? count : 100; var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten()).Where(m => m.Author == user); From 27b0d9fe36f15aa7db45b6f1f7c6a8ea4bcc4939 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 20:37:17 +0100 Subject: [PATCH 70/85] Cleverbot fix --- src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs b/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs index 7b952b1c..08269207 100644 --- a/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs +++ b/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs @@ -30,9 +30,9 @@ namespace Services.CleverBotApi public static ChatterBot Create(ChatterBotType type, object arg) { #if GLOBAL_NADEKO - var url = "http://www.cleverbot.com/webservicemin?uc=321&botapi=nadekobot"; + var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=nadekobot"; #else - var url = "http://www.cleverbot.com/webservicemin?uc=321"; + var url = "http://www.cleverbot.com/webservicemin?uc=3210"; #endif switch (type) From d848ef627076b6efede4ca506a185ae9a20c38b0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 20:37:41 +0100 Subject: [PATCH 71/85] .antiraid .antispam .antilist .antispamignore added/improved --- .../NadekoSqliteContextModelSnapshot.cs | 79 +++++ .../Commands/AntiRaidCommands.cs | 272 ------------------ .../Commands/MessageRepeater.cs | 12 +- .../Resources/CommandStrings.Designer.cs | 54 ++++ src/NadekoBot/Resources/CommandStrings.resx | 18 ++ .../Services/Database/Models/GuildConfig.cs | 6 + .../Services/Database/NadekoContext.cs | 16 ++ .../Impl/GuildConfigRepository.cs | 3 + 8 files changed, 181 insertions(+), 279 deletions(-) delete mode 100644 src/NadekoBot/Modules/Administration/Commands/AntiRaidCommands.cs diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index 8038530d..190e425e 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -17,6 +17,62 @@ namespace NadekoBot.Migrations modelBuilder .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.Property("UserThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiRaidSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AntiSpamSettingId"); + + b.Property("ChannelId"); + + b.HasKey("Id"); + + b.HasIndex("AntiSpamSettingId"); + + b.ToTable("AntiSpamIgnore"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("GuildConfigId"); + + b.Property("MessageThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiSpamSetting"); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => { b.Property("Id") @@ -722,6 +778,29 @@ namespace NadekoBot.Migrations b.ToTable("PokeGame"); }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiRaidSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiRaidSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.HasOne("NadekoBot.Services.Database.Models.AntiSpamSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("AntiSpamSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiSpamSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiSpamSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => { b.HasOne("NadekoBot.Services.Database.Models.BotConfig") diff --git a/src/NadekoBot/Modules/Administration/Commands/AntiRaidCommands.cs b/src/NadekoBot/Modules/Administration/Commands/AntiRaidCommands.cs deleted file mode 100644 index 4551ef48..00000000 --- a/src/NadekoBot/Modules/Administration/Commands/AntiRaidCommands.cs +++ /dev/null @@ -1,272 +0,0 @@ -using Discord; -using Discord.Commands; -using NadekoBot.Attributes; -using NadekoBot.Extensions; -using NLog; -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Administration -{ - public partial class Administration - { - public enum PunishmentAction - { - Mute, - Kick, - Ban, - } - - public enum ProtectionType - { - Raiding, - Spamming, - } - - private class AntiRaidSetting - { - public int UserThreshold { get; set; } - public int Seconds { get; set; } - public PunishmentAction Action { get; set; } - public int UsersCount { get; set; } - public ConcurrentHashSet RaidUsers { get; set; } = new ConcurrentHashSet(); - } - - private class AntiSpamSetting - { - public PunishmentAction Action { get; set; } - public int MessageThreshold { get; set; } = 3; - public ConcurrentDictionary UserStats { get; set; } - = new ConcurrentDictionary(); - } - - private class UserSpamStats - { - public int Count { get; set; } - public string LastMessage { get; set; } - - public UserSpamStats(string msg) - { - Count = 1; - LastMessage = msg.ToUpperInvariant(); - } - - public void ApplyNextMessage(string message) - { - var upperMsg = message.ToUpperInvariant(); - if (upperMsg == LastMessage) - Count++; - else - { - LastMessage = upperMsg; - Count = 0; - } - } - } - - [Group] - public class AntiRaidCommands : ModuleBase - { - private static ConcurrentDictionary antiRaidGuilds = - new ConcurrentDictionary(); - // guildId | (userId|messages) - private static ConcurrentDictionary antiSpamGuilds = - new ConcurrentDictionary(); - - private static Logger _log { get; } - - static AntiRaidCommands() - { - _log = LogManager.GetCurrentClassLogger(); - - NadekoBot.Client.MessageReceived += async (imsg) => - { - - try - { - var msg = imsg as IUserMessage; - if (msg == null || msg.Author.IsBot) - return; - - var channel = msg.Channel as ITextChannel; - if (channel == null) - return; - AntiSpamSetting spamSettings; - if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings)) - return; - - var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content), - (id, old) => { old.ApplyNextMessage(msg.Content); return old; }); - - if (stats.Count >= spamSettings.MessageThreshold) - { - if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) - { - await PunishUsers(spamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author) - .ConfigureAwait(false); - } - } - } - catch { } - }; - - NadekoBot.Client.UserJoined += async (usr) => - { - try - { - if (usr.IsBot) - return; - AntiRaidSetting settings; - if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) - return; - if (!settings.RaidUsers.Add(usr)) - return; - - ++settings.UsersCount; - - if (settings.UsersCount >= settings.UserThreshold) - { - var users = settings.RaidUsers.ToArray(); - settings.RaidUsers.Clear(); - - await PunishUsers(settings.Action, ProtectionType.Raiding, users).ConfigureAwait(false); - } - await Task.Delay(1000 * settings.Seconds).ConfigureAwait(false); - - settings.RaidUsers.TryRemove(usr); - --settings.UsersCount; - - } - catch { } - }; - } - - private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus) - { - foreach (var gu in gus) - { - switch (action) - { - case PunishmentAction.Mute: - try - { - await MuteCommands.MuteUser(gu).ConfigureAwait(false); - } - catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); } - break; - case PunishmentAction.Kick: - try - { - await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false); - try - { - await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false); - } - catch - { - await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false); - // try it twice, really don't want to ban user if - // only kick has been specified as the punishement - } - } - catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } - break; - case PunishmentAction.Ban: - try - { - await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false); - } - catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } - break; - default: - break; - } - } - await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false); - } - - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - [RequireUserPermission(GuildPermission.Administrator)] - public async Task AntiRaid(int userThreshold, int seconds, PunishmentAction action) - { - if (userThreshold < 2 || userThreshold > 30) - { - await Context.Channel.SendErrorAsync("❗️User threshold must be between **2** and **30**.").ConfigureAwait(false); - return; - } - - if (seconds < 2 || seconds > 300) - { - await Context.Channel.SendErrorAsync("❗️Time must be between **2** and **300** seconds.").ConfigureAwait(false); - return; - } - - try - { - await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false); - } - catch (Exception ex) - { - await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + - "or create 'nadeko-mute' role with disabled SendMessages and try again.") - .ConfigureAwait(false); - _log.Warn(ex); - return; - } - - var setting = new AntiRaidSetting() - { - Action = action, - Seconds = seconds, - UserThreshold = userThreshold, - }; - antiRaidGuilds.AddOrUpdate(Context.Guild.Id, setting, (id, old) => setting); - - await Context.Channel.SendConfirmAsync($"ℹ️ {Context.User.Mention} If **{userThreshold}** or more users join within **{seconds}** seconds, I will **{action}** them.") - .ConfigureAwait(false); - } - - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - [RequireUserPermission(GuildPermission.Administrator)] - public async Task AntiSpam(int messageCount=3, PunishmentAction action = PunishmentAction.Mute) - { - if (messageCount < 2 || messageCount > 10) - return; - - AntiSpamSetting throwaway; - if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway)) - { - await Context.Channel.SendConfirmAsync("🆗 **Anti-Spam feature** has been **disabled** on this server.").ConfigureAwait(false); - } - else - { - try - { - await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false); - } - catch (Exception ex) - { - await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + - "or create 'nadeko-mute' role with disabled SendMessages and try again.") - .ConfigureAwait(false); - _log.Warn(ex); - return; - } - - if (antiSpamGuilds.TryAdd(Context.Guild.Id, new AntiSpamSetting() - { - Action = action, - MessageThreshold = messageCount, - })) - await Context.Channel.SendConfirmAsync("✅ **Anti-Spam feature** has been **enabled** on this server.").ConfigureAwait(false); - } - - } - } - } -} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index da737ce4..2bbe9805 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -91,13 +91,11 @@ namespace NadekoBot.Modules.Administration { var _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); - using (var uow = DbHandler.UnitOfWork()) - { - repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs - .ToDictionary(gc => gc.GuildId, - gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) - .Where(gr => gr.Channel != null)))); - } + + repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs + .ToDictionary(gc => gc.GuildId, + gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) + .Where(gr => gr.Channel != null)))); sw.Stop(); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index fc8637da..eb72d8f1 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -437,6 +437,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to antilist antilst. + /// + public static string antilist_cmd { + get { + return ResourceManager.GetString("antilist_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shows currently enabled protection features.. + /// + public static string antilist_desc { + get { + return ResourceManager.GetString("antilist_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}antilist`. + /// + public static string antilist_usage { + get { + return ResourceManager.GetString("antilist_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to antiraid. /// @@ -491,6 +518,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to antispamignore. + /// + public static string antispamignore_cmd { + get { + return ResourceManager.GetString("antispamignore_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Toggles whether antispam ignores current channel. Antispam must be enabled.. + /// + public static string antispamignore_desc { + get { + return ResourceManager.GetString("antispamignore_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}antispamignore`. + /// + public static string antispamignore_usage { + get { + return ResourceManager.GetString("antispamignore_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to asar. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index cc144d2d..d96b1c3f 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2907,4 +2907,22 @@ `{0}reprm 2` + + antilist antilst + + + Shows currently enabled protection features. + + + `{0}antilist` + + + antispamignore + + + Toggles whether antispam ignores current channel. Antispam must be enabled. + + + `{0}antispamignore` + \ No newline at end of file diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 2dc13474..7931e8c9 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using static NadekoBot.Modules.Administration.Administration; namespace NadekoBot.Services.Database.Models { @@ -59,6 +60,11 @@ namespace NadekoBot.Services.Database.Models public string MuteRoleName { get; set; } public bool CleverbotEnabled { get; set; } public HashSet GuildRepeaters { get; set; } = new HashSet(); + + public AntiRaidSetting AntiRaidSetting { get; set; } + public AntiSpamSetting AntiSpamSetting { get; set; } + + //public List ProtectionIgnoredChannels { get; set; } = new List(); } public class FilterChannelId : DbEntity diff --git a/src/NadekoBot/Services/Database/NadekoContext.cs b/src/NadekoBot/Services/Database/NadekoContext.cs index dcf73c5e..e955409f 100644 --- a/src/NadekoBot/Services/Database/NadekoContext.cs +++ b/src/NadekoBot/Services/Database/NadekoContext.cs @@ -142,6 +142,17 @@ namespace NadekoBot.Services.Database .HasIndex(c => c.GuildId) .IsUnique(); + modelBuilder.Entity() + .HasOne(x => x.GuildConfig) + .WithOne(x => x.AntiSpamSetting); + + modelBuilder.Entity() + .HasOne(x => x.GuildConfig) + .WithOne(x => x.AntiRaidSetting); + + //modelBuilder.Entity() + // .HasAlternateKey(c => new { c.ChannelId, c.ProtectionType }); + #endregion #region BotConfig @@ -221,6 +232,11 @@ namespace NadekoBot.Services.Database .IsUnique(); + #endregion + + #region Protection + + #endregion } } diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs index b2cce5a0..89c59419 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/GuildConfigRepository.cs @@ -27,6 +27,9 @@ namespace NadekoBot.Services.Database.Repositories.Impl .Include(gc => gc.FilteredWords) .Include(gc => gc.CommandCooldowns) .Include(gc => gc.GuildRepeaters) + .Include(gc => gc.AntiRaidSetting) + .Include(gc => gc.AntiSpamSetting) + .ThenInclude(x => x.IgnoredChannels) .ToList(); /// From ef688d096b427fb860df08a4ed80970265433841 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 20:40:52 +0100 Subject: [PATCH 72/85] Woopsie --- .../20170110180534_protection.Designer.cs | 942 ++++++++++++++++++ .../Migrations/20170110180534_protection.cs | 104 ++ .../Commands/ProtectionCommands.cs | 425 ++++++++ .../Database/Models/AntiProtection.cs | 53 + 4 files changed, 1524 insertions(+) create mode 100644 src/NadekoBot/Migrations/20170110180534_protection.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170110180534_protection.cs create mode 100644 src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs create mode 100644 src/NadekoBot/Services/Database/Models/AntiProtection.cs diff --git a/src/NadekoBot/Migrations/20170110180534_protection.Designer.cs b/src/NadekoBot/Migrations/20170110180534_protection.Designer.cs new file mode 100644 index 00000000..ecc134cd --- /dev/null +++ b/src/NadekoBot/Migrations/20170110180534_protection.Designer.cs @@ -0,0 +1,942 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170110180534_protection")] + partial class protection + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.Property("UserThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiRaidSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AntiSpamSettingId"); + + b.Property("ChannelId"); + + b.HasKey("Id"); + + b.HasIndex("AntiSpamSettingId"); + + b.ToTable("AntiSpamIgnore"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("GuildConfigId"); + + b.Property("MessageThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiSpamSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ItemId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("BlacklistItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BufferSize"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("MigrationVersion"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.HasKey("Id"); + + b.ToTable("BotConfig"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BaseDestroyed"); + + b.Property("CallUser"); + + b.Property("ClashWarId"); + + b.Property("SequenceNumber"); + + b.Property("Stars"); + + b.Property("TimeAdded"); + + b.HasKey("Id"); + + b.HasIndex("ClashWarId"); + + b.ToTable("ClashCallers"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("EnemyClan"); + + b.Property("GuildId"); + + b.Property("Size"); + + b.Property("StartedAt"); + + b.Property("WarState"); + + b.HasKey("Id"); + + b.ToTable("ClashOfClans"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommandName"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("IsRegex"); + + b.Property("OwnerOnly"); + + b.Property("Response"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.ToTable("CustomReactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Name"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Donators"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildConfigId1"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.HasIndex("GuildConfigId1"); + + b.ToTable("FilterChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("Word"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FilteredWord"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GCChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoAssignRoleId"); + + b.Property("AutoDeleteByeMessages"); + + b.Property("AutoDeleteByeMessagesTimer"); + + b.Property("AutoDeleteGreetMessages"); + + b.Property("AutoDeleteGreetMessagesTimer"); + + b.Property("AutoDeleteSelfAssignedRoleMessages"); + + b.Property("ByeMessageChannelId"); + + b.Property("ChannelByeMessageText"); + + b.Property("ChannelGreetMessageText"); + + b.Property("CleverbotEnabled"); + + b.Property("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.HasIndex("LogSettingId"); + + b.HasIndex("RootPermissionId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GuildRepeater"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelCreatedId"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelDestroyedId"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("ChannelUpdatedId"); + + b.Property("IsLogging"); + + b.Property("LogOtherId"); + + b.Property("LogUserPresence"); + + b.Property("LogUserPresenceId"); + + b.Property("LogVoicePresence"); + + b.Property("LogVoicePresenceId"); + + b.Property("LogVoicePresenceTTSId"); + + b.Property("MessageDeleted"); + + b.Property("MessageDeletedId"); + + b.Property("MessageUpdated"); + + b.Property("MessageUpdatedId"); + + b.Property("UserBanned"); + + b.Property("UserBannedId"); + + b.Property("UserJoined"); + + b.Property("UserJoinedId"); + + b.Property("UserLeft"); + + b.Property("UserLeftId"); + + b.Property("UserMutedId"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUnbannedId"); + + b.Property("UserUpdated"); + + b.Property("UserUpdatedId"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ModuleName"); + + b.Property("Prefix"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("ModulePrefixes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("AuthorId"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("MutedUserId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("NextId"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("NextId") + .IsUnique(); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Status"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("PlayingStatus"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("MusicPlaylistId"); + + b.Property("Provider"); + + b.Property("ProviderType"); + + b.Property("Query"); + + b.Property("Title"); + + b.Property("Uri"); + + b.HasKey("Id"); + + b.HasIndex("MusicPlaylistId"); + + b.ToTable("PlaylistSong"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("AuthorName") + .IsRequired(); + + b.Property("GuildId"); + + b.Property("Keyword") + .IsRequired(); + + b.Property("Text") + .IsRequired(); + + b.HasKey("Id"); + + b.ToTable("Quotes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Icon"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("RaceAnimals"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("IsPrivate"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("UserId"); + + b.Property("When"); + + b.HasKey("Id"); + + b.ToTable("Reminders"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildId", "RoleId") + .IsUnique(); + + b.ToTable("SelfAssignableRoles"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiRaidSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiRaidSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.HasOne("NadekoBot.Services.Database.Models.AntiSpamSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("AntiSpamSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiSpamSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiSpamSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("Blacklist") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar") + .WithMany("Bases") + .HasForeignKey("ClashWarId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandCooldowns") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("EightBallResponses") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterInvitesChannelIds") + .HasForeignKey("GuildConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterWordsChannelIds") + .HasForeignKey("GuildConfigId1"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilteredWords") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GenerateCurrencyChannelIds") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + + b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission") + .WithMany() + .HasForeignKey("RootPermissionId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GuildRepeaters") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("ModulePrefixes") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("MutedUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") + .WithOne("Previous") + .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RotatingStatusMessages") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist") + .WithMany("Songs") + .HasForeignKey("MusicPlaylistId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RaceAnimals") + .HasForeignKey("BotConfigId"); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170110180534_protection.cs b/src/NadekoBot/Migrations/20170110180534_protection.cs new file mode 100644 index 00000000..fca7f541 --- /dev/null +++ b/src/NadekoBot/Migrations/20170110180534_protection.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class protection : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "AntiRaidSetting", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Action = table.Column(nullable: false), + GuildConfigId = table.Column(nullable: false), + Seconds = table.Column(nullable: false), + UserThreshold = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AntiRaidSetting", x => x.Id); + table.ForeignKey( + name: "FK_AntiRaidSetting_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "AntiSpamSetting", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Action = table.Column(nullable: false), + GuildConfigId = table.Column(nullable: false), + MessageThreshold = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AntiSpamSetting", x => x.Id); + table.ForeignKey( + name: "FK_AntiSpamSetting_GuildConfigs_GuildConfigId", + column: x => x.GuildConfigId, + principalTable: "GuildConfigs", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "AntiSpamIgnore", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + AntiSpamSettingId = table.Column(nullable: true), + ChannelId = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AntiSpamIgnore", x => x.Id); + table.ForeignKey( + name: "FK_AntiSpamIgnore_AntiSpamSetting_AntiSpamSettingId", + column: x => x.AntiSpamSettingId, + principalTable: "AntiSpamSetting", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_AntiRaidSetting_GuildConfigId", + table: "AntiRaidSetting", + column: "GuildConfigId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_AntiSpamIgnore_AntiSpamSettingId", + table: "AntiSpamIgnore", + column: "AntiSpamSettingId"); + + migrationBuilder.CreateIndex( + name: "IX_AntiSpamSetting_GuildConfigId", + table: "AntiSpamSetting", + column: "GuildConfigId", + unique: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AntiRaidSetting"); + + migrationBuilder.DropTable( + name: "AntiSpamIgnore"); + + migrationBuilder.DropTable( + name: "AntiSpamSetting"); + } + } +} diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs new file mode 100644 index 00000000..e37f4850 --- /dev/null +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -0,0 +1,425 @@ +using Discord; +using Discord.Commands; +using Microsoft.EntityFrameworkCore; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Services; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NLog; +using System; +using System.Collections.Concurrent; +using System.ComponentModel.DataAnnotations.Schema; +using System.Linq; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Administration +{ + public partial class Administration + { + public enum ProtectionType + { + Raiding, + Spamming, + } + + public class AntiRaidStats + { + public AntiRaidSetting AntiRaidSettings { get; set; } + public int UsersCount { get; set; } = 0; + public ConcurrentHashSet RaidUsers { get; set; } = new ConcurrentHashSet(); + + public override string ToString() => + $"If **{AntiRaidSettings.UserThreshold}** or more users join within **{AntiRaidSettings.Seconds}** seconds," + + $" I will **{AntiRaidSettings.Action}** them."; + } + + public class AntiSpamStats + { + public AntiSpamSetting AntiSpamSettings { get; set; } + public ConcurrentDictionary UserStats { get; set; } + = new ConcurrentDictionary(); + + public override string ToString() + { + var ignoredString = string.Join(", ", AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>")); + + if (string.IsNullOrWhiteSpace(ignoredString)) + ignoredString = "none"; + return $"If a user posts **{AntiSpamSettings.MessageThreshold}** same messages in a row, I will **{AntiSpamSettings.Action}** them." + + $"\n\t__IgnoredChannels__: {ignoredString}"; + } + } + + public class UserSpamStats + { + public int Count { get; set; } + public string LastMessage { get; set; } + + public UserSpamStats(string msg) + { + Count = 1; + LastMessage = msg.ToUpperInvariant(); + } + + public void ApplyNextMessage(string message) + { + var upperMsg = message.ToUpperInvariant(); + if (upperMsg == LastMessage) + Count++; + else + { + LastMessage = upperMsg; + Count = 0; + } + } + } + + [Group] + public class ProtectionCommands : ModuleBase + { + private static ConcurrentDictionary antiRaidGuilds = + new ConcurrentDictionary(); + // guildId | (userId|messages) + private static ConcurrentDictionary antiSpamGuilds = + new ConcurrentDictionary(); + + private static Logger _log { get; } + + static ProtectionCommands() + { + _log = LogManager.GetCurrentClassLogger(); + + foreach (var gc in NadekoBot.AllGuildConfigs) + { + var raid = gc.AntiRaidSetting; + var spam = gc.AntiSpamSetting; + + if (raid != null) + { + var raidStats = new AntiRaidStats() { AntiRaidSettings = raid }; + antiRaidGuilds.TryAdd(gc.GuildId, raidStats); + } + + if (spam != null) + antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam }); + } + + NadekoBot.Client.MessageReceived += async (imsg) => + { + + try + { + var msg = imsg as IUserMessage; + if (msg == null || msg.Author.IsBot) + return; + + var channel = msg.Channel as ITextChannel; + if (channel == null) + return; + AntiSpamStats spamSettings; + if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || + spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore() + { + ChannelId = channel.Id + })) + return; + + var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content), + (id, old) => { old.ApplyNextMessage(msg.Content); return old; }); + + if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold) + { + if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) + { + await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author) + .ConfigureAwait(false); + } + } + } + catch { } + }; + + NadekoBot.Client.UserJoined += async (usr) => + { + try + { + if (usr.IsBot) + return; + AntiRaidStats settings; + if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) + return; + if (!settings.RaidUsers.Add(usr)) + return; + + ++settings.UsersCount; + + if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold) + { + var users = settings.RaidUsers.ToArray(); + settings.RaidUsers.Clear(); + + await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false); + } + await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false); + + settings.RaidUsers.TryRemove(usr); + --settings.UsersCount; + + } + catch { } + }; + } + + private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus) + { + foreach (var gu in gus) + { + switch (action) + { + case PunishmentAction.Mute: + try + { + await MuteCommands.MuteUser(gu).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex, "I can't apply punishement"); } + break; + case PunishmentAction.Kick: + try + { + await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false); + try + { + await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false); + } + catch + { + await gu.Guild.RemoveBanAsync(gu).ConfigureAwait(false); + // try it twice, really don't want to ban user if + // only kick has been specified as the punishement + } + } + catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } + break; + case PunishmentAction.Ban: + try + { + await gu.Guild.AddBanAsync(gu, 7).ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } + break; + default: + break; + } + } + await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false); + } + + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task AntiRaid(int userThreshold = 5, int seconds = 10, PunishmentAction action = PunishmentAction.Mute) + { + if (userThreshold < 2 || userThreshold > 30) + { + await Context.Channel.SendErrorAsync("❗️User threshold must be between **2** and **30**.").ConfigureAwait(false); + return; + } + + if (seconds < 2 || seconds > 300) + { + await Context.Channel.SendErrorAsync("❗️Time must be between **2** and **300** seconds.").ConfigureAwait(false); + return; + } + + AntiRaidStats throwaway; + if (antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway)) + { + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting)); + + gc.AntiRaidSetting = null; + await uow.CompleteAsync().ConfigureAwait(false); + } + await Context.Channel.SendConfirmAsync("**Anti-Raid** feature has been **disabled** on this server.").ConfigureAwait(false); + return; + } + + try + { + await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false); + } + catch (Exception ex) + { + await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + + "or create 'nadeko-mute' role with disabled SendMessages and try again.") + .ConfigureAwait(false); + _log.Warn(ex); + return; + } + + var stats = new AntiRaidStats() + { + AntiRaidSettings = new AntiRaidSetting() + { + Action = action, + Seconds = seconds, + UserThreshold = userThreshold, + } + }; + + antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); + + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiRaidSetting)); + + gc.AntiRaidSetting = stats.AntiRaidSettings; + await uow.CompleteAsync().ConfigureAwait(false); + } + + await Context.Channel.SendConfirmAsync("Anti-Raid Enabled", $"{Context.User.Mention} {stats.ToString()}") + .ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task AntiSpam(int messageCount = 3, PunishmentAction action = PunishmentAction.Mute) + { + if (messageCount < 2 || messageCount > 10) + return; + + AntiSpamStats throwaway; + if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway)) + { + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting) + .ThenInclude(x => x.IgnoredChannels)); + + gc.AntiSpamSetting = null; + await uow.CompleteAsync().ConfigureAwait(false); + } + await Context.Channel.SendConfirmAsync("**Anti-Spam** has been **disabled** on this server.").ConfigureAwait(false); + return; + } + + try + { + await MuteCommands.GetMuteRole(Context.Guild).ConfigureAwait(false); + } + catch (Exception ex) + { + await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + + "or create 'nadeko-mute' role with disabled SendMessages and try again.") + .ConfigureAwait(false); + _log.Warn(ex); + return; + } + + var stats = new AntiSpamStats + { + AntiSpamSettings = new AntiSpamSetting() + { + Action = action, + MessageThreshold = messageCount, + } + }; + + antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); + + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting)); + + gc.AntiSpamSetting = stats.AntiSpamSettings; + await uow.CompleteAsync().ConfigureAwait(false); + } + + await Context.Channel.SendConfirmAsync("Anti-Spam Enabled", $"{Context.User.Mention} {stats.ToString()}").ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task AntispamIgnore() + { + var channel = (ITextChannel)Context.Channel; + + var obj = new AntiSpamIgnore() + { + ChannelId = channel.Id + }; + bool added; + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.AntiSpamSetting).ThenInclude(x => x.IgnoredChannels)); + var spam = gc.AntiSpamSetting; + if (spam == null) + { + return; + } + + if (spam.IgnoredChannels.Add(obj)) + { + AntiSpamStats temp; + if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) + temp.AntiSpamSettings.IgnoredChannels.Add(obj); + added = true; + } + else + { + spam.IgnoredChannels.Remove(obj); + AntiSpamStats temp; + if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) + temp.AntiSpamSettings.IgnoredChannels.Remove(obj); + added = false; + } + + await uow.CompleteAsync().ConfigureAwait(false); + } + if (added) + await Context.Channel.SendConfirmAsync("Anti-Spam will ignore this channel.").ConfigureAwait(false); + else + await Context.Channel.SendConfirmAsync("Anti-Spam will no longer ignore this channel.").ConfigureAwait(false); + + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task AntiList() + { + var channel = (ITextChannel)Context.Channel; + + AntiSpamStats spam; + antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam); + + AntiRaidStats raid; + antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid); + + if (spam == null && raid == null) + { + await Context.Channel.SendConfirmAsync("No protections enabled."); + return; + } + + var embed = new EmbedBuilder().WithOkColor() + .WithTitle("Protections Enabled"); + + if (spam != null) + embed.AddField(efb => efb.WithName("Anti-Spam") + .WithValue(spam.ToString()) + .WithIsInline(true)); + + if (raid != null) + embed.AddField(efb => efb.WithName("Anti-Raid") + .WithValue(raid.ToString()) + .WithIsInline(true)); + + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Services/Database/Models/AntiProtection.cs b/src/NadekoBot/Services/Database/Models/AntiProtection.cs new file mode 100644 index 00000000..0172dd90 --- /dev/null +++ b/src/NadekoBot/Services/Database/Models/AntiProtection.cs @@ -0,0 +1,53 @@ +using Discord; +using NadekoBot.Services.Database.Models; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; +namespace NadekoBot.Services.Database.Models +{ + public class AntiRaidSetting : DbEntity + { + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public int UserThreshold { get; set; } + public int Seconds { get; set; } + public PunishmentAction Action { get; set; } + } + + public class AntiSpamSetting : DbEntity + { + public int GuildConfigId { get; set; } + public GuildConfig GuildConfig { get; set; } + + public PunishmentAction Action { get; set; } + public int MessageThreshold { get; set; } = 3; + public HashSet IgnoredChannels { get; set; } = new HashSet(); + } + + + public enum PunishmentAction + { + Mute, + Kick, + Ban, + } + + public class AntiSpamIgnore : DbEntity + { + public ulong ChannelId { get; set; } + + public override int GetHashCode() => ChannelId.GetHashCode(); + + public override bool Equals(object obj) + { + var inst = obj as AntiSpamIgnore; + + if (inst == null) + return false; + + return inst.ChannelId == ChannelId; + + } + } +} \ No newline at end of file From d7dc7bf3fb3c2a39fa0b2dbf87b7924dc49e0f41 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 10 Jan 2017 23:12:52 +0100 Subject: [PATCH 73/85] Up to 5 repeaters. --- .../Modules/Administration/Commands/MessageRepeater.cs | 2 ++ src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index 2bbe9805..34281838 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -188,6 +188,8 @@ namespace NadekoBot.Modules.Administration { var gc = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.GuildRepeaters)); + if (gc.GuildRepeaters.Count >= 5) + return; gc.GuildRepeaters.Add(toAdd); await uow.CompleteAsync().ConfigureAwait(false); diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index eb72d8f1..e58dbbbb 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -5766,7 +5766,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Repeat a message every X minutes in the current channel.. + /// Looks up a localized string similar to Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total.. /// public static string repeat_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index d96b1c3f..d2ef3388 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -274,7 +274,7 @@ repeat - Repeat a message every X minutes in the current channel. + Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. `{0}repeat 5 Hello there` From b0d1b89ef23714f666c5e6438735dfc8daf275f7 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 11 Jan 2017 05:01:17 -0500 Subject: [PATCH 74/85] Update GoogleApiService.cs --- src/NadekoBot/Services/Impl/GoogleApiService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index 56a40965..a6cdc590 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -51,7 +51,7 @@ namespace NadekoBot.Services.Impl return (await query.ExecuteAsync()).Items.Select(i => i.Id.PlaylistId); } - private readonly Regex YtVideoIdRegex = new Regex("(?:youtu\\.be\\/|v\\/|u\\/\\w\\/|embed\\/|watch\\?v=|\\&v=)(?[^#\\&\\?]*)", RegexOptions.Compiled); + private readonly Regex YtVideoIdRegex = new Regex("(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled); public async Task> GetRelatedVideosAsync(string id, int count = 1) { From b2ee4f8e665c255c41fad3054d49bc8ad73124a0 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 11 Jan 2017 05:14:39 -0500 Subject: [PATCH 75/85] Update GoogleApiService.cs --- src/NadekoBot/Services/Impl/GoogleApiService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index a6cdc590..eaa066ff 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -51,7 +51,7 @@ namespace NadekoBot.Services.Impl return (await query.ExecuteAsync()).Items.Select(i => i.Id.PlaylistId); } - private readonly Regex YtVideoIdRegex = new Regex("(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled); + private readonly Regex YtVideoIdRegex = new Regex(@"(?:youtube\.com\/\S*(?:(?:\/e(?:mbed))?\/|watch\?(?:\S*?&?v\=))|youtu\.be\/)([a-zA-Z0-9_-]{6,11})", RegexOptions.Compiled); public async Task> GetRelatedVideosAsync(string id, int count = 1) { From c183ab15deec611f124b630a1bed11ff4a72c327 Mon Sep 17 00:00:00 2001 From: Poag Date: Wed, 11 Jan 2017 11:43:03 +0000 Subject: [PATCH 76/85] Updated guide with update instructions. --- docs/guides/Docker Guide.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/docs/guides/Docker Guide.md b/docs/guides/Docker Guide.md index 9908a477..c689c2a5 100644 --- a/docs/guides/Docker Guide.md +++ b/docs/guides/Docker Guide.md @@ -8,7 +8,7 @@ Follow the respective guide for your operating system found here https://docs.do For this guide we will be using the folder /nadeko as our config root folder. ``` -docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json kwoth/nadeko:dev +docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json uirel/nadeko ``` -If you are coming from a previous version of nadeko (the old docker) make sure your crednetials.json has been copied into this directory and is the only thing in this folder. @@ -24,11 +24,27 @@ Once the log ends with "NadekoBot | Starting NadekoBot v1.0-rc2" the bot is read After a few moments you should be able to invite Nadeko to your server. If you cannot check the log file for errors -## Updates / Monitoring +## Monitoring -* Upgrade to the latest version of Nadeko simply `docker restart nadeko`. * Monitor the logs of the container in realtime `docker logs -f nadeko`. +## Updates + +* Manual +Updates are handled by pulling the new layer of the Docker Container which contains a pre compiled update to Nadeko. +The following commands are required for the default options +1. ```docker pull uirel/nadeko:latest``` +2. ```docker stop nadeko; docker rm nadeko``` +3. ```docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json uirel/nadeko``` +4. ```docker start nadeko``` + +* Automatic Updates +Automatic update are now handled by watchertower https://github.com/CenturyLinkLabs/watchtower +To setup watchtower to keep Nadeko up-to-date for you with the default settings use the following command +```docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock centurylink/watchtower --cleanup nadeko``` +This will check for updates to the docker every 5 minutes and update immediately. Alternatively using the ```--interval X``` command to change the interval, where X is the amount of time in seconds to wait. eg 21600 for 6 hours. + + If you have any issues with the docker setup, please ask in #help but indicate you are using the docker. For information about configuring your bot or its functionality, please check the http://nadekobot.readthedocs.io/en/latest guides. From 07c0018479bc2782a899f78266bdc63253c8baff Mon Sep 17 00:00:00 2001 From: Poag Date: Wed, 11 Jan 2017 12:50:39 +0000 Subject: [PATCH 77/85] Update Docker Guide.md --- docs/guides/Docker Guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/Docker Guide.md b/docs/guides/Docker Guide.md index c689c2a5..6ed3a33e 100644 --- a/docs/guides/Docker Guide.md +++ b/docs/guides/Docker Guide.md @@ -30,7 +30,7 @@ After a few moments you should be able to invite Nadeko to your server. If you c ## Updates -* Manual +# Manual Updates are handled by pulling the new layer of the Docker Container which contains a pre compiled update to Nadeko. The following commands are required for the default options 1. ```docker pull uirel/nadeko:latest``` @@ -38,7 +38,7 @@ The following commands are required for the default options 3. ```docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json uirel/nadeko``` 4. ```docker start nadeko``` -* Automatic Updates +# Automatic Updates Automatic update are now handled by watchertower https://github.com/CenturyLinkLabs/watchtower To setup watchtower to keep Nadeko up-to-date for you with the default settings use the following command ```docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock centurylink/watchtower --cleanup nadeko``` From 11da7c89b6715cde6344539459c6b11fbf6f05f3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 13:54:25 +0100 Subject: [PATCH 78/85] hangman fix? closes #956 --- .../Modules/Games/Commands/Hangman/HangmanGame.cs | 8 +++++++- src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs | 6 +++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs index 58071453..23b76e9b 100644 --- a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman } } - public class HangmanGame + public class HangmanGame: IDisposable { private readonly Logger _log; @@ -196,5 +196,11 @@ namespace NadekoBot.Modules.Games.Commands.Hangman {(Errors > 1 ? "/" : " ")} {(Errors > 2 ? "|" : " ")} {(Errors > 3 ? "\\" : " ")} | {(Errors > 4 ? "/" : " ")} {(Errors > 5 ? "\\" : " ")} | /-\"; + + public void Dispose() + { + NadekoBot.Client.MessageReceived -= PotentialGuess; + OnEnded = null; + } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index 0827ab94..50596e4c 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -52,8 +52,12 @@ namespace NadekoBot.Modules.Games { hm.Start(); } - catch (Exception ex) { + catch (Exception ex) + { try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { } + HangmanGame throwaway; + HangmanGames.TryRemove(Context.Channel.Id, out throwaway); + throwaway.Dispose(); return; } From f7e9463a941a3ed8715d4e73575a36bbdab7799f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 14:05:57 +0100 Subject: [PATCH 79/85] .savechat moved to utility module --- .../Modules/Administration/Administration.cs | 18 ---------------- src/NadekoBot/Modules/Utility/Utility.cs | 21 ++++++++++++++++++- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index e0a9bf0b..cf431be1 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -486,24 +486,6 @@ namespace NadekoBot.Modules.Administration await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); } - [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] - [OwnerOnly] - public async Task SaveChat(int cnt) - { - var sb = new StringBuilder(); - var msgs = new List(cnt); - await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false); - - var title = $"Chatlog-{Context.Guild.Name}/#{Context.Channel.Name}-{DateTime.Now}.txt"; - var grouping = msgs.GroupBy(x => $"{x.CreatedAt.Date:dd.MM.yyyy}") - .Select(g => new { date = g.Key, messages = g.OrderBy(x => x.CreatedAt).Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) }); - await (Context.User as IGuildUser).SendFileAsync( - await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), -title, title).ConfigureAwait(false); - } - - [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.MentionEveryone)] diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index b7d437d0..4a581108 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -13,6 +13,8 @@ using System.Net.Http; using System.Collections.Concurrent; using System.Threading; using ImageSharp; +using System.Collections.Generic; +using Newtonsoft.Json; namespace NadekoBot.Modules.Utility { @@ -82,7 +84,7 @@ namespace NadekoBot.Modules.Utility old.Change(Timeout.Infinite, Timeout.Infinite); return t; }); - + await channel.SendFileAsync(images, "magicalgirl.jpg", $"Rotating **{role.Name}** role's color.").ConfigureAwait(false); } @@ -326,5 +328,22 @@ namespace NadekoBot.Modules.Utility .WithIsInline(false)))) .ConfigureAwait(false); } + + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [OwnerOnly] + public async Task SaveChat(int cnt) + { + var sb = new StringBuilder(); + var msgs = new List(cnt); + await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false); + + var title = $"Chatlog-{Context.Guild.Name}/#{Context.Channel.Name}-{DateTime.Now}.txt"; + var grouping = msgs.GroupBy(x => $"{x.CreatedAt.Date:dd.MM.yyyy}") + .Select(g => new { date = g.Key, messages = g.OrderBy(x => x.CreatedAt).Select(s => $"【{s.Timestamp:HH:mm:ss}】{s.Author}:" + s.ToString()) }); + await Context.User.SendFileAsync( + await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), title, title).ConfigureAwait(false); + } } } \ No newline at end of file From ceb1ed41a279b8d0e074a62c26470845290d1b48 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 14:07:12 +0100 Subject: [PATCH 80/85] Cross server text moved to utility --- .../Commands/CrossServerTextChannel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/NadekoBot/Modules/{Administration => Utility}/Commands/CrossServerTextChannel.cs (97%) diff --git a/src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs b/src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs similarity index 97% rename from src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs rename to src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs index 62af02da..b2ae710c 100644 --- a/src/NadekoBot/Modules/Administration/Commands/CrossServerTextChannel.cs +++ b/src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs @@ -9,9 +9,9 @@ using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; -namespace NadekoBot.Modules.Administration +namespace NadekoBot.Modules.Utility { - public partial class Administration + public partial class Utility { [Group] public class CrossServerTextChannel : ModuleBase From 819f4aae3811eda04cc8c3b890d3f6162d4475eb Mon Sep 17 00:00:00 2001 From: Poag Date: Wed, 11 Jan 2017 13:07:39 +0000 Subject: [PATCH 81/85] Update Docker Guide.md --- docs/guides/Docker Guide.md | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/docs/guides/Docker Guide.md b/docs/guides/Docker Guide.md index 6ed3a33e..474337d6 100644 --- a/docs/guides/Docker Guide.md +++ b/docs/guides/Docker Guide.md @@ -1,23 +1,22 @@ # NadekoBot a Discord bot -Nadeko is written in C# and Discord.net for more information visit https://github.com/Kwoth/NadekoBot +Nadeko is written in C# and Discord.net for more information visit ## Install Docker -Follow the respective guide for your operating system found here https://docs.docker.com/engine/installation/ +Follow the respective guide for your operating system found here [Docker Engine Install Guide](https://docs.docker.com/engine/installation/) ## Nadeko Setup Guide For this guide we will be using the folder /nadeko as our config root folder. -``` +```bash docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json uirel/nadeko ``` -If you are coming from a previous version of nadeko (the old docker) make sure your crednetials.json has been copied into this directory and is the only thing in this folder. --If you are making a fresh install, create your credentials.json from the following guide and palce it in the /nadeko folder -http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/ +-If you are making a fresh install, create your credentials.json from the following guide and palce it in the /nadeko folder [Nadeko JSON Guide](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) Next start the docker up with -```docker start nadeko; docker logs -f nadeko``` +`docker start nadeko; docker logs -f nadeko` The docker will start and the log file will start scrolling past. Depending on hardware the bot start can take up to 5 minutes on a small DigitalOcean droplet. Once the log ends with "NadekoBot | Starting NadekoBot v1.0-rc2" the bot is ready and can be invited to your server. Ctrl+C at this point to stop viewing the logs. @@ -33,18 +32,27 @@ After a few moments you should be able to invite Nadeko to your server. If you c # Manual Updates are handled by pulling the new layer of the Docker Container which contains a pre compiled update to Nadeko. The following commands are required for the default options -1. ```docker pull uirel/nadeko:latest``` -2. ```docker stop nadeko; docker rm nadeko``` -3. ```docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json uirel/nadeko``` -4. ```docker start nadeko``` + +`docker pull uirel/nadeko:latest` + +`docker stop nadeko; docker rm nadeko` + +`docker create --name=nadeko -v /nadeko/data:/opt/NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data -v /nadeko/credentials.json:/opt/NadekoBot/src/NadekoBot/credentials.json uirel/nadeko` + +`docker start nadeko` + # Automatic Updates -Automatic update are now handled by watchertower https://github.com/CenturyLinkLabs/watchtower +Automatic update are now handled by watchertower [WatchTower GitHub](https://github.com/CenturyLinkLabs/watchtower) To setup watchtower to keep Nadeko up-to-date for you with the default settings use the following command -```docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock centurylink/watchtower --cleanup nadeko``` -This will check for updates to the docker every 5 minutes and update immediately. Alternatively using the ```--interval X``` command to change the interval, where X is the amount of time in seconds to wait. eg 21600 for 6 hours. + +```bash +docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock centurylink/watchtower --cleanup nadeko +``` + +This will check for updates to the docker every 5 minutes and update immediately. Alternatively using the `--interval X` command to change the interval, where X is the amount of time in seconds to wait. eg 21600 for 6 hours. If you have any issues with the docker setup, please ask in #help but indicate you are using the docker. -For information about configuring your bot or its functionality, please check the http://nadekobot.readthedocs.io/en/latest guides. +For information about configuring your bot or its functionality, please check the guides. From 5febe8453112e5ca0e94a0568c4d916235fd9f01 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 14:08:50 +0100 Subject: [PATCH 82/85] Message repeater moved to utility --- .../{Administration => Utility}/Commands/MessageRepeater.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/NadekoBot/Modules/{Administration => Utility}/Commands/MessageRepeater.cs (99%) diff --git a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs similarity index 99% rename from src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs rename to src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs index 34281838..71bd6748 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs @@ -16,9 +16,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -namespace NadekoBot.Modules.Administration +namespace NadekoBot.Modules.Utility { - public partial class Administration + public partial class Utility { [Group] public class RepeatCommands : ModuleBase From f78188ce198b7b2d45b6c343ad4724ef7b16c899 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 17:15:59 +0100 Subject: [PATCH 83/85] Added pagination to .lcr, .lcrg, !!lq. Contact me if you have more ideas where it's needed. It lasts for 30 seconds. --- .../CustomReactions/CustomReactions.cs | 51 +++++++----- src/NadekoBot/Modules/Music/Music.cs | 57 +++++++------ .../Discord/SocketMessageEventWrapper.cs | 52 +++++++----- src/NadekoBot/ShardedDiscordClient.cs | 6 ++ src/NadekoBot/_Extensions/Extensions.cs | 81 ++++++++++++++++--- 5 files changed, 172 insertions(+), 75 deletions(-) diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index d69c31ca..8d95e5d4 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -145,13 +145,17 @@ namespace NadekoBot.Modules.CustomReactions if (customReactions == null || !customReactions.Any()) await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync( - $"Page {page} of custom reactions:", - string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) - .Skip((page - 1) * 20) + { + var lastPage = customReactions.Count / 20; + await Context.Channel.SendPaginatedConfirmAsync(page, curPage => + new EmbedBuilder().WithOkColor() + .WithTitle("Custom reactions") + .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) + .Skip((curPage - 1) * 20) .Take(20) - .Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))) + .Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))), lastPage) .ConfigureAwait(false); + } } public enum All @@ -200,14 +204,22 @@ namespace NadekoBot.Modules.CustomReactions if (customReactions == null || !customReactions.Any()) await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync($"Page {page} of custom reactions (grouped):", - string.Join("\r\n", customReactions - .GroupBy(cr => cr.Trigger) - .OrderBy(cr => cr.Key) - .Skip((page - 1) * 20) - .Take(20) - .Select(cr => $"**{cr.Key.Trim().ToLowerInvariant()}** `x{cr.Count()}`"))) + { + var ordered = customReactions + .GroupBy(cr => cr.Trigger) + .OrderBy(cr => cr.Key) + .ToList(); + + var lastPage = ordered.Count / 20; + await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => + new EmbedBuilder().WithOkColor() + .WithTitle($"Custom Reactions (grouped)") + .WithDescription(string.Join("\r\n", ordered + .Skip((curPage - 1) * 20) + .Take(20) + .Select(cr => $"**{cr.Key.Trim().ToLowerInvariant()}** `x{cr.Count()}`"))), lastPage) .ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -300,13 +312,14 @@ namespace NadekoBot.Modules.CustomReactions { if (page < 1) return; - await Context.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value) - .Skip((page - 1) * 9) - .Take(9) - .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction stats page #{page}"), - (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))) - ) - .ConfigureAwait(false); + var ordered = ReactionStats.OrderByDescending(x => x.Value).ToList(); + var lastPage = ordered.Count / 9; + await Context.Channel.SendPaginatedConfirmAsync(page, + (curPage) => ordered.Skip((curPage - 1) * 9) + .Take(9) + .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction Stats"), + (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage) + .ConfigureAwait(false); } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 3ffb104c..381177a2 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -183,36 +183,41 @@ namespace NadekoBot.Modules.Music try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } const int itemsPerPage = 10; - int startAt = itemsPerPage * (page - 1); - var number = 0 + startAt; var total = musicPlayer.TotalPlaytime; var maxPlaytime = musicPlayer.MaxPlaytimeSeconds; - var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName($"Player Queue - Page {page}") - .WithMusicIcon()) - .WithDescription(string.Join("\n", musicPlayer.Playlist - .Skip(startAt) - .Take(10) - .Select(v => $"`{++number}.` {v.PrettyFullName}"))) - .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + -$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " + -(musicPlayer.FairPlay ? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) - .WithOkColor(); + var lastPage = musicPlayer.Playlist.Count / itemsPerPage; + Func printAction = (curPage) => + { + int startAt = itemsPerPage * (curPage - 1); + var number = 0 + startAt; + var embed = new EmbedBuilder() + .WithAuthor(eab => eab.WithName($"Player Queue") + .WithMusicIcon()) + .WithDescription(string.Join("\n", musicPlayer.Playlist + .Skip(startAt) + .Take(itemsPerPage) + .Select(v => $"`{++number}.` {v.PrettyFullName}"))) + .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + + $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s | " + + (musicPlayer.FairPlay ? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) + .WithOkColor(); - if (musicPlayer.RepeatSong) - { - embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}"); - } - else if (musicPlayer.RepeatPlaylist) - { - embed.WithTitle("🔁 Repeating Playlist"); - } - if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) - { - embed.WithTitle("🎵 Song queue is full!"); - } - await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); + if (musicPlayer.RepeatSong) + { + embed.WithTitle($"🔂 Repeating Song: {currentSong.SongInfo.Title} | {currentSong.PrettyFullTime}"); + } + else if (musicPlayer.RepeatPlaylist) + { + embed.WithTitle("🔁 Repeating Playlist"); + } + if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) + { + embed.WithTitle("🎵 Song queue is full!"); + } + return embed; + }; + await Context.Channel.SendPaginatedConfirmAsync(page, printAction, lastPage).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs b/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs index 7c24b564..b73961a0 100644 --- a/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs +++ b/src/NadekoBot/Services/Discord/SocketMessageEventWrapper.cs @@ -10,48 +10,60 @@ namespace NadekoBot.Services.Discord { public class ReactionEventWrapper : IDisposable { - public SocketMessage Message { get; } + public IUserMessage Message { get; } public event Action OnReactionAdded = delegate { }; public event Action OnReactionRemoved = delegate { }; public event Action OnReactionsCleared = delegate { }; - public ReactionEventWrapper(SocketMessage msg) + public ReactionEventWrapper(IUserMessage msg) { if (msg == null) throw new ArgumentNullException(nameof(msg)); Message = msg; - msg.Discord.ReactionAdded += Discord_ReactionAdded; - msg.Discord.ReactionRemoved += Discord_ReactionRemoved; - msg.Discord.ReactionsCleared += Discord_ReactionsCleared; + NadekoBot.Client.ReactionAdded += Discord_ReactionAdded; + NadekoBot.Client.ReactionRemoved += Discord_ReactionRemoved; + NadekoBot.Client.ReactionsCleared += Discord_ReactionsCleared; } - private Task Discord_ReactionsCleared(ulong messageId, Optional reaction) + private void Discord_ReactionsCleared(ulong messageId, Optional reaction) { - if (messageId == Message.Id) - OnReactionsCleared?.Invoke(); - return Task.CompletedTask; + try + { + if (messageId == Message.Id) + OnReactionsCleared?.Invoke(); + } + catch { } } - private Task Discord_ReactionRemoved(ulong messageId, Optional arg2, SocketReaction reaction) + private void Discord_ReactionRemoved(ulong messageId, Optional arg2, SocketReaction reaction) { - if (messageId == Message.Id) - OnReactionRemoved?.Invoke(reaction); - return Task.CompletedTask; + try + { + if (messageId == Message.Id) + OnReactionRemoved?.Invoke(reaction); + } + catch { } } - private Task Discord_ReactionAdded(ulong messageId, Optional message, SocketReaction reaction) + private void Discord_ReactionAdded(ulong messageId, Optional message, SocketReaction reaction) { - if(messageId == Message.Id) - OnReactionAdded?.Invoke(reaction); - return Task.CompletedTask; + try + { + if (messageId == Message.Id) + OnReactionAdded?.Invoke(reaction); + } + catch { } } public void UnsubAll() { - Message.Discord.ReactionAdded -= Discord_ReactionAdded; - Message.Discord.ReactionRemoved -= Discord_ReactionRemoved; - Message.Discord.ReactionsCleared -= Discord_ReactionsCleared; + NadekoBot.Client.ReactionAdded -= Discord_ReactionAdded; + NadekoBot.Client.ReactionRemoved -= Discord_ReactionRemoved; + NadekoBot.Client.ReactionsCleared -= Discord_ReactionsCleared; + OnReactionAdded = null; + OnReactionRemoved = null; + OnReactionsCleared = null; } private bool disposing = false; diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs index 7a538192..a59dece7 100644 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ b/src/NadekoBot/ShardedDiscordClient.cs @@ -28,6 +28,9 @@ namespace NadekoBot public event Action ChannelCreated = delegate { }; public event Action ChannelDestroyed = delegate { }; public event Action ChannelUpdated = delegate { }; + public event Action, SocketReaction> ReactionAdded = delegate { }; + public event Action, SocketReaction> ReactionRemoved = delegate { }; + public event Action> ReactionsCleared = delegate { }; public event Action JoinedGuild = delegate { }; public event Action LeftGuild = delegate { }; @@ -74,6 +77,9 @@ namespace NadekoBot client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; client.JoinedGuild += (arg1) => { JoinedGuild(arg1); ++_guildCount; return Task.CompletedTask; }; client.LeftGuild += (arg1) => { LeftGuild(arg1); --_guildCount; return Task.CompletedTask; }; + client.ReactionAdded += (arg1, arg2, arg3) => { ReactionAdded(arg1, arg2, arg3); return Task.CompletedTask; }; + client.ReactionRemoved += (arg1, arg2, arg3) => { ReactionRemoved(arg1, arg2, arg3); return Task.CompletedTask; }; + client.ReactionsCleared += (arg1, arg2) => { ReactionsCleared(arg1, arg2); return Task.CompletedTask; }; _log.Info($"Shard #{i} initialized."); #if GLOBAL_NADEKO diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 3994e54f..310b5974 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -18,10 +18,70 @@ namespace NadekoBot.Extensions { public static class Extensions { - public static ReactionEventWrapper OnReactionAdded(this SocketMessage msg, Action reactionAdded) + private const string arrow_left = "⬅"; + private const string arrow_right = "➡"; + + /// + /// danny kamisama + /// + public static async Task SendPaginatedConfirmAsync(this IMessageChannel channel, int currentPage, Func pageFunc, int? lastPage = null) { + lastPage += 1; + var embed = pageFunc(currentPage).AddPaginatedFooter(currentPage, lastPage); + + var msg = await channel.EmbedAsync(embed) as IUserMessage; + + if (currentPage >= lastPage && lastPage == 1) + return; + + await msg.AddReactionAsync(arrow_left).ConfigureAwait(false); + await msg.AddReactionAsync(arrow_right).ConfigureAwait(false); + + await Task.Delay(2000).ConfigureAwait(false); + + Action changePage = async r => + { + try + { + if (r.Emoji.Name == arrow_left) + { + if (currentPage == 1) + return; + await msg.ModifyAsync(x => x.Embed = pageFunc(--currentPage).AddPaginatedFooter(currentPage, lastPage).Build()).ConfigureAwait(false); + } + else if (r.Emoji.Name == arrow_right) + { + if (lastPage == null || lastPage > currentPage) + await msg.ModifyAsync(x => x.Embed = pageFunc(++currentPage).AddPaginatedFooter(currentPage, lastPage).Build()).ConfigureAwait(false); + } + } + catch (Exception ex) { Console.WriteLine(ex); } + }; + + using (msg.OnReaction(changePage, changePage)) + { + await Task.Delay(30000).ConfigureAwait(false); + } + + await msg.RemoveAllReactionsAsync().ConfigureAwait(false); + } + + private static EmbedBuilder AddPaginatedFooter(this EmbedBuilder embed, int curPage, int? lastPage) + { + if (lastPage != null) + return embed.WithFooter(efb => efb.WithText($"page {curPage} / {lastPage}")); + else + return embed.WithFooter(efb => efb.WithText($"page {curPage}")); + } + + public static ReactionEventWrapper OnReaction(this IUserMessage msg, Action reactionAdded, Action reactionRemoved = null) + { + if (reactionRemoved == null) + reactionRemoved = delegate { }; + var wrap = new ReactionEventWrapper(msg); wrap.OnReactionAdded += reactionAdded; + wrap.OnReactionRemoved += reactionRemoved; return wrap; } @@ -53,7 +113,8 @@ namespace NadekoBot.Extensions public static string GetPrefix(this ModuleInfo module) => NadekoBot.ModulePrefixes[module.GetTopLevelModule().Name]; - public static ModuleInfo GetTopLevelModule(this ModuleInfo module) { + public static ModuleInfo GetTopLevelModule(this ModuleInfo module) + { while (module.Parent != null) { module = module.Parent; @@ -102,7 +163,7 @@ namespace NadekoBot.Extensions public static bool IsInteger(this decimal number) => number == Math.Truncate(number); - public static string SanitizeMentions(this string str) => + public static string SanitizeMentions(this string str) => str.Replace("@everyone", "@everyοne").Replace("@here", "@һere"); public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; @@ -114,7 +175,7 @@ namespace NadekoBot.Extensions => await (await user.CreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text)); public static async Task SendConfirmAsync(this IUser user, string title, string text, string url = null) - => await(await user.CreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text) + => await (await user.CreateDMChannelAsync()).SendMessageAsync("", embed: new EmbedBuilder().WithColor(NadekoBot.OkColor).WithDescription(text) .WithTitle(title).WithUrl(url)); public static async Task SendErrorAsync(this IUser user, string title, string error, string url = null) @@ -135,7 +196,7 @@ namespace NadekoBot.Extensions public static IEnumerable Members(this IRole role) => role.Guild.GetUsersAsync().GetAwaiter().GetResult().Where(u => u.RoleIds.Contains(role.Id)) ?? Enumerable.Empty(); - + public static Task EmbedAsync(this IMessageChannel ch, EmbedBuilder embed, string msg = "") => ch.SendMessageAsync(msg, embed: embed); @@ -162,7 +223,7 @@ namespace NadekoBot.Extensions ```"); } - public static Task SendTableAsync(this IMessageChannel ch, IEnumerable items, Func howToPrint, int columns = 3) => + public static Task SendTableAsync(this IMessageChannel ch, IEnumerable items, Func howToPrint, int columns = 3) => ch.SendTableAsync("", items, howToPrint, columns); /// @@ -295,7 +356,7 @@ namespace NadekoBot.Extensions } - public static string ToJson(this T any, Formatting formatting = Formatting.Indented) => + public static string ToJson(this T any, Formatting formatting = Formatting.Indented) => JsonConvert.SerializeObject(any, formatting); public static int KiB(this int value) => value * 1024; @@ -326,7 +387,7 @@ namespace NadekoBot.Extensions var canvasPixels = canvas.Lock(); int offsetX = 0; - foreach (var img in imgList.Select(img=>img.Lock())) + foreach (var img in imgList.Select(img => img.Lock())) { for (int i = 0; i < img.Width; i++) { @@ -335,7 +396,7 @@ namespace NadekoBot.Extensions canvasPixels[i + offsetX, j] = img[i, j]; } } - offsetX += img.Width; + offsetX += img.Width; } return canvas; @@ -354,4 +415,4 @@ namespace NadekoBot.Extensions public static bool IsDiscordInvite(this string str) => filterRegex.IsMatch(str); } -} +} \ No newline at end of file From b2135e018f90a75b4050ff7e2f92e6b6b49e6c12 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 17:48:57 +0100 Subject: [PATCH 84/85] Picking/planting will now correctly say your own currency name, instead of flower --- src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 078b9183..015b4328 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -129,7 +129,7 @@ namespace NadekoBot.Modules.Games await Task.WhenAll(msgs.Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); - await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, "Picked flower(s).", msgs.Count, false).ConfigureAwait(false); + await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {Gambling.Gambling.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{Gambling.Gambling.CurrencySign}!").ConfigureAwait(false); msg.DeleteAfter(10); } @@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Plant() { - var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, "Planted a flower.", 1, false).ConfigureAwait(false); + var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {Gambling.Gambling.CurrencyName}", 1, false).ConfigureAwait(false); if (!removed) { await Context.Channel.SendErrorAsync($"You don't have any {Gambling.Gambling.CurrencyPluralName}.").ConfigureAwait(false); From 16070d896d2305de47b9191612950477b353e2ca Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 11 Jan 2017 18:20:19 +0100 Subject: [PATCH 85/85] 1.1.0 version --- src/NadekoBot/Services/Impl/StatsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 558feed7..c6d22070 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Services.Impl private ShardedDiscordClient client; private DateTime started; - public const string BotVersion = "1.1.0-beta"; + public const string BotVersion = "1.1.0"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net";