From 3f2b98f3b09dc346bb5d30bdee8fa097143dbf21 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 5 Jul 2016 02:05:53 +0200 Subject: [PATCH 01/89] fixed `!m dv` description --- NadekoBot/Modules/Music/MusicModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index a22bc843..59c86031 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -201,7 +201,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("defvol") .Alias("dv") .Description("Sets the default music volume when music playback is started (0-100)." + - " Does not persist through restarts.\n**Usage**: `!m dv 80`") + " Persists through restarts.\n**Usage**: `!m dv 80`") .Parameter("val", ParameterType.Required) .Do(async e => { From 2c3462e8f97b38086b214976faf1ce43205c0908 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 5 Jul 2016 02:07:24 +0200 Subject: [PATCH 02/89] commandlist updated --- commandlist.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/commandlist.md b/commandlist.md index 68146587..a6d2e202 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,7 +2,7 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6029.36463` +Version: `NadekoBot v0.9.6030.3793` ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -208,7 +208,7 @@ Command and aliases | Description | Usage `!m listqueue`, `!m lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2` `!m nowplaying`, `!m np` | Shows the song currently playing. | `!m np` `!m volume`, `!m vol` | Sets the music volume 0-100% | `!m vol 50` -`!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Does not persist through restarts. | `!m dv 80` +`!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80` `!m mute`, `!m min` | Sets the music volume to 0% | `!m min` `!m max` | Sets the music volume to 100% (real max is actually 150%). | `!m max` `!m half` | Sets the music volume to 50%. | `!m half` From 9d88627e194aae88bf0ede401f9d592646840cc0 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 04:44:52 +0200 Subject: [PATCH 03/89] small chance crashfix when 502 happens on sendfile --- NadekoBot/Modules/Games/Commands/PlantPick.cs | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index cbc4b01a..f84e94b8 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -31,19 +31,23 @@ namespace NadekoBot.Modules.Games.Commands private async void PotentialFlowerGeneration(object sender, Discord.MessageEventArgs e) { - if (e.Server == null || e.Channel.IsPrivate) - return; - var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id); - if (config.GenerateCurrencyChannels.Contains(e.Channel.Id)) + try { - var rnd = Math.Abs(GetRandomNumber()); - if ((rnd % 50) == 0) + if (e.Server == null || e.Channel.IsPrivate) + return; + var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id); + if (config.GenerateCurrencyChannels.Contains(e.Channel.Id)) { - var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); - await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); - plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; }); + var rnd = Math.Abs(GetRandomNumber()); + if ((rnd % 50) == 0) + { + var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); + await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); + plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; }); + } } } + catch { } } //channelid/messageid pair ConcurrentDictionary plantedFlowerChannels = new ConcurrentDictionary(); From f0c85eaafc4930b38c453026112b570ac00d4f32 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 04:46:33 +0200 Subject: [PATCH 04/89] fixed customreactions always loading default reactions, even when they are deleted #370 --- NadekoBot/NadekoBot.cs | 2 +- NadekoBot/_Models/JSONModels/Configuration.cs | 46 +++++++++++++------ 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index df3c8f7d..86d22219 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -196,7 +196,7 @@ namespace NadekoBot return; } #if NADEKO_RELEASE - await Task.Delay(100000).ConfigureAwait(false); + await Task.Delay(120000).ConfigureAwait(false); #else await Task.Delay(1000).ConfigureAwait(false); #endif diff --git a/NadekoBot/_Models/JSONModels/Configuration.cs b/NadekoBot/_Models/JSONModels/Configuration.cs index ad75f35d..bafdb84e 100644 --- a/NadekoBot/_Models/JSONModels/Configuration.cs +++ b/NadekoBot/_Models/JSONModels/Configuration.cs @@ -3,25 +3,14 @@ using NadekoBot.Extensions; using Newtonsoft.Json; using System.Collections.Generic; using System.IO; +using System.Runtime.Serialization; namespace NadekoBot.Classes.JSONModels { public class Configuration { - public bool DontJoinServers { get; set; } = false; - public bool ForwardMessages { get; set; } = true; - public bool IsRotatingStatus { get; set; } = false; - public int BufferSize { get; set; } = 4.MiB(); - [JsonIgnore] - public List Quotes { get; set; } = new List(); - - [JsonIgnore] - public List PokemonTypes { get; set; } = new List(); - - public string RemindMessageFormat { get; set; } = "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗"; - - public Dictionary> CustomReactions { get; set; } = new Dictionary>() + public static readonly Dictionary> DefaultCustomReactions = new Dictionary> { {@"\o\", new List() { "/o/" } }, @@ -93,6 +82,21 @@ namespace NadekoBot.Classes.JSONModels } } }; + public bool DontJoinServers { get; set; } = false; + public bool ForwardMessages { get; set; } = true; + public bool IsRotatingStatus { get; set; } = false; + public int BufferSize { get; set; } = 4.MiB(); + + public List Quotes { get; set; } = new List(); + + [JsonIgnore] + public List PokemonTypes { get; set; } = new List(); + + public string RemindMessageFormat { get; set; } = "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗"; + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public Dictionary> CustomReactions { get; set; } + public List RotatingStatuses { get; set; } = new List(); public CommandPrefixesModel CommandPrefixes { get; set; } = new CommandPrefixesModel(); public HashSet ServerBlacklist { get; set; } = new HashSet(); @@ -104,6 +108,22 @@ namespace NadekoBot.Classes.JSONModels 143515953525817344 }; + [OnDeserialized] + internal void OnDeserialized(StreamingContext context) + { + if (CustomReactions == null) + { + CustomReactions = DefaultCustomReactions; + } + } + [OnSerializing] + internal void OnSerializing(StreamingContext context) + { + if (CustomReactions == null) + { + CustomReactions = DefaultCustomReactions; + } + } public string[] _8BallResponses { get; set; } = { From c7d61c5d10a9a6267e6883ea1e7e3c846ff0173f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 10:15:28 +0200 Subject: [PATCH 05/89] format fix #380 , now deleting pickup message when random flower spawns after 5 second --- .../Administration/Commands/CustomReactionsCommands.cs | 2 +- NadekoBot/Modules/Games/Commands/PlantPick.cs | 4 +++- NadekoBot/bin/Debug/data/config_example.json | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs index 4aba4b10..56ac5a4d 100644 --- a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs +++ b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs @@ -85,7 +85,7 @@ namespace NadekoBot.Modules.Administration.Commands int i = 1; foreach (var reaction in items) { - message.AppendLine($"[{i++}] " + Format.Code(reaction)); + message.AppendLine($"[{i++}] " + Format.Code(Format.Escape(reaction))); } await e.Channel.SendMessage(message.ToString()); }); diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index f84e94b8..ccb9aafc 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -42,8 +42,10 @@ namespace NadekoBot.Modules.Games.Commands if ((rnd % 50) == 0) { var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); - await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); + var msg2 = await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; }); + await Task.Delay(5000); + await msg2.Delete(); } } } diff --git a/NadekoBot/bin/Debug/data/config_example.json b/NadekoBot/bin/Debug/data/config_example.json index bd094371..f8ff7769 100644 --- a/NadekoBot/bin/Debug/data/config_example.json +++ b/NadekoBot/bin/Debug/data/config_example.json @@ -3,6 +3,7 @@ "ForwardMessages": true, "IsRotatingStatus": false, "BufferSize": 4194304, + "Quotes": [], "RemindMessageFormat": "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗", "CustomReactions": { "\\o\\": [ From c0f23e48f08ed6058243fd2c369dfbc4ab482b12 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 15:39:26 +0200 Subject: [PATCH 06/89] .iamn now also deletes messages --- .../Commands/SelfAssignedRolesCommand.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index eaf59a3a..6c179eaa 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -130,8 +130,8 @@ namespace NadekoBot.Modules.Administration.Commands await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); } var msg = await e.Channel.SendMessage($":ok:You now have {role.Name} role.").ConfigureAwait(false); - await Task.Delay(3000); - await msg.Delete(); + await Task.Delay(3000).ConfigureAwait(false); + await msg.Delete().ConfigureAwait(false); try { await e.Message.Delete().ConfigureAwait(false); @@ -168,7 +168,14 @@ namespace NadekoBot.Modules.Administration.Commands return; } await e.User.RemoveRoles(role).ConfigureAwait(false); - await e.Channel.SendMessage($":ok:Successfuly removed {role.Name} role from you.").ConfigureAwait(false); + var msg = await e.Channel.SendMessage($":ok:Successfuly removed {role.Name} role from you.").ConfigureAwait(false); + await Task.Delay(3000).ConfigureAwait(false); + await msg.Delete().ConfigureAwait(false); + try + { + await e.Message.Delete().ConfigureAwait(false); + } + catch { } }); } } From 8e917172419ec2b4b4065402cdba2331e0a7ab01 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 16:41:24 +0200 Subject: [PATCH 07/89] ClashOfClans - Added ,cfw1 ,cfw2 and alias to cf - ,cf3 to finish claims with a certain number of stars. (1/2/3) --- .../Modules/ClashOfClans/ClashOfClans.cs | 12 +++- .../ClashOfClans/ClashOfClansModule.cs | 67 ++++++++++++------- 2 files changed, 53 insertions(+), 26 deletions(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 85168971..668afd73 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -7,6 +7,10 @@ using System.Threading.Tasks; namespace NadekoBot.Classes.ClashOfClans { + public enum DestroyStars + { + One, Two, Three + } internal class Caller { public string CallUser { get; } @@ -15,6 +19,9 @@ namespace NadekoBot.Classes.ClashOfClans public bool BaseDestroyed { get; internal set; } + public int Stars { get; set; } = 3; + + public Caller(string callUser, DateTime timeAdded, bool baseDestroyed) { CallUser = callUser; @@ -148,7 +155,7 @@ namespace NadekoBot.Classes.ClashOfClans { if (bases[i].BaseDestroyed) { - sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` ⭐ ⭐ ⭐"); + sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` {new string('⭐', bases[i].Stars)}"); } else { @@ -161,13 +168,14 @@ namespace NadekoBot.Classes.ClashOfClans return sb.ToString(); } - internal int FinishClaim(string user) + internal int FinishClaim(string user, int stars = 3) { user = user.Trim(); for (var i = 0; i < bases.Length; i++) { if (bases[i]?.BaseDestroyed != false || bases[i]?.CallUser != user) continue; bases[i].BaseDestroyed = true; + bases[i].Stars = stars; return i; } throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base."); diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index a96a7347..297c4b34 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; +using System.Threading.Tasks; namespace NadekoBot.Modules.ClashOfClans { @@ -179,33 +180,26 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "claimfinish") .Alias(Prefix + "cf") - .Description($"Finish your claim if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") + .Alias(Prefix + "cf3") + .Alias(Prefix + "claimfinish3") + .Description($"Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) - .Do(async e => - { - var warInfo = GetInfo(e); - if (warInfo == null || warInfo.Item1.Count == 0) - { - await e.Channel.SendMessage("💢🔰 **That war does not exist.**").ConfigureAwait(false); - return; - } - var usr = - string.IsNullOrWhiteSpace(e.GetArg("other_name")) ? - e.User.Name : - e.GetArg("other_name"); + .Do(e => FinishClaim(e)); - var war = warInfo.Item1[warInfo.Item2]; - try - { - var baseNum = war.FinishClaim(usr); - await e.Channel.SendMessage($"❗🔰{e.User.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); - } - catch (Exception ex) - { - await e.Channel.SendMessage($"💢🔰 {ex.Message}").ConfigureAwait(false); - } - }); + cgb.CreateCommand(Prefix + "claimfinish2") + .Alias(Prefix + "cf2") + .Description($"Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") + .Parameter("number", ParameterType.Required) + .Parameter("other_name", ParameterType.Unparsed) + .Do(e => FinishClaim(e, 2)); + + cgb.CreateCommand(Prefix + "claimfinish1") + .Alias(Prefix + "cf1") + .Description($"Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") + .Parameter("number", ParameterType.Required) + .Parameter("other_name", ParameterType.Unparsed) + .Do(e => FinishClaim(e, 1)); cgb.CreateCommand(Prefix + "unclaim") .Alias(Prefix + "uncall") @@ -257,6 +251,31 @@ namespace NadekoBot.Modules.ClashOfClans }); } + private async Task FinishClaim(CommandEventArgs e, int stars = 3) + { + var warInfo = GetInfo(e); + if (warInfo == null || warInfo.Item1.Count == 0) + { + await e.Channel.SendMessage("💢🔰 **That war does not exist.**").ConfigureAwait(false); + return; + } + var usr = + string.IsNullOrWhiteSpace(e.GetArg("other_name")) ? + e.User.Name : + e.GetArg("other_name"); + + var war = warInfo.Item1[warInfo.Item2]; + try + { + var baseNum = war.FinishClaim(usr, stars); + await e.Channel.SendMessage($"❗🔰{e.User.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); + } + catch (Exception ex) + { + await e.Channel.SendMessage($"💢🔰 {ex.Message}").ConfigureAwait(false); + } + } + private static Tuple, int> GetInfo(CommandEventArgs e) { //check if there are any wars From 4cae7b88fe96731dd413dbe7064dd765276ac3fe Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 16:43:18 +0200 Subject: [PATCH 08/89] typo, it actually queues up to 500. --- NadekoBot/Modules/Music/MusicModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 59c86031..b8a92441 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -275,7 +275,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("playlist") .Alias("pl") - .Description("Queues up to 50 songs from a youtube playlist specified by a link, or keywords.\n**Usage**: `!m pl playlist link or name`") + .Description("Queues up to 500 songs from a youtube playlist specified by a link, or keywords.\n**Usage**: `!m pl playlist link or name`") .Parameter("playlist", ParameterType.Unparsed) .Do(async e => { From 2fda1c0dbb66f39174a18fafe6368c4a2fa9733f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 18:43:29 +0200 Subject: [PATCH 09/89] Added optional cooldown for `>gc`, Nadeko can no longer trigger random flower by herself. --- NadekoBot/Classes/ServerSpecificConfig.cs | 6 +-- NadekoBot/Modules/Games/Commands/PlantPick.cs | 44 ++++++++++++------- 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs index bbc4eadd..f99d36dc 100644 --- a/NadekoBot/Classes/ServerSpecificConfig.cs +++ b/NadekoBot/Classes/ServerSpecificConfig.cs @@ -148,8 +148,8 @@ namespace NadekoBot.Classes } [JsonIgnore] - private ObservableCollection generateCurrencyChannels; - public ObservableCollection GenerateCurrencyChannels { + private ObservableConcurrentDictionary generateCurrencyChannels; + public ObservableConcurrentDictionary GenerateCurrencyChannels { get { return generateCurrencyChannels; } set { generateCurrencyChannels = value; @@ -204,7 +204,7 @@ namespace NadekoBot.Classes { ListOfSelfAssignableRoles = new ObservableCollection(); ObservingStreams = new ObservableCollection(); - GenerateCurrencyChannels = new ObservableCollection(); + GenerateCurrencyChannels = new ObservableConcurrentDictionary(); VoiceChannelLog = new ObservableConcurrentDictionary(); } diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index ccb9aafc..ccb68660 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -28,26 +28,32 @@ namespace NadekoBot.Modules.Games.Commands rng = new Random(); } + private static readonly ConcurrentDictionary plantpickCooldowns = new ConcurrentDictionary(); private async void PotentialFlowerGeneration(object sender, Discord.MessageEventArgs e) { try { - if (e.Server == null || e.Channel.IsPrivate) + if (e.Server == null || e.Channel.IsPrivate || e.Message.IsAuthor) return; var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id); - if (config.GenerateCurrencyChannels.Contains(e.Channel.Id)) - { - var rnd = Math.Abs(GetRandomNumber()); - if ((rnd % 50) == 0) + var now = DateTime.Now; + int cd; + DateTime lastSpawned; + if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd)) + if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) { - var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); - var msg2 = await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); - plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; }); - await Task.Delay(5000); - await msg2.Delete(); + var rnd = Math.Abs(GetRandomNumber()); + if ((rnd % 2) == 0) + { + var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); + var msg2 = await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); + plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; }); + plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now); + await Task.Delay(5000); + await msg2.Delete(); + } } - } } catch { } } @@ -110,19 +116,27 @@ namespace NadekoBot.Modules.Games.Commands cgb.CreateCommand(Prefix + "gencurrency") .Alias(Prefix + "gc") - .Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Requires Manage Messages permission. | `>gc`") + .Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60`") .AddCheck(SimpleCheckers.ManageMessages()) + .Parameter("cd", ParameterType.Unparsed) .Do(async e => { + var cdStr = e.GetArg("cd"); + int cd = 2; + if (!int.TryParse(cdStr, out cd) || cd < 0) + { + cd = 2; + } var config = SpecificConfigurations.Default.Of(e.Server.Id); - if (config.GenerateCurrencyChannels.Remove(e.Channel.Id)) + int throwaway; + if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway)) { await e.Channel.SendMessage("`Currency generation disabled on this channel.`"); } else { - config.GenerateCurrencyChannels.Add(e.Channel.Id); - await e.Channel.SendMessage("`Currency generation enabled on this channel.`"); + if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd)) + await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`"); } }); } From a511d35704803cd7f7eeadcd6e1b6a9da0740688 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 19:35:56 +0200 Subject: [PATCH 10/89] queuer name added in songs, trimmed to 12 characters. --- .../Modules/Music/Classes/MusicControls.cs | 3 ++- NadekoBot/Modules/Music/Classes/Song.cs | 3 ++- NadekoBot/Modules/Music/MusicModule.cs | 22 +++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index e1cd32bb..afe724b6 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -168,13 +168,14 @@ namespace NadekoBot.Modules.Music.Classes } } - public void AddSong(Song s) + public void AddSong(Song s, string username) { if (s == null) throw new ArgumentNullException(nameof(s)); lock (playlistLock) { s.MusicPlayer = this; + s.QueuerName = username.TrimTo(10); playlist.Add(s); } } diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index f9c7f3ae..b23c75a5 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -27,8 +27,9 @@ namespace NadekoBot.Modules.Music.Classes { public StreamState State { get; internal set; } public string PrettyName => - $"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}`"; + $"**【 {SongInfo.Title.TrimTo(55)} 】**`{(SongInfo.Provider ?? "-")}` `by {QueuerName}`"; public SongInfo SongInfo { get; } + public string QueuerName { get; set; } private PoopyBuffer songBuffer { get; } = new PoopyBuffer(NadekoBot.Config.BufferSize); diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index b8a92441..e1aae9b4 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -100,7 +100,7 @@ namespace NadekoBot.Modules.Music .Parameter("query", ParameterType.Unparsed) .Do(async e => { - await QueueSong(e.Channel, e.User.VoiceChannel, e.GetArg("query")).ConfigureAwait(false); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, e.GetArg("query")).ConfigureAwait(false); if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) { await Task.Delay(10000).ConfigureAwait(false); @@ -308,7 +308,7 @@ namespace NadekoBot.Modules.Music { try { - await QueueSong(e.Channel, e.User.VoiceChannel, id, true).ConfigureAwait(false); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, id, true).ConfigureAwait(false); } catch { } } @@ -327,7 +327,7 @@ namespace NadekoBot.Modules.Music return; var scvids = JObject.Parse(await SearchHelper.GetResponseStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Creds.SoundCloudClientID}"))["tracks"].ToObject(); - await QueueSong(e.Channel, e.User.VoiceChannel, scvids[0].TrackLink); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, scvids[0].TrackLink); MusicPlayer mp; if (!MusicPlayers.TryGetValue(e.Server, out mp)) @@ -342,7 +342,7 @@ namespace NadekoBot.Modules.Music Uri = svideo.StreamLink, ProviderType = MusicType.Normal, Query = svideo.TrackLink, - })); + }), e.User.Name); } }); @@ -362,7 +362,7 @@ namespace NadekoBot.Modules.Music .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); foreach (var file in fileEnum) { - await QueueSong(e.Channel, e.User.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); } await e.Channel.SendMessage("🎵 `Directory queue complete.`").ConfigureAwait(false); } @@ -379,7 +379,7 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage("💢 You need to be in a voice channel on this server.\n If you are already in a voice channel, try rejoining it.").ConfigureAwait(false); return; } - await QueueSong(e.Channel, e.User.VoiceChannel, e.GetArg("radio_link"), musicType: MusicType.Radio).ConfigureAwait(false); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, e.GetArg("radio_link"), musicType: MusicType.Radio).ConfigureAwait(false); if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) { await Task.Delay(10000).ConfigureAwait(false); @@ -397,7 +397,7 @@ namespace NadekoBot.Modules.Music var arg = e.GetArg("path"); if (string.IsNullOrWhiteSpace(arg)) return; - await QueueSong(e.Channel, e.User.VoiceChannel, e.GetArg("path"), musicType: MusicType.Local).ConfigureAwait(false); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, e.GetArg("path"), musicType: MusicType.Local).ConfigureAwait(false); }); cgb.CreateCommand("move") @@ -628,7 +628,7 @@ namespace NadekoBot.Modules.Music { try { - await QueueSong(textCh, voiceCh, si.Query, true, (MusicType)si.ProviderType).ConfigureAwait(false); + await QueueSong(e.User, textCh, voiceCh, si.Query, true, (MusicType)si.ProviderType).ConfigureAwait(false); } catch (Exception ex) { @@ -740,7 +740,7 @@ namespace NadekoBot.Modules.Music }); } - public static async Task QueueSong(Channel textCh, Channel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) + public static async Task QueueSong(User queuer, Channel textCh, Channel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) { if (voiceCh == null || voiceCh.Server != textCh.Server) { @@ -772,7 +772,7 @@ namespace NadekoBot.Modules.Music lastFinishedMessage = await textCh.SendMessage($"🎵`Finished`{song.PrettyName}").ConfigureAwait(false); if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.Provider == "YouTube") { - await QueueSong(textCh, voiceCh, await SearchHelper.GetRelatedVideoId(song.SongInfo.Query), silent, musicType).ConfigureAwait(false); + await QueueSong(queuer, textCh, voiceCh, await SearchHelper.GetRelatedVideoId(song.SongInfo.Query), silent, musicType).ConfigureAwait(false); } } catch (Exception e) @@ -801,7 +801,7 @@ namespace NadekoBot.Modules.Music return mp; }); var resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false); - musicPlayer.AddSong(resolvedSong); + musicPlayer.AddSong(resolvedSong, queuer.Name); if (!silent) { var queuedMessage = await textCh.SendMessage($"🎵`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count}`").ConfigureAwait(false); From 8044bbe978c84ab769b70d9ca46b03c2f80850a9 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 7 Jul 2016 19:37:06 +0200 Subject: [PATCH 11/89] removed .updtime --- NadekoBot/Modules/Conversations/Conversations.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/NadekoBot/Modules/Conversations/Conversations.cs b/NadekoBot/Modules/Conversations/Conversations.cs index 606084b1..db072e91 100644 --- a/NadekoBot/Modules/Conversations/Conversations.cs +++ b/NadekoBot/Modules/Conversations/Conversations.cs @@ -100,15 +100,6 @@ namespace NadekoBot.Modules.Conversations commands.ForEach(cmd => cmd.Init(cgb)); - cgb.CreateCommand("uptime") - .Description("Shows how long Nadeko has been running for.") - .Do(async e => - { - var time = (DateTime.Now - Process.GetCurrentProcess().StartTime); - var str = string.Format("I have been running for {0} days, {1} hours, and {2} minutes.", time.Days, time.Hours, time.Minutes); - await e.Channel.SendMessage(str).ConfigureAwait(false); - }); - cgb.CreateCommand("die") .Description("Works only for the owner. Shuts the bot down.") .Do(async e => From adc449f765757460195d974a29a01f1d1bc688c7 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Fri, 8 Jul 2016 05:20:10 +0200 Subject: [PATCH 12/89] !m smq added (setmaxqueue) --- .../Modules/Music/Classes/MusicControls.cs | 10 +++ .../Music/Classes/PlaylistFullException.cs | 12 +++ NadekoBot/Modules/Music/MusicModule.cs | 76 ++++++++++++++++--- NadekoBot/NadekoBot.csproj | 1 + 4 files changed, 87 insertions(+), 12 deletions(-) create mode 100644 NadekoBot/Modules/Music/Classes/PlaylistFullException.cs diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index afe724b6..2aba6075 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -51,6 +51,7 @@ namespace NadekoBot.Modules.Music.Classes public bool RepeatSong { get; private set; } = false; public bool RepeatPlaylist { get; private set; } = false; public bool Autoplay { get; set; } = false; + public uint MaxQueueSize { get; set; } = 0; public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) { @@ -172,6 +173,7 @@ namespace NadekoBot.Modules.Music.Classes { if (s == null) throw new ArgumentNullException(nameof(s)); + ThrowIfQueueFull(); lock (playlistLock) { s.MusicPlayer = this; @@ -244,5 +246,13 @@ namespace NadekoBot.Modules.Music.Classes internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; internal bool ToggleAutoplay() => this.Autoplay = !this.Autoplay; + + internal void ThrowIfQueueFull() + { + if (MaxQueueSize == 0) + return; + if (playlist.Count >= MaxQueueSize) + throw new PlaylistFullException(); + } } } diff --git a/NadekoBot/Modules/Music/Classes/PlaylistFullException.cs b/NadekoBot/Modules/Music/Classes/PlaylistFullException.cs new file mode 100644 index 00000000..15541d42 --- /dev/null +++ b/NadekoBot/Modules/Music/Classes/PlaylistFullException.cs @@ -0,0 +1,12 @@ +using System; + +namespace NadekoBot.Modules.Music.Classes +{ + class PlaylistFullException : Exception + { + public PlaylistFullException(string message) : base(message) + { + } + public PlaylistFullException() : base("Queue is full.") { } + } +} diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index e1aae9b4..70444a80 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("max") - .Description("Sets the music volume to 100% (real max is actually 150%).\n**Usage**: `!m max`") + .Description("Sets the music volume to 100%.\n**Usage**: `!m max`") .Do(e => { MusicPlayer musicPlayer; @@ -296,7 +296,7 @@ namespace NadekoBot.Modules.Music var ids = await SearchHelper.GetVideoIDs(plId, 500).ConfigureAwait(false); if (ids == null || ids.Count == 0) { - await e.Channel.SendMessage($"🎵`Failed to find any songs.`"); + await e.Channel.SendMessage($"🎵 `Failed to find any songs.`"); return; } //todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE @@ -310,6 +310,8 @@ namespace NadekoBot.Modules.Music { await QueueSong(e.User, e.Channel, e.User.VoiceChannel, id, true).ConfigureAwait(false); } + catch (PlaylistFullException) + { break; } catch { } } await msg.Edit("🎵 `Playlist queue complete.`").ConfigureAwait(false); @@ -335,14 +337,18 @@ namespace NadekoBot.Modules.Music foreach (var svideo in scvids.Skip(1)) { - mp.AddSong(new Song(new Classes.SongInfo + try { - Title = svideo.FullName, - Provider = "SoundCloud", - Uri = svideo.StreamLink, - ProviderType = MusicType.Normal, - Query = svideo.TrackLink, - }), e.User.Name); + mp.AddSong(new Song(new Classes.SongInfo + { + Title = svideo.FullName, + Provider = "SoundCloud", + Uri = svideo.StreamLink, + ProviderType = MusicType.Normal, + Query = svideo.TrackLink, + }), e.User.Name); + } + catch (PlaylistFullException) { break; } } }); @@ -362,7 +368,15 @@ namespace NadekoBot.Modules.Music .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); foreach (var file in fileEnum) { - await QueueSong(e.User, e.Channel, e.User.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); + try + { + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); + } + catch (PlaylistFullException) + { + break; + } + catch { } } await e.Channel.SendMessage("🎵 `Directory queue complete.`").ConfigureAwait(false); } @@ -481,6 +495,29 @@ namespace NadekoBot.Modules.Music }); + cgb.CreateCommand("setmaxqueue") + .Alias("smq") + .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. \n**Usage**: `{Prefix} smq` 50 or `{Prefix} smq`") + .Parameter("size", ParameterType.Unparsed) + .Do(async e => + { + MusicPlayer musicPlayer; + if (!MusicPlayers.TryGetValue(e.Server, out musicPlayer)) + { + return; + } + + var sizeStr = e.GetArg("size")?.Trim(); + uint size = 0; + if (string.IsNullOrWhiteSpace(sizeStr) || !uint.TryParse(sizeStr, out size)) + { + size = 0; + } + + musicPlayer.MaxQueueSize = size; + await e.Channel.SendMessage($"🎵 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); + }); + cgb.CreateCommand("cleanup") .Description("Cleans up hanging voice connections. **Bot Owner Only!**\n**Usage**: `!m cleanup`") .AddCheck(SimpleCheckers.OwnerOnly()) @@ -630,6 +667,10 @@ namespace NadekoBot.Modules.Music { await QueueSong(e.User, textCh, voiceCh, si.Query, true, (MusicType)si.ProviderType).ConfigureAwait(false); } + catch (PlaylistFullException) + { + break; + } catch (Exception ex) { Console.WriteLine($"Failed QueueSong in load playlist. {ex}"); @@ -800,8 +841,19 @@ namespace NadekoBot.Modules.Music }; return mp; }); - var resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false); - musicPlayer.AddSong(resolvedSong, queuer.Name); + Song resolvedSong; + try + { + musicPlayer.ThrowIfQueueFull(); + resolvedSong = await Song.ResolveSong(query, musicType).ConfigureAwait(false); + + musicPlayer.AddSong(resolvedSong, queuer.Name); + } + catch (PlaylistFullException) + { + await textCh.SendMessage($"🎵 `Queue is full at {musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}.` "); + throw; + } if (!silent) { var queuedMessage = await textCh.SendMessage($"🎵`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count}`").ConfigureAwait(false); diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 84efa690..09bc70ea 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -144,6 +144,7 @@ + From 415265360f006be790d8fe5e467cfc0d5a88643f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Fri, 8 Jul 2016 11:18:46 +0200 Subject: [PATCH 13/89] ;3 --- NadekoBot/Modules/Music/MusicModule.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 70444a80..337529c0 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -217,7 +217,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎵 `Default volume set to {volume}%`").ConfigureAwait(false); }); - cgb.CreateCommand("mute").Alias("min") + cgb.CreateCommand("mute") + .Alias("min") .Description("Sets the music volume to 0%\n**Usage**: `!m min`") .Do(e => { From a771e519b4d6db520ea2e50442dec011775c522a Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Fri, 8 Jul 2016 21:33:08 +0200 Subject: [PATCH 14/89] key is not needed if we're queueing links. --- NadekoBot/Classes/SearchHelper.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Classes/SearchHelper.cs b/NadekoBot/Classes/SearchHelper.cs index 42f70398..82d3d7dc 100644 --- a/NadekoBot/Classes/SearchHelper.cs +++ b/NadekoBot/Classes/SearchHelper.cs @@ -145,8 +145,6 @@ namespace NadekoBot.Classes public static async Task FindYoutubeUrlByKeywords(string keywords) { - if (string.IsNullOrWhiteSpace(NadekoBot.Creds.GoogleAPIKey)) - throw new InvalidCredentialException("Google API Key is missing."); if (string.IsNullOrWhiteSpace(keywords)) throw new ArgumentNullException(nameof(keywords), "Query not specified."); if (keywords.Length > 150) @@ -158,6 +156,10 @@ namespace NadekoBot.Classes { return $"https://www.youtube.com/watch?v={match.Groups["id"].Value}"; } + + if (string.IsNullOrWhiteSpace(NadekoBot.Creds.GoogleAPIKey)) + throw new InvalidCredentialException("Google API Key is missing."); + var response = await GetResponseStringAsync( $"https://www.googleapis.com/youtube/v3/search?" + $"part=snippet&maxResults=1" + From 7bd8163fea704abf6e32e65430ed80d78a8e3b3c Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sat, 9 Jul 2016 04:25:07 +0200 Subject: [PATCH 15/89] `!m sq` added (soundcloudqueue), allows search queries for soundcloud. --- .../Modules/Music/Classes/MusicControls.cs | 3 +- NadekoBot/Modules/Music/Classes/Song.cs | 18 ++++++- NadekoBot/Modules/Music/Classes/SoundCloud.cs | 19 ++++++- NadekoBot/Modules/Music/MusicModule.cs | 50 +++++++++---------- NadekoBot/Modules/Utility/UtilityModule.cs | 2 +- 5 files changed, 62 insertions(+), 30 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index 2aba6075..41e8c7c1 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -12,7 +12,8 @@ namespace NadekoBot.Modules.Music.Classes { Radio, Normal, - Local + Local, + Soundcloud } public enum StreamState diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index b23c75a5..e718302e 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -245,16 +245,30 @@ namespace NadekoBot.Modules.Music.Classes } if (SoundCloud.Default.IsSoundCloudLink(query)) { - var svideo = await SoundCloud.Default.GetVideoAsync(query).ConfigureAwait(false); + var svideo = await SoundCloud.Default.ResolveVideoAsync(query).ConfigureAwait(false); return new Song(new SongInfo { Title = svideo.FullName, Provider = "SoundCloud", Uri = svideo.StreamLink, ProviderType = musicType, - Query = query, + Query = svideo.TrackLink, }); } + + if (musicType == MusicType.Soundcloud) + { + var svideo = await SoundCloud.Default.GetVideoByQueryAsync(query).ConfigureAwait(false); + return new Song(new SongInfo + { + Title = svideo.FullName, + Provider = "SoundCloud", + Uri = svideo.StreamLink, + ProviderType = MusicType.Normal, + Query = svideo.TrackLink, + }); + } + var link = await SearchHelper.FindYoutubeUrlByKeywords(query).ConfigureAwait(false); if (string.IsNullOrWhiteSpace(link)) throw new OperationCanceledException("Not a valid youtube query."); diff --git a/NadekoBot/Modules/Music/Classes/SoundCloud.cs b/NadekoBot/Modules/Music/Classes/SoundCloud.cs index 159d0d9e..e743fa5c 100644 --- a/NadekoBot/Modules/Music/Classes/SoundCloud.cs +++ b/NadekoBot/Modules/Music/Classes/SoundCloud.cs @@ -1,6 +1,7 @@ using NadekoBot.Classes; using Newtonsoft.Json; using System; +using System.Linq; using System.Threading.Tasks; namespace NadekoBot.Modules.Music.Classes @@ -13,7 +14,7 @@ namespace NadekoBot.Modules.Music.Classes static SoundCloud() { } public SoundCloud() { } - public async Task GetVideoAsync(string url) + public async Task ResolveVideoAsync(string url) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); @@ -31,6 +32,22 @@ namespace NadekoBot.Modules.Music.Classes public bool IsSoundCloudLink(string url) => System.Text.RegularExpressions.Regex.IsMatch(url, "(.*)(soundcloud.com|snd.sc)(.*)"); + + internal async Task GetVideoByQueryAsync(string query) + { + if (string.IsNullOrWhiteSpace(query)) + throw new ArgumentNullException(nameof(query)); + if (string.IsNullOrWhiteSpace(NadekoBot.Creds.SoundCloudClientID)) + throw new ArgumentNullException(nameof(NadekoBot.Creds.SoundCloudClientID)); + + var response = await SearchHelper.GetResponseStringAsync($"http://api.soundcloud.com/tracks?q={Uri.EscapeDataString(query)}&client_id={NadekoBot.Creds.SoundCloudClientID}").ConfigureAwait(false); + + var responseObj = JsonConvert.DeserializeObject(response).Where(s => s.Streamable).FirstOrDefault(); + if (responseObj?.Kind != "track") + throw new InvalidOperationException("Query yielded no results."); + + return responseObj; + } } public class SoundCloudVideo diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 337529c0..5199cbb1 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -108,20 +108,20 @@ namespace NadekoBot.Modules.Music } }); - //cgb.CreateCommand("soundcloudqueue") - // .Alias("sq") - // .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + - // "**You must be in a voice channel**.\n**Usage**: `!m sq Dream Of Venice`") - // .Parameter("query", ParameterType.Unparsed) - // .Do(async e => - // { - // await QueueSong(e.Channel, e.User.VoiceChannel, e.GetArg("query")).ConfigureAwait(false); - // if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) - // { - // await Task.Delay(10000).ConfigureAwait(false); - // await e.Message.Delete().ConfigureAwait(false); - // } - // }); + cgb.CreateCommand("soundcloudqueue") + .Alias("sq") + .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + + "**You must be in a voice channel**.\n**Usage**: `!m sq Dream Of Venice`") + .Parameter("query", ParameterType.Unparsed) + .Do(async e => + { + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, e.GetArg("query"), musicType: MusicType.Soundcloud).ConfigureAwait(false); + if (e.Server.CurrentUser.GetPermissions(e.Channel).ManageMessages) + { + await Task.Delay(10000).ConfigureAwait(false); + await e.Message.Delete().ConfigureAwait(false); + } + }); cgb.CreateCommand("listqueue") .Alias("lq") @@ -297,7 +297,7 @@ namespace NadekoBot.Modules.Music var ids = await SearchHelper.GetVideoIDs(plId, 500).ConfigureAwait(false); if (ids == null || ids.Count == 0) { - await e.Channel.SendMessage($"🎵 `Failed to find any songs.`"); + await e.Channel.SendMessage($"🎵 `Failed to find any songs.`").ConfigureAwait(false); return; } //todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE @@ -329,8 +329,8 @@ namespace NadekoBot.Modules.Music if (string.IsNullOrWhiteSpace(pl)) return; - var scvids = JObject.Parse(await SearchHelper.GetResponseStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Creds.SoundCloudClientID}"))["tracks"].ToObject(); - await QueueSong(e.User, e.Channel, e.User.VoiceChannel, scvids[0].TrackLink); + var scvids = JObject.Parse(await SearchHelper.GetResponseStringAsync($"http://api.soundcloud.com/resolve?url={pl}&client_id={NadekoBot.Creds.SoundCloudClientID}").ConfigureAwait(false))["tracks"].ToObject(); + await QueueSong(e.User, e.Channel, e.User.VoiceChannel, scvids[0].TrackLink).ConfigureAwait(false); MusicPlayer mp; if (!MusicPlayers.TryGetValue(e.Server, out mp)) @@ -483,7 +483,7 @@ namespace NadekoBot.Modules.Music !int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 || n1 > playlist.Count || n2 > playlist.Count) { - await e.Channel.SendMessage("`Invalid input.`"); + await e.Channel.SendMessage("`Invalid input.`").ConfigureAwait(false); return; } @@ -492,7 +492,7 @@ namespace NadekoBot.Modules.Music var nn1 = n2 < n1 ? n1 : n1 - 1; playlist.RemoveAt(nn1); - await e.Channel.SendMessage($"🎵`Moved` {s.PrettyName} `from #{n1} to #{n2}`"); + await e.Channel.SendMessage($"🎵`Moved` {s.PrettyName} `from #{n1} to #{n2}`").ConfigureAwait(false); }); @@ -691,9 +691,9 @@ namespace NadekoBot.Modules.Music return; var result = DbHandler.Instance.GetPlaylistData(num); if (result.Count == 0) - e.Channel.SendMessage($"`No saved playlists found on page {num}`"); + e.Channel.SendMessage($"`No saved playlists found on page {num}`").ConfigureAwait(false); else - e.Channel.SendMessage($"```js\n--- List of saved playlists ---\n\n" + string.Join("\n", result.Select(r => $"'{r.Name}-{r.Id}' by {r.Creator} ({r.SongCnt} songs)")) + $"\n\n --- Page {num} ---```"); + e.Channel.SendMessage($"```js\n--- List of saved playlists ---\n\n" + string.Join("\n", result.Select(r => $"'{r.Name}-{r.Id}' by {r.Creator} ({r.SongCnt} songs)")) + $"\n\n --- Page {num} ---```").ConfigureAwait(false); }); cgb.CreateCommand("deleteplaylist") @@ -710,7 +710,7 @@ namespace NadekoBot.Modules.Music DbHandler.Instance.Delete(plnum); else DbHandler.Instance.DeleteWhere(mp => mp.Id == plnum && (long)e.User.Id == mp.CreatorId); - await e.Channel.SendMessage("`Ok.` :ok:"); + await e.Channel.SendMessage("`Ok.` :ok:").ConfigureAwait(false); }); cgb.CreateCommand("goto") @@ -761,7 +761,7 @@ namespace NadekoBot.Modules.Music var curSong = musicPlayer.CurrentSong; if (curSong == null) return; - await e.Channel.SendMessage($"🎶`Current song:` <{curSong.SongInfo.Query}>"); + await e.Channel.SendMessage($"🎶`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false); }); cgb.CreateCommand("autoplay") @@ -775,9 +775,9 @@ namespace NadekoBot.Modules.Music return; if (!musicPlayer.ToggleAutoplay()) - await e.Channel.SendMessage("🎶`Autoplay disabled.`"); + await e.Channel.SendMessage("🎶`Autoplay disabled.`").ConfigureAwait(false); else - await e.Channel.SendMessage("🎶`Autoplay enabled.`"); + await e.Channel.SendMessage("🎶`Autoplay enabled.`").ConfigureAwait(false); }); }); } diff --git a/NadekoBot/Modules/Utility/UtilityModule.cs b/NadekoBot/Modules/Utility/UtilityModule.cs index 6d510a25..c85348aa 100644 --- a/NadekoBot/Modules/Utility/UtilityModule.cs +++ b/NadekoBot/Modules/Utility/UtilityModule.cs @@ -99,7 +99,7 @@ namespace NadekoBot.Modules.Utility .Description("Shows some basic stats for Nadeko.") .Do(async e => { - await e.Channel.SendMessage(await NadekoStats.Instance.GetStats()); + await e.Channel.SendMessage(await NadekoStats.Instance.GetStats()).ConfigureAwait(false); }); cgb.CreateCommand(Prefix + "dysyd") From ac26502d57d7c32e0fe916d15b43e93054922248 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sun, 10 Jul 2016 01:05:38 +0200 Subject: [PATCH 16/89] woops, not 50%, need 2% XD --- NadekoBot/Modules/Games/Commands/PlantPick.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index ccb68660..badbfae8 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -44,7 +44,7 @@ namespace NadekoBot.Modules.Games.Commands if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) { var rnd = Math.Abs(GetRandomNumber()); - if ((rnd % 2) == 0) + if ((rnd % 50) == 0) { var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); var msg2 = await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); From 718f0d48714a2f440eb48c3653f749d600462bb0 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 11:44:33 +0200 Subject: [PATCH 17/89] $lb now shows the names of people in the server it's invoked in, if any. --- NadekoBot/Modules/Gambling/GamblingModule.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 668c1383..d54958ce 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -147,14 +147,14 @@ namespace NadekoBot.Modules.Gambling return; await e.Channel.SendMessage( richest.Aggregate(new StringBuilder( -$@"```xl -┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ -┃ Id ┃ $$$ ┃ + $@"```xl +┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━┓ +┃ Id ┃ $$$ ┃ "), (cur, cs) => cur.AppendLine( -$@"┣━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ -┃{cs.UserId,-18} ┃ {cs.Value,5} ┃") - ).ToString() + "┗━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```"); + $@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ +┃{(e.Server.Users.Where(u => u.Id == (ulong)cs.UserId).FirstOrDefault()?.Name.TrimTo(18, true) ?? cs.UserId.ToString()),-20} ┃ {cs.Value,5} ┃") + ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```"); }); }); } From 98f6498711cf9ec4cb5a5ab598580f594897f126 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 11:47:27 +0200 Subject: [PATCH 18/89] #391 take now dms the user that he lost flowers. --- NadekoBot/Classes/FlowersHandler.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Classes/FlowersHandler.cs b/NadekoBot/Classes/FlowersHandler.cs index 86e4086b..d41435c3 100644 --- a/NadekoBot/Classes/FlowersHandler.cs +++ b/NadekoBot/Classes/FlowersHandler.cs @@ -21,12 +21,12 @@ namespace NadekoBot.Classes if (silent) return; - var flows = amount +" " + NadekoBot.Config.CurrencySign; + var flows = amount + " " + NadekoBot.Config.CurrencySign; await u.SendMessage("👑Congratulations!👑\nYou received: " + flows).ConfigureAwait(false); } - public static bool RemoveFlowers(Discord.User u, string reason, int amount) + public static async Task RemoveFlowers(Discord.User u, string reason, int amount) { if (amount <= 0) return false; @@ -42,6 +42,8 @@ namespace NadekoBot.Classes UserId = (long)u.Id, Value = -amount, }); + + await u.SendMessage("👎`Bot owner has taken:" + amount + " from you.`").ConfigureAwait(false); return true; } } From 3bc636774f82583e7e5e9d5765c8a88ba09f2d06 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 11:50:38 +0200 Subject: [PATCH 19/89] afk voice channel is now ignored with .v+t command --- .../Modules/Administration/Commands/VoicePlusTextCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs b/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs index e307c796..be6bae12 100644 --- a/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommand.cs @@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Administration.Commands sendMessages: PermValue.Deny)).ConfigureAwait(false); } var afterVch = e.After.VoiceChannel; - if (afterVch != null) + if (afterVch != null && e.Server.AFKChannel != afterVch) { var textChannel = e.Server.FindChannels( GetChannelName(afterVch.Name), From f1b839337a7b3be7f12adbac91a18ee4c3c19c8f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 11:51:54 +0200 Subject: [PATCH 20/89] pokab renamed to pokeab #389 --- NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index 6bdcf00b..f9824278 100644 --- a/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Searches.Commands }); cgb.CreateCommand(Prefix + "pokemonability") - .Alias(Prefix + "pokab") + .Alias(Prefix + "pokeab") .Description("Searches for a pokemon ability.") .Parameter("abil", ParameterType.Unparsed) .Do(async e => From 57bc26dd46d9561d7822308b9f7bd07e6153653a Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 12:28:43 +0200 Subject: [PATCH 21/89] bugfix, added option in config to forward messages to all owners. --- NadekoBot/Modules/Games/Commands/PlantPick.cs | 2 +- NadekoBot/NadekoBot.cs | 40 +++++++++++++------ NadekoBot/_Models/JSONModels/Configuration.cs | 1 + 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index badbfae8..6f01c170 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -92,7 +92,7 @@ namespace NadekoBot.Modules.Games.Commands e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel."); return; } - var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1); + var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1).GetAwaiter().GetResult(); if (!removed) { e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait(); diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index 86d22219..9a4b804c 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -42,7 +42,7 @@ namespace NadekoBot public static string BotMention { get; set; } = ""; public static bool Ready { get; set; } = false; - private static Channel OwnerPrivateChannel { get; set; } + private static List OwnerPrivateChannels { get; set; } private static void Main() { @@ -205,15 +205,19 @@ namespace NadekoBot Console.WriteLine(await NadekoStats.Instance.GetStats().ConfigureAwait(false)); Console.WriteLine("-----------------"); - try - { - OwnerPrivateChannel = await Client.CreatePrivateChannel(Creds.OwnerIds[0]).ConfigureAwait(false); - } - catch - { - Console.WriteLine("Failed creating private channel with the first owner listed in credentials.json"); - } + OwnerPrivateChannels = new List(Creds.OwnerIds.Length); + foreach (var id in Creds.OwnerIds) + { + try + { + OwnerPrivateChannels.Add(await Client.CreatePrivateChannel(id).ConfigureAwait(false)); + } + catch + { + Console.WriteLine($"Failed creating private channel with the owner {id} listed in credentials.json"); + } + } Client.ClientAPI.SendingRequest += (s, e) => { var request = e.Request as Discord.API.Client.Rest.SendMessageRequest; @@ -234,8 +238,18 @@ namespace NadekoBot public static async Task SendMessageToOwner(string message) { - if (Config.ForwardMessages && OwnerPrivateChannel != null) - await OwnerPrivateChannel.SendMessage(message).ConfigureAwait(false); + if (Config.ForwardMessages && OwnerPrivateChannels.Any()) + if (Config.ForwardToAllOwners) + OwnerPrivateChannels.ForEach(async c => + { + try { await c.SendMessage(message).ConfigureAwait(false); } catch { } + }); + else + { + var c = OwnerPrivateChannels.FirstOrDefault(); + if (c != null) + await c.SendMessage(message).ConfigureAwait(false); + } } private static bool repliedRecently = false; @@ -248,8 +262,8 @@ namespace NadekoBot if (ConfigHandler.IsBlackListed(e)) return; - if (Config.ForwardMessages && !NadekoBot.Creds.OwnerIds.Contains(e.User.Id) && OwnerPrivateChannel != null) - await OwnerPrivateChannel.SendMessage(e.User + ": ```\n" + e.Message.Text + "\n```").ConfigureAwait(false); + if (Config.ForwardMessages && !NadekoBot.Creds.OwnerIds.Contains(e.User.Id) && OwnerPrivateChannels.Any()) + await SendMessageToOwner(e.User + ": ```\n" + e.Message.Text + "\n```").ConfigureAwait(false); if (repliedRecently) return; diff --git a/NadekoBot/_Models/JSONModels/Configuration.cs b/NadekoBot/_Models/JSONModels/Configuration.cs index bafdb84e..93f9ebb9 100644 --- a/NadekoBot/_Models/JSONModels/Configuration.cs +++ b/NadekoBot/_Models/JSONModels/Configuration.cs @@ -84,6 +84,7 @@ namespace NadekoBot.Classes.JSONModels public bool DontJoinServers { get; set; } = false; public bool ForwardMessages { get; set; } = true; + public bool ForwardToAllOwners { get; set; } = false; public bool IsRotatingStatus { get; set; } = false; public int BufferSize { get; set; } = 4.MiB(); From 33176f894398ebe6d1b9e9c08327fd460e7d7d13 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 13:44:19 +0200 Subject: [PATCH 22/89] >attack command clarified, commandlist updated, `.lcr all` is now a thing and sends you list of all custom reactions in DM --- .gitignore | 3 ++- NadekoBot/Classes/SearchHelper.cs | 2 +- .../Commands/CustomReactionsCommands.cs | 20 ++++++++++++++++--- NadekoBot/Modules/Help/HelpModule.cs | 4 ++++ NadekoBot/Modules/Pokemon/PokemonModule.cs | 2 +- NadekoBot/bin/Debug/data/config_example.json | 1 + 6 files changed, 26 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0b9a35ab..1dd55335 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ NadekoBot/bin/Debug/data/ServerSpecificConfigs.json NadekoBot.sln.iml .idea/workspace.xml .idea/vcs.xml -.idea/modules.xml \ No newline at end of file +.idea/modules.xml +NadekoBot/bin/Debug/data/config_xnaas.json \ No newline at end of file diff --git a/NadekoBot/Classes/SearchHelper.cs b/NadekoBot/Classes/SearchHelper.cs index 82d3d7dc..77c46151 100644 --- a/NadekoBot/Classes/SearchHelper.cs +++ b/NadekoBot/Classes/SearchHelper.cs @@ -359,7 +359,7 @@ namespace NadekoBot.Classes using (var streamWriter = new StreamWriter(await httpWebRequest.GetRequestStreamAsync().ConfigureAwait(false))) { - var json = "{\"longUrl\":\"" + url + "\"}"; + var json = "{\"longUrl\":\"" + Uri.EscapeDataString(url) + "\"}"; streamWriter.Write(json); } diff --git a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs index 56ac5a4d..2950f8aa 100644 --- a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs +++ b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs @@ -2,6 +2,7 @@ using Discord.Commands; using NadekoBot.Classes; using NadekoBot.Modules.Permissions.Classes; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -46,16 +47,29 @@ namespace NadekoBot.Modules.Administration.Commands cgb.CreateCommand(Prefix + "listcustreact") .Alias(Prefix + "lcr") - .Description($"Lists all current custom reactions (paginated with 30 commands per page).\n**Usage**:{Prefix}lcr 1") + .Description($"Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. \n**Usage**:{Prefix}lcr 1") .Parameter("num", ParameterType.Required) .Do(async e => { + var numStr = e.GetArg("num"); + + if (numStr.ToUpperInvariant() == "ALL") + { + var fullstr = String.Join("\n", NadekoBot.Config.CustomReactions.Select(kvp => kvp.Key)); + do + { + var str = string.Concat(fullstr.Take(1900)); + fullstr = new string(fullstr.Skip(1900).ToArray()); + await e.User.SendMessage("```xl\n" + str + "```"); + } while (fullstr.Length != 0); + return; + } int num; - if (!int.TryParse(e.GetArg("num"), out num) || num <= 0) num = 1; + if (!int.TryParse(numStr, out num) || num <= 0) num = 1; var cmds = GetCustomsOnPage(num - 1); if (!cmds.Any()) { - await e.Channel.SendMessage(""); + await e.Channel.SendMessage("`There are no custom reactions.`"); } else { diff --git a/NadekoBot/Modules/Help/HelpModule.cs b/NadekoBot/Modules/Help/HelpModule.cs index 0b17e336..511779c2 100644 --- a/NadekoBot/Modules/Help/HelpModule.cs +++ b/NadekoBot/Modules/Help/HelpModule.cs @@ -55,11 +55,15 @@ namespace NadekoBot.Modules.Help } var i = 0; if (module != "customreactions" && module != "conversations") + { await e.Channel.SendMessage("`List Of Commands:`\n" + SearchHelper.ShowInPrettyCode(cmdsArray, el => $"{el.Text,-15}{"[" + el.Aliases.FirstOrDefault() + "]",-8}")) .ConfigureAwait(false); + } else + { await e.Channel.SendMessage("`List Of Commands:`\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Text}"))); + } await e.Channel.SendMessage($"`You can type \"{Prefix}h command_name\" to see the help about that specific command.`").ConfigureAwait(false); }); }); diff --git a/NadekoBot/Modules/Pokemon/PokemonModule.cs b/NadekoBot/Modules/Pokemon/PokemonModule.cs index 7cc90720..0e585b6d 100644 --- a/NadekoBot/Modules/Pokemon/PokemonModule.cs +++ b/NadekoBot/Modules/Pokemon/PokemonModule.cs @@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Pokemon commands.ForEach(cmd => cmd.Init(cgb)); cgb.CreateCommand(Prefix + "attack") - .Description("Attacks a target with the given move") + .Description($"Attacks a target with the given move. Use `{Prefix}movelist` to see a list of moves your type can use. | `{Prefix}attack \"vine whip\" @someguy`") .Parameter("move", ParameterType.Required) .Parameter("target", ParameterType.Unparsed) .Do(async e => diff --git a/NadekoBot/bin/Debug/data/config_example.json b/NadekoBot/bin/Debug/data/config_example.json index f8ff7769..469aa4c0 100644 --- a/NadekoBot/bin/Debug/data/config_example.json +++ b/NadekoBot/bin/Debug/data/config_example.json @@ -1,6 +1,7 @@ { "DontJoinServers": false, "ForwardMessages": true, + "ForwardToAllOwners": false, "IsRotatingStatus": false, "BufferSize": 4194304, "Quotes": [], From d89298e3d88ac1a80489c6287d7fb57b140f0787 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 13:51:06 +0200 Subject: [PATCH 23/89] commandlist updated really, woopsie --- commandlist.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/commandlist.md b/commandlist.md index a6d2e202..8529b6e2 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,7 +2,7 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6030.3793` +Version: `NadekoBot v0.9.6036.24599` ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -45,7 +45,7 @@ Command and aliases | Description | Usage `.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 `.addcustreact`, `.acr` | Add a custom reaction. Guide here: **Bot Owner Only!** | .acr "hello" I love saying hello to %user% -`.listcustreact`, `.lcr` | Lists all current custom reactions (paginated with 30 commands per page). | .lcr 1 +`.listcustreact`, `.lcr` | Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. | .lcr 1 `.showcustreact`, `.scr` | Shows all possible responses from a single custom reaction. | .scr %mention% bb `.editcustreact`, `.ecr` | Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `.ecr "%mention% disguise" 2 Test 123` `.delcustreact`, `.dcr` | Deletes a custom reaction with given name (and index) @@ -153,7 +153,6 @@ Command and aliases | Description | Usage `...` | Shows a random quote with a specified name. | .. abc `..qdel`, `..quotedelete` | Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. | `..qdel abc` `@BotName rip` | Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000 -`@BotName uptime` | Shows how long Nadeko has been running for. `@BotName die` | Works only for the owner. Shuts the bot down. `@BotName do you love me` | Replies with positive answer only to the bot owner. `@BotName how are you`, `@BotName how are you?` | Replies positive only if bot owner is online. @@ -190,7 +189,7 @@ Command and aliases | Description | Usage `>pollend` | Stops active poll on this server and prints the results in this channel. `>pick` | Picks a flower planted in this channel. `>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) -`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Requires Manage Messages permission. | `>gc` +`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60` `>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello `>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more `>8ball` | Ask the 8ball a yes/no question. @@ -205,15 +204,16 @@ Command and aliases | Description | Usage `!m destroy`, `!m d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!m d` `!m pause`, `!m p` | Pauses or Unpauses the song. | `!m p` `!m queue`, `!m q`, `!m yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!m q Dream Of Venice` +`!m soundcloudqueue`, `!m sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!m sq Dream Of Venice` `!m listqueue`, `!m lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2` `!m nowplaying`, `!m np` | Shows the song currently playing. | `!m np` `!m volume`, `!m vol` | Sets the music volume 0-100% | `!m vol 50` `!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80` `!m mute`, `!m min` | Sets the music volume to 0% | `!m min` -`!m max` | Sets the music volume to 100% (real max is actually 150%). | `!m max` +`!m max` | Sets the music volume to 100%. | `!m max` `!m half` | Sets the music volume to 50%. | `!m half` `!m shuffle`, `!m sh` | Shuffles the current playlist. | `!m sh` -`!m playlist`, `!m pl` | Queues up to 50 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name` +`!m playlist`, `!m pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name` `!m soundcloudpl`, `!m scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` `!m localplaylst`, `!m lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical` `!m radio`, `!m ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf | `!m ra radio link here` @@ -221,6 +221,7 @@ Command and aliases | Description | Usage `!m move`, `!m mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!m mv` `!m remove`, `!m rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5` `!m movesong`, `!m ms` | Moves a song from one position to another. | `!m ms` 5>3 +`!m setmaxqueue`, `!m smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!m smq` 50 or `!m smq` `!m cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup` `!m reptcursong`, `!m rcs` | Toggles repeat of current song. | `!m rcs` `!m rpeatplaylst`, `!m rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl` @@ -253,7 +254,7 @@ Command and aliases | Description | Usage `~osu b` | Shows information about an osu beatmap. | ~osu b https://osu.ppy.sh/s/127712 `~osu top5` | Displays a user's top 5 plays. | ~osu top5 Name `~pokemon`, `~poke` | Searches for a pokemon. -`~pokemonability`, `~pokab` | Searches for a pokemon ability. +`~pokemonability`, `~pokeab` | Searches for a pokemon ability. `~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ `~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` `~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | ~we Moscow RF @@ -301,14 +302,16 @@ Command and aliases | Description | Usage `,startwar`, `,sw` | Starts a war with a given number. `,listwar`, `,lw` | Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | ,lw [war_number] or ,lw `,claim`, `,call`, `,c` | Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | ,call [war_number] [base_number] [optional_other_name] -`,claimfinish`, `,cf` | Finish your claim if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] +`,claimfinish`, `,cf`, `,cf3`, `,claimfinish3` | Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] +`,claimfinish2`, `,cf2` | Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] +`,claimfinish1`, `,cf1` | Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] `,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | ,uc [war_number] [optional_other_name] `,endwar`, `,ew` | Ends the war with a given index. | ,ew [war_number] ### Pokegame Command and aliases | Description | Usage ----------------|--------------|------- -`>attack` | Attacks a target with the given move +`>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 `>heal` | Heals someone. Revives those that fainted. Costs a NadekoFlower | >revive @someone `>type` | Get the poketype of the target. | >type @someone From 7de315bc04940ee0c71d24512f4f8dab5ccef690 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 16:23:42 +0200 Subject: [PATCH 24/89] #392 fixed searching abilities with spaces --- NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index f9824278..a171abe9 100644 --- a/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Searches.Commands .Parameter("abil", ParameterType.Unparsed) .Do(async e => { - var ab = e.GetArg("abil")?.Trim().ToUpperInvariant(); + var ab = e.GetArg("abil")?.Trim().ToUpperInvariant().Replace(" ", ""); if (string.IsNullOrWhiteSpace(ab)) return; foreach (var kvp in pokemonAbilities) From 16213abc8bda6561849cf40b4aff4ca4b129fd49 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 18:17:41 +0200 Subject: [PATCH 25/89] More proper Examples for Permissions module. Changed Arcane \n**Usage**: keyword everywhere with a simple | --- .../Administration/AdministrationModule.cs | 26 +++---- .../Commands/CustomReactionsCommands.cs | 6 +- .../Commands/MessageRepeater.cs | 2 +- .../Commands/SelfAssignedRolesCommand.cs | 6 +- .../Administration/Commands/SelfCommands.cs | 2 +- .../Commands/ServerGreetCommand.cs | 4 +- .../Commands/VoiceNotificationCommand.cs | 2 +- .../ClashOfClans/ClashOfClansModule.cs | 16 ++-- .../Conversations/Commands/RipCommand.cs | 2 +- .../Modules/Conversations/Conversations.cs | 8 +- .../CustomReactions/CustomReactions.cs | 2 +- NadekoBot/Modules/Gambling/DiceRollCommand.cs | 4 +- NadekoBot/Modules/Gambling/DrawCommand.cs | 2 +- NadekoBot/Modules/Gambling/FlipCoinCommand.cs | 2 +- NadekoBot/Modules/Gambling/GamblingModule.cs | 4 +- NadekoBot/Modules/Games/Commands/Leet.cs | 2 +- .../Modules/Games/Commands/PollCommand.cs | 2 +- .../Modules/Games/Commands/TriviaCommand.cs | 2 +- NadekoBot/Modules/Games/GamesModule.cs | 6 +- .../Modules/Help/Commands/HelpCommand.cs | 4 +- NadekoBot/Modules/Music/MusicModule.cs | 56 +++++++------- NadekoBot/Modules/NSFW/NSFWModule.cs | 10 +-- .../Commands/FilterInvitesCommand.cs | 4 +- .../Commands/FilterWordsCommand.cs | 10 +-- .../Modules/Permissions/PermissionsModule.cs | 52 ++++++------- NadekoBot/Modules/Pokemon/PokemonModule.cs | 6 +- .../Modules/Searches/Commands/EvalCommand.cs | 2 +- .../Modules/Searches/Commands/LoLCommands.cs | 2 +- .../Modules/Searches/Commands/OsuCommands.cs | 6 +- .../Searches/Commands/StreamNotifications.cs | 16 ++-- NadekoBot/Modules/Searches/SearchesModule.cs | 18 ++--- .../Modules/Translator/TranslateCommand.cs | 2 +- NadekoBot/Modules/Trello/TrelloModule.cs | 2 +- .../Modules/Utility/Commands/InfoCommands.cs | 6 +- NadekoBot/Modules/Utility/Commands/Remind.cs | 2 +- commandlist.md | 76 +++++++++---------- 36 files changed, 187 insertions(+), 187 deletions(-) diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index 36538813..3acaf92b 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Administration }); cgb.CreateCommand(Prefix + "setrole").Alias(Prefix + "sr") - .Description("Sets a role for a given user.\n**Usage**: .sr @User Guest") + .Description("Sets a role for a given user. | .sr @User Guest") .Parameter("user_name", ParameterType.Required) .Parameter("role_name", ParameterType.Unparsed) .AddCheck(SimpleCheckers.CanManageRoles) @@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Administration }); cgb.CreateCommand(Prefix + "removerole").Alias(Prefix + "rr") - .Description("Removes a role from a given user.\n**Usage**: .rr @User Admin") + .Description("Removes a role from a given user. | .rr @User Admin") .Parameter("user_name", ParameterType.Required) .Parameter("role_name", ParameterType.Unparsed) .AddCheck(SimpleCheckers.CanManageRoles) @@ -171,7 +171,7 @@ namespace NadekoBot.Modules.Administration cgb.CreateCommand(Prefix + "renamerole") .Alias(Prefix + "renr") - .Description($"Renames a role. Role you are renaming must be lower than bot's highest role.\n**Usage**: `{Prefix}renr \"First role\" SecondRole`") + .Description($"Renames a role. Role you are renaming must be lower than bot's highest role. | `{Prefix}renr \"First role\" SecondRole`") .Parameter("r1", ParameterType.Required) .Parameter("r2", ParameterType.Required) .AddCheck(new SimpleCheckers.ManageRoles()) @@ -204,7 +204,7 @@ namespace NadekoBot.Modules.Administration }); cgb.CreateCommand(Prefix + "removeallroles").Alias(Prefix + "rar") - .Description("Removes all roles from a mentioned user.\n**Usage**: .rar @User") + .Description("Removes all roles from a mentioned user. | .rar @User") .Parameter("user_name", ParameterType.Unparsed) .AddCheck(SimpleCheckers.CanManageRoles) .Do(async e => @@ -253,7 +253,7 @@ namespace NadekoBot.Modules.Administration .Parameter("r", ParameterType.Optional) .Parameter("g", ParameterType.Optional) .Parameter("b", ParameterType.Optional) - .Description("Set a role's color to the hex or 0-255 rgb color value provided.\n**Usage**: `.color Admin 255 200 100` or `.color Admin ffba55`") + .Description("Set a role's color to the hex or 0-255 rgb color value provided. | `.color Admin 255 200 100` or `.color Admin ffba55`") .Do(async e => { if (!e.User.ServerPermissions.ManageRoles) @@ -298,7 +298,7 @@ namespace NadekoBot.Modules.Administration cgb.CreateCommand(Prefix + "ban").Alias(Prefix + "b") .Parameter("user", ParameterType.Required) .Parameter("msg", ParameterType.Unparsed) - .Description("Bans a user by id or name with an optional message.\n**Usage**: .b \"@some Guy\" Your behaviour is toxic.") + .Description("Bans a user by id or name with an optional message. | .b \"@some Guy\" Your behaviour is toxic.") .Do(async e => { var msg = e.GetArg("msg"); @@ -333,7 +333,7 @@ namespace NadekoBot.Modules.Administration cgb.CreateCommand(Prefix + "softban").Alias(Prefix + "sb") .Parameter("user", ParameterType.Required) .Parameter("msg", ParameterType.Unparsed) - .Description("Bans and then unbans a user by id or name with an optional message.\n**Usage**: .sb \"@some Guy\" Your behaviour is toxic.") + .Description("Bans and then unbans a user by id or name with an optional message. | .sb \"@some Guy\" Your behaviour is toxic.") .Do(async e => { var msg = e.GetArg("msg"); @@ -592,7 +592,7 @@ namespace NadekoBot.Modules.Administration cgb.CreateCommand(Prefix + "settopic") .Alias(Prefix + "st") - .Description($"Sets a topic on the current channel.\n**Usage**: `{Prefix}st My new topic`") + .Description($"Sets a topic on the current channel. | `{Prefix}st My new topic`") .AddCheck(SimpleCheckers.ManageChannels()) .Parameter("topic", ParameterType.Unparsed) .Do(async e => @@ -628,7 +628,7 @@ namespace NadekoBot.Modules.Administration cgb.CreateCommand(Prefix + "prune") .Alias(Prefix + "clr") .Description( - "`.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.\n**Usage**: `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X`") + "`.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`") .Parameter("user_or_num", ParameterType.Optional) .Parameter("num", ParameterType.Optional) .Do(async e => @@ -719,7 +719,7 @@ namespace NadekoBot.Modules.Administration cgb.CreateCommand(Prefix + "newavatar") .Alias(Prefix + "setavatar") - .Description("Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!**\n**Usage**: `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg`") + .Description("Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg`") .Parameter("img", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -750,7 +750,7 @@ namespace NadekoBot.Modules.Administration }); cgb.CreateCommand(Prefix + "send") - .Description("Send a message to someone on a different server through the bot. **Bot Owner Only!**\n**Usage**: `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!`") + .Description("Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!`") .Parameter("ids", ParameterType.Required) .Parameter("msg", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) @@ -877,7 +877,7 @@ namespace NadekoBot.Modules.Administration }); cgb.CreateCommand(Prefix + "announce") - .Description($"Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!**\n**Usage**: {Prefix}announce Useless spam") + .Description($"Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | {Prefix}announce Useless spam") .Parameter("msg", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -891,7 +891,7 @@ namespace NadekoBot.Modules.Administration }); cgb.CreateCommand(Prefix + "leave") - .Description("Leaves a server with a supplied ID.\n**Usage**: `.leave 493243292839`") + .Description("Leaves a server with a supplied ID. | `.leave 493243292839`") .Parameter("num", ParameterType.Required) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => diff --git a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs index 2950f8aa..fcd970a7 100644 --- a/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs +++ b/NadekoBot/Modules/Administration/Commands/CustomReactionsCommands.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Administration.Commands cgb.CreateCommand(Prefix + "addcustreact") .Alias(Prefix + "acr") - .Description($"Add a custom reaction. Guide here: **Bot Owner Only!** \n**Usage**: {Prefix}acr \"hello\" I love saying hello to %user%") + .Description($"Add a custom reaction. Guide here: **Bot Owner Only!** | {Prefix}acr \"hello\" I love saying hello to %user%") .AddCheck(SimpleCheckers.OwnerOnly()) .Parameter("name", ParameterType.Required) .Parameter("message", ParameterType.Unparsed) @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Administration.Commands cgb.CreateCommand(Prefix + "listcustreact") .Alias(Prefix + "lcr") - .Description($"Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. \n**Usage**:{Prefix}lcr 1") + .Description($"Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. |{Prefix}lcr 1") .Parameter("num", ParameterType.Required) .Do(async e => { @@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Administration.Commands cgb.CreateCommand(Prefix + "showcustreact") .Alias(Prefix + "scr") - .Description($"Shows all possible responses from a single custom reaction.\n**Usage**:{Prefix}scr %mention% bb") + .Description($"Shows all possible responses from a single custom reaction. |{Prefix}scr %mention% bb") .Parameter("name", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs index abcf35e5..4aa521a1 100644 --- a/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs +++ b/NadekoBot/Modules/Administration/Commands/MessageRepeater.cs @@ -73,7 +73,7 @@ namespace NadekoBot.Modules.Administration.Commands cgb.CreateCommand(Module.Prefix + "repeat") .Description("Repeat a message every X minutes. If no parameters are specified, " + - "repeat is disabled. Requires manage messages.\n**Usage**:`.repeat 5 Hello there`") + "repeat is disabled. Requires manage messages. |`.repeat 5 Hello there`") .Parameter("minutes", ParameterType.Optional) .Parameter("msg", ParameterType.Unparsed) .AddCheck(SimpleCheckers.ManageMessages()) diff --git a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index 6c179eaa..723b0f06 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Modules.Administration.Commands { cgb.CreateCommand(Module.Prefix + "asar") .Description("Adds a role, or list of roles separated by whitespace" + - "(use quotations for multiword roles) to the list of self-assignable roles.\n**Usage**: .asar Gamer") + "(use quotations for multiword roles) to the list of self-assignable roles. | .asar Gamer") .Parameter("roles", ParameterType.Multiple) .AddCheck(SimpleCheckers.CanManageRoles) .Do(async e => @@ -97,7 +97,7 @@ namespace NadekoBot.Modules.Administration.Commands cgb.CreateCommand(Module.Prefix + "iam") .Description("Adds a role to you that you choose. " + "Role must be on a list of self-assignable roles." + - "\n**Usage**: .iam Gamer") + " | .iam Gamer") .Parameter("role", ParameterType.Unparsed) .Do(async e => { @@ -143,7 +143,7 @@ namespace NadekoBot.Modules.Administration.Commands .Alias(Module.Prefix + "iamn") .Description("Removes a role to you that you choose. " + "Role must be on a list of self-assignable roles." + - "\n**Usage**: .iamn Gamer") + " | .iamn Gamer") .Parameter("role", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index e905d807..c3b5fad2 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Administration.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "leave") - .Description("Makes Nadeko leave the server. Either name or id required.\n**Usage**: `.leave 123123123331`") + .Description("Makes Nadeko leave the server. Either name or id required. | `.leave 123123123331`") .Parameter("arg", ParameterType.Required) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => diff --git a/NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs b/NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs index cfa898c5..54db0305 100644 --- a/NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/ServerGreetCommand.cs @@ -245,7 +245,7 @@ namespace NadekoBot.Modules.Administration.Commands }); cgb.CreateCommand(Module.Prefix + "greetmsg") - .Description("Sets a new join announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current greet message.\n**Usage**: .greetmsg Welcome to the server, %user%.") + .Description("Sets a new join announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. | .greetmsg Welcome to the server, %user%.") .Parameter("msg", ParameterType.Unparsed) .Do(async e => { @@ -278,7 +278,7 @@ namespace NadekoBot.Modules.Administration.Commands }); cgb.CreateCommand(Module.Prefix + "byemsg") - .Description("Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current bye message.\n**Usage**: .byemsg %user% has left the server.") + .Description("Sets a new leave announcement message. Type %user% if you want to mention the new member. Using it with no message will show the current bye message. | .byemsg %user% has left the server.") .Parameter("msg", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs b/NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs index b17fd1a9..ae63a7db 100644 --- a/NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/VoiceNotificationCommand.cs @@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Administration.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "voicenotif") - .Description("Enables notifications on who joined/left the voice channel.\n**Usage**:.voicenotif Karaoke club") + .Description("Enables notifications on who joined/left the voice channel. |.voicenotif Karaoke club") .Parameter("voice_name", ParameterType.Unparsed) .Do(DoFunc()); } diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index 297c4b34..fce926c9 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "createwar") .Alias(Prefix + "cw") .Description( - $"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name.\n**Usage**:{Prefix}cw 15 The Enemy Clan") + $"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{Prefix}cw 15 The Enemy Clan") .Parameter("size") .Parameter("enemy_clan", ParameterType.Unparsed) .Do(async e => @@ -103,7 +103,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "listwar") .Alias(Prefix + "lw") - .Description($"Shows the active war claims by a number. Shows all wars in a short way if no number is specified.\n**Usage**: {Prefix}lw [war_number] or {Prefix}lw") + .Description($"Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | {Prefix}lw [war_number] or {Prefix}lw") .Parameter("number", ParameterType.Optional) .Do(async e => { @@ -144,7 +144,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "claim") .Alias(Prefix + "call") .Alias(Prefix + "c") - .Description($"Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. \n**Usage**: {Prefix}call [war_number] [base_number] [optional_other_name]") + .Description($"Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | {Prefix}call [war_number] [base_number] [optional_other_name]") .Parameter("number") .Parameter("baseNumber") .Parameter("other_name", ParameterType.Unparsed) @@ -182,21 +182,21 @@ namespace NadekoBot.Modules.ClashOfClans .Alias(Prefix + "cf") .Alias(Prefix + "cf3") .Alias(Prefix + "claimfinish3") - .Description($"Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") + .Description($"Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | {Prefix}cf [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) .Do(e => FinishClaim(e)); cgb.CreateCommand(Prefix + "claimfinish2") .Alias(Prefix + "cf2") - .Description($"Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") + .Description($"Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. | {Prefix}cf [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) .Do(e => FinishClaim(e, 2)); cgb.CreateCommand(Prefix + "claimfinish1") .Alias(Prefix + "cf1") - .Description($"Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else.\n**Usage**: {Prefix}cf [war_number] [optional_other_name]") + .Description($"Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. | {Prefix}cf [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) .Do(e => FinishClaim(e, 1)); @@ -204,7 +204,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "unclaim") .Alias(Prefix + "uncall") .Alias(Prefix + "uc") - .Description($"Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim\n**Usage**: {Prefix}uc [war_number] [optional_other_name]") + .Description($"Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | {Prefix}uc [war_number] [optional_other_name]") .Parameter("number", ParameterType.Required) .Parameter("other_name", ParameterType.Unparsed) .Do(async e => @@ -233,7 +233,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "endwar") .Alias(Prefix + "ew") - .Description($"Ends the war with a given index.\n**Usage**:{Prefix}ew [war_number]") + .Description($"Ends the war with a given index. |{Prefix}ew [war_number]") .Parameter("number") .Do(async e => { diff --git a/NadekoBot/Modules/Conversations/Commands/RipCommand.cs b/NadekoBot/Modules/Conversations/Commands/RipCommand.cs index 5557ade8..f6960e5f 100644 --- a/NadekoBot/Modules/Conversations/Commands/RipCommand.cs +++ b/NadekoBot/Modules/Conversations/Commands/RipCommand.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Conversations.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand("rip") - .Description("Shows a grave image of someone with a start year\n**Usage**: @NadekoBot rip @Someone 2000") + .Description("Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000") .Parameter("user", ParameterType.Required) .Parameter("year", ParameterType.Optional) .Do(async e => diff --git a/NadekoBot/Modules/Conversations/Conversations.cs b/NadekoBot/Modules/Conversations/Conversations.cs index db072e91..215bfb90 100644 --- a/NadekoBot/Modules/Conversations/Conversations.cs +++ b/NadekoBot/Modules/Conversations/Conversations.cs @@ -32,7 +32,7 @@ namespace NadekoBot.Modules.Conversations cgb.AddCheck(PermissionChecker.Instance); cgb.CreateCommand("..") - .Description("Adds a new quote with the specified name (single word) and message (no limit).\n**Usage**: .. abc My message") + .Description("Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message") .Parameter("keyword", ParameterType.Required) .Parameter("text", ParameterType.Unparsed) .Do(async e => @@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Conversations }); cgb.CreateCommand("...") - .Description("Shows a random quote with a specified name.\n**Usage**: .. abc") + .Description("Shows a random quote with a specified name. | .. abc") .Parameter("keyword", ParameterType.Required) .Do(async e => { @@ -73,7 +73,7 @@ namespace NadekoBot.Modules.Conversations cgb.CreateCommand("..qdel") .Alias("..quotedelete") - .Description("Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it.\n**Usage**: `..qdel abc`") + .Description("Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. | `..qdel abc`") .Parameter("quote", ParameterType.Required) .Do(async e => { @@ -149,7 +149,7 @@ namespace NadekoBot.Modules.Conversations }); cgb.CreateCommand("fire") - .Description("Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire.\n**Usage**: @NadekoBot fire [x]") + .Description("Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire. | @NadekoBot fire [x]") .Parameter("times", ParameterType.Optional) .Do(async e => { diff --git a/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/NadekoBot/Modules/CustomReactions/CustomReactions.cs index a2e3973c..a613b6c5 100644 --- a/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -50,7 +50,7 @@ namespace NadekoBot.Modules.CustomReactions var c = cgb.CreateCommand(commandName); if (commandName.Contains(NadekoBot.BotMention)) c.Alias(commandName.Replace("<@", "<@!")); - c.Description($"Custom reaction.\n**Usage**:{command.Key}") + c.Description($"Custom reaction. |{command.Key}") .Parameter("args", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Gambling/DiceRollCommand.cs b/NadekoBot/Modules/Gambling/DiceRollCommand.cs index 2724d3ab..5e1a7833 100644 --- a/NadekoBot/Modules/Gambling/DiceRollCommand.cs +++ b/NadekoBot/Modules/Gambling/DiceRollCommand.cs @@ -21,11 +21,11 @@ namespace NadekoBot.Modules.Gambling { cgb.CreateCommand(Module.Prefix + "roll") .Description("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.\n**Usage**: $roll or $roll 7 or $roll 3d5") + " If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5") .Parameter("num", ParameterType.Optional) .Do(RollFunc()); cgb.CreateCommand(Module.Prefix + "nroll") - .Description("Rolls in a given range.\n**Usage**: `$nroll 5` (rolls 0-5) or `$nroll 5-15`") + .Description("Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`") .Parameter("range", ParameterType.Required) .Do(NRollFunc()); } diff --git a/NadekoBot/Modules/Gambling/DrawCommand.cs b/NadekoBot/Modules/Gambling/DrawCommand.cs index 0f7d92e9..35d789bf 100644 --- a/NadekoBot/Modules/Gambling/DrawCommand.cs +++ b/NadekoBot/Modules/Gambling/DrawCommand.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "draw") - .Description("Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck.\n**Usage**: $draw [x]") + .Description("Draws a card from the deck.If you supply number [x], she draws up to 5 cards from the deck. | $draw [x]") .Parameter("count", ParameterType.Optional) .Do(DrawCardFunc()); diff --git a/NadekoBot/Modules/Gambling/FlipCoinCommand.cs b/NadekoBot/Modules/Gambling/FlipCoinCommand.cs index 5c23cd5b..b28f8346 100644 --- a/NadekoBot/Modules/Gambling/FlipCoinCommand.cs +++ b/NadekoBot/Modules/Gambling/FlipCoinCommand.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Modules.Gambling internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "flip") - .Description("Flips coin(s) - heads or tails, and shows an image.\n**Usage**: `$flip` or `$flip 3`") + .Description("Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`") .Parameter("count", ParameterType.Optional) .Do(FlipCoinFunc()); } diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index d54958ce..79fa8553 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Gambling }); cgb.CreateCommand(Prefix + "$$") - .Description(string.Format("Check how much {0}s a person has. (Defaults to yourself)\n**Usage**:`{1}$$` or `{1}$$ @Someone`", + .Description(string.Format("Check how much {0}s a person has. (Defaults to yourself) |`{1}$$` or `{1}$$ @Someone`", NadekoBot.Config.CurrencyName, Prefix)) .Parameter("all", ParameterType.Unparsed) .Do(async e => @@ -94,7 +94,7 @@ namespace NadekoBot.Modules.Gambling }); cgb.CreateCommand(Prefix + "award") - .Description("Gives someone a certain amount of flowers. **Bot Owner Only!**\n**Usage**: `$award 100 @person`") + .Description("Gives someone a certain amount of flowers. **Bot Owner Only!** | `$award 100 @person`") .AddCheck(SimpleCheckers.OwnerOnly()) .Parameter("amount", ParameterType.Required) .Parameter("receiver", ParameterType.Unparsed) diff --git a/NadekoBot/Modules/Games/Commands/Leet.cs b/NadekoBot/Modules/Games/Commands/Leet.cs index 33845299..22cdedbf 100644 --- a/NadekoBot/Modules/Games/Commands/Leet.cs +++ b/NadekoBot/Modules/Games/Commands/Leet.cs @@ -297,7 +297,7 @@ namespace NadekoBot.Modules.Games.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "leet") - .Description($"Converts a text to leetspeak with 6 (1-6) severity levels\n**Usage**: {Module.Prefix}leet 3 Hello") + .Description($"Converts a text to leetspeak with 6 (1-6) severity levels | {Module.Prefix}leet 3 Hello") .Parameter("level", ParameterType.Required) .Parameter("text", ParameterType.Unparsed) .Do(async e => diff --git a/NadekoBot/Modules/Games/Commands/PollCommand.cs b/NadekoBot/Modules/Games/Commands/PollCommand.cs index 1e1c7b03..e1cd75e4 100644 --- a/NadekoBot/Modules/Games/Commands/PollCommand.cs +++ b/NadekoBot/Modules/Games/Commands/PollCommand.cs @@ -23,7 +23,7 @@ namespace NadekoBot.Modules.Games.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "poll") - .Description("Creates a poll, only person who has manage server permission can do it.\n**Usage**: >poll Question?;Answer1;Answ 2;A_3") + .Description("Creates a poll, only person who has manage server permission can do it. | >poll Question?;Answer1;Answ 2;A_3") .Parameter("allargs", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Games/Commands/TriviaCommand.cs b/NadekoBot/Modules/Games/Commands/TriviaCommand.cs index 57cc4a4a..1873c254 100644 --- a/NadekoBot/Modules/Games/Commands/TriviaCommand.cs +++ b/NadekoBot/Modules/Games/Commands/TriviaCommand.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Games.Commands cgb.CreateCommand(Module.Prefix + "t") .Description($"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." + - $"\n**Usage**:`{Module.Prefix}t nohint` or `{Module.Prefix}t 5 nohint`") + $" |`{Module.Prefix}t nohint` or `{Module.Prefix}t 5 nohint`") .Parameter("args", ParameterType.Multiple) .Do(async e => { diff --git a/NadekoBot/Modules/Games/GamesModule.cs b/NadekoBot/Modules/Games/GamesModule.cs index 22280e00..29fea7f8 100644 --- a/NadekoBot/Modules/Games/GamesModule.cs +++ b/NadekoBot/Modules/Games/GamesModule.cs @@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Games commands.ForEach(cmd => cmd.Init(cgb)); cgb.CreateCommand(Prefix + "choose") - .Description("Chooses a thing from a list of things\n**Usage**: >choose Get up;Sleep;Sleep more") + .Description("Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more") .Parameter("list", ParameterType.Unparsed) .Do(async e => { @@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Games }); cgb.CreateCommand(Prefix + "rps") - .Description("Play a game of rocket paperclip scissors with Nadeko.\n**Usage**: >rps scissors") + .Description("Play a game of rocket paperclip scissors with Nadeko. | >rps scissors") .Parameter("input", ParameterType.Required) .Do(async e => { @@ -107,7 +107,7 @@ namespace NadekoBot.Modules.Games }); cgb.CreateCommand(Prefix + "linux") - .Description($"Prints a customizable Linux interjection\n**Usage**: `{Prefix}linux Spyware Windows`") + .Description($"Prints a customizable Linux interjection | `{Prefix}linux Spyware Windows`") .Parameter("gnu", ParameterType.Required) .Parameter("linux", ParameterType.Required) .Do(async e => diff --git a/NadekoBot/Modules/Help/Commands/HelpCommand.cs b/NadekoBot/Modules/Help/Commands/HelpCommand.cs index c1157574..e80f365c 100644 --- a/NadekoBot/Modules/Help/Commands/HelpCommand.cs +++ b/NadekoBot/Modules/Help/Commands/HelpCommand.cs @@ -62,7 +62,7 @@ Version: `{NadekoStats.Instance.BotVersion}`"; helpstr += PrintCommandHelp(com); } helpstr = helpstr.Replace(NadekoBot.BotMention, "@BotName"); - helpstr = helpstr.Replace("\n**Usage**:", " | ").Replace("**Usage**:", " | ").Replace("**Description:**", " | ").Replace("\n|", " | \n"); + helpstr = helpstr.Replace(" |", " | ").Replace("**Usage**:", " | ").Replace("**Description:**", " | ").Replace("\n|", " | \n"); #if DEBUG File.WriteAllText("../../../commandlist.md", helpstr); #else @@ -74,7 +74,7 @@ Version: `{NadekoStats.Instance.BotVersion}`"; { cgb.CreateCommand(Module.Prefix + "h") .Alias(Module.Prefix + "help", NadekoBot.BotMention + " help", NadekoBot.BotMention + " h", "~h") - .Description("Either shows a help for a single command, or PMs you help link if no arguments are specified.\n**Usage**: '-h !m q' or just '-h' ") + .Description("Either shows a help for a single command, or PMs you help link if no arguments are specified. | '-h !m q' or just '-h' ") .Parameter("command", ParameterType.Unparsed) .Do(HelpFunc()); cgb.CreateCommand(Module.Prefix + "hgit") diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 5199cbb1..c8216ee0 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("next") .Alias("n") .Alias("skip") - .Description("Goes to the next song in the queue. You have to be in the same voice channel as the bot.\n**Usage**: `!m n`") + .Description("Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n`") .Do(e => { MusicPlayer musicPlayer; @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("stop") .Alias("s") - .Description("Stops the music and clears the playlist. Stays in the channel.\n**Usage**: `!m s`") + .Description("Stops the music and clears the playlist. Stays in the channel. | `!m s`") .Do(e => { MusicPlayer musicPlayer; @@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("destroy") .Alias("d") .Description("Completely stops the music and unbinds the bot from the channel. " + - "(may cause weird behaviour)\n**Usage**: `!m d`") + "(may cause weird behaviour) | `!m d`") .Do(e => { MusicPlayer musicPlayer; @@ -78,7 +78,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("pause") .Alias("p") - .Description("Pauses or Unpauses the song.\n**Usage**: `!m p`") + .Description("Pauses or Unpauses the song. | `!m p`") .Do(async e => { MusicPlayer musicPlayer; @@ -96,7 +96,7 @@ namespace NadekoBot.Modules.Music .Alias("q") .Alias("yq") .Description("Queue a song using keywords or a link. Bot will join your voice channel." + - "**You must be in a voice channel**.\n**Usage**: `!m q Dream Of Venice`") + "**You must be in a voice channel**. | `!m q Dream Of Venice`") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("soundcloudqueue") .Alias("sq") .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + - "**You must be in a voice channel**.\n**Usage**: `!m sq Dream Of Venice`") + "**You must be in a voice channel**. | `!m sq Dream Of Venice`") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -125,7 +125,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("listqueue") .Alias("lq") - .Description("Lists 15 currently queued songs per page. Default page is 1.\n**Usage**: `!m lq` or `!m lq 2`") + .Description("Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2`") .Parameter("page", ParameterType.Optional) .Do(async e => { @@ -163,7 +163,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("nowplaying") .Alias("np") - .Description("Shows the song currently playing.\n**Usage**: `!m np`") + .Description("Shows the song currently playing. | `!m np`") .Do(async e => { MusicPlayer musicPlayer; @@ -178,7 +178,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("volume") .Alias("vol") - .Description("Sets the music volume 0-100%\n**Usage**: `!m vol 50`") + .Description("Sets the music volume 0-100% | `!m vol 50`") .Parameter("val", ParameterType.Required) .Do(async e => { @@ -201,7 +201,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("defvol") .Alias("dv") .Description("Sets the default music volume when music playback is started (0-100)." + - " Persists through restarts.\n**Usage**: `!m dv 80`") + " Persists through restarts. | `!m dv 80`") .Parameter("val", ParameterType.Required) .Do(async e => { @@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("mute") .Alias("min") - .Description("Sets the music volume to 0%\n**Usage**: `!m min`") + .Description("Sets the music volume to 0% | `!m min`") .Do(e => { MusicPlayer musicPlayer; @@ -231,7 +231,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("max") - .Description("Sets the music volume to 100%.\n**Usage**: `!m max`") + .Description("Sets the music volume to 100%. | `!m max`") .Do(e => { MusicPlayer musicPlayer; @@ -243,7 +243,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("half") - .Description("Sets the music volume to 50%.\n**Usage**: `!m half`") + .Description("Sets the music volume to 50%. | `!m half`") .Do(e => { MusicPlayer musicPlayer; @@ -256,7 +256,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("shuffle") .Alias("sh") - .Description("Shuffles the current playlist.\n**Usage**: `!m sh`") + .Description("Shuffles the current playlist. | `!m sh`") .Do(async e => { MusicPlayer musicPlayer; @@ -276,7 +276,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("playlist") .Alias("pl") - .Description("Queues up to 500 songs from a youtube playlist specified by a link, or keywords.\n**Usage**: `!m pl playlist link or name`") + .Description("Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name`") .Parameter("playlist", ParameterType.Unparsed) .Do(async e => { @@ -355,7 +355,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("localplaylst") .Alias("lopl") - .Description("Queues all songs from a directory. **Bot Owner Only!**\n**Usage**: `!m lopl C:/music/classical`") + .Description("Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical`") .Parameter("directory", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -385,7 +385,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("radio").Alias("ra") - .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf\n**Usage**: `!m ra radio link here`") + .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf | `!m ra radio link here`") .Parameter("radio_link", ParameterType.Required) .Do(async e => { @@ -404,7 +404,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("local") .Alias("lo") - .Description("Queues a local file by specifying a full path. **Bot Owner Only!**\n**Usage**: `!m lo C:/music/mysong.mp3`") + .Description("Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3`") .Parameter("path", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -417,7 +417,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("move") .Alias("mv") - .Description("Moves the bot to your voice channel. (works only if music is already playing)\n**Usage**: `!m mv`") + .Description("Moves the bot to your voice channel. (works only if music is already playing) | `!m mv`") .Do(e => { MusicPlayer musicPlayer; @@ -429,7 +429,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("remove") .Alias("rm") - .Description("Remove a song by its # in the queue, or 'all' to remove whole queue.\n**Usage**: `!m rm 5`") + .Description("Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5`") .Parameter("num", ParameterType.Required) .Do(async e => { @@ -462,7 +462,7 @@ namespace NadekoBot.Modules.Music //var msRegex = new Regex(@"(?\d+)>(?\d+)", RegexOptions.Compiled); cgb.CreateCommand("movesong") .Alias("ms") - .Description($"Moves a song from one position to another.\n**Usage**: `{Prefix} ms` 5>3") + .Description($"Moves a song from one position to another. | `{Prefix} ms` 5>3") .Parameter("fromto") .Do(async e => { @@ -498,7 +498,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("setmaxqueue") .Alias("smq") - .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. \n**Usage**: `{Prefix} smq` 50 or `{Prefix} smq`") + .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. | `{Prefix} smq` 50 or `{Prefix} smq`") .Parameter("size", ParameterType.Unparsed) .Do(async e => { @@ -520,7 +520,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("cleanup") - .Description("Cleans up hanging voice connections. **Bot Owner Only!**\n**Usage**: `!m cleanup`") + .Description("Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup`") .AddCheck(SimpleCheckers.OwnerOnly()) .Do(e => { @@ -539,7 +539,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("reptcursong") .Alias("rcs") - .Description("Toggles repeat of current song.\n**Usage**: `!m rcs`") + .Description("Toggles repeat of current song. | `!m rcs`") .Do(async e => { MusicPlayer musicPlayer; @@ -557,7 +557,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("rpeatplaylst") .Alias("rpl") - .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue).\n**Usage**: `!m rpl`") + .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl`") .Do(async e => { MusicPlayer musicPlayer; @@ -568,7 +568,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("save") - .Description("Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes.\n**Usage**: `!m save classical1`") + .Description("Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1`") .Parameter("name", ParameterType.Unparsed) .Do(async e => { @@ -621,7 +621,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("load") - .Description("Loads a playlist under a certain name. \n**Usage**: `!m load classical-1`") + .Description("Loads a playlist under a certain name. | `!m load classical-1`") .Parameter("name", ParameterType.Unparsed) .Do(async e => { @@ -681,7 +681,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand("playlists") .Alias("pls") - .Description("Lists all playlists. Paginated. 20 per page. Default page is 0.\n**Usage**:`!m pls 1`") + .Description("Lists all playlists. Paginated. 20 per page. Default page is 0. |`!m pls 1`") .Parameter("num", ParameterType.Optional) .Do(e => { diff --git a/NadekoBot/Modules/NSFW/NSFWModule.cs b/NadekoBot/Modules/NSFW/NSFWModule.cs index 5ea67408..e54790e2 100644 --- a/NadekoBot/Modules/NSFW/NSFWModule.cs +++ b/NadekoBot/Modules/NSFW/NSFWModule.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.NSFW cgb.AddCheck(PermissionChecker.Instance); cgb.CreateCommand(Prefix + "hentai") - .Description("Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +)\n**Usage**: ~hentai yuri+kissing") + .Description("Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~hentai yuri+kissing") .Parameter("tag", ParameterType.Unparsed) .Do(async e => { @@ -39,7 +39,7 @@ namespace NadekoBot.Modules.NSFW await e.Channel.SendMessage("`No results.`"); }); cgb.CreateCommand(Prefix + "danbooru") - .Description("Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +)\n**Usage**: ~danbooru yuri+kissing") + .Description("Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~danbooru yuri+kissing") .Parameter("tag", ParameterType.Unparsed) .Do(async e => { @@ -51,7 +51,7 @@ namespace NadekoBot.Modules.NSFW await e.Channel.SendMessage(link).ConfigureAwait(false); }); cgb.CreateCommand(Prefix + "gelbooru") - .Description("Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +)\n**Usage**: ~gelbooru yuri+kissing") + .Description("Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~gelbooru yuri+kissing") .Parameter("tag", ParameterType.Unparsed) .Do(async e => { @@ -64,7 +64,7 @@ namespace NadekoBot.Modules.NSFW }); cgb.CreateCommand(Prefix + "rule34") - .Description("Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +)\n**Usage**: ~rule34 yuri+kissing") + .Description("Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~rule34 yuri+kissing") .Parameter("tag", ParameterType.Unparsed) .Do(async e => { @@ -76,7 +76,7 @@ namespace NadekoBot.Modules.NSFW await e.Channel.SendMessage(link).ConfigureAwait(false); }); cgb.CreateCommand(Prefix + "e621") - .Description("Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags.\n**Usage**: ~e621 yuri kissing") + .Description("Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | ~e621 yuri kissing") .Parameter("tag", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs b/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs index ebf8534a..c8e63f23 100644 --- a/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs +++ b/NadekoBot/Modules/Permissions/Commands/FilterInvitesCommand.cs @@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Permissions.Commands .Alias(Module.Prefix + "cfi") .Description("Enables or disables automatic deleting of invites on the channel." + "If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once." + - "\n**Usage**: ;cfi enable #general-chat") + " | ;cfi enable #general-chat") .Parameter("bool") .Parameter("channel", ParameterType.Optional) .Do(async e => @@ -95,7 +95,7 @@ namespace NadekoBot.Modules.Permissions.Commands cgb.CreateCommand(Module.Prefix + "srvrfilterinv") .Alias(Module.Prefix + "sfi") - .Description("Enables or disables automatic deleting of invites on the server.\n**Usage**: ;sfi disable") + .Description("Enables or disables automatic deleting of invites on the server. | ;sfi disable") .Parameter("bool") .Do(async e => { diff --git a/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs b/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs index 5607592c..3764245d 100644 --- a/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs +++ b/NadekoBot/Modules/Permissions/Commands/FilterWordsCommand.cs @@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Permissions.Commands .Alias(Module.Prefix + "cfw") .Description("Enables or disables automatic deleting of messages containing banned words on the channel." + "If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once." + - "\n**Usage**: ;cfw enable #general-chat") + " | ;cfw enable #general-chat") .Parameter("bool") .Parameter("channel", ParameterType.Optional) .Do(async e => @@ -89,7 +89,7 @@ namespace NadekoBot.Modules.Permissions.Commands cgb.CreateCommand(Module.Prefix + "addfilterword") .Alias(Module.Prefix + "afw") .Description("Adds a new word to the list of filtered words" + - "\n**Usage**: ;afw poop") + " | ;afw poop") .Parameter("word", ParameterType.Unparsed) .Do(async e => { @@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Permissions.Commands cgb.CreateCommand(Module.Prefix + "rmvfilterword") .Alias(Module.Prefix + "rfw") .Description("Removes the word from the list of filtered words" + - "\n**Usage**: ;rw poop") + " | ;rw poop") .Parameter("word", ParameterType.Unparsed) .Do(async e => { @@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Permissions.Commands cgb.CreateCommand(Module.Prefix + "lstfilterwords") .Alias(Module.Prefix + "lfw") .Description("Shows a list of filtered words" + - "\n**Usage**: ;lfw") + " | ;lfw") .Do(async e => { try @@ -152,7 +152,7 @@ namespace NadekoBot.Modules.Permissions.Commands cgb.CreateCommand(Module.Prefix + "srvrfilterwords") .Alias(Module.Prefix + "sfw") - .Description("Enables or disables automatic deleting of messages containing forbidden words on the server.\n**Usage**: ;sfw disable") + .Description("Enables or disables automatic deleting of messages containing forbidden words on the server. | ;sfw disable") .Parameter("bool") .Do(async e => { diff --git a/NadekoBot/Modules/Permissions/PermissionsModule.cs b/NadekoBot/Modules/Permissions/PermissionsModule.cs index 4a0c4b30..e210c1e2 100644 --- a/NadekoBot/Modules/Permissions/PermissionsModule.cs +++ b/NadekoBot/Modules/Permissions/PermissionsModule.cs @@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "rolepermscopy") .Alias(Prefix + "rpc") - .Description($"Copies BOT PERMISSIONS (not discord permissions) from one role to another.\n**Usage**:`{Prefix}rpc Some Role ~ Some other role`") + .Description($"Copies BOT PERMISSIONS (not discord permissions) from one role to another. |`{Prefix}rpc Some Role ~ Some other role`") .Parameter("from_to", ParameterType.Unparsed) .Do(async e => { @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "chnlpermscopy") .Alias(Prefix + "cpc") - .Description($"Copies BOT PERMISSIONS (not discord permissions) from one channel to another.\n**Usage**:`{Prefix}cpc Some Channel ~ Some other channel`") + .Description($"Copies BOT PERMISSIONS (not discord permissions) from one channel to another. |`{Prefix}cpc Some Channel ~ Some other channel`") .Parameter("from_to", ParameterType.Unparsed) .Do(async e => { @@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "usrpermscopy") .Alias(Prefix + "upc") - .Description($"Copies BOT PERMISSIONS (not discord permissions) from one role to another.\n**Usage**:`{Prefix}upc @SomeUser ~ @SomeOtherUser`") + .Description($"Copies BOT PERMISSIONS (not discord permissions) from one role to another. |`{Prefix}upc @SomeUser ~ @SomeOtherUser`") .Parameter("from_to", ParameterType.Unparsed) .Do(async e => { @@ -145,7 +145,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "verbose") .Alias(Prefix + "v") - .Description("Sets whether to show when a command/module is blocked.\n**Usage**: ;verbose true") + .Description("Sets whether to show when a command/module is blocked. | ;verbose true") .Parameter("arg", ParameterType.Required) .Do(async e => { @@ -168,7 +168,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "roleperms") .Alias(Prefix + "rp") - .Description("Shows banned permissions for a certain role. No argument means for everyone.\n**Usage**: ;rp AwesomeRole") + .Description("Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole") .Parameter("role", ParameterType.Unparsed) .Do(async e => { @@ -194,7 +194,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "chnlperms") .Alias(Prefix + "cp") - .Description("Shows banned permissions for a certain channel. No argument means for this channel.\n**Usage**: ;cp #dev") + .Description("Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev") .Parameter("channel", ParameterType.Unparsed) .Do(async e => { @@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "userperms") .Alias(Prefix + "up") - .Description("Shows banned permissions for a certain user. No argument means for yourself.\n**Usage**: ;up Kwoth") + .Description("Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth") .Parameter("user", ParameterType.Unparsed) .Do(async e => { @@ -245,7 +245,7 @@ namespace NadekoBot.Modules.Permissions .Alias(Prefix + "sm") .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) - .Description("Sets a module's permission at the server level.\n**Usage**: ;sm [module_name] enable") + .Description("Sets a module's permission at the server level. | ;sm \"module name\" enable") .Do(async e => { try @@ -269,7 +269,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "srvrcmd").Alias(Prefix + "sc") .Parameter("command", ParameterType.Required) .Parameter("bool", ParameterType.Required) - .Description("Sets a command's permission at the server level.\n**Usage**: ;sc [command_name] disable") + .Description("Sets a command's permission at the server level. | ;sc \"command name\" disable") .Do(async e => { try @@ -294,7 +294,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("role", ParameterType.Unparsed) - .Description("Sets a module's permission at the role level.\n**Usage**: ;rm [module_name] enable [role_name]") + .Description("Sets a module's permission at the role level. | ;rm \"module name\" enable MyRole") .Do(async e => { try @@ -332,7 +332,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("command", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("role", ParameterType.Unparsed) - .Description("Sets a command's permission at the role level.\n**Usage**: ;rc [command_name] disable [role_name]") + .Description("Sets a command's permission at the role level. | ;rc \"command name\" disable MyRole") .Do(async e => { try @@ -370,7 +370,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("channel", ParameterType.Unparsed) - .Description("Sets a module's permission at the channel level.\n**Usage**: ;cm [module_name] enable [channel_name]") + .Description("Sets a module's permission at the channel level. | ;cm \"module name\" enable SomeChannel") .Do(async e => { try @@ -413,7 +413,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("command", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("channel", ParameterType.Unparsed) - .Description("Sets a command's permission at the channel level.\n**Usage**: ;cc [command_name] enable [channel_name]") + .Description("Sets a command's permission at the channel level. | ;cc \"command name\" enable SomeChannel") .Do(async e => { try @@ -451,7 +451,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("user", ParameterType.Unparsed) - .Description("Sets a module's permission at the user level.\n**Usage**: ;um [module_name] enable [user_name]") + .Description("Sets a module's permission at the user level. | ;um \"module name\" enable SomeUsername") .Do(async e => { try @@ -477,7 +477,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("command", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("user", ParameterType.Unparsed) - .Description("Sets a command's permission at the user level.\n**Usage**: ;uc [command_name] enable [user_name]") + .Description("Sets a command's permission at the user level. | ;uc \"command name\" enable SomeUsername") .Do(async e => { try @@ -501,7 +501,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "allsrvrmdls").Alias(Prefix + "asm") .Parameter("bool", ParameterType.Required) - .Description("Sets permissions for all modules at the server level.\n**Usage**: ;asm [enable/disable]") + .Description("Sets permissions for all modules at the server level. | ;asm [enable/disable]") .Do(async e => { try @@ -527,7 +527,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "allsrvrcmds").Alias(Prefix + "asc") .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) - .Description("Sets permissions for all commands from a certain module at the server level.\n**Usage**: ;asc [module_name] [enable/disable]") + .Description("Sets permissions for all commands from a certain module at the server level. | ;asc \"module name\" [enable/disable]") .Do(async e => { try @@ -554,7 +554,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "allchnlmdls").Alias(Prefix + "acm") .Parameter("bool", ParameterType.Required) .Parameter("channel", ParameterType.Unparsed) - .Description("Sets permissions for all modules at the channel level.\n**Usage**: ;acm [enable/disable] [channel_name]") + .Description("Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel") .Do(async e => { try @@ -583,7 +583,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("channel", ParameterType.Unparsed) - .Description("Sets permissions for all commands from a certain module at the channel level.\n**Usage**: ;acc [module_name] [enable/disable] [channel_name]") + .Description("Sets permissions for all commands from a certain module at the channel level. | ;acc \"module name\" [enable/disable] SomeChannel") .Do(async e => { try @@ -610,7 +610,7 @@ namespace NadekoBot.Modules.Permissions cgb.CreateCommand(Prefix + "allrolemdls").Alias(Prefix + "arm") .Parameter("bool", ParameterType.Required) .Parameter("role", ParameterType.Unparsed) - .Description("Sets permissions for all modules at the role level.\n**Usage**: ;arm [enable/disable] [role_name]") + .Description("Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole") .Do(async e => { try @@ -638,7 +638,7 @@ namespace NadekoBot.Modules.Permissions .Parameter("module", ParameterType.Required) .Parameter("bool", ParameterType.Required) .Parameter("role", ParameterType.Unparsed) - .Description("Sets permissions for all commands from a certain module at the role level.\n**Usage**: ;arc [module_name] [enable/disable] [role_name]") + .Description("Sets permissions for all commands from a certain module at the role level. | ;arc \"module name\" [enable/disable] MyRole") .Do(async e => { try @@ -678,7 +678,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "ubl") - .Description("Blacklists a mentioned user.\n**Usage**: ;ubl [user_mention]") + .Description("Blacklists a mentioned user. | ;ubl [user_mention]") .Parameter("user", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -694,7 +694,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "uubl") - .Description($"Unblacklists a mentioned user.\n**Usage**: {Prefix}uubl [user_mention]") + .Description($"Unblacklists a mentioned user. | {Prefix}uubl [user_mention]") .Parameter("user", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -717,7 +717,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "cbl") - .Description("Blacklists a mentioned channel (#general for example).\n**Usage**: ;cbl [channel_mention]") + .Description("Blacklists a mentioned channel (#general for example). | ;cbl #some_channel") .Parameter("channel", ParameterType.Unparsed) .Do(async e => { @@ -732,7 +732,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "cubl") - .Description("Unblacklists a mentioned channel (#general for example).\n**Usage**: ;cubl [channel_mention]") + .Description("Unblacklists a mentioned channel (#general for example). | ;cubl #some_channel") .Parameter("channel", ParameterType.Unparsed) .Do(async e => { @@ -747,7 +747,7 @@ namespace NadekoBot.Modules.Permissions }); cgb.CreateCommand(Prefix + "sbl") - .Description("Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY**\n**Usage**: ;sbl [servername/serverid]") + .Description("Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | ;sbl [servername/serverid]") .Parameter("server", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => diff --git a/NadekoBot/Modules/Pokemon/PokemonModule.cs b/NadekoBot/Modules/Pokemon/PokemonModule.cs index 0e585b6d..9d9c4a29 100644 --- a/NadekoBot/Modules/Pokemon/PokemonModule.cs +++ b/NadekoBot/Modules/Pokemon/PokemonModule.cs @@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Pokemon }); cgb.CreateCommand(Prefix + "heal") - .Description($"Heals someone. Revives those that fainted. Costs a {NadekoBot.Config.CurrencyName} \n**Usage**:{Prefix}revive @someone") + .Description($"Heals someone. Revives those that fainted. Costs a {NadekoBot.Config.CurrencyName} |{Prefix}revive @someone") .Parameter("target", ParameterType.Unparsed) .Do(async e => { @@ -263,7 +263,7 @@ namespace NadekoBot.Modules.Pokemon }); cgb.CreateCommand(Prefix + "type") - .Description($"Get the poketype of the target.\n**Usage**: {Prefix}type @someone") + .Description($"Get the poketype of the target. | {Prefix}type @someone") .Parameter("target", ParameterType.Unparsed) .Do(async e => { @@ -282,7 +282,7 @@ namespace NadekoBot.Modules.Pokemon }); cgb.CreateCommand(Prefix + "settype") - .Description($"Set your poketype. Costs a {NadekoBot.Config.CurrencyName}.\n**Usage**: {Prefix}settype fire") + .Description($"Set your poketype. Costs a {NadekoBot.Config.CurrencyName}. | {Prefix}settype fire") .Parameter("targetType", ParameterType.Unparsed) .Do(async e => { diff --git a/NadekoBot/Modules/Searches/Commands/EvalCommand.cs b/NadekoBot/Modules/Searches/Commands/EvalCommand.cs index 57f45342..0e815f71 100644 --- a/NadekoBot/Modules/Searches/Commands/EvalCommand.cs +++ b/NadekoBot/Modules/Searches/Commands/EvalCommand.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches.Commands { cgb.CreateCommand(Module.Prefix + "calculate") .Alias(Module.Prefix + "calc") - .Description("Evaluate a mathematical expression.\n**Usage**: ~calc 1+1") + .Description("Evaluate a mathematical expression. | ~calc 1+1") .Parameter("expression", ParameterType.Unparsed) .Do(EvalFunc()); } diff --git a/NadekoBot/Modules/Searches/Commands/LoLCommands.cs b/NadekoBot/Modules/Searches/Commands/LoLCommands.cs index 13f92741..bbe2eca9 100644 --- a/NadekoBot/Modules/Searches/Commands/LoLCommands.cs +++ b/NadekoBot/Modules/Searches/Commands/LoLCommands.cs @@ -75,7 +75,7 @@ namespace NadekoBot.Modules.Searches.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "lolchamp") - .Description("Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role.\n**Usage**:~lolchamp Riven or ~lolchamp Annie sup") + .Description("Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. |~lolchamp Riven or ~lolchamp Annie sup") .Parameter("champ", ParameterType.Required) .Parameter("position", ParameterType.Unparsed) .Do(async e => diff --git a/NadekoBot/Modules/Searches/Commands/OsuCommands.cs b/NadekoBot/Modules/Searches/Commands/OsuCommands.cs index 8af28d0f..451e9a13 100644 --- a/NadekoBot/Modules/Searches/Commands/OsuCommands.cs +++ b/NadekoBot/Modules/Searches/Commands/OsuCommands.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Searches.Commands internal override void Init(CommandGroupBuilder cgb) { cgb.CreateCommand(Module.Prefix + "osu") - .Description("Shows osu stats for a player.\n**Usage**: `~osu Name` or `~osu Name taiko`") + .Description("Shows osu stats for a player. | `~osu Name` or `~osu Name taiko`") .Parameter("usr", ParameterType.Required) .Parameter("mode", ParameterType.Unparsed) .Do(async e => @@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Searches.Commands }); cgb.CreateCommand(Module.Prefix + "osu b") - .Description("Shows information about an osu beatmap.\n**Usage**:~osu b https://osu.ppy.sh/s/127712") + .Description("Shows information about an osu beatmap. |~osu b https://osu.ppy.sh/s/127712") .Parameter("map", ParameterType.Unparsed) .Do(async e => { @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Searches.Commands }); cgb.CreateCommand(Module.Prefix + "osu top5") - .Description("Displays a user's top 5 plays. \n**Usage**:~osu top5 Name") + .Description("Displays a user's top 5 plays. |~osu top5 Name") .Parameter("usr", ParameterType.Required) .Parameter("mode", ParameterType.Unparsed) .Do(async e => diff --git a/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs b/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs index 5895c932..d5c6c755 100644 --- a/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs +++ b/NadekoBot/Modules/Searches/Commands/StreamNotifications.cs @@ -122,7 +122,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "hitbox") .Alias(Module.Prefix + "hb") .Description("Notifies this channel when a certain user starts streaming." + - "\n**Usage**: ~hitbox SomeStreamer") + " | ~hitbox SomeStreamer") .Parameter("username", ParameterType.Unparsed) .AddCheck(SimpleCheckers.ManageServer()) .Do(TrackStream(StreamNotificationConfig.StreamType.Hitbox)); @@ -130,7 +130,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "twitch") .Alias(Module.Prefix + "tw") .Description("Notifies this channel when a certain user starts streaming." + - "\n**Usage**: ~twitch SomeStreamer") + " | ~twitch SomeStreamer") .AddCheck(SimpleCheckers.ManageServer()) .Parameter("username", ParameterType.Unparsed) .Do(TrackStream(StreamNotificationConfig.StreamType.Twitch)); @@ -138,7 +138,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "beam") .Alias(Module.Prefix + "bm") .Description("Notifies this channel when a certain user starts streaming." + - "\n**Usage**: ~beam SomeStreamer") + " | ~beam SomeStreamer") .AddCheck(SimpleCheckers.ManageServer()) .Parameter("username", ParameterType.Unparsed) .Do(TrackStream(StreamNotificationConfig.StreamType.Beam)); @@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "checkhitbox") .Alias(Module.Prefix + "chhb") .Description("Checks if a certain user is streaming on the hitbox platform." + - "\n**Usage**: ~chhb SomeStreamer") + " | ~chhb SomeStreamer") .Parameter("username", ParameterType.Unparsed) .AddCheck(SimpleCheckers.ManageServer()) .Do(async e => @@ -175,7 +175,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "checktwitch") .Alias(Module.Prefix + "chtw") .Description("Checks if a certain user is streaming on the twitch platform." + - "\n**Usage**: ~chtw SomeStreamer") + " | ~chtw SomeStreamer") .AddCheck(SimpleCheckers.ManageServer()) .Parameter("username", ParameterType.Unparsed) .Do(async e => @@ -204,7 +204,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "checkbeam") .Alias(Module.Prefix + "chbm") .Description("Checks if a certain user is streaming on the beam platform." + - "\n**Usage**: ~chbm SomeStreamer") + " | ~chbm SomeStreamer") .AddCheck(SimpleCheckers.ManageServer()) .Parameter("username", ParameterType.Unparsed) .Do(async e => @@ -233,7 +233,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "removestream") .Alias(Module.Prefix + "rms") .Description("Removes notifications of a certain streamer on this channel." + - "\n**Usage**: ~rms SomeGuy") + " | ~rms SomeGuy") .AddCheck(SimpleCheckers.ManageServer()) .Parameter("username", ParameterType.Unparsed) .Do(async e => @@ -261,7 +261,7 @@ namespace NadekoBot.Modules.Searches.Commands cgb.CreateCommand(Module.Prefix + "liststreams") .Alias(Module.Prefix + "ls") .Description("Lists all streams you are following on this server." + - "\n**Usage**: ~ls") + " | ~ls") .Do(async e => { diff --git a/NadekoBot/Modules/Searches/SearchesModule.cs b/NadekoBot/Modules/Searches/SearchesModule.cs index 8b03984d..8761ce69 100644 --- a/NadekoBot/Modules/Searches/SearchesModule.cs +++ b/NadekoBot/Modules/Searches/SearchesModule.cs @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Searches commands.ForEach(cmd => cmd.Init(cgb)); cgb.CreateCommand(Prefix + "we") - .Description($"Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations.\n**Usage**: {Prefix}we Moscow RF") + .Description($"Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | {Prefix}we Moscow RF") .Parameter("city", ParameterType.Required) .Parameter("country", ParameterType.Required) .Do(async e => @@ -157,7 +157,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "i") - .Description("Pulls the first image found using a search parameter. Use ~ir for different results.\n**Usage**: ~i cute kitten") + .Description("Pulls the first image found using a search parameter. Use ~ir for different results. | ~i cute kitten") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -183,7 +183,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "ir") - .Description("Pulls a random image using a search parameter.\n**Usage**: ~ir cute kitten") + .Description("Pulls a random image using a search parameter. | ~ir cute kitten") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -220,7 +220,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "hs") - .Description("Searches for a Hearthstone card and shows its image. Takes a while to complete.\n**Usage**:~hs Ysera") + .Description("Searches for a Hearthstone card and shows its image. Takes a while to complete. |~hs Ysera") .Parameter("name", ParameterType.Unparsed) .Do(async e => { @@ -261,7 +261,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "ud") - .Description("Searches Urban Dictionary for a word.\n**Usage**:~ud Pineapple") + .Description("Searches Urban Dictionary for a word. |~ud Pineapple") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -290,7 +290,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); // thanks to Blaubeerwald cgb.CreateCommand(Prefix + "#") - .Description("Searches Tagdef.com for a hashtag.\n**Usage**:~# ff") + .Description("Searches Tagdef.com for a hashtag. |~# ff") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -404,7 +404,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "safebooru") - .Description("Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +)\n**Usage**: ~safebooru yuri+kissing") + .Description("Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~safebooru yuri+kissing") .Parameter("tag", ParameterType.Unparsed) .Do(async e => { @@ -431,7 +431,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "clr") - .Description("Shows you what color corresponds to that hex.\n**Usage**: `~clr 00ff00`") + .Description("Shows you what color corresponds to that hex. | `~clr 00ff00`") .Parameter("color", ParameterType.Unparsed) .Do(async e => { @@ -480,7 +480,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 cgb.CreateCommand(Prefix + "av") .Alias(Prefix + "avatar") .Parameter("mention", ParameterType.Required) - .Description("Shows a mentioned person's avatar.\n**Usage**: ~av @X") + .Description("Shows a mentioned person's avatar. | ~av @X") .Do(async e => { var usr = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault(); diff --git a/NadekoBot/Modules/Translator/TranslateCommand.cs b/NadekoBot/Modules/Translator/TranslateCommand.cs index e4512980..9663c7bc 100644 --- a/NadekoBot/Modules/Translator/TranslateCommand.cs +++ b/NadekoBot/Modules/Translator/TranslateCommand.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Translator { cgb.CreateCommand(Module.Prefix + "translate") .Alias(Module.Prefix + "trans") - .Description($"Translates from>to text. From the given language to the destiation language.\n**Usage**: {Module.Prefix}trans en>fr Hello") + .Description($"Translates from>to text. From the given language to the destiation language. | {Module.Prefix}trans en>fr Hello") .Parameter("langs", ParameterType.Required) .Parameter("text", ParameterType.Unparsed) .Do(TranslateFunc()); diff --git a/NadekoBot/Modules/Trello/TrelloModule.cs b/NadekoBot/Modules/Trello/TrelloModule.cs index fc30deed..5a98fe40 100644 --- a/NadekoBot/Modules/Trello/TrelloModule.cs +++ b/NadekoBot/Modules/Trello/TrelloModule.cs @@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Trello cgb.CreateCommand(Prefix + "bind") .Description("Bind a trello bot to a single channel. " + "You will receive notifications from your board when something is added or edited." + - "\n**Usage**: bind [board_id]") + " | bind [board_id]") .Parameter("board_id", Discord.Commands.ParameterType.Required) .Do(async e => { diff --git a/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 4447537a..7d14a16f 100644 --- a/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Utility.Commands { cgb.CreateCommand(Module.Prefix + "serverinfo") .Alias(Module.Prefix + "sinfo") - .Description($"Shows info about the server the bot is on. If no channel is supplied, it defaults to current one.\n**Usage**:{Module.Prefix}sinfo Some Server") + .Description($"Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. |{Module.Prefix}sinfo Some Server") .Parameter("server", ParameterType.Optional) .Do(async e => { @@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Utility.Commands cgb.CreateCommand(Module.Prefix + "channelinfo") .Alias(Module.Prefix + "cinfo") - .Description($"Shows info about the channel. If no channel is supplied, it defaults to current one.\n**Usage**:{Module.Prefix}cinfo #some-channel") + .Description($"Shows info about the channel. If no channel is supplied, it defaults to current one. |{Module.Prefix}cinfo #some-channel") .Parameter("channel", ParameterType.Optional) .Do(async e => { @@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Utility.Commands cgb.CreateCommand(Module.Prefix + "userinfo") .Alias(Module.Prefix + "uinfo") - .Description($"Shows info about the user. If no user is supplied, it defaults a user running the command.\n**Usage**:{Module.Prefix}uinfo @SomeUser") + .Description($"Shows info about the user. If no user is supplied, it defaults a user running the command. |{Module.Prefix}uinfo @SomeUser") .Parameter("user", ParameterType.Optional) .Do(async e => { diff --git a/NadekoBot/Modules/Utility/Commands/Remind.cs b/NadekoBot/Modules/Utility/Commands/Remind.cs index 0ce58d6f..dabd4158 100644 --- a/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -88,7 +88,7 @@ namespace NadekoBot.Modules.Utility.Commands .Description("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. " + - "\n**Usage**: `.remind me 1d5h Do something` or `.remind #general Start now!`") + " | `.remind me 1d5h Do something` or `.remind #general Start now!`") .Parameter("meorchannel", ParameterType.Required) .Parameter("time", ParameterType.Required) .Parameter("message", ParameterType.Unparsed) diff --git a/commandlist.md b/commandlist.md index 8529b6e2..3a9cce39 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,9 +2,9 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6036.24599` +Version: `NadekoBot v0.9.6036.32870` ### Help -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `-h`, `-help`, `@BotName help`, `@BotName h`, `~h` | Either shows a help for a single command, or PMs you help link if no arguments are specified. | '-h !m q' or just '-h' `-hgit` | Generates the commandlist.md file. **Bot Owner Only!** @@ -14,7 +14,7 @@ Command and aliases | Description | Usage `-commands`, `.commands` | List all of the bot's commands from a certain module. ### Administration -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `.grdel` | Toggles automatic deletion of greet and bye messages. `.greet` | Toggles anouncements on the current channel when someone joins the server. @@ -47,7 +47,7 @@ Command and aliases | Description | Usage `.addcustreact`, `.acr` | Add a custom reaction. Guide here: **Bot Owner Only!** | .acr "hello" I love saying hello to %user% `.listcustreact`, `.lcr` | Lists custom reactions (paginated with 30 commands per page). Use 'all' instead of page number to get all custom reactions DM-ed to you. | .lcr 1 `.showcustreact`, `.scr` | Shows all possible responses from a single custom reaction. | .scr %mention% bb -`.editcustreact`, `.ecr` | Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `.ecr "%mention% disguise" 2 Test 123` +`.editcustreact`, `.ecr` | Edits a custom reaction, arguments are custom reactions name, index to change, and a (multiword) message **Bot Owner Only** | `.ecr "%mention% disguise" 2 Test 123` `.delcustreact`, `.dcr` | Deletes a custom reaction with given name (and index) `.autoassignrole`, `.aar` | Automaticaly assigns a specified role to every user who joins the server. Type `.aar` to disable, `.aar Role Name` to enable `.leave` | Makes Nadeko leave the server. Either name or id required. | `.leave 123123123331` @@ -87,10 +87,10 @@ Command and aliases | Description | Usage `.donadd` | Add a donator to the database. `.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | .announce Useless spam `.leave` | Leaves a server with a supplied ID. | `.leave 493243292839` -`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150` +`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150` ### Utility -Command and aliases | Description | Usage +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 Start now!` `.remindmsg` | 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!** @@ -108,7 +108,7 @@ Command and aliases | Description | Usage `.roles` | List all roles on this server or a single user if specified. ### Permissions -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `;chnlfilterinv`, `;cfi` | Enables or disables automatic deleting of invites on the channel.If no channel supplied, it will default to current one. Use ALL to apply to all existing channels at once. | ;cfi enable #general-chat `;srvrfilterinv`, `;sfi` | Enables or disables automatic deleting of invites on the server. | ;sfi disable @@ -126,28 +126,28 @@ Command and aliases | Description | Usage `;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole `;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev `;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth -`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | ;sm [module_name] enable -`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | ;sc [command_name] disable -`;rolemdl`, `;rm` | Sets a module's permission at the role level. | ;rm [module_name] enable [role_name] -`;rolecmd`, `;rc` | Sets a command's permission at the role level. | ;rc [command_name] disable [role_name] -`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | ;cm [module_name] enable [channel_name] -`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | ;cc [command_name] enable [channel_name] -`;usrmdl`, `;um` | Sets a module's permission at the user level. | ;um [module_name] enable [user_name] -`;usrcmd`, `;uc` | Sets a command's permission at the user level. | ;uc [command_name] enable [user_name] +`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | ;sm "module name" enable +`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | ;sc "command name" disable +`;rolemdl`, `;rm` | Sets a module's permission at the role level. | ;rm "module name" enable MyRole +`;rolecmd`, `;rc` | Sets a command's permission at the role level. | ;rc "command name" disable MyRole +`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | ;cm "module name" enable SomeChannel +`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | ;cc "command name" enable SomeChannel +`;usrmdl`, `;um` | Sets a module's permission at the user level. | ;um "module name" enable SomeUsername +`;usrcmd`, `;uc` | Sets a command's permission at the user level. | ;uc "command name" enable SomeUsername `;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | ;asm [enable/disable] -`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | ;asc [module_name] [enable/disable] -`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | ;acm [enable/disable] [channel_name] -`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | ;acc [module_name] [enable/disable] [channel_name] -`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | ;arm [enable/disable] [role_name] -`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | ;arc [module_name] [enable/disable] [role_name] +`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | ;asc "module name" [enable/disable] +`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel +`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | ;acc "module name" [enable/disable] SomeChannel +`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole +`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | ;arc "module name" [enable/disable] MyRole `;ubl` | Blacklists a mentioned user. | ;ubl [user_mention] `;uubl` | Unblacklists a mentioned user. | ;uubl [user_mention] -`;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl [channel_mention] -`;cubl` | Unblacklists a mentioned channel (#general for example). | ;cubl [channel_mention] +`;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl #some_channel +`;cubl` | Unblacklists a mentioned channel (#general for example). | ;cubl #some_channel `;sbl` | Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | ;sbl [servername/serverid] ### Conversations -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `..` | Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message `...` | Shows a random quote with a specified name. | .. abc @@ -162,7 +162,7 @@ Command and aliases | Description | Usage `@BotName ab` | Try to get 'abalabahaha' ### Gambling -Command and aliases | Description | Usage +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 [x] `$shuffle`, `$sh` | Reshuffles all cards back into the deck. @@ -177,7 +177,7 @@ Command and aliases | Description | Usage `$leaderboard`, `$lb` | ### Games -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `>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 nohint` or `>t 5 nohint` `>tl` | Shows a current trivia leaderboard. @@ -189,7 +189,7 @@ Command and aliases | Description | Usage `>pollend` | Stops active poll on this server and prints the results in this channel. `>pick` | Picks a flower planted in this channel. `>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) -`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60` +`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60` `>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello `>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more `>8ball` | Ask the 8ball a yes/no question. @@ -197,7 +197,7 @@ Command and aliases | Description | Usage `>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows` ### Music -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `!m next`, `!m n`, `!m skip` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n` `!m stop`, `!m s` | Stops the music and clears the playlist. Stays in the channel. | `!m s` @@ -214,7 +214,7 @@ Command and aliases | Description | Usage `!m half` | Sets the music volume to 50%. | `!m half` `!m shuffle`, `!m sh` | Shuffles the current playlist. | `!m sh` `!m playlist`, `!m pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name` -`!m soundcloudpl`, `!m scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` +`!m soundcloudpl`, `!m scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` `!m localplaylst`, `!m lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical` `!m radio`, `!m ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf | `!m ra radio link here` `!m local`, `!m lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3` @@ -228,13 +228,13 @@ Command and aliases | Description | Usage `!m save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1` `!m load` | Loads a playlist under a certain name. | `!m load classical-1` `!m playlists`, `!m pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!m pls 1` -`!m deleteplaylist`, `!m delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5` +`!m deleteplaylist`, `!m delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5` `!m goto` | Goes to a specific time in seconds in a song. `!m getlink`, `!m gl` | Shows a link to the currently playing song. `!m autoplay`, `!m ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) ### Searches -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `~lolchamp` | Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. | ~lolchamp Riven or ~lolchamp Annie sup `~lolban` | Shows top 6 banned champions ordered by ban rate. Ban these champions and you will be Plat 5 in no time. @@ -256,7 +256,7 @@ Command and aliases | Description | Usage `~pokemon`, `~poke` | Searches for a pokemon. `~pokemonability`, `~pokeab` | Searches for a pokemon ability. `~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ -`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` +`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` `~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | ~we Moscow RF `~yt` | Searches youtubes and shows the first result `~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result. @@ -284,7 +284,7 @@ Command and aliases | Description | Usage `~av`, `~avatar` | Shows a mentioned person's avatar. | ~av @X ### NSFW -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `~hentai` | Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~hentai yuri+kissing `~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~danbooru yuri+kissing @@ -296,7 +296,7 @@ Command and aliases | Description | Usage `~butts`, `~ass`, `~butt` | Real adult content. ### ClashOfClans -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `,createwar`, `,cw` | Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. | ,cw 15 The Enemy Clan `,startwar`, `,sw` | Starts a war with a given number. @@ -309,22 +309,22 @@ Command and aliases | Description | Usage `,endwar`, `,ew` | Ends the war with a given index. | ,ew [war_number] ### Pokegame -Command and aliases | Description | Usage +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 `>heal` | Heals someone. Revives those that fainted. Costs a NadekoFlower | >revive @someone `>type` | Get the poketype of the target. | >type @someone `>settype` | Set your poketype. Costs a NadekoFlower. | >settype fire ### Translator -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `~translate`, `~trans` | Translates from>to text. From the given language to the destiation language. | ~trans en>fr Hello `~translangs` | List the valid languages for translation. ### Customreactions -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `\o\` | Custom reaction. | \o\ `/o/` | Custom reaction. | /o/ @@ -343,7 +343,7 @@ Command and aliases | Description | Usage `@BotName disguise`, `<@!119777021319577610> disguise` | Custom reaction. | %mention% disguise ### Trello -Command and aliases | Description | Usage +Command and aliases | Description | Usage ----------------|--------------|------- `trello bind` | Bind a trello bot to a single channel. You will receive notifications from your board when something is added or edited. | bind [board_id] `trello unbind` | Unbinds a bot from the channel and board. From bb3f8797297ae398b5e6a2d576e834c03585d376 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Mon, 11 Jul 2016 18:33:50 +0200 Subject: [PATCH 26/89] Prettier `-h command` command-specific help, and made it so it works with new | instead of usage --- NadekoBot/Modules/Help/Commands/HelpCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Help/Commands/HelpCommand.cs b/NadekoBot/Modules/Help/Commands/HelpCommand.cs index e80f365c..2b4ad3b8 100644 --- a/NadekoBot/Modules/Help/Commands/HelpCommand.cs +++ b/NadekoBot/Modules/Help/Commands/HelpCommand.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Classes.Help.Commands .FirstOrDefault(c => c.Text.ToLowerInvariant().Equals(comToFind) || c.Aliases.Select(a => a.ToLowerInvariant()).Contains(comToFind)); if (com != null) - await e.Channel.SendMessage($"`Help for '{com.Text}':` {com.Description}").ConfigureAwait(false); + await e.Channel.SendMessage($"**__Help for `{com.Text}`__ / __`{("" + com.Aliases.FirstOrDefault() + "" ?? "")}`__**\n**Desc:** {com.Description.Replace("|", "\n**Usage:**")}").ConfigureAwait(false); }).ConfigureAwait(false); }; public static string HelpString { From 3548b4ecbf647907c75163edf8639c087e6df4f1 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 12 Jul 2016 05:00:14 +0200 Subject: [PATCH 27/89] Update linuxsetup with correct certmgr command instead of mozroots --- LinuxSetup.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/LinuxSetup.md b/LinuxSetup.md index 14152c90..2126fe44 100644 --- a/LinuxSetup.md +++ b/LinuxSetup.md @@ -121,12 +121,10 @@ Note if the command is not be initiated, hit **Enter** ######NOW WE NEED TO IMPORT SOME DISCORD CERTS **13)** -
mozroots --import --ask-remove --machine
-
+`certmgr -ssl https://discordapp.com` **14)** -
certmgr --ssl https://gateway.discord.gg
-
+`certmgr --ssl https://gateway.discord.gg` Type `yes` and hit Enter **(three times - as it will ask for three times)** From bce18642615e992d695de005641fe382bc3a4746 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 12 Jul 2016 12:55:35 +0200 Subject: [PATCH 28/89] deleted tests projects, will readd it when i actually write some tests --- NadekoBot.sln | 14 +---- Tests/Properties/AssemblyInfo.cs | 36 ------------- Tests/TestCards.cs | 45 ---------------- Tests/Tests.csproj | 90 -------------------------------- 4 files changed, 2 insertions(+), 183 deletions(-) delete mode 100644 Tests/Properties/AssemblyInfo.cs delete mode 100644 Tests/TestCards.cs delete mode 100644 Tests/Tests.csproj diff --git a/NadekoBot.sln b/NadekoBot.sln index 096b6db8..155512c4 100644 --- a/NadekoBot.sln +++ b/NadekoBot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.25123.0 +VisualStudioVersion = 14.0.25420.1 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NadekoBot", "NadekoBot\NadekoBot.csproj", "{27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}" EndProject @@ -13,13 +13,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Modules", "disc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Discord.Net.Commands", "discord.net\src\Discord.Net.Commands.Net45\Discord.Net.Commands.csproj", "{1B5603B4-6F8F-4289-B945-7BAAE523D740}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{45B2545D-C612-4919-B34C-D65EA1371C51}" -EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution - discord.net\src\Discord.Net.Shared\Discord.Net.Shared.projitems*{7bfef748-b934-4621-9b11-6302e3a9f6b3}*SharedItemsImports = 4 discord.net\src\Discord.Net.Shared\Discord.Net.Shared.projitems*{1b5603b4-6f8f-4289-b945-7baae523d740}*SharedItemsImports = 4 discord.net\src\Discord.Net.Shared\Discord.Net.Shared.projitems*{3091164f-66ae-4543-a63d-167c1116241d}*SharedItemsImports = 4 + discord.net\src\Discord.Net.Shared\Discord.Net.Shared.projitems*{7bfef748-b934-4621-9b11-6302e3a9f6b3}*SharedItemsImports = 4 discord.net\src\Discord.Net.Shared\Discord.Net.Shared.projitems*{8d71a857-879a-4a10-859e-5ff824ed6688}*SharedItemsImports = 4 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -69,14 +67,6 @@ Global {1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.Build.0 = Release|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.Debug|Any CPU.Build.0 = Debug|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.FullDebug|Any CPU.Build.0 = Debug|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.Release|Any CPU.ActiveCfg = Release|Any CPU - {45B2545D-C612-4919-B34C-D65EA1371C51}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Tests/Properties/AssemblyInfo.cs b/Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 78a9225b..00000000 --- a/Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Tests")] -[assembly: AssemblyCopyright("Copyright © 2016")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("45b2545d-c612-4919-b34c-d65ea1371c51")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tests/TestCards.cs b/Tests/TestCards.cs deleted file mode 100644 index f346fc37..00000000 --- a/Tests/TestCards.cs +++ /dev/null @@ -1,45 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using static NadekoBot.Modules.Gambling.Helpers.Cards; - -namespace Tests -{ - [TestClass] - public class TestCards - { - [TestMethod] - public void TestHandValues() - { - var setting1 = new List { - new Card(CardSuit.Clubs,10), - new Card(CardSuit.Clubs,10), - new Card(CardSuit.Clubs,10), - new Card(CardSuit.Clubs,11), - new Card(CardSuit.Diamonds,12), - }; - var result1 = "Three Of A Kind"; - - var setting2 = new List { - new Card(CardSuit.Clubs,1), - new Card(CardSuit.Hearts,2), - new Card(CardSuit.Clubs,3), - new Card(CardSuit.Spades,4), - new Card(CardSuit.Diamonds,5), - }; - var result2 = "Straight"; - - var setting3 = new List { - new Card(CardSuit.Diamonds,10), - new Card(CardSuit.Diamonds,11), - new Card(CardSuit.Diamonds,12), - new Card(CardSuit.Diamonds,13), - new Card(CardSuit.Diamonds,1), - }; - var result3 = "Royal Flush"; - - Assert.AreEqual(GetHandValue(setting1), result1); - Assert.AreEqual(GetHandValue(setting2), result2); - Assert.AreEqual(GetHandValue(setting3), result3); - } - } -} diff --git a/Tests/Tests.csproj b/Tests/Tests.csproj deleted file mode 100644 index 2a99410a..00000000 --- a/Tests/Tests.csproj +++ /dev/null @@ -1,90 +0,0 @@ - - - - Debug - AnyCPU - {45B2545D-C612-4919-B34C-D65EA1371C51} - Library - Properties - Tests - Tests - v4.6 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 10.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - {27a886f5-cdda-4f4a-81ee-6dafcce9de46} - NadekoBot - - - - - - - False - - - False - - - False - - - False - - - - - - - - From a3754f8210c994a824b263a1a8db43a810e2dcc7 Mon Sep 17 00:00:00 2001 From: miraai Date: Tue, 12 Jul 2016 21:19:33 +0200 Subject: [PATCH 29/89] Update ComprehensiveGuide.md --- ComprehensiveGuide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ComprehensiveGuide.md b/ComprehensiveGuide.md index a66c3d5f..2bf7fe74 100644 --- a/ComprehensiveGuide.md +++ b/ComprehensiveGuide.md @@ -47,3 +47,4 @@ ________________________________________________________________________________ - On the left tab, access Credentials. There will be a line saying "If you wish to skip this step and create an API key, client ID or service account." Click on API Key, and then Server Key in the new window that appears. Enter in a name for the server key. A new window will appear with your Google API key. Copy the key. - Open up credentials.json. For "GoogleAPIKey", fill in with the new key. - Go to (https://soundcloud.com/you/apps/new). Enter a name for the app and create it. You will see a page with the title of your app, and a field labeled Client ID. Copy the ID. In credentials.json, fill in "SoundcloudClientID" with the copied ID. +- Restart your computer. From 0aca439ddb1dc71b48ffa6548e8394c54884d652 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 14 Jul 2016 05:15:06 +0200 Subject: [PATCH 30/89] Show currency sign when taking, #403 --- NadekoBot/Classes/FlowersHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Classes/FlowersHandler.cs b/NadekoBot/Classes/FlowersHandler.cs index d41435c3..4d7f3676 100644 --- a/NadekoBot/Classes/FlowersHandler.cs +++ b/NadekoBot/Classes/FlowersHandler.cs @@ -43,7 +43,7 @@ namespace NadekoBot.Classes Value = -amount, }); - await u.SendMessage("👎`Bot owner has taken:" + amount + " from you.`").ConfigureAwait(false); + await u.SendMessage($"👎`Bot owner has taken:{amount}{NadekoBot.Config.CurrencySign} from you.`").ConfigureAwait(false); return true; } } From 06a156d73f75949c8547e04ecb9ea404c1fe0a05 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 14 Jul 2016 05:45:45 +0200 Subject: [PATCH 31/89] removed @bot slm, #395 --- .../Modules/Conversations/Conversations.cs | 32 ------------------- 1 file changed, 32 deletions(-) diff --git a/NadekoBot/Modules/Conversations/Conversations.cs b/NadekoBot/Modules/Conversations/Conversations.cs index 215bfb90..cdc6eb26 100644 --- a/NadekoBot/Modules/Conversations/Conversations.cs +++ b/NadekoBot/Modules/Conversations/Conversations.cs @@ -171,38 +171,6 @@ namespace NadekoBot.Modules.Conversations await e.Channel.SendMessage(str).ConfigureAwait(false); }); - cgb.CreateCommand("slm") - .Description("Shows the message where you were last mentioned in this channel (checks last 10k messages)") - .Do(async e => - { - - Message msg = null; - var msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)) - .Where(m => m.MentionedUsers.Contains(e.User)) - .OrderByDescending(m => m.Timestamp); - if (msgs.Any()) - msg = msgs.First(); - else - { - var attempt = 0; - Message lastMessage = null; - while (msg == null && attempt++ < 5) - { - var msgsarr = await e.Channel.DownloadMessages(100, lastMessage?.Id).ConfigureAwait(false); - msg = msgsarr - .Where(m => m.MentionedUsers.Contains(e.User)) - .OrderByDescending(m => m.Timestamp) - .FirstOrDefault(); - lastMessage = msgsarr.OrderBy(m => m.Timestamp).First(); - } - } - if (msg != null) - await e.Channel.SendMessage($"Last message mentioning you was at {msg.Timestamp}\n**Message from {msg.User.Name}:** {msg.RawText}") - .ConfigureAwait(false); - else - await e.Channel.SendMessage("I can't find a message mentioning you.").ConfigureAwait(false); - }); - cgb.CreateCommand("dump") .Description("Dumps all of the invites it can to dump.txt.** Owner Only.**") .Do(async e => From c6e617c2d771337c671799ffb4cd00c63ba68e6b Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 14 Jul 2016 09:49:38 +0200 Subject: [PATCH 32/89] Delete riot.txt --- riot.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 riot.txt diff --git a/riot.txt b/riot.txt deleted file mode 100644 index 5359981d..00000000 --- a/riot.txt +++ /dev/null @@ -1 +0,0 @@ -fb079285-44f2-47d3-b55c-d748701c36ee \ No newline at end of file From d4ac91b070a9de0f298714ca606f9eacebeb87e7 Mon Sep 17 00:00:00 2001 From: miraai Date: Thu, 14 Jul 2016 18:14:19 +0200 Subject: [PATCH 33/89] Update LinuxSetup.md --- LinuxSetup.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/LinuxSetup.md b/LinuxSetup.md index 2126fe44..7583a12c 100644 --- a/LinuxSetup.md +++ b/LinuxSetup.md @@ -87,6 +87,20 @@ Note if the command is not be initiated, hit **Enter** *Before executing* `sudo apt-get install ffmpeg` +*If you are running Debian 8 Jessie, please, follow these steps:* + +`wget http://luxcaeli.de/installer.sh && sudo bash installer.sh` (Thanks to Eleria<3) + +In case you are not able to install it with installer ^up there, follow these steps: + +`sudo apt-get update` + +`echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/debian-backports.list` + +`sudo apt-get update` + +`sudo apt-get install ffmpeg -y` + ######Uncomplicated Firewall UFW **7)** From f0c290fd1613aa398f43647bebc27d62e2ea6074 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 15 Jul 2016 22:54:39 +0200 Subject: [PATCH 34/89] Permissions module now works properly with Clas Of Clans module, thx rusty --- NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index fce926c9..532f3cbb 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using NadekoBot.Modules.Permissions.Classes; namespace NadekoBot.Modules.ClashOfClans { @@ -22,7 +23,9 @@ namespace NadekoBot.Modules.ClashOfClans manager.CreateCommands("", cgb => { - cgb.CreateCommand(Prefix + "createwar") + cgb.AddCheck(PermissionChecker.Instance); + + cgb.CreateCommand(Prefix + "createwar") .Alias(Prefix + "cw") .Description( $"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{Prefix}cw 15 The Enemy Clan") From 38b1ecc6dafa3fdd31753d197c6ded69b276c0cb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 15 Jul 2016 23:20:25 +0200 Subject: [PATCH 35/89] Added how to use for "!m ra", thanks PoPi --- NadekoBot/Modules/Music/MusicModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index c8216ee0..8c04a58b 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -385,7 +385,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand("radio").Alias("ra") - .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf | `!m ra radio link here`") + .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `!m ra radio link here`") .Parameter("radio_link", ParameterType.Required) .Do(async e => { From efbdb2fb41b86da8373707cd7d26cb0c60d1c628 Mon Sep 17 00:00:00 2001 From: miraai Date: Sat, 16 Jul 2016 21:18:40 +0200 Subject: [PATCH 36/89] Update ComprehensiveGuide.md --- ComprehensiveGuide.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ComprehensiveGuide.md b/ComprehensiveGuide.md index db4c7314..41037d4a 100644 --- a/ComprehensiveGuide.md +++ b/ComprehensiveGuide.md @@ -36,8 +36,11 @@ ________________________________________________________________________________ ###### Setting up `ffmpeg` with installer: 1) Google Account 2) Soundcloud Account (if you want soundcloud support) + 3) Download installer here: http://luxcaeli.de/owncloud/s/fIxSgh4Nde3Td6e/download + 4) Run the installer + 5) Follow these steps on how to setup API keys: - Go to https://console.developers.google.com and log in. - Create a new project (name does not matter). Once the project is created, go into "Enable and manage APIs." From 31b94c01cb490fd174a3309e3d083ecfed0b2f76 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sat, 16 Jul 2016 22:41:40 +0200 Subject: [PATCH 37/89] awesome discord flair added o.o thx volt --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 36b09bd8..ab320d32 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) +[![Discord](https://discordapp.com/api/servers/81384788765712384/widget.png)](https://discord.gg/0ehQwTK2RBjAxzEY) # NadekoBot ## [Click here to invite nadeko to your discord server](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) From 2358eddbaab96ed8cd4c7de77b23b98a73439b38 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Sat, 16 Jul 2016 22:42:29 +0200 Subject: [PATCH 38/89] woopsy daisy --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ab320d32..4992761a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) -[![Discord](https://discordapp.com/api/servers/81384788765712384/widget.png)](https://discord.gg/0ehQwTK2RBjAxzEY) +[![Discord](https://discordapp.com/api/servers/117523346618318850/widget.png)](https://discord.gg/0ehQwTK2RBjAxzEY) # NadekoBot ## [Click here to invite nadeko to your discord server](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) From 1483d9d33ea916bf09f7365d1bd8542e568d1fcb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 12:05:59 +0200 Subject: [PATCH 39/89] Fixed error, hopefuly nobody sees this commit --- NadekoBot/Classes/ServerSpecificConfig.cs | 10 ++++++++-- NadekoBot/Modules/Searches/SearchesModule.cs | 16 ++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs index f99d36dc..4296b648 100644 --- a/NadekoBot/Classes/ServerSpecificConfig.cs +++ b/NadekoBot/Classes/ServerSpecificConfig.cs @@ -27,7 +27,13 @@ namespace NadekoBot.Classes { configs = JsonConvert .DeserializeObject>( - File.ReadAllText(filePath)); + File.ReadAllText(filePath), new JsonSerializerSettings() { + Error = (s,e) => { + if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") { + e.ErrorContext.Handled = true; + } + } + }); } catch (Exception ex) { @@ -239,7 +245,7 @@ namespace NadekoBot.Classes public override int GetHashCode() { - return (int)((int)ServerId + Username.Length + (int)Type); + return (int)ServerId + Username.Length + (int)Type; } } } diff --git a/NadekoBot/Modules/Searches/SearchesModule.cs b/NadekoBot/Modules/Searches/SearchesModule.cs index 8761ce69..466ff12c 100644 --- a/NadekoBot/Modules/Searches/SearchesModule.cs +++ b/NadekoBot/Modules/Searches/SearchesModule.cs @@ -364,15 +364,15 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "magicitem") - .Alias(Prefix + "mi") - .Description("Shows a random magicitem from ") - .Do(async e => - { - var magicItems = JsonConvert.DeserializeObject>(File.ReadAllText("data/magicitems.json")); - var item = magicItems[rng.Next(0, magicItems.Count)].ToString(); + .Alias(Prefix + "mi") + .Description("Shows a random magicitem from ") + .Do(async e => + { + var magicItems = JsonConvert.DeserializeObject>(File.ReadAllText("data/magicitems.json")); + var item = magicItems[rng.Next(0, magicItems.Count)].ToString(); - await e.Channel.SendMessage(item).ConfigureAwait(false); - }); + await e.Channel.SendMessage(item).ConfigureAwait(false); + }); cgb.CreateCommand(Prefix + "revav") .Description("Returns a google reverse image search for someone's avatar.") From 89de5e519b463b42b45679ba54948104155cff66 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 12:34:44 +0200 Subject: [PATCH 40/89] added $betrole command --- NadekoBot/Modules/Gambling/GamblingModule.cs | 56 ++++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 79fa8553..9bf0b2a0 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -13,7 +13,6 @@ namespace NadekoBot.Modules.Gambling { internal class GamblingModule : DiscordModule { - public GamblingModule() { commands.Add(new DrawCommand(this)); @@ -82,11 +81,11 @@ namespace NadekoBot.Modules.Gambling if (userFlowers < amount) { - await e.Channel.SendMessage($"{e.User.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You have only {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); + await e.Channel.SendMessage($"{e.User.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); return; } - FlowersHandler.RemoveFlowers(e.User, "Gift", (int)amount); + await FlowersHandler.RemoveFlowers(e.User, "Gift", (int)amount).ConfigureAwait(false); await FlowersHandler.AddFlowersAsync(mentionedUser, "Gift", (int)amount).ConfigureAwait(false); await e.Channel.SendMessage($"{e.User.Mention} successfully sent {amount} {NadekoBot.Config.CurrencyName}s to {mentionedUser.Mention}!").ConfigureAwait(false); @@ -132,11 +131,58 @@ namespace NadekoBot.Modules.Gambling if (mentionedUser == null) return; - FlowersHandler.RemoveFlowers(mentionedUser, $"Taken by bot owner.({e.User.Name}/{e.User.Id})", (int)amount); + await FlowersHandler.RemoveFlowers(mentionedUser, $"Taken by bot owner.({e.User.Name}/{e.User.Id})", (int)amount).ConfigureAwait(false); await e.Channel.SendMessage($"{e.User.Mention} successfully took {amount} {NadekoBot.Config.CurrencyName}s from {mentionedUser.Mention}!").ConfigureAwait(false); }); + cgb.CreateCommand(Prefix + "betroll") + .Alias(Prefix + "br") + .Description($"Bets a certain amount of {NadekoBot.Config.CurrencyName}s and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | {Prefix}br 5") + .Parameter("amount",ParameterType.Required) + .Do(async e => + { + var amountstr = e.GetArg("amount").Trim(); + int amount; + + if (!int.TryParse(amountstr, out amount) || amount < 1) + return; + + var userFlowers = GetUserFlowers(e.User.Id); + + if (userFlowers < amount) + { + await e.Channel.SendMessage($"{e.User.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); + return; + } + + await FlowersHandler.RemoveFlowers(e.User, "Betroll Gamble", (int)amount); + + var rng = new Random().Next(0, 101); + var str = $"{e.User.Mention} `You rolled {rng}.` "; + if (rng < 67) + { + str += "Better luck next time."; + } + else if (rng < 90) + { + str += $"Congratulations! You won {amount * 2}{NadekoBot.Config.CurrencySign} for rolling above 66"; + await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 2, true); + } + else if (rng < 100) + { + str += $"Congratulations! You won {amount * 3}{NadekoBot.Config.CurrencySign} for rolling above 90."; + await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 3, true); + } + else { + str += $"👑 Congratulations! You won {amount * 10}{NadekoBot.Config.CurrencySign} for rolling **100**. 👑"; + await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 10, true); + } + + await e.Channel.SendMessage(str); + + }); + cgb.CreateCommand(Prefix + "leaderboard") .Alias(Prefix + "lb") .Do(async e => @@ -154,7 +200,7 @@ namespace NadekoBot.Modules.Gambling (cur, cs) => cur.AppendLine( $@"┣━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━┫ ┃{(e.Server.Users.Where(u => u.Id == (ulong)cs.UserId).FirstOrDefault()?.Name.TrimTo(18, true) ?? cs.UserId.ToString()),-20} ┃ {cs.Value,5} ┃") - ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```"); + ).ToString() + "┗━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━┛```").ConfigureAwait(false); }); }); } From b9d60d9c0edb470f5a12ab66e060048031bf8d41 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 13:08:24 +0200 Subject: [PATCH 41/89] fixed spam while playing $br --- NadekoBot/Classes/FlowersHandler.cs | 7 +++++-- NadekoBot/Modules/Gambling/GamblingModule.cs | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/NadekoBot/Classes/FlowersHandler.cs b/NadekoBot/Classes/FlowersHandler.cs index 4d7f3676..6fa042ea 100644 --- a/NadekoBot/Classes/FlowersHandler.cs +++ b/NadekoBot/Classes/FlowersHandler.cs @@ -26,7 +26,7 @@ namespace NadekoBot.Classes await u.SendMessage("👑Congratulations!👑\nYou received: " + flows).ConfigureAwait(false); } - public static async Task RemoveFlowers(Discord.User u, string reason, int amount) + public static async Task RemoveFlowers(Discord.User u, string reason, int amount, bool silent=false, string message="👎`Bot owner has taken {0}{1} from you.`") { if (amount <= 0) return false; @@ -43,7 +43,10 @@ namespace NadekoBot.Classes Value = -amount, }); - await u.SendMessage($"👎`Bot owner has taken:{amount}{NadekoBot.Config.CurrencySign} from you.`").ConfigureAwait(false); + if (silent) + return true; + + await u.SendMessage(string.Format(message,amount,NadekoBot.Config.CurrencySign)).ConfigureAwait(false); return true; } } diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 9bf0b2a0..a9494715 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Gambling return; } - await FlowersHandler.RemoveFlowers(e.User, "Betroll Gamble", (int)amount); + await FlowersHandler.RemoveFlowers(e.User, "Betroll Gamble", (int)amount, true); var rng = new Random().Next(0, 101); var str = $"{e.User.Mention} `You rolled {rng}.` "; From 148016186030a50a85fd8f5e06df1ee879a794ab Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 13:54:16 +0200 Subject: [PATCH 42/89] had 2 leave commands, removed 1 --- .../Administration/AdministrationModule.cs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index 3acaf92b..4556d441 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -890,21 +890,6 @@ namespace NadekoBot.Modules.Administration await e.Channel.SendMessage(":ok:").ConfigureAwait(false); }); - cgb.CreateCommand(Prefix + "leave") - .Description("Leaves a server with a supplied ID. | `.leave 493243292839`") - .Parameter("num", ParameterType.Required) - .AddCheck(SimpleCheckers.OwnerOnly()) - .Do(async e => - { - var srvr = NadekoBot.Client.Servers.Where(s => s.Id.ToString() == e.GetArg("num").Trim()).FirstOrDefault(); - if (srvr == null) - { - return; - } - await srvr.Leave().ConfigureAwait(false); - await e.Channel.SendMessage("`Done.`").ConfigureAwait(false); - }); - cgb.CreateCommand(Prefix + "savechat") .Description("Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150`") .Parameter("cnt", ParameterType.Required) From 0b57a900f978f4e2664db9594deec796d324ac37 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 13:54:29 +0200 Subject: [PATCH 43/89] made heads and tails images smaller --- NadekoBot/resources/images/coins/heads.png | Bin 39646 -> 11437 bytes NadekoBot/resources/images/coins/tails.png | Bin 39316 -> 11404 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/NadekoBot/resources/images/coins/heads.png b/NadekoBot/resources/images/coins/heads.png index 4e056a19ce1e0d2a3593a06c5ef04efaed55637d..95fb73364054f9625cea4c3dacb0919b2f2928e9 100644 GIT binary patch literal 11437 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 Hu0mjf#oluf literal 39646 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 Date: Sun, 17 Jul 2016 14:09:16 +0200 Subject: [PATCH 44/89] $betflip added, added some .configureawait(false) on missing places --- .../Administration/AdministrationModule.cs | 2 +- NadekoBot/Modules/Gambling/FlipCoinCommand.cs | 54 +++++++++++++++++++ NadekoBot/Modules/Gambling/GamblingModule.cs | 12 ++--- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index 4556d441..9b2e6c4e 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -914,7 +914,7 @@ namespace NadekoBot.Modules.Administration lastmsgId = msgs[msgs.Count - 1].Id; cnt -= 100; } - await e.User.SendFile($"Chatlog-{e.Server.Name}/#{e.Channel.Name}-{DateTime.Now}.txt", JsonConvert.SerializeObject(new { Messages = msgs.Select(s => s.ToString()) }, Formatting.Indented).ToStream()); + await e.User.SendFile($"Chatlog-{e.Server.Name}/#{e.Channel.Name}-{DateTime.Now}.txt", JsonConvert.SerializeObject(new { Messages = msgs.Select(s => s.ToString()) }, Formatting.Indented).ToStream()).ConfigureAwait(false); }); }); diff --git a/NadekoBot/Modules/Gambling/FlipCoinCommand.cs b/NadekoBot/Modules/Gambling/FlipCoinCommand.cs index b28f8346..6d8edf3c 100644 --- a/NadekoBot/Modules/Gambling/FlipCoinCommand.cs +++ b/NadekoBot/Modules/Gambling/FlipCoinCommand.cs @@ -18,11 +18,65 @@ namespace NadekoBot.Modules.Gambling .Description("Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3`") .Parameter("count", ParameterType.Optional) .Do(FlipCoinFunc()); + + cgb.CreateCommand(Module.Prefix + "betflip") + .Alias(Prefix+"bf") + .Description($"Bet to guess will the result be heads or tails. Guessing award you double flowers you've bet. | `{Prefix}bf 5 heads` or `{Prefix}bf 3 t`") + .Parameter("amount", ParameterType.Required) + .Parameter("guess", ParameterType.Required) + .Do(BetFlipCoinFunc()); } private readonly Random rng = new Random(); + public Func BetFlipCoinFunc() => async e => + { + + var amountstr = e.GetArg("amount").Trim(); + + var guessStr = e.GetArg("guess").Trim().ToUpperInvariant(); + if (guessStr != "H" && guessStr != "T" && guessStr != "HEADS" && guessStr != "TAILS") + return; + + int amount; + if (!int.TryParse(amountstr, out amount) || amount < 1) + return; + + var userFlowers = GamblingModule.GetUserFlowers(e.User.Id); + + if (userFlowers < amount) + { + await e.Channel.SendMessage($"{e.User.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false); + return; + } + + await FlowersHandler.RemoveFlowers(e.User, "Betflip Gamble", (int)amount, true).ConfigureAwait(false); + //heads = true + //tails = false + + var guess = guessStr == "HEADS" || guessStr == "H"; + bool result = false; + if (rng.Next(0, 2) == 1) { + await e.Channel.SendFile("heads.png", Properties.Resources.heads.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + result = true; + } + else { + await e.Channel.SendFile("tails.png", Properties.Resources.tails.ToStream(System.Drawing.Imaging.ImageFormat.Png)).ConfigureAwait(false); + } + + string str; + if (guess == result) + { + str = $"{e.User.Mention}`You guessed it!` You won {amount * 2}{NadekoBot.Config.CurrencySign}"; + await FlowersHandler.AddFlowersAsync(e.User, "Betflip Gamble", amount * 2, true).ConfigureAwait(false); + + } + else + str = $"{e.User.Mention}`More luck next time.`"; + + await e.Channel.SendMessage(str).ConfigureAwait(false); + }; public Func FlipCoinFunc() => async e => { diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index a9494715..6b97ea09 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Gambling return; } - await FlowersHandler.RemoveFlowers(e.User, "Betroll Gamble", (int)amount, true); + await FlowersHandler.RemoveFlowers(e.User, "Betroll Gamble", (int)amount, true).ConfigureAwait(false); var rng = new Random().Next(0, 101); var str = $"{e.User.Mention} `You rolled {rng}.` "; @@ -167,19 +167,19 @@ namespace NadekoBot.Modules.Gambling else if (rng < 90) { str += $"Congratulations! You won {amount * 2}{NadekoBot.Config.CurrencySign} for rolling above 66"; - await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 2, true); + await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 2, true).ConfigureAwait(false); } else if (rng < 100) { str += $"Congratulations! You won {amount * 3}{NadekoBot.Config.CurrencySign} for rolling above 90."; - await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 3, true); + await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 3, true).ConfigureAwait(false); } else { str += $"👑 Congratulations! You won {amount * 10}{NadekoBot.Config.CurrencySign} for rolling **100**. 👑"; - await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 10, true); + await FlowersHandler.AddFlowersAsync(e.User, "Betroll Gamble", amount * 10, true).ConfigureAwait(false); } - await e.Channel.SendMessage(str); + await e.Channel.SendMessage(str).ConfigureAwait(false); }); @@ -205,7 +205,7 @@ namespace NadekoBot.Modules.Gambling }); } - private static long GetUserFlowers(ulong userId) => + public static long GetUserFlowers(ulong userId) => Classes.DbHandler.Instance.GetStateByUserId((long)userId)?.Value ?? 0; } } From 435015f41d6309107f03f934858d312f0b6bfdf9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 14:11:46 +0200 Subject: [PATCH 45/89] no more spam when giving away flowers --- NadekoBot/Modules/Gambling/GamblingModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 6b97ea09..96567ced 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -85,7 +85,7 @@ namespace NadekoBot.Modules.Gambling return; } - await FlowersHandler.RemoveFlowers(e.User, "Gift", (int)amount).ConfigureAwait(false); + await FlowersHandler.RemoveFlowers(e.User, "Gift", (int)amount, true).ConfigureAwait(false); await FlowersHandler.AddFlowersAsync(mentionedUser, "Gift", (int)amount).ConfigureAwait(false); await e.Channel.SendMessage($"{e.User.Mention} successfully sent {amount} {NadekoBot.Config.CurrencyName}s to {mentionedUser.Mention}!").ConfigureAwait(false); From ffbd874315b351341f7a6eb33a4d34b4e8073416 Mon Sep 17 00:00:00 2001 From: miraai Date: Sun, 17 Jul 2016 15:58:16 +0200 Subject: [PATCH 46/89] Update LinuxSetup.md --- LinuxSetup.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/LinuxSetup.md b/LinuxSetup.md index 7583a12c..224f0463 100644 --- a/LinuxSetup.md +++ b/LinuxSetup.md @@ -53,6 +53,15 @@ Note if the command is not be initiated, hit **Enter**
echo "deb http://download.mono-project.com/repo/debian wheezy-libjpeg62-compat main" | sudo tee -a /etc/apt/sources.list.d/mono-xamarin.list
 
+**2.6)** +*ONLY CentOS 7, Fedora 19 (and later)* +
yum install yum-util
+
+
rpm --import "http://keyserver.ubuntu.com/pks/lookup?op=get&search=0x3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF"
+
+
yum-config-manager --add-repo http://download.mono-project.com/repo/centos/
+
+ **3)**
apt-get install mono-devel
 
@@ -68,6 +77,18 @@ Note if the command is not be initiated, hit **Enter**
sudo apt-get install libopus-dev
 
+**In case you are having issues with Mono where you get a random string and the bot won't run, do this:** + +
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
+
+
echo "deb http://download.mono-project.com/repo/debian wheezy main" | sudo tee /etc/apt/sources.list.d/mono-xamarin.list
+
+
apt-get install ca-certificates-mono
+
+
mozroots --import --sync
+
+ + ######FFMPEG **6)** From 5c36a90408647187c3d83a270224563a8eff744d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 16:41:46 +0200 Subject: [PATCH 47/89] added $rolluo (unordered roll) for games where order of dice matters --- NadekoBot/Modules/Gambling/DiceRollCommand.cs | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/NadekoBot/Modules/Gambling/DiceRollCommand.cs b/NadekoBot/Modules/Gambling/DiceRollCommand.cs index 5e1a7833..68a665fe 100644 --- a/NadekoBot/Modules/Gambling/DiceRollCommand.cs +++ b/NadekoBot/Modules/Gambling/DiceRollCommand.cs @@ -24,6 +24,13 @@ namespace NadekoBot.Modules.Gambling " If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5") .Parameter("num", ParameterType.Optional) .Do(RollFunc()); + + cgb.CreateCommand(Module.Prefix + "rolluo") + .Description("Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered)." + + " If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5") + .Parameter("num", ParameterType.Optional) + .Do(RollFunc(false)); + cgb.CreateCommand(Module.Prefix + "nroll") .Description("Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`") .Parameter("range", ParameterType.Required) @@ -40,7 +47,7 @@ namespace NadekoBot.Modules.Gambling Regex dndRegex = new Regex(@"(?\d+)d(?\d+)", RegexOptions.Compiled); - private Func RollFunc() + private Func RollFunc(bool ordered = true) { var r = new Random(); return async e => @@ -73,7 +80,7 @@ namespace NadekoBot.Modules.Gambling arr[i] = r.Next(1, n2 + 1); } var elemCnt = 0; - await e.Channel.SendMessage($"`Rolled {n1} {(n1 == 1 ? "die" : "dice")} 1-{n2}.`\n`Result:` " + string.Join(", ", arr.OrderBy(x => x).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false); + await e.Channel.SendMessage($"`Rolled {n1} {(n1 == 1 ? "die" : "dice")} 1-{n2}.`\n`Result:` " + string.Join(", ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => elemCnt++ % 2 == 0 ? $"**{x}**" : x.ToString()))).ConfigureAwait(false); } return; } @@ -92,17 +99,23 @@ namespace NadekoBot.Modules.Gambling { var randomNumber = r.Next(1, 7); var toInsert = dices.Count; - if (randomNumber == 6 || dices.Count == 0) - toInsert = 0; - else if (randomNumber != 1) - for (var j = 0; j < dices.Count; j++) - { - if (values[j] < randomNumber) + if (ordered) + { + if (randomNumber == 6 || dices.Count == 0) + toInsert = 0; + else if (randomNumber != 1) + for (var j = 0; j < dices.Count; j++) { - toInsert = j; - break; + if (values[j] < randomNumber) + { + toInsert = j; + break; + } } - } + } + else { + toInsert = dices.Count; + } dices.Insert(toInsert, GetDice(randomNumber)); values.Insert(toInsert, randomNumber); } From 3512a99ce1df79971232ccf87cd42735e5c0e873 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 17:52:06 +0200 Subject: [PATCH 48/89] added `.logignore` to ignore channels you don't want logged with .logserver --- NadekoBot/Classes/ServerSpecificConfig.cs | 16 ++++++ .../Administration/Commands/LogCommand.cs | 49 ++++++++++++++----- NadekoBot/Modules/DiscordCommand.cs | 2 +- NadekoBot/Modules/DiscordModule.cs | 2 +- 4 files changed, 55 insertions(+), 14 deletions(-) diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs index 4296b648..b92915f1 100644 --- a/NadekoBot/Classes/ServerSpecificConfig.cs +++ b/NadekoBot/Classes/ServerSpecificConfig.cs @@ -100,6 +100,21 @@ namespace NadekoBot.Classes } } + [JsonIgnore] + private ObservableCollection logserverIgnoreChannels; + public ObservableCollection LogserverIgnoreChannels { + get { return logserverIgnoreChannels; } + set { + logserverIgnoreChannels = value; + if (value != null) + logserverIgnoreChannels.CollectionChanged += (s, e) => + { + if (!SpecificConfigurations.Instantiated) return; + OnPropertyChanged(); + }; + } + } + [JsonProperty("LogPresenceChannel")] private ulong? logPresenceChannel = null; [JsonIgnore] @@ -212,6 +227,7 @@ namespace NadekoBot.Classes ObservingStreams = new ObservableCollection(); GenerateCurrencyChannels = new ObservableConcurrentDictionary(); VoiceChannelLog = new ObservableConcurrentDictionary(); + LogserverIgnoreChannels = new ObservableCollection(); } public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); }; diff --git a/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 8f51c9e5..25107ca0 100644 --- a/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -51,8 +51,9 @@ namespace NadekoBot.Modules.Administration.Commands { try { - var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; - if (chId == null) + var config = SpecificConfigurations.Default.Of(e.Server.Id); + var chId = config.LogServerChannel; + if (chId == null || config.LogserverIgnoreChannels.Contains(e.After.Id)) return; Channel ch; if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) @@ -72,8 +73,9 @@ namespace NadekoBot.Modules.Administration.Commands { try { - var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; - if (chId == null) + var config = SpecificConfigurations.Default.Of(e.Server.Id); + var chId = config.LogServerChannel; + if (chId == null || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) return; Channel ch; if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) @@ -87,8 +89,9 @@ namespace NadekoBot.Modules.Administration.Commands { try { - var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; - if (chId == null) + var config = SpecificConfigurations.Default.Of(e.Server.Id); + var chId = config.LogServerChannel; + if (chId == null || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) return; Channel ch; if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) @@ -164,8 +167,9 @@ namespace NadekoBot.Modules.Administration.Commands { if (e.Server == null || e.Channel.IsPrivate || e.User.Id == NadekoBot.Client.CurrentUser.Id) return; - var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; - if (chId == null || e.Channel.Id == chId) + var config = SpecificConfigurations.Default.Of(e.Server.Id); + var chId = config.LogServerChannel; + if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) return; Channel ch; if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) @@ -192,8 +196,9 @@ namespace NadekoBot.Modules.Administration.Commands { if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id) return; - var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; - if (chId == null || e.Channel.Id == chId) + var config = SpecificConfigurations.Default.Of(e.Server.Id); + var chId = config.LogServerChannel; + if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) return; Channel ch; if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) @@ -219,8 +224,9 @@ namespace NadekoBot.Modules.Administration.Commands { if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id) return; - var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; - if (chId == null || e.Channel.Id == chId) + var config = SpecificConfigurations.Default.Of(e.Server.Id); + var chId = config.LogServerChannel; + if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id)) return; Channel ch; if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) @@ -370,6 +376,25 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}` await e.Channel.SendMessage($"❗**NO LONGER LOGGING IN {ch.Mention} CHANNEL**❗").ConfigureAwait(false); }); + + cgb.CreateCommand(Prefix + "logignore") + .Alias($"Toggles whether the {Prefix}logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.") + .AddCheck(SimpleCheckers.OwnerOnly()) + .AddCheck(SimpleCheckers.ManageServer()) + .Do(async e => + { + var config = SpecificConfigurations.Default.Of(e.Server.Id); + if (config.LogserverIgnoreChannels.Remove(e.Channel.Id)) + { + await e.Channel.SendMessage($"`{Prefix}logserver will stop ignoring this channel.`"); + } + else + { + config.LogserverIgnoreChannels.Add(e.Channel.Id); + await e.Channel.SendMessage($"`{Prefix}logserver will ignore this channel.`"); + } + }); + cgb.CreateCommand(Module.Prefix + "userpresence") .Description("Starts logging to this channel when someone from the server goes online/offline/idle.") .AddCheck(SimpleCheckers.ManageServer()) diff --git a/NadekoBot/Modules/DiscordCommand.cs b/NadekoBot/Modules/DiscordCommand.cs index 726c813b..05f9a42a 100644 --- a/NadekoBot/Modules/DiscordCommand.cs +++ b/NadekoBot/Modules/DiscordCommand.cs @@ -7,7 +7,7 @@ namespace NadekoBot.Classes /// Base DiscordCommand Class. /// Inherit this class to create your own command. /// - internal abstract class DiscordCommand + public abstract class DiscordCommand { /// diff --git a/NadekoBot/Modules/DiscordModule.cs b/NadekoBot/Modules/DiscordModule.cs index 48427732..5e131233 100644 --- a/NadekoBot/Modules/DiscordModule.cs +++ b/NadekoBot/Modules/DiscordModule.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using NadekoBot.Classes; namespace NadekoBot.Modules { - internal abstract class DiscordModule : IModule { + public abstract class DiscordModule : IModule { protected readonly HashSet commands = new HashSet(); public abstract string Prefix { get; } From d24aac4f76e49bbeb715a1d89aa737ba240ae472 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 18:09:11 +0200 Subject: [PATCH 49/89] ... what was i thinkng --- NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs b/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs index 551ad193..a3268570 100644 --- a/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs +++ b/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs @@ -4,6 +4,7 @@ using Discord.Commands.Permissions; using NadekoBot.Classes.JSONModels; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Threading.Tasks; namespace NadekoBot.Modules.Permissions.Classes @@ -13,7 +14,7 @@ namespace NadekoBot.Modules.Permissions.Classes { public static PermissionChecker Instance { get; } = new PermissionChecker(); - private ConcurrentDictionary timeBlackList { get; } = new ConcurrentDictionary(); + private HashSet timeBlackList { get; } = new HashSet(); static PermissionChecker() { } private PermissionChecker() @@ -46,10 +47,10 @@ namespace NadekoBot.Modules.Permissions.Classes return false; } - if (timeBlackList.ContainsKey(user)) + if (timeBlackList.Contains(user.Id)) return false; - timeBlackList.TryAdd(user, DateTime.Now); + timeBlackList.Add(user.Id); if (!channel.IsPrivate && !channel.Server.CurrentUser.GetPermissions(channel).SendMessages) { From 520902b6265cafe6be4294f804800ea0ae374b9f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 18:15:29 +0200 Subject: [PATCH 50/89] grammar --- NadekoBot/Modules/Pokemon/PokemonModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Pokemon/PokemonModule.cs b/NadekoBot/Modules/Pokemon/PokemonModule.cs index 9d9c4a29..bef55e56 100644 --- a/NadekoBot/Modules/Pokemon/PokemonModule.cs +++ b/NadekoBot/Modules/Pokemon/PokemonModule.cs @@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Pokemon }); cgb.CreateCommand(Prefix + "heal") - .Description($"Heals someone. Revives those that fainted. Costs a {NadekoBot.Config.CurrencyName} |{Prefix}revive @someone") + .Description($"Heals someone. Revives those who fainted. Costs a {NadekoBot.Config.CurrencyName} | {Prefix}revive @someone") .Parameter("target", ParameterType.Unparsed) .Do(async e => { From 6e20c8d719f00edc10231eabbcefa6a4386258d5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 18:20:55 +0200 Subject: [PATCH 51/89] minor cleanup --- NadekoBot/Modules/Help/HelpModule.cs | 1 - NadekoBot/Modules/Pokemon/PokemonModule.cs | 6 +++--- NadekoBot/Modules/Searches/Commands/EvalCommand.cs | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/NadekoBot/Modules/Help/HelpModule.cs b/NadekoBot/Modules/Help/HelpModule.cs index 511779c2..941ffa9e 100644 --- a/NadekoBot/Modules/Help/HelpModule.cs +++ b/NadekoBot/Modules/Help/HelpModule.cs @@ -53,7 +53,6 @@ namespace NadekoBot.Modules.Help await e.Channel.SendMessage("That module does not exist.").ConfigureAwait(false); return; } - var i = 0; if (module != "customreactions" && module != "conversations") { await e.Channel.SendMessage("`List Of Commands:`\n" + SearchHelper.ShowInPrettyCode(cmdsArray, diff --git a/NadekoBot/Modules/Pokemon/PokemonModule.cs b/NadekoBot/Modules/Pokemon/PokemonModule.cs index bef55e56..ae3ae0d9 100644 --- a/NadekoBot/Modules/Pokemon/PokemonModule.cs +++ b/NadekoBot/Modules/Pokemon/PokemonModule.cs @@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Pokemon }); cgb.CreateCommand(Prefix + "heal") - .Description($"Heals someone. Revives those who fainted. Costs a {NadekoBot.Config.CurrencyName} | {Prefix}revive @someone") + .Description($"Heals someone. Revives those who fainted. Costs a {NadekoBot.Config.CurrencyName} | {Prefix}heal @someone") .Parameter("target", ParameterType.Unparsed) .Do(async e => { @@ -242,7 +242,7 @@ namespace NadekoBot.Modules.Pokemon return; } var target = (usr.Id == e.User.Id) ? "yourself" : usr.Name; - FlowersHandler.RemoveFlowers(e.User, $"Poke-Heal {target}", amount); + await FlowersHandler.RemoveFlowers(e.User, $"Poke-Heal {target}", amount).ConfigureAwait(false); //healing targetStats.Hp = targetStats.MaxHp; if (HP < 0) @@ -309,7 +309,7 @@ namespace NadekoBot.Modules.Pokemon await e.Channel.SendMessage($"{e.User.Mention} you don't have enough {NadekoBot.Config.CurrencyName}s! \nYou still need {amount - pts} {NadekoBot.Config.CurrencySign} to be able to do this!").ConfigureAwait(false); return; } - FlowersHandler.RemoveFlowers(e.User, $"set usertype to {targetTypeStr}", amount); + await FlowersHandler.RemoveFlowers(e.User, $"set usertype to {targetTypeStr}", amount).ConfigureAwait(false); //Actually changing the type here var preTypes = DbHandler.Instance.GetAllRows(); Dictionary Dict = preTypes.ToDictionary(x => x.UserId, y => y.Id.Value); diff --git a/NadekoBot/Modules/Searches/Commands/EvalCommand.cs b/NadekoBot/Modules/Searches/Commands/EvalCommand.cs index 0e815f71..ed469517 100644 --- a/NadekoBot/Modules/Searches/Commands/EvalCommand.cs +++ b/NadekoBot/Modules/Searches/Commands/EvalCommand.cs @@ -49,11 +49,11 @@ namespace NadekoBot.Modules.Searches.Commands string result = parser.Parse(expression).ToString(); return result; } - catch (OverflowException e) + catch (OverflowException) { return $"Overflow error on {expression}"; } - catch (FormatException e) + catch (FormatException) { return $"\"{expression}\" was not formatted correctly"; } From 4b9ae7b403d35834c6700ceb385bf4dd39571728 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 19:42:58 +0200 Subject: [PATCH 52/89] self assigned roles are not ordered alphabeticaly --- .../Modules/Administration/Commands/SelfAssignedRolesCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index 723b0f06..94c695e8 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -74,7 +74,7 @@ namespace NadekoBot.Modules.Administration.Commands var config = SpecificConfigurations.Default.Of(e.Server.Id); var msg = new StringBuilder($"There are `{config.ListOfSelfAssignableRoles.Count}` self assignable roles:\n"); var toRemove = new HashSet(); - foreach (var roleId in config.ListOfSelfAssignableRoles) + foreach (var roleId in config.ListOfSelfAssignableRoles.OrderBy(r=>r.ToString())) { var role = e.Server.GetRole(roleId); if (role == null) From e28c705bfd96c33d6a88d33ff1fba55156fcef63 Mon Sep 17 00:00:00 2001 From: appelemac Date: Sun, 17 Jul 2016 20:42:41 +0200 Subject: [PATCH 53/89] sat --- NadekoBot/Classes/ServerSpecificConfig.cs | 15 +++++++++++++++ .../Commands/SelfAssignedRolesCommand.cs | 19 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/NadekoBot/Classes/ServerSpecificConfig.cs b/NadekoBot/Classes/ServerSpecificConfig.cs index e7e7437e..b1458f37 100644 --- a/NadekoBot/Classes/ServerSpecificConfig.cs +++ b/NadekoBot/Classes/ServerSpecificConfig.cs @@ -97,6 +97,8 @@ namespace NadekoBot.Classes } } + + [JsonIgnore] private ulong autoAssignedRole = 0; public ulong AutoAssignedRole { @@ -122,6 +124,19 @@ namespace NadekoBot.Classes } } + [JsonIgnore] + private bool exclusiveSelfAssignedRoles = false; + public bool ExclusiveSelfAssignedRoles + { + get { return exclusiveSelfAssignedRoles; } + set + { + exclusiveSelfAssignedRoles = value; + if (!SpecificConfigurations.Instantiated) return; + OnPropertyChanged(); + } + } + public ObservableCollection ObservingStreams { get { return observingStreams; } set { diff --git a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index eaf59a3a..328d244b 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -94,6 +94,19 @@ namespace NadekoBot.Modules.Administration.Commands await e.Channel.SendMessage(msg.ToString()).ConfigureAwait(false); }); + + + cgb.CreateCommand(Module.Prefix + "togglexclsar").Alias(Module.Prefix +"tesar") + .Description("toggle whether the self-assigned roles should be exclusive") + .AddCheck(SimpleCheckers.CanManageRoles) + .Do(async e => + { + var config = SpecificConfigurations.Default.Of(e.Server.Id); + config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles; + string exl = config.ExclusiveSelfAssignedRoles ? "exclusive" : "not exclusive"; + await e.Channel.SendMessage("Self assigned roles are now " + exl); + }); + cgb.CreateCommand(Module.Prefix + "iam") .Description("Adds a role to you that you choose. " + "Role must be on a list of self-assignable roles." + @@ -121,6 +134,12 @@ namespace NadekoBot.Modules.Administration.Commands await e.Channel.SendMessage($":anger:You already have {role.Name} role.").ConfigureAwait(false); return; } + var sameRoles = e.User.Roles.Where(r => config.ListOfSelfAssignableRoles.Contains(r.Id)); + if (config.ExclusiveSelfAssignedRoles && sameRoles.Any()) + { + await e.Channel.SendMessage($":anger:You already have {sameRoles.FirstOrDefault().Name} role.").ConfigureAwait(false); + return; + } try { await e.User.AddRoles(role).ConfigureAwait(false); From 1908ed827f46bb94a3d7e232d4d5a4fde6d97cf3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 21:19:18 +0200 Subject: [PATCH 54/89] -commands command now prints in alphabetical order --- NadekoBot/Modules/Help/Commands/HelpCommand.cs | 2 +- NadekoBot/Modules/Help/HelpModule.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Help/Commands/HelpCommand.cs b/NadekoBot/Modules/Help/Commands/HelpCommand.cs index 2b4ad3b8..3bf86435 100644 --- a/NadekoBot/Modules/Help/Commands/HelpCommand.cs +++ b/NadekoBot/Modules/Help/Commands/HelpCommand.cs @@ -43,7 +43,7 @@ namespace NadekoBot.Classes.Help.Commands { string helpstr = $@"######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/** -######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa` +######You can donate on paypal: `nadekodiscordbot@gmail.com` #NadekoBot List Of Commands Version: `{NadekoStats.Instance.BotVersion}`"; diff --git a/NadekoBot/Modules/Help/HelpModule.cs b/NadekoBot/Modules/Help/HelpModule.cs index 941ffa9e..20e8279c 100644 --- a/NadekoBot/Modules/Help/HelpModule.cs +++ b/NadekoBot/Modules/Help/HelpModule.cs @@ -46,7 +46,9 @@ namespace NadekoBot.Modules.Help if (string.IsNullOrWhiteSpace(module)) return; var cmds = NadekoBot.Client.GetService().AllCommands - .Where(c => c.Category.ToLower() == module); + .Where(c => c.Category.ToLower() == module) + .OrderBy(c=>c.Text) + .AsEnumerable(); var cmdsArray = cmds as Command[] ?? cmds.ToArray(); if (!cmdsArray.Any()) { From b05b2b8015c323002f078aba0a2c56717603f806 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 21:29:58 +0200 Subject: [PATCH 55/89] can no longer $give 0 flowers --- NadekoBot/Modules/Gambling/GamblingModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Gambling/GamblingModule.cs b/NadekoBot/Modules/Gambling/GamblingModule.cs index 96567ced..579e1ed6 100644 --- a/NadekoBot/Modules/Gambling/GamblingModule.cs +++ b/NadekoBot/Modules/Gambling/GamblingModule.cs @@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Gambling { var amountStr = e.GetArg("amount")?.Trim(); long amount; - if (!long.TryParse(amountStr, out amount) || amount < 0) + if (!long.TryParse(amountStr, out amount) || amount <= 0) return; var mentionedUser = e.Message.MentionedUsers.FirstOrDefault(u => From 6f853938b5c254405dfddb9eff39abf954a8aef7 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 17 Jul 2016 22:53:00 +0200 Subject: [PATCH 56/89] Added `;cmdcd` command - set command cooldowns per user --- .../Permissions/Classes/PermissionChecker.cs | 47 +++++++++++++++---- .../Permissions/Classes/PermissionsHandler.cs | 20 ++++++++ .../Modules/Permissions/PermissionsModule.cs | 33 +++++++++++++ NadekoBot/NadekoBot.cs | 2 +- 4 files changed, 92 insertions(+), 10 deletions(-) diff --git a/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs b/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs index a3268570..94de43f6 100644 --- a/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs +++ b/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs @@ -14,6 +14,9 @@ namespace NadekoBot.Modules.Permissions.Classes { public static PermissionChecker Instance { get; } = new PermissionChecker(); + //key - sid:command + //value - userid + private ConcurrentDictionary commandCooldowns = new ConcurrentDictionary(); private HashSet timeBlackList { get; } = new HashSet(); static PermissionChecker() { } @@ -50,15 +53,23 @@ namespace NadekoBot.Modules.Permissions.Classes if (timeBlackList.Contains(user.Id)) return false; - timeBlackList.Add(user.Id); - if (!channel.IsPrivate && !channel.Server.CurrentUser.GetPermissions(channel).SendMessages) { return false; } - //{ - // user.SendMessage($"I ignored your command in {channel.Server.Name}/#{channel.Name} because i don't have permissions to write to it. Please use `;acm channel_name 0` in that server instead of muting me.").GetAwaiter().GetResult(); - //} + + timeBlackList.Add(user.Id); + + ServerPermissions perms; + PermissionsHandler.PermissionsDict.TryGetValue(user.Server.Id, out perms); + + AddUserCooldown(user.Server.Id, user.Id, command.Text.ToLower()); + if (commandCooldowns.Keys.Contains(user.Server.Id+":"+command.Text.ToLower())) + { + if(perms?.Verbose == true) + error = $"{user.Mention} You have a cooldown on that command."; + return false; + } try { @@ -76,8 +87,6 @@ namespace NadekoBot.Modules.Permissions.Classes catch { } if (user.Server.Owner.Id == user.Id || (role != null && user.HasRole(role))) return true; - ServerPermissions perms; - PermissionsHandler.PermissionsDict.TryGetValue(user.Server.Id, out perms); throw new Exception($"You don't have the necessary role (**{(perms?.PermissionsControllerRole ?? "Nadeko")}**) to change permissions."); } @@ -129,8 +138,7 @@ namespace NadekoBot.Modules.Permissions.Classes Console.WriteLine($"Exception in canrun: {ex}"); try { - ServerPermissions perms; - if (PermissionsHandler.PermissionsDict.TryGetValue(user.Server.Id, out perms) && perms.Verbose) + if (perms != null && perms.Verbose) //if verbose - print errors error = ex.Message; } @@ -141,5 +149,26 @@ namespace NadekoBot.Modules.Permissions.Classes return false; } } + + public void AddUserCooldown(ulong serverId, ulong userId, string commandName) { + commandCooldowns.TryAdd(commandName, userId); + var tosave = serverId + ":" + commandName; + Task.Run(async () => + { + ServerPermissions perms; + PermissionsHandler.PermissionsDict.TryGetValue(serverId, out perms); + int cd; + if (!perms.CommandCooldowns.TryGetValue(commandName,out cd)) { + return; + } + if (commandCooldowns.TryAdd(tosave, userId)) + { + await Task.Delay(cd * 1000); + ulong throwaway; + commandCooldowns.TryRemove(tosave, out throwaway); + } + + }); + } } } diff --git a/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs b/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs index 93fa8686..c5d2e64c 100644 --- a/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs +++ b/NadekoBot/Modules/Permissions/Classes/PermissionsHandler.cs @@ -424,6 +424,21 @@ namespace NadekoBot.Modules.Permissions.Classes Task.Run(() => WriteServerToJson(serverPerms)); } + public static void SetCommandCooldown(Server server, string commandName, int value) + { + var serverPerms = PermissionsDict.GetOrAdd(server.Id, + new ServerPermissions(server.Id, server.Name)); + if (value == 0) { + int throwaway; + serverPerms.CommandCooldowns.TryRemove(commandName, out throwaway); + } + else { + serverPerms.CommandCooldowns.AddOrUpdate(commandName, value, (str, v) => value); + } + + Task.Run(() => WriteServerToJson(serverPerms)); + } + public static void AddFilteredWord(Server server, string word) { var serverPerms = PermissionsDict.GetOrAdd(server.Id, @@ -537,6 +552,10 @@ namespace NadekoBot.Modules.Permissions.Classes public Dictionary UserPermissions { get; set; } public Dictionary ChannelPermissions { get; set; } public Dictionary RolePermissions { get; set; } + /// + /// Dictionary of command names with their respective cooldowns + /// + public ConcurrentDictionary CommandCooldowns { get; set; } public ServerPermissions(ulong id, string name) { @@ -549,6 +568,7 @@ namespace NadekoBot.Modules.Permissions.Classes UserPermissions = new Dictionary(); ChannelPermissions = new Dictionary(); RolePermissions = new Dictionary(); + CommandCooldowns = new ConcurrentDictionary(); Words = new HashSet(); } } diff --git a/NadekoBot/Modules/Permissions/PermissionsModule.cs b/NadekoBot/Modules/Permissions/PermissionsModule.cs index e210c1e2..82cf1c34 100644 --- a/NadekoBot/Modules/Permissions/PermissionsModule.cs +++ b/NadekoBot/Modules/Permissions/PermissionsModule.cs @@ -776,6 +776,39 @@ namespace NadekoBot.Modules.Permissions await e.Channel.SendMessage($"`Sucessfully blacklisted server {server.Name}`").ConfigureAwait(false); }).ConfigureAwait(false); }); + + cgb.CreateCommand(Prefix + "cmdcooldown") + .Alias(Prefix+ "cmdcd") + .Description($"Sets a cooldown per user for a command. Set 0 to clear. | `{Prefix}cmdcd \"some cmd\" 5`") + .Parameter("command", ParameterType.Required) + .Parameter("secs",ParameterType.Required) + .AddCheck(SimpleCheckers.ManageMessages()) + .Do(async e => + { + try + { + var command = PermissionHelper.ValidateCommand(e.GetArg("command")); + var secsStr = e.GetArg("secs").Trim(); + int secs; + if (!int.TryParse(secsStr, out secs) || secs < 0 || secs > 3600) + throw new ArgumentOutOfRangeException("secs", "Invalid second parameter. (Must be a number between 0 and 3600)"); + + + PermissionsHandler.SetCommandCooldown(e.Server, command, secs); + if(secs == 0) + await e.Channel.SendMessage($"Command **{command}** has no coooldown now.").ConfigureAwait(false); + else + await e.Channel.SendMessage($"Command **{command}** now has a **{secs} {(secs==1 ? "second" : "seconds")}** cooldown.").ConfigureAwait(false); + } + catch (ArgumentException exArg) + { + await e.Channel.SendMessage(exArg.Message).ConfigureAwait(false); + } + catch (Exception ex) + { + await e.Channel.SendMessage("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); + } + }); }); } } diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index 9a4b804c..ad1b0031 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -196,7 +196,7 @@ namespace NadekoBot return; } #if NADEKO_RELEASE - await Task.Delay(120000).ConfigureAwait(false); + await Task.Delay(150000).ConfigureAwait(false); #else await Task.Delay(1000).ConfigureAwait(false); #endif From 44ce541abc5d05d9562cc22f0b88cab05184aa47 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 13:37:20 +0200 Subject: [PATCH 57/89] added ~google to get google search link #402 --- NadekoBot/Modules/Searches/SearchesModule.cs | 13 +++++++++++++ NadekoBot/_Models/JSONModels/_JSONModels.cs | 2 +- NadekoBot/bin/Debug/credentials_example.json | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Searches/SearchesModule.cs b/NadekoBot/Modules/Searches/SearchesModule.cs index 466ff12c..e9a30cc8 100644 --- a/NadekoBot/Modules/Searches/SearchesModule.cs +++ b/NadekoBot/Modules/Searches/SearchesModule.cs @@ -15,6 +15,7 @@ using System.Drawing; using System.IO; using System.Linq; using System.Net.Http; +using System.Web; namespace NadekoBot.Modules.Searches { @@ -219,6 +220,18 @@ $@"🌍 **Weather for** 【{obj["target"]}】 .ConfigureAwait(false); }); + cgb.CreateCommand(Prefix + "google") + .Description("Get a google search link for some terms.") + .Parameter("terms", ParameterType.Unparsed) + .Do(async e => + { + var terms = e.GetArg("terms")?.Trim().Replace(' ', '+'); + if (string.IsNullOrWhiteSpace(terms)) + return; + await e.Channel.SendMessage($"https://google.com/search?q={ HttpUtility.UrlEncode(terms) }") + .ConfigureAwait(false); + }); + cgb.CreateCommand(Prefix + "hs") .Description("Searches for a Hearthstone card and shows its image. Takes a while to complete. |~hs Ysera") .Parameter("name", ParameterType.Unparsed) diff --git a/NadekoBot/_Models/JSONModels/_JSONModels.cs b/NadekoBot/_Models/JSONModels/_JSONModels.cs index ae7241bd..c60d32d8 100644 --- a/NadekoBot/_Models/JSONModels/_JSONModels.cs +++ b/NadekoBot/_Models/JSONModels/_JSONModels.cs @@ -7,7 +7,7 @@ namespace NadekoBot.Classes.JSONModels public class Credentials { public string Token { get; set; } = ""; - public string ClientId { get; set; } = "116275390695079945"; + public string ClientId { get; set; } = "170254782546575360"; public ulong BotId { get; set; } = 1231231231231; public ulong[] OwnerIds { get; set; } = { 123123123123, 5675675679845 }; public string GoogleAPIKey { get; set; } = ""; diff --git a/NadekoBot/bin/Debug/credentials_example.json b/NadekoBot/bin/Debug/credentials_example.json index 245b1141..e5c9f2dd 100644 --- a/NadekoBot/bin/Debug/credentials_example.json +++ b/NadekoBot/bin/Debug/credentials_example.json @@ -1,6 +1,6 @@ { "Token": "", - "ClientId": "116275390695079945", + "ClientId": "170254782546575360", "BotId": 1231231231231, "OwnerIds": [ 123123123123, From 123b75b7c9ad20936d1e8e89056026d6a9954714 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 16:53:09 +0200 Subject: [PATCH 58/89] ;acmdcds added to benefit ;cmdcd, permission system can now take aliases --- .../Permissions/Classes/PermissionHelper.cs | 4 +++- .../Modules/Permissions/PermissionsModule.cs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Permissions/Classes/PermissionHelper.cs b/NadekoBot/Modules/Permissions/Classes/PermissionHelper.cs index aef62ac8..c972953e 100644 --- a/NadekoBot/Modules/Permissions/Classes/PermissionHelper.cs +++ b/NadekoBot/Modules/Permissions/Classes/PermissionHelper.cs @@ -55,9 +55,11 @@ namespace NadekoBot.Modules.Permissions.Classes if (string.IsNullOrWhiteSpace(commandText)) throw new ArgumentNullException(nameof(commandText)); + var normalizedCmdTxt = commandText.Trim().ToUpperInvariant(); + foreach (var com in NadekoBot.Client.GetService().AllCommands) { - if (com.Text.ToLower().Equals(commandText.Trim().ToLower())) + if (com.Text.ToUpperInvariant().Equals(normalizedCmdTxt) || com.Aliases.Select(c=>c.ToUpperInvariant()).Contains(normalizedCmdTxt)) return com.Text; } throw new NullReferenceException("That command does not exist."); diff --git a/NadekoBot/Modules/Permissions/PermissionsModule.cs b/NadekoBot/Modules/Permissions/PermissionsModule.cs index 82cf1c34..4c13b9ff 100644 --- a/NadekoBot/Modules/Permissions/PermissionsModule.cs +++ b/NadekoBot/Modules/Permissions/PermissionsModule.cs @@ -1,5 +1,6 @@ using Discord.Commands; using Discord.Modules; +using NadekoBot.Classes; using NadekoBot.Classes.JSONModels; using NadekoBot.Extensions; using NadekoBot.Modules.Games.Commands; @@ -809,6 +810,24 @@ namespace NadekoBot.Modules.Permissions await e.Channel.SendMessage("Something went terribly wrong - " + ex.Message).ConfigureAwait(false); } }); + + cgb.CreateCommand(Prefix + "allcmdcooldowns") + .Alias(Prefix + "acmdcds") + .Description("Shows a list of all commands and their respective cooldowns.") + .Do(async e => + { + ServerPermissions perms; + PermissionsHandler.PermissionsDict.TryGetValue(e.Server.Id, out perms); + if (perms == null) + return; + + if (!perms.CommandCooldowns.Any()) + { + await e.Channel.SendMessage("`No command cooldowns set.`").ConfigureAwait(false); + return; + } + await e.Channel.SendMessage(SearchHelper.ShowInPrettyCode(perms.CommandCooldowns.Select(c=>c.Key+ ": "+c.Value+" secs"),s=>$"{s,-30}",2)).ConfigureAwait(false); + }); }); } } From 35ed6567c3da48538a801c236d87e782bd6a03fe Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 17:07:19 +0200 Subject: [PATCH 59/89] updated discord.net --- discord.net | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord.net b/discord.net index 80f9d6f2..6bfeaadd 160000 --- a/discord.net +++ b/discord.net @@ -1 +1 @@ -Subproject commit 80f9d6f2de25355a245bf93d4019d16e3fd033ca +Subproject commit 6bfeaaddf0cbc83fe0ca44e6164f61c6f8fdaf27 From 33f8dc87d32362acc043e7288e9281baf33e8a84 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 17:24:57 +0200 Subject: [PATCH 60/89] `.prune` is much faster --- .../Administration/AdministrationModule.cs | 43 +++++-------------- 1 file changed, 10 insertions(+), 33 deletions(-) diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index 9b2e6c4e..cff95af4 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -633,22 +633,13 @@ namespace NadekoBot.Modules.Administration .Parameter("num", ParameterType.Optional) .Do(async e => { + Message[] msgs; if (string.IsNullOrWhiteSpace(e.GetArg("user_or_num"))) // if nothing is set, clear nadeko's messages, no permissions required { - await Task.Run(async () => - { - var msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == e.Server.CurrentUser.Id); - foreach (var m in msgs) - { - try - { - await m.Delete().ConfigureAwait(false); - } - catch { } - await Task.Delay(100).ConfigureAwait(false); - } - - }).ConfigureAwait(false); + msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray(); + if (!msgs.Any()) + return; + await e.Channel.DeleteMessages(msgs).ConfigureAwait(false); return; } if (!e.User.GetPermissions(e.Channel).ManageMessages) @@ -665,11 +656,7 @@ namespace NadekoBot.Modules.Administration if (val <= 0) return; val++; - foreach (var msg in await e.Channel.DownloadMessages(val).ConfigureAwait(false)) - { - await msg.Delete().ConfigureAwait(false); - await Task.Delay(100).ConfigureAwait(false); - } + await e.Channel.DeleteMessages((await e.Channel.DownloadMessages(val).ConfigureAwait(false)).ToArray()).ConfigureAwait(false); return; } //else if first argument is user @@ -679,20 +666,10 @@ namespace NadekoBot.Modules.Administration val = 100; if (!int.TryParse(e.GetArg("num"), out val)) val = 100; - await Task.Run(async () => - { - var msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == usr.Id).Take(val); - foreach (var m in msgs) - { - try - { - await m.Delete().ConfigureAwait(false); - } - catch { } - await Task.Delay(100).ConfigureAwait(false); - } - - }).ConfigureAwait(false); + msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == usr.Id).Take(val).ToArray(); + if (!msgs.Any()) + return; + await e.Channel.DeleteMessages(msgs).ConfigureAwait(false); }); cgb.CreateCommand(Prefix + "die") From 60bc7d4f7179a951fd115244e9c655b366881ee7 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 18:27:16 +0200 Subject: [PATCH 61/89] changed !m to !!, and there is no space now. This won't apply to people with old configuration.json --- NadekoBot/Modules/Music/MusicModule.cs | 124 +++++++++--------- NadekoBot/_Models/JSONModels/Configuration.cs | 2 +- NadekoBot/bin/Debug/data/config_example.json | 2 +- 3 files changed, 64 insertions(+), 64 deletions(-) diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 8c04a58b..c9900e7c 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -31,16 +31,16 @@ namespace NadekoBot.Modules.Music { var client = NadekoBot.Client; - manager.CreateCommands(Prefix, cgb => + manager.CreateCommands("", cgb => { cgb.AddCheck(PermissionChecker.Instance); commands.ForEach(cmd => cmd.Init(cgb)); - cgb.CreateCommand("next") - .Alias("n") - .Alias("skip") + cgb.CreateCommand(Prefix + "next") + .Alias(Prefix + "n") + .Alias(Prefix + "skip") .Description("Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n`") .Do(e => { @@ -50,8 +50,8 @@ namespace NadekoBot.Modules.Music musicPlayer.Next(); }); - cgb.CreateCommand("stop") - .Alias("s") + cgb.CreateCommand(Prefix + "stop") + .Alias(Prefix + "s") .Description("Stops the music and clears the playlist. Stays in the channel. | `!m s`") .Do(e => { @@ -64,8 +64,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("destroy") - .Alias("d") + cgb.CreateCommand(Prefix + "destroy") + .Alias(Prefix + "d") .Description("Completely stops the music and unbinds the bot from the channel. " + "(may cause weird behaviour) | `!m d`") .Do(e => @@ -76,8 +76,8 @@ namespace NadekoBot.Modules.Music musicPlayer.Destroy(); }); - cgb.CreateCommand("pause") - .Alias("p") + cgb.CreateCommand(Prefix + "pause") + .Alias(Prefix + "p") .Description("Pauses or Unpauses the song. | `!m p`") .Do(async e => { @@ -92,9 +92,9 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage("🎵`Music Player unpaused.`").ConfigureAwait(false); }); - cgb.CreateCommand("queue") - .Alias("q") - .Alias("yq") + cgb.CreateCommand(Prefix + "queue") + .Alias(Prefix + "q") + .Alias(Prefix + "yq") .Description("Queue a song using keywords or a link. Bot will join your voice channel." + "**You must be in a voice channel**. | `!m q Dream Of Venice`") .Parameter("query", ParameterType.Unparsed) @@ -108,8 +108,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("soundcloudqueue") - .Alias("sq") + cgb.CreateCommand(Prefix + "soundcloudqueue") + .Alias(Prefix + "sq") .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + "**You must be in a voice channel**. | `!m sq Dream Of Venice`") .Parameter("query", ParameterType.Unparsed) @@ -123,8 +123,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("listqueue") - .Alias("lq") + cgb.CreateCommand(Prefix + "listqueue") + .Alias(Prefix + "lq") .Description("Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2`") .Parameter("page", ParameterType.Optional) .Do(async e => @@ -161,8 +161,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false); }); - cgb.CreateCommand("nowplaying") - .Alias("np") + cgb.CreateCommand(Prefix + "nowplaying") + .Alias(Prefix + "np") .Description("Shows the song currently playing. | `!m np`") .Do(async e => { @@ -176,8 +176,8 @@ namespace NadekoBot.Modules.Music $"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false); }); - cgb.CreateCommand("volume") - .Alias("vol") + cgb.CreateCommand(Prefix + "volume") + .Alias(Prefix + "vol") .Description("Sets the music volume 0-100% | `!m vol 50`") .Parameter("val", ParameterType.Required) .Do(async e => @@ -198,8 +198,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎵 `Volume set to {volume}%`").ConfigureAwait(false); }); - cgb.CreateCommand("defvol") - .Alias("dv") + cgb.CreateCommand(Prefix + "defvol") + .Alias(Prefix + "dv") .Description("Sets the default music volume when music playback is started (0-100)." + " Persists through restarts. | `!m dv 80`") .Parameter("val", ParameterType.Required) @@ -217,8 +217,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎵 `Default volume set to {volume}%`").ConfigureAwait(false); }); - cgb.CreateCommand("mute") - .Alias("min") + cgb.CreateCommand(Prefix + "mute") + .Alias(Prefix + "min") .Description("Sets the music volume to 0% | `!m min`") .Do(e => { @@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Music musicPlayer.SetVolume(0); }); - cgb.CreateCommand("max") + cgb.CreateCommand(Prefix + "max") .Description("Sets the music volume to 100%. | `!m max`") .Do(e => { @@ -242,7 +242,7 @@ namespace NadekoBot.Modules.Music musicPlayer.SetVolume(100); }); - cgb.CreateCommand("half") + cgb.CreateCommand(Prefix + "half") .Description("Sets the music volume to 50%. | `!m half`") .Do(e => { @@ -254,8 +254,8 @@ namespace NadekoBot.Modules.Music musicPlayer.SetVolume(50); }); - cgb.CreateCommand("shuffle") - .Alias("sh") + cgb.CreateCommand(Prefix + "shuffle") + .Alias(Prefix + "sh") .Description("Shuffles the current playlist. | `!m sh`") .Do(async e => { @@ -274,8 +274,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage("🎵 `Songs shuffled.`").ConfigureAwait(false); }); - cgb.CreateCommand("playlist") - .Alias("pl") + cgb.CreateCommand(Prefix + "playlist") + .Alias(Prefix + "pl") .Description("Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name`") .Parameter("playlist", ParameterType.Unparsed) .Do(async e => @@ -318,8 +318,8 @@ namespace NadekoBot.Modules.Music await msg.Edit("🎵 `Playlist queue complete.`").ConfigureAwait(false); }); - cgb.CreateCommand("soundcloudpl") - .Alias("scpl") + cgb.CreateCommand(Prefix + "soundcloudpl") + .Alias(Prefix + "scpl") .Description("Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony`") .Parameter("pl", ParameterType.Unparsed) .Do(async e => @@ -353,8 +353,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("localplaylst") - .Alias("lopl") + cgb.CreateCommand(Prefix + "localplaylst") + .Alias(Prefix + "lopl") .Description("Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical`") .Parameter("directory", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) @@ -384,7 +384,7 @@ namespace NadekoBot.Modules.Music catch { } }); - cgb.CreateCommand("radio").Alias("ra") + cgb.CreateCommand(Prefix + "radio").Alias(Prefix + "ra") .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `!m ra radio link here`") .Parameter("radio_link", ParameterType.Required) .Do(async e => @@ -402,8 +402,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("local") - .Alias("lo") + cgb.CreateCommand(Prefix + "local") + .Alias(Prefix + "lo") .Description("Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3`") .Parameter("path", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) @@ -415,8 +415,8 @@ namespace NadekoBot.Modules.Music await QueueSong(e.User, e.Channel, e.User.VoiceChannel, e.GetArg("path"), musicType: MusicType.Local).ConfigureAwait(false); }); - cgb.CreateCommand("move") - .Alias("mv") + cgb.CreateCommand(Prefix + "move") + .Alias(Prefix + "mv") .Description("Moves the bot to your voice channel. (works only if music is already playing) | `!m mv`") .Do(e => { @@ -427,8 +427,8 @@ namespace NadekoBot.Modules.Music musicPlayer.MoveToVoiceChannel(voiceChannel); }); - cgb.CreateCommand("remove") - .Alias("rm") + cgb.CreateCommand(Prefix + "remove") + .Alias(Prefix + "rm") .Description("Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5`") .Parameter("num", ParameterType.Required) .Do(async e => @@ -460,8 +460,8 @@ namespace NadekoBot.Modules.Music }); //var msRegex = new Regex(@"(?\d+)>(?\d+)", RegexOptions.Compiled); - cgb.CreateCommand("movesong") - .Alias("ms") + cgb.CreateCommand(Prefix + "movesong") + .Alias(Prefix + "ms") .Description($"Moves a song from one position to another. | `{Prefix} ms` 5>3") .Parameter("fromto") .Do(async e => @@ -496,8 +496,8 @@ namespace NadekoBot.Modules.Music }); - cgb.CreateCommand("setmaxqueue") - .Alias("smq") + cgb.CreateCommand(Prefix + "setmaxqueue") + .Alias(Prefix + "smq") .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. | `{Prefix} smq` 50 or `{Prefix} smq`") .Parameter("size", ParameterType.Unparsed) .Do(async e => @@ -519,7 +519,7 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎵 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); }); - cgb.CreateCommand("cleanup") + cgb.CreateCommand(Prefix + "cleanup") .Description("Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup`") .AddCheck(SimpleCheckers.OwnerOnly()) .Do(e => @@ -537,8 +537,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("reptcursong") - .Alias("rcs") + cgb.CreateCommand(Prefix + "reptcursong") + .Alias(Prefix + "rcs") .Description("Toggles repeat of current song. | `!m rcs`") .Do(async e => { @@ -555,8 +555,8 @@ namespace NadekoBot.Modules.Music .ConfigureAwait(false); }); - cgb.CreateCommand("rpeatplaylst") - .Alias("rpl") + cgb.CreateCommand(Prefix + "rpeatplaylst") + .Alias(Prefix + "rpl") .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl`") .Do(async e => { @@ -567,7 +567,7 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎵🔁`Repeat playlist {(currentValue ? "enabled" : "disabled")}`").ConfigureAwait(false); }); - cgb.CreateCommand("save") + cgb.CreateCommand(Prefix + "save") .Description("Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1`") .Parameter("name", ParameterType.Unparsed) .Do(async e => @@ -620,7 +620,7 @@ namespace NadekoBot.Modules.Music }); - cgb.CreateCommand("load") + cgb.CreateCommand(Prefix + "load") .Description("Loads a playlist under a certain name. | `!m load classical-1`") .Parameter("name", ParameterType.Unparsed) .Do(async e => @@ -679,8 +679,8 @@ namespace NadekoBot.Modules.Music } }); - cgb.CreateCommand("playlists") - .Alias("pls") + cgb.CreateCommand(Prefix + "playlists") + .Alias(Prefix + "pls") .Description("Lists all playlists. Paginated. 20 per page. Default page is 0. |`!m pls 1`") .Parameter("num", ParameterType.Optional) .Do(e => @@ -696,8 +696,8 @@ namespace NadekoBot.Modules.Music e.Channel.SendMessage($"```js\n--- List of saved playlists ---\n\n" + string.Join("\n", result.Select(r => $"'{r.Name}-{r.Id}' by {r.Creator} ({r.SongCnt} songs)")) + $"\n\n --- Page {num} ---```").ConfigureAwait(false); }); - cgb.CreateCommand("deleteplaylist") - .Alias("delpls") + cgb.CreateCommand(Prefix + "deleteplaylist") + .Alias(Prefix + "delpls") .Description("Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5`") .Parameter("pl", ParameterType.Required) .Do(async e => @@ -713,7 +713,7 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage("`Ok.` :ok:").ConfigureAwait(false); }); - cgb.CreateCommand("goto") + cgb.CreateCommand(Prefix + "goto") .Description("Goes to a specific time in seconds in a song.") .Parameter("time") .Do(async e => @@ -750,8 +750,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false); }); - cgb.CreateCommand("getlink") - .Alias("gl") + cgb.CreateCommand(Prefix + "getlink") + .Alias(Prefix + "gl") .Description("Shows a link to the currently playing song.") .Do(async e => { @@ -764,8 +764,8 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎶`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false); }); - cgb.CreateCommand("autoplay") - .Alias("ap") + cgb.CreateCommand(Prefix + "autoplay") + .Alias(Prefix + "ap") .Description("Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty)") .Do(async e => { diff --git a/NadekoBot/_Models/JSONModels/Configuration.cs b/NadekoBot/_Models/JSONModels/Configuration.cs index 93f9ebb9..295134fe 100644 --- a/NadekoBot/_Models/JSONModels/Configuration.cs +++ b/NadekoBot/_Models/JSONModels/Configuration.cs @@ -175,7 +175,7 @@ Nadeko Support Server: "; public string Conversations { get; set; } = "<@{0}>"; public string ClashOfClans { get; set; } = ","; public string Help { get; set; } = "-"; - public string Music { get; set; } = "!m"; + public string Music { get; set; } = "!!"; public string Trello { get; set; } = "trello "; public string Games { get; set; } = ">"; public string Gambling { get; set; } = "$"; diff --git a/NadekoBot/bin/Debug/data/config_example.json b/NadekoBot/bin/Debug/data/config_example.json index 469aa4c0..1ae8a26e 100644 --- a/NadekoBot/bin/Debug/data/config_example.json +++ b/NadekoBot/bin/Debug/data/config_example.json @@ -83,7 +83,7 @@ "Conversations": "<@{0}>", "ClashOfClans": ",", "Help": "-", - "Music": "!m", + "Music": "!!", "Trello": "trello ", "Games": ">", "Gambling": "$", From f08f12b0b20c5d6a736a83f2e2358add342e101a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 20:47:05 +0200 Subject: [PATCH 62/89] Image and isntruction in planting/picking/generatingcurrency are now deleted when image is deleted #387 --- NadekoBot/Modules/Games/Commands/PlantPick.cs | 35 +++++++++---------- 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index 6f01c170..c417b129 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -1,9 +1,11 @@ using Discord; using Discord.Commands; using NadekoBot.Classes; +using NadekoBot.Extensions; using NadekoBot.Modules.Permissions.Classes; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -44,21 +46,18 @@ namespace NadekoBot.Modules.Games.Commands if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) { var rnd = Math.Abs(GetRandomNumber()); - if ((rnd % 50) == 0) + if ((rnd % 2) == 0) { - var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); - var msg2 = await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`"); - plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; }); + var msgs = new[] { await e.Channel.SendFile(GetRandomCurrencyImagePath()), await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`") }; + plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msgs, (u, m) => { m.ForEach(async msgToDelete => { try { await msgToDelete.Delete(); } catch { } }); return msgs; }); plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now); - await Task.Delay(5000); - await msg2.Delete(); } } } catch { } } //channelid/messageid pair - ConcurrentDictionary plantedFlowerChannels = new ConcurrentDictionary(); + ConcurrentDictionary> plantedFlowerChannels = new ConcurrentDictionary>(); private object locker = new object(); @@ -68,22 +67,24 @@ namespace NadekoBot.Modules.Games.Commands .Description("Picks a flower planted in this channel.") .Do(async e => { - Message msg; + IEnumerable msgs; await e.Message.Delete().ConfigureAwait(false); - if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msg)) + if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msgs)) return; - await msg.Delete().ConfigureAwait(false); + foreach(var msgToDelete in msgs) + await msgToDelete.Delete().ConfigureAwait(false); + await FlowersHandler.AddFlowersAsync(e.User, "Picked a flower.", 1, true).ConfigureAwait(false); - msg = await e.Channel.SendMessage($"**{e.User.Name}** picked a {NadekoBot.Config.CurrencyName}!").ConfigureAwait(false); + var msg = await e.Channel.SendMessage($"**{e.User.Name}** picked a {NadekoBot.Config.CurrencyName}!").ConfigureAwait(false); await Task.Delay(10000).ConfigureAwait(false); await msg.Delete().ConfigureAwait(false); }); cgb.CreateCommand(Module.Prefix + "plant") .Description("Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)") - .Do(async e => + .Do(e => { lock (locker) { @@ -92,7 +93,7 @@ namespace NadekoBot.Modules.Games.Commands e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel."); return; } - var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1).GetAwaiter().GetResult(); + var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).GetAwaiter().GetResult(); if (!removed) { e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait(); @@ -106,12 +107,10 @@ namespace NadekoBot.Modules.Games.Commands msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult(); else msg = e.Channel.SendFile(file).GetAwaiter().GetResult(); - plantedFlowerChannels.TryAdd(e.Channel.Id, msg); + var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]); + var msg2 = e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").GetAwaiter().GetResult(); + plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 }); } - var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]); - var msg2 = await e.Channel.SendMessage($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick"); - await Task.Delay(20000).ConfigureAwait(false); - await msg2.Delete().ConfigureAwait(false); }); cgb.CreateCommand(Prefix + "gencurrency") From 90612676d378a4f008394bef78e369d2392fd459 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 19 Jul 2016 23:27:19 +0200 Subject: [PATCH 63/89] >gc reduced to 1% --- NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs | 3 +-- NadekoBot/Modules/Games/Commands/PlantPick.cs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs index 532f3cbb..9c383509 100644 --- a/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs +++ b/NadekoBot/Modules/ClashOfClans/ClashOfClansModule.cs @@ -27,8 +27,7 @@ namespace NadekoBot.Modules.ClashOfClans cgb.CreateCommand(Prefix + "createwar") .Alias(Prefix + "cw") - .Description( - $"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{Prefix}cw 15 The Enemy Clan") + .Description($"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{Prefix}cw 15 The Enemy Clan") .Parameter("size") .Parameter("enemy_clan", ParameterType.Unparsed) .Do(async e => diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index c417b129..ec56fc3c 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -45,8 +45,8 @@ namespace NadekoBot.Modules.Games.Commands if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd)) if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) { - var rnd = Math.Abs(GetRandomNumber()); - if ((rnd % 2) == 0) + var rnd = Math.Abs(rng.Next(0,101)); + if (rnd == 0) { var msgs = new[] { await e.Channel.SendFile(GetRandomCurrencyImagePath()), await e.Channel.SendMessage($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`") }; plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msgs, (u, m) => { m.ForEach(async msgToDelete => { try { await msgToDelete.Delete(); } catch { } }); return msgs; }); From 0ea71eb490de355e38ff2646cb8a22e4709d0349 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 16:39:02 +0200 Subject: [PATCH 64/89] ~g is alias for ~google now --- NadekoBot/Modules/Searches/SearchesModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NadekoBot/Modules/Searches/SearchesModule.cs b/NadekoBot/Modules/Searches/SearchesModule.cs index e9a30cc8..15c1c9be 100644 --- a/NadekoBot/Modules/Searches/SearchesModule.cs +++ b/NadekoBot/Modules/Searches/SearchesModule.cs @@ -221,6 +221,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】 }); cgb.CreateCommand(Prefix + "google") + .Alias(Prefix + "g") .Description("Get a google search link for some terms.") .Parameter("terms", ParameterType.Unparsed) .Do(async e => From e54f1b8575c5480dc5bd7f906ee78d694cb293c1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 16:40:05 +0200 Subject: [PATCH 65/89] reduced global nadeko startup wait, added a callback on when the IsReady flag is set to true --- NadekoBot/NadekoBot.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/NadekoBot/NadekoBot.cs b/NadekoBot/NadekoBot.cs index ad1b0031..4a091e3e 100644 --- a/NadekoBot/NadekoBot.cs +++ b/NadekoBot/NadekoBot.cs @@ -41,6 +41,7 @@ namespace NadekoBot public static LocalizedStrings Locale { get; set; } = new LocalizedStrings(); public static string BotMention { get; set; } = ""; public static bool Ready { get; set; } = false; + public static Action OnReady { get; set; } = delegate { }; private static List OwnerPrivateChannels { get; set; } @@ -196,7 +197,7 @@ namespace NadekoBot return; } #if NADEKO_RELEASE - await Task.Delay(150000).ConfigureAwait(false); + await Task.Delay(90000).ConfigureAwait(false); #else await Task.Delay(1000).ConfigureAwait(false); #endif @@ -229,6 +230,7 @@ namespace NadekoBot }; PermissionsHandler.Initialize(); NadekoBot.Ready = true; + NadekoBot.OnReady(); }); Console.WriteLine("Exiting..."); Console.ReadKey(); @@ -277,5 +279,3 @@ namespace NadekoBot } } } - -//95520984584429568 meany From 4a45749ae9c54469834c13a41b2a73a35eb3a5a5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 17:26:52 +0200 Subject: [PATCH 66/89] commandlist updated to reflect 0.99.9 --- commandlist.md | 103 ++++++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 48 deletions(-) diff --git a/commandlist.md b/commandlist.md index 3a9cce39..6ea01ca6 100644 --- a/commandlist.md +++ b/commandlist.md @@ -1,8 +1,8 @@ ######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/** -######You can donate on paypal: `nadekodiscordbot@gmail.com` or Bitcoin `17MZz1JAqME39akMLrVT4XBPffQJ2n1EPa` +######You can donate on paypal: `nadekodiscordbot@gmail.com` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6036.32870` +Version: `NadekoBot v0.9.6045.31170` ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -25,6 +25,7 @@ Command and aliases | Description | Usage `.greetpm` | Toggles whether the greet messages will be sent in a PM or in the text channel. `.spmom` | Toggles whether mentions of other offline users on your server will send a pm to them. `.logserver` | Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!** +`.logignore`, `Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.` | `.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle. `.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now. `.repeatinvoke`, `.repinv` | Immediately shows the repeat message and restarts the timer. @@ -42,6 +43,7 @@ Command and aliases | Description | Usage `.asar` | Adds a role, or list of roles separated by whitespace(use quotations for multiword roles) to the list of self-assignable roles. | .asar Gamer `.rsar` | Removes a specified role from the list of self-assignable roles. `.lsar` | Lists all self-assignable roles. +`.togglexclsar`, `.tesar` | toggle whether the self-assigned roles should be exclusive `.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 `.addcustreact`, `.acr` | Add a custom reaction. Guide here: **Bot Owner Only!** | .acr "hello" I love saying hello to %user% @@ -86,7 +88,6 @@ Command and aliases | Description | Usage `.donators` | List of lovely people who donated to keep this project alive. `.donadd` | Add a donator to the database. `.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | .announce Useless spam -`.leave` | Leaves a server with a supplied ID. | `.leave 493243292839` `.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150` ### Utility @@ -145,6 +146,8 @@ Command and aliases | Description | Usage `;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl #some_channel `;cubl` | Unblacklists a mentioned channel (#general for example). | ;cubl #some_channel `;sbl` | Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | ;sbl [servername/serverid] +`;cmdcooldown`, `;cmdcd` | Sets a cooldown per user for a command. Set 0 to clear. | `;cmdcd "some cmd" 5` +`;allcmdcooldowns`, `;acmdcds` | Shows a list of all commands and their respective cooldowns. ### Conversations Command and aliases | Description | Usage @@ -157,7 +160,6 @@ Command and aliases | Description | Usage `@BotName do you love me` | Replies with positive answer only to the bot owner. `@BotName how are you`, `@BotName how are you?` | Replies positive only if bot owner is online. `@BotName fire` | Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire. | @NadekoBot fire [x] -`@BotName slm` | Shows the message where you were last mentioned in this channel (checks last 10k messages) `@BotName dump` | Dumps all of the invites it can to dump.txt.** Owner Only.** `@BotName ab` | Try to get 'abalabahaha' @@ -167,13 +169,16 @@ 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 [x] `$shuffle`, `$sh` | Reshuffles all cards back into the deck. `$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 award you double flowers you've bet. | `$bf 5 heads` or `$bf 3 t` `$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. | $roll or $roll 7 or $roll 3d5 +`$rolluo` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered). If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5 `$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` `$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. `$$$` | Check how much NadekoFlowers a person has. (Defaults to yourself) | `$$$` or `$$$ @Someone` `$give` | Give someone a certain amount of NadekoFlowers `$award` | Gives someone a certain amount of flowers. **Bot Owner Only!** | `$award 100 @person` `$take` | Takes a certain amount of flowers from someone. **Bot Owner Only!** +`$betroll`, `$br` | Bets a certain amount of NadekoFlowers and rolls a dice. Rolling over 66 yields x2 flowers, over 90 - x3 and 100 x10. | $br 5 `$leaderboard`, `$lb` | ### Games @@ -199,39 +204,39 @@ Command and aliases | Description | Usage ### Music Command and aliases | Description | Usage ----------------|--------------|------- -`!m next`, `!m n`, `!m skip` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n` -`!m stop`, `!m s` | Stops the music and clears the playlist. Stays in the channel. | `!m s` -`!m destroy`, `!m d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!m d` -`!m pause`, `!m p` | Pauses or Unpauses the song. | `!m p` -`!m queue`, `!m q`, `!m yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!m q Dream Of Venice` -`!m soundcloudqueue`, `!m sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!m sq Dream Of Venice` -`!m listqueue`, `!m lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2` -`!m nowplaying`, `!m np` | Shows the song currently playing. | `!m np` -`!m volume`, `!m vol` | Sets the music volume 0-100% | `!m vol 50` -`!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80` -`!m mute`, `!m min` | Sets the music volume to 0% | `!m min` -`!m max` | Sets the music volume to 100%. | `!m max` -`!m half` | Sets the music volume to 50%. | `!m half` -`!m shuffle`, `!m sh` | Shuffles the current playlist. | `!m sh` -`!m playlist`, `!m pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name` -`!m soundcloudpl`, `!m scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` -`!m localplaylst`, `!m lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical` -`!m radio`, `!m ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf | `!m ra radio link here` -`!m local`, `!m lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3` -`!m move`, `!m mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!m mv` -`!m remove`, `!m rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5` -`!m movesong`, `!m ms` | Moves a song from one position to another. | `!m ms` 5>3 -`!m setmaxqueue`, `!m smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!m smq` 50 or `!m smq` -`!m cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup` -`!m reptcursong`, `!m rcs` | Toggles repeat of current song. | `!m rcs` -`!m rpeatplaylst`, `!m rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl` -`!m save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1` -`!m load` | Loads a playlist under a certain name. | `!m load classical-1` -`!m playlists`, `!m pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!m pls 1` -`!m deleteplaylist`, `!m delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5` -`!m goto` | Goes to a specific time in seconds in a song. -`!m getlink`, `!m gl` | Shows a link to the currently playing song. -`!m autoplay`, `!m ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) +`!!next`, `!!n`, `!!skip` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n` +`!!stop`, `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!m s` +`!!destroy`, `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!m d` +`!!pause`, `!!p` | Pauses or Unpauses the song. | `!m p` +`!!queue`, `!!q`, `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!m q Dream Of Venice` +`!!soundcloudqueue`, `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!m sq Dream Of Venice` +`!!listqueue`, `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2` +`!!nowplaying`, `!!np` | Shows the song currently playing. | `!m np` +`!!volume`, `!!vol` | Sets the music volume 0-100% | `!m vol 50` +`!!defvol`, `!!dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80` +`!!mute`, `!!min` | Sets the music volume to 0% | `!m min` +`!!max` | Sets the music volume to 100%. | `!m max` +`!!half` | Sets the music volume to 50%. | `!m half` +`!!shuffle`, `!!sh` | Shuffles the current playlist. | `!m sh` +`!!playlist`, `!!pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name` +`!!soundcloudpl`, `!!scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` +`!!localplaylst`, `!!lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical` +`!!radio`, `!!ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `!m ra radio link here` +`!!local`, `!!lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3` +`!!move`, `!!mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!m mv` +`!!remove`, `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5` +`!!movesong`, `!!ms` | Moves a song from one position to another. | `!! ms` 5>3 +`!!setmaxqueue`, `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!! smq` 50 or `!! smq` +`!!cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup` +`!!reptcursong`, `!!rcs` | Toggles repeat of current song. | `!m rcs` +`!!rpeatplaylst`, `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl` +`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1` +`!!load` | Loads a playlist under a certain name. | `!m load classical-1` +`!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!m pls 1` +`!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5` +`!!goto` | Goes to a specific time in seconds in a song. +`!!getlink`, `!!gl` | Shows a link to the currently playing song. +`!!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) ### Searches Command and aliases | Description | Usage @@ -266,6 +271,7 @@ Command and aliases | Description | Usage `~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. +`~google`, `~g` | Get a google search link for some terms. `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | ~hs Ysera `~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple `~#` | Searches Tagdef.com for a hashtag. | ~# ff @@ -313,7 +319,7 @@ 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` `>movelist`, `>ml` | Lists the moves you are able to use -`>heal` | Heals someone. Revives those that fainted. Costs a NadekoFlower | >revive @someone +`>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. | >settype fire @@ -331,16 +337,17 @@ Command and aliases | Description | Usage `moveto` | Custom reaction. | moveto `comeatmebro` | Custom reaction. | comeatmebro `e` | Custom reaction. | e -`@BotName insult`, `<@!119777021319577610> insult` | Custom reaction. | %mention% insult -`@BotName praise`, `<@!119777021319577610> praise` | Custom reaction. | %mention% praise -`@BotName pat`, `<@!119777021319577610> pat` | Custom reaction. | %mention% pat -`@BotName cry`, `<@!119777021319577610> cry` | Custom reaction. | %mention% cry -`@BotName are you real?`, `<@!119777021319577610> are you real?` | Custom reaction. | %mention% are you real? -`@BotName are you there?`, `<@!119777021319577610> are you there?` | Custom reaction. | %mention% are you there? -`@BotName draw`, `<@!119777021319577610> draw` | Custom reaction. | %mention% draw -`@BotName bb`, `<@!119777021319577610> bb` | Custom reaction. | %mention% bb -`@BotName call`, `<@!119777021319577610> call` | Custom reaction. | %mention% call -`@BotName disguise`, `<@!119777021319577610> disguise` | Custom reaction. | %mention% disguise +`@BotName insult`, `<@!116275390695079945> insult` | Custom reaction. | %mention% insult +`@BotName praise`, `<@!116275390695079945> praise` | Custom reaction. | %mention% praise +`@BotName pat`, `<@!116275390695079945> pat` | Custom reaction. | %mention% pat +`@BotName cry`, `<@!116275390695079945> cry` | Custom reaction. | %mention% cry +`@BotName are you real?`, `<@!116275390695079945> are you real?` | Custom reaction. | %mention% are you real? +`@BotName are you there?`, `<@!116275390695079945> are you there?` | Custom reaction. | %mention% are you there? +`@BotName draw`, `<@!116275390695079945> draw` | Custom reaction. | %mention% draw +`@BotName bb`, `<@!116275390695079945> bb` | Custom reaction. | %mention% bb +`@BotName call`, `<@!116275390695079945> call` | Custom reaction. | %mention% call +`@BotName disguise`, `<@!116275390695079945> disguise` | Custom reaction. | %mention% disguise +`~hentai` | Custom reaction. | ~hentai ### Trello Command and aliases | Description | Usage From de70e8c87fa51beb2bb01d97e63df0afaae971ea Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 17:38:02 +0200 Subject: [PATCH 67/89] Don't treat warning as errors --- NadekoBot/NadekoBot.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 09bc70ea..b2dc869e 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -44,6 +44,7 @@ prompt 4 true + CS AnyCPU From 14c5aeecf5b2862f027837cfd5a84a69138b16b0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 17:49:20 +0200 Subject: [PATCH 68/89] maybe? --- NadekoBot/NadekoBot.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index b2dc869e..d3984e55 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -40,11 +40,11 @@ full false bin\Debug\ - TRACE;DEBUG;__DEMO__,__DEMO_EXPERIMENTAL__ + TRACE;DEBUG prompt 4 true - CS + CS0612 AnyCPU From 01970345644ead39d2277ca7f957222a2fb9ec2d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 17:54:01 +0200 Subject: [PATCH 69/89] ok i give up --- NadekoBot/NadekoBot.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index d3984e55..90802d35 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -42,9 +42,9 @@ bin\Debug\ TRACE;DEBUG prompt - 4 + 0 true - CS0612 + 0612 AnyCPU From 659c751055f7b7e7b25bf58b05aadcf20eaafe2f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 20:25:10 +0200 Subject: [PATCH 70/89] fixed .logignore description/alias --- NadekoBot/Modules/Administration/Commands/LogCommand.cs | 2 +- NadekoBot/NadekoBot.csproj | 5 +++-- commandlist.md | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 25107ca0..3bbce6d2 100644 --- a/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -378,7 +378,7 @@ $@"🕔`{prettyCurrentTime}` **Message** 📝 `#{e.Channel.Name}` cgb.CreateCommand(Prefix + "logignore") - .Alias($"Toggles whether the {Prefix}logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.") + .Description($"Toggles whether the {Prefix}logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.") .AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.ManageServer()) .Do(async e => diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 90802d35..7feefb56 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -42,9 +42,10 @@ bin\Debug\ TRACE;DEBUG prompt - 0 + 4 true - 0612 + + AnyCPU diff --git a/commandlist.md b/commandlist.md index 6ea01ca6..48ad48d5 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,7 +2,7 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6045.31170` +Version: `NadekoBot v0.9.6045.36710` ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -25,7 +25,7 @@ Command and aliases | Description | Usage `.greetpm` | Toggles whether the greet messages will be sent in a PM or in the text channel. `.spmom` | Toggles whether mentions of other offline users on your server will send a pm to them. `.logserver` | Toggles logging in this channel. Logs every message sent/deleted/edited on the server. **Bot Owner Only!** -`.logignore`, `Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel.` | +`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. `.userpresence` | Starts logging to this channel when someone from the server goes online/offline/idle. `.voicepresence` | Toggles logging to this channel whenever someone joins or leaves a voice channel you are in right now. `.repeatinvoke`, `.repinv` | Immediately shows the repeat message and restarts the timer. From c5c9e98b516a6ef4aa51999e306be429e803aa9c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 22:41:02 +0200 Subject: [PATCH 71/89] fix #429 --- NadekoBot/Modules/Searches/SearchesModule.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Searches/SearchesModule.cs b/NadekoBot/Modules/Searches/SearchesModule.cs index 15c1c9be..1a2ed20b 100644 --- a/NadekoBot/Modules/Searches/SearchesModule.cs +++ b/NadekoBot/Modules/Searches/SearchesModule.cs @@ -226,10 +226,10 @@ $@"🌍 **Weather for** 【{obj["target"]}】 .Parameter("terms", ParameterType.Unparsed) .Do(async e => { - var terms = e.GetArg("terms")?.Trim().Replace(' ', '+'); + var terms = e.GetArg("terms")?.Trim(); if (string.IsNullOrWhiteSpace(terms)) return; - await e.Channel.SendMessage($"https://google.com/search?q={ HttpUtility.UrlEncode(terms) }") + await e.Channel.SendMessage($"https://google.com/search?q={ HttpUtility.UrlEncode(terms).Replace(' ', '+') }") .ConfigureAwait(false); }); From 2e6bdc9f08d1af46cd5b4cc788b2405cab89a0df Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 22:51:41 +0200 Subject: [PATCH 72/89] woops, fixed #426 , no longer saying queue full when its not --- NadekoBot/Modules/Music/Classes/MusicControls.cs | 2 -- NadekoBot/Modules/Music/MusicModule.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index 41e8c7c1..146ca5ea 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -27,8 +27,6 @@ namespace NadekoBot.Modules.Music.Classes public class MusicPlayer { - public static int MaximumPlaylistSize => 50; - private IAudioClient audioClient { get; set; } private readonly List playlist = new List(); diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index c9900e7c..18b8966c 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Music else if (musicPlayer.RepeatPlaylist) toSend += "🔁"; toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` "; - if (musicPlayer.Playlist.Count >= MusicPlayer.MaximumPlaylistSize) + if (musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) toSend += "**Song queue is full!**\n"; else toSend += "\n"; From ae6deee1db4c6fefaf196b6e77cda8266b6b7cd0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 20 Jul 2016 22:58:41 +0200 Subject: [PATCH 73/89] ace is the strongest high card #423 --- NadekoBot/Modules/Gambling/Helpers/Cards.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Gambling/Helpers/Cards.cs b/NadekoBot/Modules/Gambling/Helpers/Cards.cs index 76dc73a5..1cd4ba95 100644 --- a/NadekoBot/Modules/Gambling/Helpers/Cards.cs +++ b/NadekoBot/Modules/Gambling/Helpers/Cards.cs @@ -226,7 +226,7 @@ namespace NadekoBot.Modules.Gambling.Helpers { return kvp.Key; } - return "High card " + cards.Max().GetName(); + return "High card " + (cards.FirstOrDefault(c => c.Number == 1)?.GetName() ?? cards.Max().GetName()); } } } From c06047b67c670c23aab70d4cc07584875d3880ef Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 01:51:47 +0200 Subject: [PATCH 74/89] reduced per user cooldown to 1 second --- NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs b/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs index 94de43f6..412ce69a 100644 --- a/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs +++ b/NadekoBot/Modules/Permissions/Classes/PermissionChecker.cs @@ -26,8 +26,8 @@ namespace NadekoBot.Modules.Permissions.Classes { while (true) { - //blacklist is cleared every 1.75 seconds. That is the most time anyone will be blocked - await Task.Delay(1750).ConfigureAwait(false); + //blacklist is cleared every 1.00 seconds. That is the most time anyone will be blocked + await Task.Delay(1000).ConfigureAwait(false); timeBlackList.Clear(); } }); From b506064d37ed837a24acfd9d6927bd80399211b8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 02:13:16 +0200 Subject: [PATCH 75/89] rewrite started --- .../Modules/Music/Classes/MusicControls.cs | 200 ++++++++++-------- 1 file changed, 111 insertions(+), 89 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index 146ca5ea..2f0082ee 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -2,7 +2,9 @@ using Discord.Audio; using NadekoBot.Extensions; using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Music.Classes @@ -52,6 +54,8 @@ namespace NadekoBot.Modules.Music.Classes public bool Autoplay { get; set; } = false; public uint MaxQueueSize { get; set; } = 0; + private ConcurrentQueue actionQueue { get; set; } = new ConcurrentQueue(); + public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) { if (startingVoiceChannel == null) @@ -66,65 +70,92 @@ namespace NadekoBot.Modules.Music.Classes Task.Run(async () => { - while (!Destroyed) + try { - try - { - if (audioClient?.State != ConnectionState.Connected) - audioClient = await PlaybackVoiceChannel.JoinAudio().ConfigureAwait(false); - } - catch - { - await Task.Delay(1000).ConfigureAwait(false); - continue; - } - CurrentSong = GetNextSong(); - var curSong = CurrentSong; - if (curSong != null) + while (!Destroyed) { try { - OnStarted(this, curSong); - await curSong.Play(audioClient, cancelToken).ConfigureAwait(false); + Action action; + if (actionQueue.TryDequeue(out action)) + { + action(); + } } - catch (OperationCanceledException) + finally { - Console.WriteLine("Song canceled"); + await Task.Delay(100).ConfigureAwait(false); } - catch (Exception ex) - { - Console.WriteLine($"Exception in PlaySong: {ex}"); - } - OnCompleted(this, curSong); - curSong = CurrentSong; //to check if its null now - if (curSong != null) - if (RepeatSong) - playlist.Insert(0, curSong); - else if (RepeatPlaylist) - playlist.Insert(playlist.Count, curSong); - SongCancelSource = new CancellationTokenSource(); - cancelToken = SongCancelSource.Token; } - await Task.Delay(1000).ConfigureAwait(false); } - }); + catch (Exception ex) + { + Console.WriteLine("Action queue crashed"); + Console.WriteLine(ex); + } + }).ConfigureAwait(false); + + var t = new Thread(new ThreadStart(async () => + { + try + { + while (!Destroyed) + { + try + { + if (audioClient.State != ConnectionState.Connected) + { + audioClient = await PlaybackVoiceChannel.JoinAudio(); + continue; + } + + var song = CurrentSong; + + if (song == null) + continue; + + try + { + await song.Play(audioClient, cancelToken); + } + catch (OperationCanceledException) + { + Console.WriteLine("Song canceled"); + SongCancelSource = new CancellationTokenSource(); + cancelToken = SongCancelSource.Token; + } + OnCompleted(this, song); + + if (RepeatPlaylist) + AddSong(song, song.QueuerName); + + if (RepeatSong) + AddSong(song, 0); + } + finally + { + await Task.Delay(300).ConfigureAwait(false); + } + } + } + catch (Exception ex) { + Console.WriteLine("Music thread crashed."); + Console.WriteLine(ex); + } + })); + + t.Start(); } public void Next() { - lock (playlistLock) - { - if (!SongCancelSource.IsCancellationRequested) - { - Paused = false; - SongCancelSource.Cancel(); - } - } + Paused = false; + SongCancelSource.Cancel(); } public void Stop() { - lock (playlistLock) + actionQueue.Enqueue(() => { playlist.Clear(); CurrentSong = null; @@ -132,19 +163,11 @@ namespace NadekoBot.Modules.Music.Classes RepeatSong = false; if (!SongCancelSource.IsCancellationRequested) SongCancelSource.Cancel(); - } + }); } public void TogglePause() => Paused = !Paused; - public void Shuffle() - { - lock (playlistLock) - { - playlist.Shuffle(); - } - } - public int SetVolume(int volume) { if (volume < 0) @@ -156,16 +179,15 @@ namespace NadekoBot.Modules.Music.Classes return volume; } - private Song GetNextSong() + private Song GetNextSong() => + playlist.FirstOrDefault(); + + public void Shuffle() { - lock (playlistLock) + actionQueue.Enqueue(() => { - if (playlist.Count == 0) - return null; - var toReturn = playlist[0]; - playlist.RemoveAt(0); - return toReturn; - } + playlist.Shuffle(); + }); } public void AddSong(Song s, string username) @@ -173,42 +195,63 @@ namespace NadekoBot.Modules.Music.Classes if (s == null) throw new ArgumentNullException(nameof(s)); ThrowIfQueueFull(); - lock (playlistLock) + actionQueue.Enqueue(() => { s.MusicPlayer = this; s.QueuerName = username.TrimTo(10); playlist.Add(s); - } + }); } public void AddSong(Song s, int index) { if (s == null) throw new ArgumentNullException(nameof(s)); - lock (playlistLock) + actionQueue.Enqueue(() => { playlist.Insert(index, s); - } + }); } public void RemoveSong(Song s) { if (s == null) throw new ArgumentNullException(nameof(s)); - lock (playlistLock) + actionQueue.Enqueue(() => { playlist.Remove(s); - } + }); } public void RemoveSongAt(int index) { - lock (playlistLock) + actionQueue.Enqueue(() => { if (index < 0 || index >= playlist.Count) - throw new ArgumentException("Invalid index"); + return; playlist.RemoveAt(index); - } + }); + } + + internal void ClearQueue() + { + actionQueue.Enqueue(() => + { + playlist.Clear(); + }); + } + + public void Destroy() + { + actionQueue.Enqueue(() => + { + playlist.Clear(); + Destroyed = true; + CurrentSong = null; + if (!SongCancelSource.IsCancellationRequested) + SongCancelSource.Cancel(); + audioClient.Disconnect(); + }); } internal Task MoveToVoiceChannel(Channel voiceChannel) @@ -219,27 +262,6 @@ namespace NadekoBot.Modules.Music.Classes return PlaybackVoiceChannel.JoinAudio(); } - internal void ClearQueue() - { - lock (playlistLock) - { - playlist.Clear(); - } - } - - public void Destroy() - { - lock (playlistLock) - { - playlist.Clear(); - Destroyed = true; - CurrentSong = null; - if (!SongCancelSource.IsCancellationRequested) - SongCancelSource.Cancel(); - audioClient.Disconnect(); - } - } - internal bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; From c6af3a6c3eccb58c5f378aa09e50ec3ac405152f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 12:42:17 +0200 Subject: [PATCH 76/89] more musicplayer fixes/rewrites --- .../Modules/Music/Classes/MusicControls.cs | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index 2f0082ee..7cc43771 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -22,7 +22,6 @@ namespace NadekoBot.Modules.Music.Classes { Resolving, Queued, - Buffering, //not using it atm Playing, Completed } @@ -35,7 +34,7 @@ namespace NadekoBot.Modules.Music.Classes public IReadOnlyCollection Playlist => playlist; private readonly object playlistLock = new object(); - public Song CurrentSong { get; set; } = default(Song); + public Song CurrentSong { get; private set; } private CancellationTokenSource SongCancelSource { get; set; } private CancellationToken cancelToken { get; set; } @@ -103,20 +102,22 @@ namespace NadekoBot.Modules.Music.Classes { try { - if (audioClient.State != ConnectionState.Connected) + if (audioClient?.State != ConnectionState.Connected) { audioClient = await PlaybackVoiceChannel.JoinAudio(); continue; } - var song = CurrentSong; + CurrentSong = GetNextSong(); + RemoveSongAt(0); - if (song == null) + if (CurrentSong == null) continue; try { - await song.Play(audioClient, cancelToken); + OnStarted(this, CurrentSong); + await CurrentSong.Play(audioClient, cancelToken); } catch (OperationCanceledException) { @@ -124,17 +125,19 @@ namespace NadekoBot.Modules.Music.Classes SongCancelSource = new CancellationTokenSource(); cancelToken = SongCancelSource.Token; } - OnCompleted(this, song); + OnCompleted(this, CurrentSong); if (RepeatPlaylist) - AddSong(song, song.QueuerName); + AddSong(CurrentSong, CurrentSong.QueuerName); if (RepeatSong) - AddSong(song, 0); + AddSong(CurrentSong, 0); + } finally { await Task.Delay(300).ConfigureAwait(false); + CurrentSong = null; } } } @@ -149,18 +152,20 @@ namespace NadekoBot.Modules.Music.Classes public void Next() { - Paused = false; - SongCancelSource.Cancel(); + actionQueue.Enqueue(() => + { + Paused = false; + SongCancelSource.Cancel(); + }); } public void Stop() { actionQueue.Enqueue(() => { - playlist.Clear(); - CurrentSong = null; RepeatPlaylist = false; RepeatSong = false; + playlist.Clear(); if (!SongCancelSource.IsCancellationRequested) SongCancelSource.Cancel(); }); @@ -245,9 +250,10 @@ namespace NadekoBot.Modules.Music.Classes { actionQueue.Enqueue(() => { - playlist.Clear(); + RepeatPlaylist = false; + RepeatSong = false; Destroyed = true; - CurrentSong = null; + playlist.Clear(); if (!SongCancelSource.IsCancellationRequested) SongCancelSource.Cancel(); audioClient.Disconnect(); From a8f33e56656ba15de910cead14a0c51d59747876 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 12:42:48 +0200 Subject: [PATCH 77/89] song queue full real fix --- NadekoBot/Modules/Games/Commands/PlantPick.cs | 1 - NadekoBot/Modules/Games/Commands/SpeedTyping.cs | 2 -- NadekoBot/Modules/Music/MusicModule.cs | 3 +-- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/NadekoBot/Modules/Games/Commands/PlantPick.cs b/NadekoBot/Modules/Games/Commands/PlantPick.cs index ec56fc3c..6af69857 100644 --- a/NadekoBot/Modules/Games/Commands/PlantPick.cs +++ b/NadekoBot/Modules/Games/Commands/PlantPick.cs @@ -102,7 +102,6 @@ namespace NadekoBot.Modules.Games.Commands var file = GetRandomCurrencyImagePath(); Message msg; - //todo send message after, not in lock if (file == null) msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult(); else diff --git a/NadekoBot/Modules/Games/Commands/SpeedTyping.cs b/NadekoBot/Modules/Games/Commands/SpeedTyping.cs index 8a807208..c224de1d 100644 --- a/NadekoBot/Modules/Games/Commands/SpeedTyping.cs +++ b/NadekoBot/Modules/Games/Commands/SpeedTyping.cs @@ -190,8 +190,6 @@ namespace NadekoBot.Modules.Games.Commands await e.Channel.SendMessage("Added new article for typing game.").ConfigureAwait(false); }); - - //todo add user submissions } } } diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 18b8966c..9148c6b9 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Music else if (musicPlayer.RepeatPlaylist) toSend += "🔁"; toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` "; - if (musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) + if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize) toSend += "**Song queue is full!**\n"; else toSend += "\n"; @@ -300,7 +300,6 @@ namespace NadekoBot.Modules.Music await e.Channel.SendMessage($"🎵 `Failed to find any songs.`").ConfigureAwait(false); return; } - //todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE var idArray = ids as string[] ?? ids.ToArray(); var count = idArray.Length; var msg = From f1cf11ae8e6703880f29aea277ca396b024cfbd6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 12:50:33 +0200 Subject: [PATCH 78/89] ~trans now correctly translates multiple sentences at once. Thanks @Myoshu --- NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs b/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs index 1743af45..e6abf475 100644 --- a/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs +++ b/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs @@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Translator.Helpers text = await http.GetStringAsync(url).ConfigureAwait(false); } - return JArray.Parse(text)[0][0][0].ToString(); + return (string.Join("", JArray.Parse(text)[0].Select(x => x[0]))); } #endregion From ee415d166eee0175481a5fefc04bbff82cce1fbb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 12:57:57 +0200 Subject: [PATCH 79/89] string.join("" -> string.concat --- NadekoBot/Classes/Extensions.cs | 6 +++--- NadekoBot/Classes/SearchHelper.cs | 2 +- NadekoBot/Modules/Gambling/Helpers/Cards.cs | 2 +- NadekoBot/Modules/Searches/Commands/MemegenCommands.cs | 2 +- NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs | 2 +- NadekoBot/Modules/Utility/UtilityModule.cs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/NadekoBot/Classes/Extensions.cs b/NadekoBot/Classes/Extensions.cs index 1b162209..a4071ded 100644 --- a/NadekoBot/Classes/Extensions.cs +++ b/NadekoBot/Classes/Extensions.cs @@ -46,10 +46,10 @@ namespace NadekoBot.Extensions if (num == 0) return string.Empty; if (num <= 3) - return string.Join("", str.Select(c => '.')); + return string.Concat(str.Select(c => '.')); if (str.Length < num) return str; - return string.Join("", str.Take(num - 3)) + (hideDots ? "" : "..."); + return string.Concat(str.Take(num - 3)) + (hideDots ? "" : "..."); } /// /// Removes trailing S or ES (if specified) on the given string if the num is 1 @@ -237,7 +237,7 @@ namespace NadekoBot.Extensions public static string Matrix(this string s) => - string.Join("", s.Select(c => c.ToString() + " ̵̢̬̜͉̞̭̖̰͋̉̎ͬ̔̇̌̀".TrimTo(rng.Next(0, 12), true))); + string.Concat(s.Select(c => c.ToString() + " ̵̢̬̜͉̞̭̖̰͋̉̎ͬ̔̇̌̀".TrimTo(rng.Next(0, 12), true))); //.Replace("`", ""); public static void ForEach(this IEnumerable source, Action action) diff --git a/NadekoBot/Classes/SearchHelper.cs b/NadekoBot/Classes/SearchHelper.cs index 77c46151..43bfbe37 100644 --- a/NadekoBot/Classes/SearchHelper.cs +++ b/NadekoBot/Classes/SearchHelper.cs @@ -384,7 +384,7 @@ namespace NadekoBot.Classes { var i = 0; return "```xl\n" + string.Join("\n", items.GroupBy(item => (i++) / cols) - .Select(ig => string.Join("", ig.Select(el => howToPrint(el))))) + .Select(ig => string.Concat(ig.Select(el => howToPrint(el))))) + $"\n```"; } } diff --git a/NadekoBot/Modules/Gambling/Helpers/Cards.cs b/NadekoBot/Modules/Gambling/Helpers/Cards.cs index 1cd4ba95..8408535e 100644 --- a/NadekoBot/Modules/Gambling/Helpers/Cards.cs +++ b/NadekoBot/Modules/Gambling/Helpers/Cards.cs @@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Gambling.Helpers var orderedPool = cardPool.OrderBy(x => r.Next()); cardPool = cardPool as List ?? orderedPool.ToList(); } - public override string ToString() => string.Join("", cardPool.Select(c => c.ToString())) + Environment.NewLine; + public override string ToString() => string.Concat(cardPool.Select(c => c.ToString())) + Environment.NewLine; private static void InitHandValues() { diff --git a/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs b/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs index ebbdbf34..5eff7c78 100644 --- a/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs +++ b/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches.Commands string.Join("\n", JsonConvert.DeserializeObject>(await SearchHelper.GetResponseStringAsync("http://memegen.link/templates/")) .Select(kvp => Path.GetFileName(kvp.Value)) .GroupBy(item => (i++) / 4) - .Select(ig => string.Join("", ig.Select(el => $"{el,-17}")))) + .Select(ig => string.Concat(ig.Select(el => $"{el,-17}")))) + $"\n```").ConfigureAwait(false); }); diff --git a/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs b/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs index e6abf475..b1a10a8c 100644 --- a/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs +++ b/NadekoBot/Modules/Translator/Helpers/GoogleTranslator.cs @@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Translator.Helpers text = await http.GetStringAsync(url).ConfigureAwait(false); } - return (string.Join("", JArray.Parse(text)[0].Select(x => x[0]))); + return (string.Concat(JArray.Parse(text)[0].Select(x => x[0]))); } #endregion diff --git a/NadekoBot/Modules/Utility/UtilityModule.cs b/NadekoBot/Modules/Utility/UtilityModule.cs index c85348aa..380e5acf 100644 --- a/NadekoBot/Modules/Utility/UtilityModule.cs +++ b/NadekoBot/Modules/Utility/UtilityModule.cs @@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Utility if (arr.Length == 0) await e.Channel.SendMessage("Nobody. (not 100% sure)").ConfigureAwait(false); else - await e.Channel.SendMessage("```xl\n" + string.Join("\n", arr.GroupBy(item => (i++) / 3).Select(ig => string.Join("", ig.Select(el => $" {el,-35}")))) + "\n```").ConfigureAwait(false); + await e.Channel.SendMessage("```xl\n" + string.Join("\n", arr.GroupBy(item => (i++) / 3).Select(ig => string.Concat(ig.Select(el => $" {el,-35}")))) + "\n```").ConfigureAwait(false); }); cgb.CreateCommand(Prefix + "inrole") From d69c783f3fdb294ea542745fb7b93cbd886e4c6a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 13:23:22 +0200 Subject: [PATCH 80/89] `@nadeko fire` makes more sense now. --- .../Modules/Conversations/Conversations.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/NadekoBot/Modules/Conversations/Conversations.cs b/NadekoBot/Modules/Conversations/Conversations.cs index cdc6eb26..328be562 100644 --- a/NadekoBot/Modules/Conversations/Conversations.cs +++ b/NadekoBot/Modules/Conversations/Conversations.cs @@ -9,13 +9,14 @@ using System; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using System.Threading.Tasks; namespace NadekoBot.Modules.Conversations { internal class Conversations : DiscordModule { - private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥"; + private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥"; public Conversations() { commands.Add(new RipCommand(this)); @@ -153,22 +154,23 @@ namespace NadekoBot.Modules.Conversations .Parameter("times", ParameterType.Optional) .Do(async e => { - var count = 1; - int.TryParse(e.Args[0], out count); - if (count == 0) + int count; + if (string.IsNullOrWhiteSpace(e.Args[0])) count = 1; + else + int.TryParse(e.Args[0], out count); if (count < 1 || count > 12) { - await e.Channel.SendMessage("Number must be between 0 and 12").ConfigureAwait(false); + await e.Channel.SendMessage("Number must be between 1 and 12").ConfigureAwait(false); return; } - var str = ""; + var str = new StringBuilder(); for (var i = 0; i < count; i++) { - str += firestr; + str.Append(firestr); } - await e.Channel.SendMessage(str).ConfigureAwait(false); + await e.Channel.SendMessage(str.ToString()).ConfigureAwait(false); }); cgb.CreateCommand("dump") From 1195a4c9c8ecee1657eb4c19a97cf54c8f035b54 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 14:07:26 +0200 Subject: [PATCH 81/89] resetting buffer? --- NadekoBot/Modules/Music/Classes/Song.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Music/Classes/Song.cs b/NadekoBot/Modules/Music/Classes/Song.cs index e718302e..025863e0 100644 --- a/NadekoBot/Modules/Music/Classes/Song.cs +++ b/NadekoBot/Modules/Music/Classes/Song.cs @@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Music.Classes public SongInfo SongInfo { get; } public string QueuerName { get; set; } - private PoopyBuffer songBuffer { get; } = new PoopyBuffer(NadekoBot.Config.BufferSize); + private PoopyBuffer songBuffer { get; set; } private bool prebufferingComplete { get; set; } = false; public MusicPlayer MusicPlayer { get; set; } @@ -137,6 +137,9 @@ namespace NadekoBot.Modules.Music.Classes internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) { + // initialize the buffer here because if this song was playing before (requeued), we must delete old buffer data + songBuffer = new PoopyBuffer(NadekoBot.Config.BufferSize); + var bufferTask = BufferSong(cancelToken).ConfigureAwait(false); var bufferAttempts = 0; const int waitPerAttempt = 500; @@ -145,7 +148,6 @@ namespace NadekoBot.Modules.Music.Classes { await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false); } - cancelToken.ThrowIfCancellationRequested(); Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}"); const int blockSize = 3840; var attempt = 0; From dfc710cb86df58b16e6830a76288bc2c617bc632 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 14:07:58 +0200 Subject: [PATCH 82/89] fixed "-h command name" when there is no alias --- NadekoBot/Modules/Help/Commands/HelpCommand.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Help/Commands/HelpCommand.cs b/NadekoBot/Modules/Help/Commands/HelpCommand.cs index 3bf86435..623490e4 100644 --- a/NadekoBot/Modules/Help/Commands/HelpCommand.cs +++ b/NadekoBot/Modules/Help/Commands/HelpCommand.cs @@ -24,8 +24,13 @@ namespace NadekoBot.Classes.Help.Commands var com = NadekoBot.Client.GetService().AllCommands .FirstOrDefault(c => c.Text.ToLowerInvariant().Equals(comToFind) || c.Aliases.Select(a => a.ToLowerInvariant()).Contains(comToFind)); + + var str = ""; + var alias = com.Aliases.FirstOrDefault(); + if (alias != null) + str = $" / `{ com.Aliases.FirstOrDefault()}`"; if (com != null) - await e.Channel.SendMessage($"**__Help for `{com.Text}`__ / __`{("" + com.Aliases.FirstOrDefault() + "" ?? "")}`__**\n**Desc:** {com.Description.Replace("|", "\n**Usage:**")}").ConfigureAwait(false); + await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {com.Description.Replace("|", "\n**Usage:**")}").ConfigureAwait(false); }).ConfigureAwait(false); }; public static string HelpString { From 1437f60d75f7b284c949d61bc330f28d605a41bb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 21 Jul 2016 22:35:49 +0200 Subject: [PATCH 83/89] fixed -h for .send --- NadekoBot/Modules/Help/Commands/HelpCommand.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Help/Commands/HelpCommand.cs b/NadekoBot/Modules/Help/Commands/HelpCommand.cs index 623490e4..4d3fa441 100644 --- a/NadekoBot/Modules/Help/Commands/HelpCommand.cs +++ b/NadekoBot/Modules/Help/Commands/HelpCommand.cs @@ -5,6 +5,7 @@ using NadekoBot.Modules.Permissions.Classes; using System; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using System.Threading.Tasks; namespace NadekoBot.Classes.Help.Commands @@ -30,7 +31,7 @@ namespace NadekoBot.Classes.Help.Commands if (alias != null) str = $" / `{ com.Aliases.FirstOrDefault()}`"; if (com != null) - await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {com.Description.Replace("|", "\n**Usage:**")}").ConfigureAwait(false); + await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**",1)}").ConfigureAwait(false); }).ConfigureAwait(false); }; public static string HelpString { From bd2d0098e03ccbc2ecadbde7f4c78b65464104bb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 22 Jul 2016 13:11:49 +0200 Subject: [PATCH 84/89] Print error to console when .iam throws an exception --- .../Administration/Commands/SelfAssignedRolesCommand.cs | 4 +++- NadekoBot/Modules/Music/Classes/MusicControls.cs | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index 51bf8859..d5855eb4 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -1,6 +1,7 @@ using Discord.Commands; using NadekoBot.Classes; using NadekoBot.Modules.Permissions.Classes; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -144,9 +145,10 @@ namespace NadekoBot.Modules.Administration.Commands { await e.User.AddRoles(role).ConfigureAwait(false); } - catch + catch (Exception ex) { await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); + Console.WriteLine(ex); } var msg = await e.Channel.SendMessage($":ok:You now have {role.Name} role.").ConfigureAwait(false); await Task.Delay(3000).ConfigureAwait(false); diff --git a/NadekoBot/Modules/Music/Classes/MusicControls.cs b/NadekoBot/Modules/Music/Classes/MusicControls.cs index 7cc43771..bcd661f8 100644 --- a/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Music.Classes private readonly List playlist = new List(); public IReadOnlyCollection Playlist => playlist; - private readonly object playlistLock = new object(); public Song CurrentSong { get; private set; } private CancellationTokenSource SongCancelSource { get; set; } From 9a6695cc90778560103c64cd55bc826608040008 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 22 Jul 2016 13:26:05 +0200 Subject: [PATCH 85/89] changed submodule to point to my fork of discord.net --- .gitmodules | 2 +- discord.net | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9ff1a297..1fa69c2d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "discord.net"] path = discord.net - url = git://github.com/rogueexception/discord.net.git + url = git://github.com/kwoth/discord.net.git diff --git a/discord.net b/discord.net index 6bfeaadd..3e519b5e 160000 --- a/discord.net +++ b/discord.net @@ -1 +1 @@ -Subproject commit 6bfeaaddf0cbc83fe0ca44e6164f61c6f8fdaf27 +Subproject commit 3e519b5e0b33175e5a5ca247322b7082de484e15 From 8a9f6f4e2677c97ca5b8569fe34a36f017ab4d1e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 22 Jul 2016 13:41:41 +0200 Subject: [PATCH 86/89] .send is now correctly splitting on "|", not "-" (fixed .send) --- NadekoBot/Modules/Administration/AdministrationModule.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NadekoBot/Modules/Administration/AdministrationModule.cs b/NadekoBot/Modules/Administration/AdministrationModule.cs index cff95af4..ab6dafae 100644 --- a/NadekoBot/Modules/Administration/AdministrationModule.cs +++ b/NadekoBot/Modules/Administration/AdministrationModule.cs @@ -738,7 +738,7 @@ namespace NadekoBot.Modules.Administration if (string.IsNullOrWhiteSpace(msg)) return; - var ids = e.GetArg("ids").Split('-'); + var ids = e.GetArg("ids").Split('|'); if (ids.Length != 2) return; var sid = ulong.Parse(ids[0]); From 68a66e281e367b3e0fa1ca67140935e96f3a1b11 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 22 Jul 2016 15:17:19 +0200 Subject: [PATCH 87/89] fixed error message when doing `.iam`, thanks PeaceLord<3 for debugging --- .../Administration/Commands/SelfAssignedRolesCommand.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index d5855eb4..2ef54772 100644 --- a/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -1,4 +1,5 @@ using Discord.Commands; +using Discord.Net; using NadekoBot.Classes; using NadekoBot.Modules.Permissions.Classes; using System; @@ -145,10 +146,12 @@ namespace NadekoBot.Modules.Administration.Commands { await e.User.AddRoles(role).ConfigureAwait(false); } - catch (Exception ex) + catch(HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError) + { + } + catch (Exception) { await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); - Console.WriteLine(ex); } var msg = await e.Channel.SendMessage($":ok:You now have {role.Name} role.").ConfigureAwait(false); await Task.Delay(3000).ConfigureAwait(false); From c3bd1f5986099f3bb5d539ace48d177f94715371 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 22 Jul 2016 20:50:57 +0200 Subject: [PATCH 88/89] music usages fixed, 64bit release test --- NadekoBot.sln | 44 +++++++++++++++++++ NadekoBot/Classes/DBHandler.cs | 4 +- NadekoBot/Modules/Music/MusicModule.cs | 58 +++++++++++++------------- NadekoBot/NadekoBot.csproj | 40 ++++++++++++++++++ 4 files changed, 114 insertions(+), 32 deletions(-) diff --git a/NadekoBot.sln b/NadekoBot.sln index 155512c4..eb9860d0 100644 --- a/NadekoBot.sln +++ b/NadekoBot.sln @@ -22,51 +22,95 @@ Global EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 FullDebug|Any CPU = FullDebug|Any CPU + FullDebug|x64 = FullDebug|x64 NadekoRelease|Any CPU = NadekoRelease|Any CPU + NadekoRelease|x64 = NadekoRelease|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|Any CPU.Build.0 = Debug|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|x64.ActiveCfg = Debug|x64 + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|x64.Build.0 = Debug|x64 {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|Any CPU.Build.0 = Debug|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|x64.ActiveCfg = Debug|x64 + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|x64.Build.0 = Debug|x64 {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.NadekoRelease|Any CPU.ActiveCfg = NadekoRelease|Any CPU {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.NadekoRelease|Any CPU.Build.0 = NadekoRelease|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.NadekoRelease|x64.ActiveCfg = NadekoRelease|x64 + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.NadekoRelease|x64.Build.0 = NadekoRelease|x64 {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|Any CPU.ActiveCfg = Release|Any CPU {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|Any CPU.Build.0 = Release|Any CPU + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|x64.ActiveCfg = Release|x64 + {27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|x64.Build.0 = Release|x64 {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Debug|x64.ActiveCfg = Debug|x64 + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Debug|x64.Build.0 = Debug|x64 {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|Any CPU.Build.0 = Debug|Any CPU + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|x64.ActiveCfg = Debug|x64 + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|x64.Build.0 = Debug|x64 {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|x64.ActiveCfg = Release|x64 + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|x64.Build.0 = Release|x64 {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|Any CPU.Build.0 = Release|Any CPU + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|x64.ActiveCfg = Release|x64 + {7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|x64.Build.0 = Release|x64 {8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|x64.ActiveCfg = Debug|x64 + {8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|x64.Build.0 = Debug|x64 {8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU {8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|Any CPU.Build.0 = Debug|Any CPU + {8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|x64.ActiveCfg = Debug|x64 + {8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|x64.Build.0 = Debug|x64 {8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU {8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU + {8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|x64.ActiveCfg = Release|x64 + {8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|x64.Build.0 = Release|x64 {8D71A857-879A-4A10-859E-5FF824ED6688}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D71A857-879A-4A10-859E-5FF824ED6688}.Release|Any CPU.Build.0 = Release|Any CPU + {8D71A857-879A-4A10-859E-5FF824ED6688}.Release|x64.ActiveCfg = Release|x64 + {8D71A857-879A-4A10-859E-5FF824ED6688}.Release|x64.Build.0 = Release|x64 {3091164F-66AE-4543-A63D-167C1116241D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3091164F-66AE-4543-A63D-167C1116241D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3091164F-66AE-4543-A63D-167C1116241D}.Debug|x64.ActiveCfg = Debug|x64 + {3091164F-66AE-4543-A63D-167C1116241D}.Debug|x64.Build.0 = Debug|x64 {3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU {3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|Any CPU.Build.0 = Debug|Any CPU + {3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|x64.ActiveCfg = Debug|x64 + {3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|x64.Build.0 = Debug|x64 {3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU {3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU + {3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|x64.ActiveCfg = Release|x64 + {3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|x64.Build.0 = Release|x64 {3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.ActiveCfg = Release|Any CPU {3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.Build.0 = Release|Any CPU + {3091164F-66AE-4543-A63D-167C1116241D}.Release|x64.ActiveCfg = Release|x64 + {3091164F-66AE-4543-A63D-167C1116241D}.Release|x64.Build.0 = Release|x64 {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|x64.ActiveCfg = Debug|x64 + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|x64.Build.0 = Debug|x64 {1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|Any CPU.ActiveCfg = Debug|Any CPU {1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|Any CPU.Build.0 = Debug|Any CPU + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|x64.ActiveCfg = Debug|x64 + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|x64.Build.0 = Debug|x64 {1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.ActiveCfg = Release|Any CPU {1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.Build.0 = Release|Any CPU + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|x64.ActiveCfg = Release|x64 + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|x64.Build.0 = Release|x64 {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.Build.0 = Release|Any CPU + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|x64.ActiveCfg = Release|x64 + {1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/NadekoBot/Classes/DBHandler.cs b/NadekoBot/Classes/DBHandler.cs index 9fb664f9..3f2963cc 100644 --- a/NadekoBot/Classes/DBHandler.cs +++ b/NadekoBot/Classes/DBHandler.cs @@ -161,9 +161,7 @@ namespace NadekoBot.Classes using (var conn = new SQLiteConnection(FilePath)) { foreach (var o in ocol) - { - conn.InsertOrReplace(o, typeof(T)); - } + conn.InsertOrReplace(o); } } diff --git a/NadekoBot/Modules/Music/MusicModule.cs b/NadekoBot/Modules/Music/MusicModule.cs index 9148c6b9..ba04d598 100644 --- a/NadekoBot/Modules/Music/MusicModule.cs +++ b/NadekoBot/Modules/Music/MusicModule.cs @@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "next") .Alias(Prefix + "n") .Alias(Prefix + "skip") - .Description("Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n`") + .Description($"Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `{Prefix}n`") .Do(e => { MusicPlayer musicPlayer; @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "stop") .Alias(Prefix + "s") - .Description("Stops the music and clears the playlist. Stays in the channel. | `!m s`") + .Description($"Stops the music and clears the playlist. Stays in the channel. | `{Prefix}s`") .Do(e => { MusicPlayer musicPlayer; @@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "destroy") .Alias(Prefix + "d") .Description("Completely stops the music and unbinds the bot from the channel. " + - "(may cause weird behaviour) | `!m d`") + $"(may cause weird behaviour) | `{Prefix}d`") .Do(e => { MusicPlayer musicPlayer; @@ -78,7 +78,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "pause") .Alias(Prefix + "p") - .Description("Pauses or Unpauses the song. | `!m p`") + .Description($"Pauses or Unpauses the song. | `{Prefix}p`") .Do(async e => { MusicPlayer musicPlayer; @@ -96,7 +96,7 @@ namespace NadekoBot.Modules.Music .Alias(Prefix + "q") .Alias(Prefix + "yq") .Description("Queue a song using keywords or a link. Bot will join your voice channel." + - "**You must be in a voice channel**. | `!m q Dream Of Venice`") + $"**You must be in a voice channel**. | `{Prefix}q Dream Of Venice`") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -111,7 +111,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "soundcloudqueue") .Alias(Prefix + "sq") .Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + - "**You must be in a voice channel**. | `!m sq Dream Of Venice`") + $"**You must be in a voice channel**. | `{Prefix}sq Dream Of Venice`") .Parameter("query", ParameterType.Unparsed) .Do(async e => { @@ -125,7 +125,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "listqueue") .Alias(Prefix + "lq") - .Description("Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2`") + .Description($"Lists 15 currently queued songs per page. Default page is 1. | `{Prefix}lq` or `{Prefix}lq 2`") .Parameter("page", ParameterType.Optional) .Do(async e => { @@ -163,7 +163,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "nowplaying") .Alias(Prefix + "np") - .Description("Shows the song currently playing. | `!m np`") + .Description($"Shows the song currently playing. | `{Prefix}np`") .Do(async e => { MusicPlayer musicPlayer; @@ -178,7 +178,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "volume") .Alias(Prefix + "vol") - .Description("Sets the music volume 0-100% | `!m vol 50`") + .Description($"Sets the music volume 0-100% | `{Prefix}vol 50`") .Parameter("val", ParameterType.Required) .Do(async e => { @@ -201,7 +201,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "defvol") .Alias(Prefix + "dv") .Description("Sets the default music volume when music playback is started (0-100)." + - " Persists through restarts. | `!m dv 80`") + $" Persists through restarts. | `{Prefix}dv 80`") .Parameter("val", ParameterType.Required) .Do(async e => { @@ -219,7 +219,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "mute") .Alias(Prefix + "min") - .Description("Sets the music volume to 0% | `!m min`") + .Description($"Sets the music volume to 0% | `{Prefix}min`") .Do(e => { MusicPlayer musicPlayer; @@ -231,7 +231,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand(Prefix + "max") - .Description("Sets the music volume to 100%. | `!m max`") + .Description($"Sets the music volume to 100%. | `{Prefix}max`") .Do(e => { MusicPlayer musicPlayer; @@ -243,7 +243,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand(Prefix + "half") - .Description("Sets the music volume to 50%. | `!m half`") + .Description($"Sets the music volume to 50%. | `{Prefix}half`") .Do(e => { MusicPlayer musicPlayer; @@ -256,7 +256,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "shuffle") .Alias(Prefix + "sh") - .Description("Shuffles the current playlist. | `!m sh`") + .Description($"Shuffles the current playlist. | `{Prefix}sh`") .Do(async e => { MusicPlayer musicPlayer; @@ -276,7 +276,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "playlist") .Alias(Prefix + "pl") - .Description("Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name`") + .Description($"Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `{Prefix}pl playlist link or name`") .Parameter("playlist", ParameterType.Unparsed) .Do(async e => { @@ -319,7 +319,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "soundcloudpl") .Alias(Prefix + "scpl") - .Description("Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony`") + .Description($"Queue a soundcloud playlist using a link. | `{Prefix}scpl https://soundcloud.com/saratology/sets/symphony`") .Parameter("pl", ParameterType.Unparsed) .Do(async e => { @@ -354,7 +354,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "localplaylst") .Alias(Prefix + "lopl") - .Description("Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical`") + .Description($"Queues all songs from a directory. **Bot Owner Only!** | `{Prefix}lopl C:/music/classical`") .Parameter("directory", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -384,7 +384,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand(Prefix + "radio").Alias(Prefix + "ra") - .Description("Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `!m ra radio link here`") + .Description($"Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `{Prefix}ra radio link here`") .Parameter("radio_link", ParameterType.Required) .Do(async e => { @@ -403,7 +403,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "local") .Alias(Prefix + "lo") - .Description("Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3`") + .Description($"Queues a local file by specifying a full path. **Bot Owner Only!** | `{Prefix}lo C:/music/mysong.mp3`") .Parameter("path", ParameterType.Unparsed) .AddCheck(SimpleCheckers.OwnerOnly()) .Do(async e => @@ -416,7 +416,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "move") .Alias(Prefix + "mv") - .Description("Moves the bot to your voice channel. (works only if music is already playing) | `!m mv`") + .Description($"Moves the bot to your voice channel. (works only if music is already playing) | `{Prefix}mv`") .Do(e => { MusicPlayer musicPlayer; @@ -428,7 +428,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "remove") .Alias(Prefix + "rm") - .Description("Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5`") + .Description($"Remove a song by its # in the queue, or 'all' to remove whole queue. | `{Prefix}rm 5`") .Parameter("num", ParameterType.Required) .Do(async e => { @@ -497,7 +497,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "setmaxqueue") .Alias(Prefix + "smq") - .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. | `{Prefix} smq` 50 or `{Prefix} smq`") + .Description($"Sets a maximum queue size. Supply 0 or no argument to have no limit. | `{Prefix}smq` 50 or `{Prefix}smq`") .Parameter("size", ParameterType.Unparsed) .Do(async e => { @@ -519,7 +519,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand(Prefix + "cleanup") - .Description("Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup`") + .Description($"Cleans up hanging voice connections. **Bot Owner Only!** | `{Prefix}cleanup`") .AddCheck(SimpleCheckers.OwnerOnly()) .Do(e => { @@ -538,7 +538,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "reptcursong") .Alias(Prefix + "rcs") - .Description("Toggles repeat of current song. | `!m rcs`") + .Description($"Toggles repeat of current song. | `{Prefix}rcs`") .Do(async e => { MusicPlayer musicPlayer; @@ -556,7 +556,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "rpeatplaylst") .Alias(Prefix + "rpl") - .Description("Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl`") + .Description($"Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `{Prefix}rpl`") .Do(async e => { MusicPlayer musicPlayer; @@ -567,7 +567,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand(Prefix + "save") - .Description("Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1`") + .Description($"Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `{Prefix}save classical1`") .Parameter("name", ParameterType.Unparsed) .Do(async e => { @@ -620,7 +620,7 @@ namespace NadekoBot.Modules.Music }); cgb.CreateCommand(Prefix + "load") - .Description("Loads a playlist under a certain name. | `!m load classical-1`") + .Description($"Loads a playlist under a certain name. | `{Prefix}load classical-1`") .Parameter("name", ParameterType.Unparsed) .Do(async e => { @@ -680,7 +680,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "playlists") .Alias(Prefix + "pls") - .Description("Lists all playlists. Paginated. 20 per page. Default page is 0. |`!m pls 1`") + .Description($"Lists all playlists. Paginated. 20 per page. Default page is 0. |`{Prefix}pls 1`") .Parameter("num", ParameterType.Optional) .Do(e => { @@ -697,7 +697,7 @@ namespace NadekoBot.Modules.Music cgb.CreateCommand(Prefix + "deleteplaylist") .Alias(Prefix + "delpls") - .Description("Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5`") + .Description($"Deletes a saved playlist. Only if you made it or if you are the bot owner. | `{Prefix}delpls animu-5`") .Parameter("pl", ParameterType.Required) .Do(async e => { diff --git a/NadekoBot/NadekoBot.csproj b/NadekoBot/NadekoBot.csproj index 7feefb56..cd467b7f 100644 --- a/NadekoBot/NadekoBot.csproj +++ b/NadekoBot/NadekoBot.csproj @@ -76,6 +76,46 @@ MinimumRecommendedRules.ruleset true + + true + bin\x64\Debug\ + TRACE;DEBUG + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + true + bin\x64\PRIVATE\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + true + + + bin\Release\ + TRACE;NADEKO_RELEASE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + true + ..\packages\VideoLibrary.1.3.3\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\libvideo.dll From 106a779552b7cfc5a81ea2d182126b24f6904dea Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 23 Jul 2016 01:40:44 +0200 Subject: [PATCH 89/89] music commandlist updated --- commandlist.md | 60 +++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/commandlist.md b/commandlist.md index 48ad48d5..50cb13cc 100644 --- a/commandlist.md +++ b/commandlist.md @@ -2,7 +2,7 @@ ######You can donate on paypal: `nadekodiscordbot@gmail.com` #NadekoBot List Of Commands -Version: `NadekoBot v0.9.6045.36710` +Version: `NadekoBot v0.9.6048.2992` ### Help Command and aliases | Description | Usage ----------------|--------------|------- @@ -204,36 +204,36 @@ Command and aliases | Description | Usage ### Music Command and aliases | Description | Usage ----------------|--------------|------- -`!!next`, `!!n`, `!!skip` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!m n` -`!!stop`, `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!m s` -`!!destroy`, `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!m d` -`!!pause`, `!!p` | Pauses or Unpauses the song. | `!m p` -`!!queue`, `!!q`, `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!m q Dream Of Venice` -`!!soundcloudqueue`, `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!m sq Dream Of Venice` -`!!listqueue`, `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2` -`!!nowplaying`, `!!np` | Shows the song currently playing. | `!m np` -`!!volume`, `!!vol` | Sets the music volume 0-100% | `!m vol 50` -`!!defvol`, `!!dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80` -`!!mute`, `!!min` | Sets the music volume to 0% | `!m min` -`!!max` | Sets the music volume to 100%. | `!m max` -`!!half` | Sets the music volume to 50%. | `!m half` -`!!shuffle`, `!!sh` | Shuffles the current playlist. | `!m sh` -`!!playlist`, `!!pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!m pl playlist link or name` -`!!soundcloudpl`, `!!scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` -`!!localplaylst`, `!!lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!m lopl C:/music/classical` -`!!radio`, `!!ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `!m ra radio link here` -`!!local`, `!!lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!m lo C:/music/mysong.mp3` -`!!move`, `!!mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!m mv` -`!!remove`, `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5` +`!!next`, `!!n`, `!!skip` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!!n` +`!!stop`, `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s` +`!!destroy`, `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d` +`!!pause`, `!!p` | Pauses or Unpauses the song. | `!!p` +`!!queue`, `!!q`, `!!yq` | Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. | `!!q Dream Of Venice` +`!!soundcloudqueue`, `!!sq` | Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. | `!!sq Dream Of Venice` +`!!listqueue`, `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2` +`!!nowplaying`, `!!np` | Shows the song currently playing. | `!!np` +`!!volume`, `!!vol` | Sets the music volume 0-100% | `!!vol 50` +`!!defvol`, `!!dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!!dv 80` +`!!mute`, `!!min` | Sets the music volume to 0% | `!!min` +`!!max` | Sets the music volume to 100%. | `!!max` +`!!half` | Sets the music volume to 50%. | `!!half` +`!!shuffle`, `!!sh` | Shuffles the current playlist. | `!!sh` +`!!playlist`, `!!pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!!pl playlist link or name` +`!!soundcloudpl`, `!!scpl` | Queue a soundcloud playlist using a link. | `!!scpl https://soundcloud.com/saratology/sets/symphony` +`!!localplaylst`, `!!lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!!lopl C:/music/classical` +`!!radio`, `!!ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: ) | `!!ra radio link here` +`!!local`, `!!lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!!lo C:/music/mysong.mp3` +`!!move`, `!!mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!!mv` +`!!remove`, `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5` `!!movesong`, `!!ms` | Moves a song from one position to another. | `!! ms` 5>3 -`!!setmaxqueue`, `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!! smq` 50 or `!! smq` -`!!cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup` -`!!reptcursong`, `!!rcs` | Toggles repeat of current song. | `!m rcs` -`!!rpeatplaylst`, `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!m rpl` -`!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!m save classical1` -`!!load` | Loads a playlist under a certain name. | `!m load classical-1` -`!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!m pls 1` -`!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!m delpls animu-5` +`!!setmaxqueue`, `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq` 50 or `!!smq` +`!!cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!!cleanup` +`!!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 playlist under a certain name. | `!!load classical-1` +`!!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. `!!getlink`, `!!gl` | Shows a link to the currently playing song. `!!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)