Merge pull request #3 from miraai/master

Merged dev
This commit is contained in:
miraai 2016-07-24 14:10:10 +02:00 committed by GitHub
commit cafbadc0de
41 changed files with 686 additions and 377 deletions

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "discord.net"] [submodule "discord.net"]
path = discord.net path = discord.net
url = git://github.com/rogueexception/discord.net.git url = git://github.com/kwoth/discord.net.git

View File

@ -22,51 +22,95 @@ Global
EndGlobalSection EndGlobalSection
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
FullDebug|Any CPU = FullDebug|Any CPU FullDebug|Any CPU = FullDebug|Any CPU
FullDebug|x64 = FullDebug|x64
NadekoRelease|Any CPU = NadekoRelease|Any CPU NadekoRelease|Any CPU = NadekoRelease|Any CPU
NadekoRelease|x64 = NadekoRelease|x64
Release|Any CPU = Release|Any CPU Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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|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.ActiveCfg = Debug|Any CPU
{27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.FullDebug|Any CPU.Build.0 = 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.ActiveCfg = NadekoRelease|Any CPU
{27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.NadekoRelease|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{27A886F5-CDDA-4F4A-81EE-6DAFCCE9DE46}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.FullDebug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.NadekoRelease|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{7BFEF748-B934-4621-9B11-6302E3A9F6B3}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.FullDebug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.NadekoRelease|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{8D71A857-879A-4A10-859E-5FF824ED6688}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.FullDebug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.NadekoRelease|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{3091164F-66AE-4543-A63D-167C1116241D}.Release|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.Debug|Any CPU.Build.0 = 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.ActiveCfg = Debug|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.FullDebug|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.NadekoRelease|Any CPU.Build.0 = 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.ActiveCfg = Release|Any CPU
{1B5603B4-6F8F-4289-B945-7BAAE523D740}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@ -161,9 +161,7 @@ namespace NadekoBot.Classes
using (var conn = new SQLiteConnection(FilePath)) using (var conn = new SQLiteConnection(FilePath))
{ {
foreach (var o in ocol) foreach (var o in ocol)
{ conn.InsertOrReplace(o);
conn.InsertOrReplace(o, typeof(T));
}
} }
} }

View File

@ -46,10 +46,10 @@ namespace NadekoBot.Extensions
if (num == 0) if (num == 0)
return string.Empty; return string.Empty;
if (num <= 3) if (num <= 3)
return string.Join("", str.Select(c => '.')); return string.Concat(str.Select(c => '.'));
if (str.Length < num) if (str.Length < num)
return str; return str;
return string.Join("", str.Take(num - 3)) + (hideDots ? "" : "..."); return string.Concat(str.Take(num - 3)) + (hideDots ? "" : "...");
} }
/// <summary> /// <summary>
/// Removes trailing S or ES (if specified) on the given string if the num is 1 /// 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) 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("`", ""); //.Replace("`", "");
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)

View File

@ -384,7 +384,7 @@ namespace NadekoBot.Classes
{ {
var i = 0; var i = 0;
return "```xl\n" + string.Join("\n", items.GroupBy(item => (i++) / cols) 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```"; + $"\n```";
} }
} }

View File

@ -100,6 +100,21 @@ namespace NadekoBot.Classes
} }
} }
[JsonIgnore]
private ObservableCollection<ulong> logserverIgnoreChannels;
public ObservableCollection<ulong> LogserverIgnoreChannels {
get { return logserverIgnoreChannels; }
set {
logserverIgnoreChannels = value;
if (value != null)
logserverIgnoreChannels.CollectionChanged += (s, e) =>
{
if (!SpecificConfigurations.Instantiated) return;
OnPropertyChanged();
};
}
}
[JsonProperty("LogPresenceChannel")] [JsonProperty("LogPresenceChannel")]
private ulong? logPresenceChannel = null; private ulong? logPresenceChannel = null;
[JsonIgnore] [JsonIgnore]
@ -142,6 +157,8 @@ namespace NadekoBot.Classes
} }
} }
[JsonIgnore] [JsonIgnore]
private ulong autoAssignedRole = 0; private ulong autoAssignedRole = 0;
public ulong AutoAssignedRole { public ulong AutoAssignedRole {
@ -179,6 +196,19 @@ namespace NadekoBot.Classes
} }
} }
[JsonIgnore]
private bool exclusiveSelfAssignedRoles = false;
public bool ExclusiveSelfAssignedRoles
{
get { return exclusiveSelfAssignedRoles; }
set
{
exclusiveSelfAssignedRoles = value;
if (!SpecificConfigurations.Instantiated) return;
OnPropertyChanged();
}
}
[JsonIgnore] [JsonIgnore]
private ObservableCollection<StreamNotificationConfig> observingStreams; private ObservableCollection<StreamNotificationConfig> observingStreams;
@ -212,6 +242,7 @@ namespace NadekoBot.Classes
ObservingStreams = new ObservableCollection<StreamNotificationConfig>(); ObservingStreams = new ObservableCollection<StreamNotificationConfig>();
GenerateCurrencyChannels = new ObservableConcurrentDictionary<ulong, int>(); GenerateCurrencyChannels = new ObservableConcurrentDictionary<ulong, int>();
VoiceChannelLog = new ObservableConcurrentDictionary<ulong, ulong>(); VoiceChannelLog = new ObservableConcurrentDictionary<ulong, ulong>();
LogserverIgnoreChannels = new ObservableCollection<ulong>();
} }
public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); }; public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); };

View File

@ -633,22 +633,13 @@ namespace NadekoBot.Modules.Administration
.Parameter("num", ParameterType.Optional) .Parameter("num", ParameterType.Optional)
.Do(async e => .Do(async e =>
{ {
Message[] msgs;
if (string.IsNullOrWhiteSpace(e.GetArg("user_or_num"))) // if nothing is set, clear nadeko's messages, no permissions required if (string.IsNullOrWhiteSpace(e.GetArg("user_or_num"))) // if nothing is set, clear nadeko's messages, no permissions required
{ {
await Task.Run(async () => msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray();
{ if (!msgs.Any())
var msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == e.Server.CurrentUser.Id); return;
foreach (var m in msgs) await e.Channel.DeleteMessages(msgs).ConfigureAwait(false);
{
try
{
await m.Delete().ConfigureAwait(false);
}
catch { }
await Task.Delay(100).ConfigureAwait(false);
}
}).ConfigureAwait(false);
return; return;
} }
if (!e.User.GetPermissions(e.Channel).ManageMessages) if (!e.User.GetPermissions(e.Channel).ManageMessages)
@ -665,11 +656,7 @@ namespace NadekoBot.Modules.Administration
if (val <= 0) if (val <= 0)
return; return;
val++; val++;
foreach (var msg in await e.Channel.DownloadMessages(val).ConfigureAwait(false)) await e.Channel.DeleteMessages((await e.Channel.DownloadMessages(val).ConfigureAwait(false)).ToArray()).ConfigureAwait(false);
{
await msg.Delete().ConfigureAwait(false);
await Task.Delay(100).ConfigureAwait(false);
}
return; return;
} }
//else if first argument is user //else if first argument is user
@ -679,20 +666,10 @@ namespace NadekoBot.Modules.Administration
val = 100; val = 100;
if (!int.TryParse(e.GetArg("num"), out val)) if (!int.TryParse(e.GetArg("num"), out val))
val = 100; val = 100;
await Task.Run(async () => msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == usr.Id).Take(val).ToArray();
{ if (!msgs.Any())
var msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == usr.Id).Take(val); return;
foreach (var m in msgs) await e.Channel.DeleteMessages(msgs).ConfigureAwait(false);
{
try
{
await m.Delete().ConfigureAwait(false);
}
catch { }
await Task.Delay(100).ConfigureAwait(false);
}
}).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "die") cgb.CreateCommand(Prefix + "die")
@ -761,7 +738,7 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(msg)) if (string.IsNullOrWhiteSpace(msg))
return; return;
var ids = e.GetArg("ids").Split('-'); var ids = e.GetArg("ids").Split('|');
if (ids.Length != 2) if (ids.Length != 2)
return; return;
var sid = ulong.Parse(ids[0]); var sid = ulong.Parse(ids[0]);

View File

@ -51,8 +51,9 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
try try
{ {
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (chId == null) var chId = config.LogServerChannel;
if (chId == null || config.LogserverIgnoreChannels.Contains(e.After.Id))
return; return;
Channel ch; Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@ -72,8 +73,9 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
try try
{ {
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (chId == null) var chId = config.LogServerChannel;
if (chId == null || config.LogserverIgnoreChannels.Contains(e.Channel.Id))
return; return;
Channel ch; Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null)
@ -87,8 +89,9 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
try try
{ {
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (chId == null) var chId = config.LogServerChannel;
if (chId == null || config.LogserverIgnoreChannels.Contains(e.Channel.Id))
return; return;
Channel ch; Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) 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) if (e.Server == null || e.Channel.IsPrivate || e.User.Id == NadekoBot.Client.CurrentUser.Id)
return; return;
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (chId == null || e.Channel.Id == chId) var chId = config.LogServerChannel;
if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id))
return; return;
Channel ch; Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) 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) if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id)
return; return;
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (chId == null || e.Channel.Id == chId) var chId = config.LogServerChannel;
if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id))
return; return;
Channel ch; Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) 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) if (e.Server == null || e.Channel.IsPrivate || e.User?.Id == NadekoBot.Client.CurrentUser.Id)
return; return;
var chId = SpecificConfigurations.Default.Of(e.Server.Id).LogServerChannel; var config = SpecificConfigurations.Default.Of(e.Server.Id);
if (chId == null || e.Channel.Id == chId) var chId = config.LogServerChannel;
if (chId == null || e.Channel.Id == chId || config.LogserverIgnoreChannels.Contains(e.Channel.Id))
return; return;
Channel ch; Channel ch;
if ((ch = e.Server.TextChannels.Where(tc => tc.Id == chId).FirstOrDefault()) == null) 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); await e.Channel.SendMessage($"❗**NO LONGER LOGGING IN {ch.Mention} CHANNEL**❗").ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "logignore")
.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 =>
{
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") cgb.CreateCommand(Module.Prefix + "userpresence")
.Description("Starts logging to this channel when someone from the server goes online/offline/idle.") .Description("Starts logging to this channel when someone from the server goes online/offline/idle.")
.AddCheck(SimpleCheckers.ManageServer()) .AddCheck(SimpleCheckers.ManageServer())

View File

@ -1,6 +1,8 @@
using Discord.Commands; using Discord.Commands;
using Discord.Net;
using NadekoBot.Classes; using NadekoBot.Classes;
using NadekoBot.Modules.Permissions.Classes; using NadekoBot.Modules.Permissions.Classes;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -74,7 +76,7 @@ namespace NadekoBot.Modules.Administration.Commands
var config = SpecificConfigurations.Default.Of(e.Server.Id); var config = SpecificConfigurations.Default.Of(e.Server.Id);
var msg = new StringBuilder($"There are `{config.ListOfSelfAssignableRoles.Count}` self assignable roles:\n"); var msg = new StringBuilder($"There are `{config.ListOfSelfAssignableRoles.Count}` self assignable roles:\n");
var toRemove = new HashSet<ulong>(); var toRemove = new HashSet<ulong>();
foreach (var roleId in config.ListOfSelfAssignableRoles) foreach (var roleId in config.ListOfSelfAssignableRoles.OrderBy(r=>r.ToString()))
{ {
var role = e.Server.GetRole(roleId); var role = e.Server.GetRole(roleId);
if (role == null) if (role == null)
@ -94,6 +96,19 @@ namespace NadekoBot.Modules.Administration.Commands
await e.Channel.SendMessage(msg.ToString()).ConfigureAwait(false); 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") cgb.CreateCommand(Module.Prefix + "iam")
.Description("Adds a role to you that you choose. " + .Description("Adds a role to you that you choose. " +
"Role must be on a list of self-assignable roles." + "Role must be on a list of self-assignable roles." +
@ -121,11 +136,20 @@ namespace NadekoBot.Modules.Administration.Commands
await e.Channel.SendMessage($":anger:You already have {role.Name} role.").ConfigureAwait(false); await e.Channel.SendMessage($":anger:You already have {role.Name} role.").ConfigureAwait(false);
return; 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 try
{ {
await e.User.AddRoles(role).ConfigureAwait(false); await e.User.AddRoles(role).ConfigureAwait(false);
} }
catch 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); 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);
} }

View File

@ -27,8 +27,7 @@ namespace NadekoBot.Modules.ClashOfClans
cgb.CreateCommand(Prefix + "createwar") cgb.CreateCommand(Prefix + "createwar")
.Alias(Prefix + "cw") .Alias(Prefix + "cw")
.Description( .Description($"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{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("size")
.Parameter("enemy_clan", ParameterType.Unparsed) .Parameter("enemy_clan", ParameterType.Unparsed)
.Do(async e => .Do(async e =>

View File

@ -9,13 +9,14 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Conversations namespace NadekoBot.Modules.Conversations
{ {
internal class Conversations : DiscordModule internal class Conversations : DiscordModule
{ {
private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥"; private const string firestr = "🔥 ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็ด้้้้้็็็็็้้้้้็็็็็้้้้้้้้็็็็็้้้้้็็็็็้้้้ 🔥";
public Conversations() public Conversations()
{ {
commands.Add(new RipCommand(this)); commands.Add(new RipCommand(this));
@ -153,22 +154,23 @@ namespace NadekoBot.Modules.Conversations
.Parameter("times", ParameterType.Optional) .Parameter("times", ParameterType.Optional)
.Do(async e => .Do(async e =>
{ {
var count = 1; int count;
int.TryParse(e.Args[0], out count); if (string.IsNullOrWhiteSpace(e.Args[0]))
if (count == 0)
count = 1; count = 1;
else
int.TryParse(e.Args[0], out count);
if (count < 1 || count > 12) 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; return;
} }
var str = ""; var str = new StringBuilder();
for (var i = 0; i < count; i++) 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") cgb.CreateCommand("dump")

View File

@ -7,7 +7,7 @@ namespace NadekoBot.Classes
/// Base DiscordCommand Class. /// Base DiscordCommand Class.
/// Inherit this class to create your own command. /// Inherit this class to create your own command.
/// </summary> /// </summary>
internal abstract class DiscordCommand public abstract class DiscordCommand
{ {
/// <summary> /// <summary>

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using NadekoBot.Classes; using NadekoBot.Classes;
namespace NadekoBot.Modules { namespace NadekoBot.Modules {
internal abstract class DiscordModule : IModule { public abstract class DiscordModule : IModule {
protected readonly HashSet<DiscordCommand> commands = new HashSet<DiscordCommand>(); protected readonly HashSet<DiscordCommand> commands = new HashSet<DiscordCommand>();
public abstract string Prefix { get; } public abstract string Prefix { get; }

View File

@ -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") " 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) .Parameter("num", ParameterType.Optional)
.Do(RollFunc()); .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") cgb.CreateCommand(Module.Prefix + "nroll")
.Description("Rolls in a given range. | `$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) .Parameter("range", ParameterType.Required)
@ -40,7 +47,7 @@ namespace NadekoBot.Modules.Gambling
Regex dndRegex = new Regex(@"(?<n1>\d+)d(?<n2>\d+)", RegexOptions.Compiled); Regex dndRegex = new Regex(@"(?<n1>\d+)d(?<n2>\d+)", RegexOptions.Compiled);
private Func<CommandEventArgs, Task> RollFunc() private Func<CommandEventArgs, Task> RollFunc(bool ordered = true)
{ {
var r = new Random(); var r = new Random();
return async e => return async e =>
@ -73,7 +80,7 @@ namespace NadekoBot.Modules.Gambling
arr[i] = r.Next(1, n2 + 1); arr[i] = r.Next(1, n2 + 1);
} }
var elemCnt = 0; 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; return;
} }
@ -92,17 +99,23 @@ namespace NadekoBot.Modules.Gambling
{ {
var randomNumber = r.Next(1, 7); var randomNumber = r.Next(1, 7);
var toInsert = dices.Count; var toInsert = dices.Count;
if (randomNumber == 6 || dices.Count == 0) if (ordered)
toInsert = 0; {
else if (randomNumber != 1) if (randomNumber == 6 || dices.Count == 0)
for (var j = 0; j < dices.Count; j++) toInsert = 0;
{ else if (randomNumber != 1)
if (values[j] < randomNumber) for (var j = 0; j < dices.Count; j++)
{ {
toInsert = j; if (values[j] < randomNumber)
break; {
toInsert = j;
break;
}
} }
} }
else {
toInsert = dices.Count;
}
dices.Insert(toInsert, GetDice(randomNumber)); dices.Insert(toInsert, GetDice(randomNumber));
values.Insert(toInsert, randomNumber); values.Insert(toInsert, randomNumber);
} }

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var amountStr = e.GetArg("amount")?.Trim(); var amountStr = e.GetArg("amount")?.Trim();
long amount; long amount;
if (!long.TryParse(amountStr, out amount) || amount < 0) if (!long.TryParse(amountStr, out amount) || amount <= 0)
return; return;
var mentionedUser = e.Message.MentionedUsers.FirstOrDefault(u => var mentionedUser = e.Message.MentionedUsers.FirstOrDefault(u =>

View File

@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Gambling.Helpers
var orderedPool = cardPool.OrderBy(x => r.Next()); var orderedPool = cardPool.OrderBy(x => r.Next());
cardPool = cardPool as List<Card> ?? orderedPool.ToList(); cardPool = cardPool as List<Card> ?? 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() private static void InitHandValues()
{ {
@ -226,7 +226,7 @@ namespace NadekoBot.Modules.Gambling.Helpers
{ {
return kvp.Key; return kvp.Key;
} }
return "High card " + cards.Max().GetName(); return "High card " + (cards.FirstOrDefault(c => c.Number == 1)?.GetName() ?? cards.Max().GetName());
} }
} }
} }

View File

@ -1,9 +1,11 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Classes; using NadekoBot.Classes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions.Classes; using NadekoBot.Modules.Permissions.Classes;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
@ -43,22 +45,19 @@ namespace NadekoBot.Modules.Games.Commands
if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd)) if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd))
if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now) if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now)
{ {
var rnd = Math.Abs(GetRandomNumber()); var rnd = Math.Abs(rng.Next(0,101));
if ((rnd % 50) == 0) if (rnd == 0)
{ {
var msg = await e.Channel.SendFile(GetRandomCurrencyImagePath()); var msgs = new[] { 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, msgs, (u, m) => { m.ForEach(async msgToDelete => { try { await msgToDelete.Delete(); } catch { } }); return msgs; });
plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msg, (u, m) => { m.Delete().GetAwaiter().GetResult(); return msg; });
plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now); plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now);
await Task.Delay(5000);
await msg2.Delete();
} }
} }
} }
catch { } catch { }
} }
//channelid/messageid pair //channelid/messageid pair
ConcurrentDictionary<ulong, Message> plantedFlowerChannels = new ConcurrentDictionary<ulong, Message>(); ConcurrentDictionary<ulong, IEnumerable<Message>> plantedFlowerChannels = new ConcurrentDictionary<ulong, IEnumerable<Message>>();
private object locker = new object(); private object locker = new object();
@ -68,22 +67,24 @@ namespace NadekoBot.Modules.Games.Commands
.Description("Picks a flower planted in this channel.") .Description("Picks a flower planted in this channel.")
.Do(async e => .Do(async e =>
{ {
Message msg; IEnumerable<Message> msgs;
await e.Message.Delete().ConfigureAwait(false); await e.Message.Delete().ConfigureAwait(false);
if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msg)) if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msgs))
return; 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); 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 Task.Delay(10000).ConfigureAwait(false);
await msg.Delete().ConfigureAwait(false); await msg.Delete().ConfigureAwait(false);
}); });
cgb.CreateCommand(Module.Prefix + "plant") 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)") .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) lock (locker)
{ {
@ -92,7 +93,7 @@ namespace NadekoBot.Modules.Games.Commands
e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel."); e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel.");
return; 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) if (!removed)
{ {
e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait(); e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait();
@ -101,17 +102,14 @@ namespace NadekoBot.Modules.Games.Commands
var file = GetRandomCurrencyImagePath(); var file = GetRandomCurrencyImagePath();
Message msg; Message msg;
//todo send message after, not in lock
if (file == null) if (file == null)
msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult(); msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult();
else else
msg = e.Channel.SendFile(file).GetAwaiter().GetResult(); 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") cgb.CreateCommand(Prefix + "gencurrency")

View File

@ -190,8 +190,6 @@ namespace NadekoBot.Modules.Games.Commands
await e.Channel.SendMessage("Added new article for typing game.").ConfigureAwait(false); await e.Channel.SendMessage("Added new article for typing game.").ConfigureAwait(false);
}); });
//todo add user submissions
} }
} }
} }

View File

@ -5,6 +5,7 @@ using NadekoBot.Modules.Permissions.Classes;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Classes.Help.Commands namespace NadekoBot.Classes.Help.Commands
@ -24,8 +25,13 @@ namespace NadekoBot.Classes.Help.Commands
var com = NadekoBot.Client.GetService<CommandService>().AllCommands var com = NadekoBot.Client.GetService<CommandService>().AllCommands
.FirstOrDefault(c => c.Text.ToLowerInvariant().Equals(comToFind) || .FirstOrDefault(c => c.Text.ToLowerInvariant().Equals(comToFind) ||
c.Aliases.Select(a => a.ToLowerInvariant()).Contains(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) 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:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**",1)}").ConfigureAwait(false);
}).ConfigureAwait(false); }).ConfigureAwait(false);
}; };
public static string HelpString { public static string HelpString {
@ -43,7 +49,7 @@ namespace NadekoBot.Classes.Help.Commands
{ {
string helpstr = string helpstr =
$@"######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/** $@"######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 #NadekoBot List Of Commands
Version: `{NadekoStats.Instance.BotVersion}`"; Version: `{NadekoStats.Instance.BotVersion}`";

View File

@ -46,14 +46,15 @@ namespace NadekoBot.Modules.Help
if (string.IsNullOrWhiteSpace(module)) if (string.IsNullOrWhiteSpace(module))
return; return;
var cmds = NadekoBot.Client.GetService<CommandService>().AllCommands var cmds = NadekoBot.Client.GetService<CommandService>().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(); var cmdsArray = cmds as Command[] ?? cmds.ToArray();
if (!cmdsArray.Any()) if (!cmdsArray.Any())
{ {
await e.Channel.SendMessage("That module does not exist.").ConfigureAwait(false); await e.Channel.SendMessage("That module does not exist.").ConfigureAwait(false);
return; return;
} }
var i = 0;
if (module != "customreactions" && module != "conversations") if (module != "customreactions" && module != "conversations")
{ {
await e.Channel.SendMessage("`List Of Commands:`\n" + SearchHelper.ShowInPrettyCode<Command>(cmdsArray, await e.Channel.SendMessage("`List Of Commands:`\n" + SearchHelper.ShowInPrettyCode<Command>(cmdsArray,

View File

@ -2,7 +2,9 @@
using Discord.Audio; using Discord.Audio;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Music.Classes namespace NadekoBot.Modules.Music.Classes
@ -20,22 +22,18 @@ namespace NadekoBot.Modules.Music.Classes
{ {
Resolving, Resolving,
Queued, Queued,
Buffering, //not using it atm
Playing, Playing,
Completed Completed
} }
public class MusicPlayer public class MusicPlayer
{ {
public static int MaximumPlaylistSize => 50;
private IAudioClient audioClient { get; set; } private IAudioClient audioClient { get; set; }
private readonly List<Song> playlist = new List<Song>(); private readonly List<Song> playlist = new List<Song>();
public IReadOnlyCollection<Song> Playlist => playlist; public IReadOnlyCollection<Song> 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 CancellationTokenSource SongCancelSource { get; set; }
private CancellationToken cancelToken { get; set; } private CancellationToken cancelToken { get; set; }
@ -54,6 +52,8 @@ namespace NadekoBot.Modules.Music.Classes
public bool Autoplay { get; set; } = false; public bool Autoplay { get; set; } = false;
public uint MaxQueueSize { get; set; } = 0; public uint MaxQueueSize { get; set; } = 0;
private ConcurrentQueue<Action> actionQueue { get; set; } = new ConcurrentQueue<Action>();
public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume) public MusicPlayer(Channel startingVoiceChannel, float? defaultVolume)
{ {
if (startingVoiceChannel == null) if (startingVoiceChannel == null)
@ -68,85 +68,110 @@ namespace NadekoBot.Modules.Music.Classes
Task.Run(async () => Task.Run(async () =>
{ {
while (!Destroyed) try
{ {
try while (!Destroyed)
{
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)
{ {
try try
{ {
OnStarted(this, curSong); Action action;
await curSong.Play(audioClient, cancelToken).ConfigureAwait(false); 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;
}
CurrentSong = GetNextSong();
RemoveSongAt(0);
if (CurrentSong == null)
continue;
try
{
OnStarted(this, CurrentSong);
await CurrentSong.Play(audioClient, cancelToken);
}
catch (OperationCanceledException)
{
Console.WriteLine("Song canceled");
SongCancelSource = new CancellationTokenSource();
cancelToken = SongCancelSource.Token;
}
OnCompleted(this, CurrentSong);
if (RepeatPlaylist)
AddSong(CurrentSong, CurrentSong.QueuerName);
if (RepeatSong)
AddSong(CurrentSong, 0);
}
finally
{
await Task.Delay(300).ConfigureAwait(false);
CurrentSong = null;
}
}
}
catch (Exception ex) {
Console.WriteLine("Music thread crashed.");
Console.WriteLine(ex);
}
}));
t.Start();
} }
public void Next() public void Next()
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
if (!SongCancelSource.IsCancellationRequested) Paused = false;
{ SongCancelSource.Cancel();
Paused = false; });
SongCancelSource.Cancel();
}
}
} }
public void Stop() public void Stop()
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
playlist.Clear();
CurrentSong = null;
RepeatPlaylist = false; RepeatPlaylist = false;
RepeatSong = false; RepeatSong = false;
playlist.Clear();
if (!SongCancelSource.IsCancellationRequested) if (!SongCancelSource.IsCancellationRequested)
SongCancelSource.Cancel(); SongCancelSource.Cancel();
} });
} }
public void TogglePause() => Paused = !Paused; public void TogglePause() => Paused = !Paused;
public void Shuffle()
{
lock (playlistLock)
{
playlist.Shuffle();
}
}
public int SetVolume(int volume) public int SetVolume(int volume)
{ {
if (volume < 0) if (volume < 0)
@ -158,16 +183,15 @@ namespace NadekoBot.Modules.Music.Classes
return volume; return volume;
} }
private Song GetNextSong() private Song GetNextSong() =>
playlist.FirstOrDefault();
public void Shuffle()
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
if (playlist.Count == 0) playlist.Shuffle();
return null; });
var toReturn = playlist[0];
playlist.RemoveAt(0);
return toReturn;
}
} }
public void AddSong(Song s, string username) public void AddSong(Song s, string username)
@ -175,42 +199,64 @@ namespace NadekoBot.Modules.Music.Classes
if (s == null) if (s == null)
throw new ArgumentNullException(nameof(s)); throw new ArgumentNullException(nameof(s));
ThrowIfQueueFull(); ThrowIfQueueFull();
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
s.MusicPlayer = this; s.MusicPlayer = this;
s.QueuerName = username.TrimTo(10); s.QueuerName = username.TrimTo(10);
playlist.Add(s); playlist.Add(s);
} });
} }
public void AddSong(Song s, int index) public void AddSong(Song s, int index)
{ {
if (s == null) if (s == null)
throw new ArgumentNullException(nameof(s)); throw new ArgumentNullException(nameof(s));
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
playlist.Insert(index, s); playlist.Insert(index, s);
} });
} }
public void RemoveSong(Song s) public void RemoveSong(Song s)
{ {
if (s == null) if (s == null)
throw new ArgumentNullException(nameof(s)); throw new ArgumentNullException(nameof(s));
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
playlist.Remove(s); playlist.Remove(s);
} });
} }
public void RemoveSongAt(int index) public void RemoveSongAt(int index)
{ {
lock (playlistLock) actionQueue.Enqueue(() =>
{ {
if (index < 0 || index >= playlist.Count) if (index < 0 || index >= playlist.Count)
throw new ArgumentException("Invalid index"); return;
playlist.RemoveAt(index); playlist.RemoveAt(index);
} });
}
internal void ClearQueue()
{
actionQueue.Enqueue(() =>
{
playlist.Clear();
});
}
public void Destroy()
{
actionQueue.Enqueue(() =>
{
RepeatPlaylist = false;
RepeatSong = false;
Destroyed = true;
playlist.Clear();
if (!SongCancelSource.IsCancellationRequested)
SongCancelSource.Cancel();
audioClient.Disconnect();
});
} }
internal Task MoveToVoiceChannel(Channel voiceChannel) internal Task MoveToVoiceChannel(Channel voiceChannel)
@ -221,27 +267,6 @@ namespace NadekoBot.Modules.Music.Classes
return PlaybackVoiceChannel.JoinAudio(); 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 ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong;
internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; internal bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist;

View File

@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Music.Classes
public SongInfo SongInfo { get; } public SongInfo SongInfo { get; }
public string QueuerName { get; set; } 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; private bool prebufferingComplete { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; } public MusicPlayer MusicPlayer { get; set; }
@ -137,6 +137,9 @@ namespace NadekoBot.Modules.Music.Classes
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) 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 bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
var bufferAttempts = 0; var bufferAttempts = 0;
const int waitPerAttempt = 500; const int waitPerAttempt = 500;
@ -145,7 +148,6 @@ namespace NadekoBot.Modules.Music.Classes
{ {
await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false); await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false);
} }
cancelToken.ThrowIfCancellationRequested();
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}"); Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
const int blockSize = 3840; const int blockSize = 3840;
var attempt = 0; var attempt = 0;

View File

@ -31,17 +31,17 @@ namespace NadekoBot.Modules.Music
{ {
var client = NadekoBot.Client; var client = NadekoBot.Client;
manager.CreateCommands(Prefix, cgb => manager.CreateCommands("", cgb =>
{ {
cgb.AddCheck(PermissionChecker.Instance); cgb.AddCheck(PermissionChecker.Instance);
commands.ForEach(cmd => cmd.Init(cgb)); commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand("next") cgb.CreateCommand(Prefix + "next")
.Alias("n") .Alias(Prefix + "n")
.Alias("skip") .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 => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -50,9 +50,9 @@ namespace NadekoBot.Modules.Music
musicPlayer.Next(); musicPlayer.Next();
}); });
cgb.CreateCommand("stop") cgb.CreateCommand(Prefix + "stop")
.Alias("s") .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 => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -64,10 +64,10 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("destroy") cgb.CreateCommand(Prefix + "destroy")
.Alias("d") .Alias(Prefix + "d")
.Description("Completely stops the music and unbinds the bot from the channel. " + .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 => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -76,9 +76,9 @@ namespace NadekoBot.Modules.Music
musicPlayer.Destroy(); musicPlayer.Destroy();
}); });
cgb.CreateCommand("pause") cgb.CreateCommand(Prefix + "pause")
.Alias("p") .Alias(Prefix + "p")
.Description("Pauses or Unpauses the song. | `!m p`") .Description($"Pauses or Unpauses the song. | `{Prefix}p`")
.Do(async e => .Do(async e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -92,11 +92,11 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage("🎵`Music Player unpaused.`").ConfigureAwait(false); await e.Channel.SendMessage("🎵`Music Player unpaused.`").ConfigureAwait(false);
}); });
cgb.CreateCommand("queue") cgb.CreateCommand(Prefix + "queue")
.Alias("q") .Alias(Prefix + "q")
.Alias("yq") .Alias(Prefix + "yq")
.Description("Queue a song using keywords or a link. Bot will join your voice channel." + .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) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -108,10 +108,10 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("soundcloudqueue") cgb.CreateCommand(Prefix + "soundcloudqueue")
.Alias("sq") .Alias(Prefix + "sq")
.Description("Queue a soundcloud song using keywords. Bot will join your voice channel." + .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) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -123,9 +123,9 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("listqueue") cgb.CreateCommand(Prefix + "listqueue")
.Alias("lq") .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) .Parameter("page", ParameterType.Optional)
.Do(async e => .Do(async e =>
{ {
@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Music
else if (musicPlayer.RepeatPlaylist) else if (musicPlayer.RepeatPlaylist)
toSend += "🔁"; toSend += "🔁";
toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` "; toSend += $" **{musicPlayer.Playlist.Count}** `tracks currently queued. Showing page {page}` ";
if (musicPlayer.Playlist.Count >= MusicPlayer.MaximumPlaylistSize) if (musicPlayer.MaxQueueSize != 0 && musicPlayer.Playlist.Count >= musicPlayer.MaxQueueSize)
toSend += "**Song queue is full!**\n"; toSend += "**Song queue is full!**\n";
else else
toSend += "\n"; toSend += "\n";
@ -161,9 +161,9 @@ 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); await e.Channel.SendMessage(toSend + string.Join("\n", musicPlayer.Playlist.Skip(startAt).Take(15).Select(v => $"`{number++}.` {v.PrettyName}"))).ConfigureAwait(false);
}); });
cgb.CreateCommand("nowplaying") cgb.CreateCommand(Prefix + "nowplaying")
.Alias("np") .Alias(Prefix + "np")
.Description("Shows the song currently playing. | `!m np`") .Description($"Shows the song currently playing. | `{Prefix}np`")
.Do(async e => .Do(async e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -176,9 +176,9 @@ namespace NadekoBot.Modules.Music
$"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false); $"{currentSong.PrettyCurrentTime()}").ConfigureAwait(false);
}); });
cgb.CreateCommand("volume") cgb.CreateCommand(Prefix + "volume")
.Alias("vol") .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) .Parameter("val", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -198,10 +198,10 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎵 `Volume set to {volume}%`").ConfigureAwait(false); await e.Channel.SendMessage($"🎵 `Volume set to {volume}%`").ConfigureAwait(false);
}); });
cgb.CreateCommand("defvol") cgb.CreateCommand(Prefix + "defvol")
.Alias("dv") .Alias(Prefix + "dv")
.Description("Sets the default music volume when music playback is started (0-100)." + .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) .Parameter("val", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -217,9 +217,9 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎵 `Default volume set to {volume}%`").ConfigureAwait(false); await e.Channel.SendMessage($"🎵 `Default volume set to {volume}%`").ConfigureAwait(false);
}); });
cgb.CreateCommand("mute") cgb.CreateCommand(Prefix + "mute")
.Alias("min") .Alias(Prefix + "min")
.Description("Sets the music volume to 0% | `!m min`") .Description($"Sets the music volume to 0% | `{Prefix}min`")
.Do(e => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -230,8 +230,8 @@ namespace NadekoBot.Modules.Music
musicPlayer.SetVolume(0); musicPlayer.SetVolume(0);
}); });
cgb.CreateCommand("max") cgb.CreateCommand(Prefix + "max")
.Description("Sets the music volume to 100%. | `!m max`") .Description($"Sets the music volume to 100%. | `{Prefix}max`")
.Do(e => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -242,8 +242,8 @@ namespace NadekoBot.Modules.Music
musicPlayer.SetVolume(100); musicPlayer.SetVolume(100);
}); });
cgb.CreateCommand("half") cgb.CreateCommand(Prefix + "half")
.Description("Sets the music volume to 50%. | `!m half`") .Description($"Sets the music volume to 50%. | `{Prefix}half`")
.Do(e => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -254,9 +254,9 @@ namespace NadekoBot.Modules.Music
musicPlayer.SetVolume(50); musicPlayer.SetVolume(50);
}); });
cgb.CreateCommand("shuffle") cgb.CreateCommand(Prefix + "shuffle")
.Alias("sh") .Alias(Prefix + "sh")
.Description("Shuffles the current playlist. | `!m sh`") .Description($"Shuffles the current playlist. | `{Prefix}sh`")
.Do(async e => .Do(async e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -274,9 +274,9 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage("🎵 `Songs shuffled.`").ConfigureAwait(false); await e.Channel.SendMessage("🎵 `Songs shuffled.`").ConfigureAwait(false);
}); });
cgb.CreateCommand("playlist") cgb.CreateCommand(Prefix + "playlist")
.Alias("pl") .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) .Parameter("playlist", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -300,7 +300,6 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎵 `Failed to find any songs.`").ConfigureAwait(false); await e.Channel.SendMessage($"🎵 `Failed to find any songs.`").ConfigureAwait(false);
return; return;
} }
//todo TEMPORARY SOLUTION, USE RESOLVE QUEUE IN THE FUTURE
var idArray = ids as string[] ?? ids.ToArray(); var idArray = ids as string[] ?? ids.ToArray();
var count = idArray.Length; var count = idArray.Length;
var msg = var msg =
@ -318,9 +317,9 @@ namespace NadekoBot.Modules.Music
await msg.Edit("🎵 `Playlist queue complete.`").ConfigureAwait(false); await msg.Edit("🎵 `Playlist queue complete.`").ConfigureAwait(false);
}); });
cgb.CreateCommand("soundcloudpl") cgb.CreateCommand(Prefix + "soundcloudpl")
.Alias("scpl") .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) .Parameter("pl", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -353,9 +352,9 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("localplaylst") cgb.CreateCommand(Prefix + "localplaylst")
.Alias("lopl") .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) .Parameter("directory", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -384,8 +383,8 @@ namespace NadekoBot.Modules.Music
catch { } 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: <https://streamable.com/al54>) | `!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: <https://streamable.com/al54>) | `{Prefix}ra radio link here`")
.Parameter("radio_link", ParameterType.Required) .Parameter("radio_link", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -402,9 +401,9 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("local") cgb.CreateCommand(Prefix + "local")
.Alias("lo") .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) .Parameter("path", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -415,9 +414,9 @@ namespace NadekoBot.Modules.Music
await QueueSong(e.User, 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") cgb.CreateCommand(Prefix + "move")
.Alias("mv") .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 => .Do(e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -427,9 +426,9 @@ namespace NadekoBot.Modules.Music
musicPlayer.MoveToVoiceChannel(voiceChannel); musicPlayer.MoveToVoiceChannel(voiceChannel);
}); });
cgb.CreateCommand("remove") cgb.CreateCommand(Prefix + "remove")
.Alias("rm") .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) .Parameter("num", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -460,8 +459,8 @@ namespace NadekoBot.Modules.Music
}); });
//var msRegex = new Regex(@"(?<n1>\d+)>(?<n2>\d+)", RegexOptions.Compiled); //var msRegex = new Regex(@"(?<n1>\d+)>(?<n2>\d+)", RegexOptions.Compiled);
cgb.CreateCommand("movesong") cgb.CreateCommand(Prefix + "movesong")
.Alias("ms") .Alias(Prefix + "ms")
.Description($"Moves a song from one position to another. | `{Prefix} ms` 5>3") .Description($"Moves a song from one position to another. | `{Prefix} ms` 5>3")
.Parameter("fromto") .Parameter("fromto")
.Do(async e => .Do(async e =>
@ -496,9 +495,9 @@ namespace NadekoBot.Modules.Music
}); });
cgb.CreateCommand("setmaxqueue") cgb.CreateCommand(Prefix + "setmaxqueue")
.Alias("smq") .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) .Parameter("size", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -519,8 +518,8 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎵 `Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}`"); 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`") .Description($"Cleans up hanging voice connections. **Bot Owner Only!** | `{Prefix}cleanup`")
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(e => .Do(e =>
{ {
@ -537,9 +536,9 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("reptcursong") cgb.CreateCommand(Prefix + "reptcursong")
.Alias("rcs") .Alias(Prefix + "rcs")
.Description("Toggles repeat of current song. | `!m rcs`") .Description($"Toggles repeat of current song. | `{Prefix}rcs`")
.Do(async e => .Do(async e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -555,9 +554,9 @@ namespace NadekoBot.Modules.Music
.ConfigureAwait(false); .ConfigureAwait(false);
}); });
cgb.CreateCommand("rpeatplaylst") cgb.CreateCommand(Prefix + "rpeatplaylst")
.Alias("rpl") .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 => .Do(async e =>
{ {
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
@ -567,8 +566,8 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎵🔁`Repeat playlist {(currentValue ? "enabled" : "disabled")}`").ConfigureAwait(false); 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`") .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) .Parameter("name", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -620,8 +619,8 @@ namespace NadekoBot.Modules.Music
}); });
cgb.CreateCommand("load") 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) .Parameter("name", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -679,9 +678,9 @@ namespace NadekoBot.Modules.Music
} }
}); });
cgb.CreateCommand("playlists") cgb.CreateCommand(Prefix + "playlists")
.Alias("pls") .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) .Parameter("num", ParameterType.Optional)
.Do(e => .Do(e =>
{ {
@ -696,9 +695,9 @@ 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); 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") cgb.CreateCommand(Prefix + "deleteplaylist")
.Alias("delpls") .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) .Parameter("pl", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -713,7 +712,7 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage("`Ok.` :ok:").ConfigureAwait(false); 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.") .Description("Goes to a specific time in seconds in a song.")
.Parameter("time") .Parameter("time")
.Do(async e => .Do(async e =>
@ -750,8 +749,8 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false); await e.Channel.SendMessage($"`Skipped to {minutes}:{seconds}`").ConfigureAwait(false);
}); });
cgb.CreateCommand("getlink") cgb.CreateCommand(Prefix + "getlink")
.Alias("gl") .Alias(Prefix + "gl")
.Description("Shows a link to the currently playing song.") .Description("Shows a link to the currently playing song.")
.Do(async e => .Do(async e =>
{ {
@ -764,8 +763,8 @@ namespace NadekoBot.Modules.Music
await e.Channel.SendMessage($"🎶`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false); await e.Channel.SendMessage($"🎶`Current song:` <{curSong.SongInfo.Query}>").ConfigureAwait(false);
}); });
cgb.CreateCommand("autoplay") cgb.CreateCommand(Prefix + "autoplay")
.Alias("ap") .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)") .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 => .Do(async e =>
{ {

View File

@ -4,6 +4,7 @@ using Discord.Commands.Permissions;
using NadekoBot.Classes.JSONModels; using NadekoBot.Classes.JSONModels;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Permissions.Classes namespace NadekoBot.Modules.Permissions.Classes
@ -13,7 +14,10 @@ namespace NadekoBot.Modules.Permissions.Classes
{ {
public static PermissionChecker Instance { get; } = new PermissionChecker(); public static PermissionChecker Instance { get; } = new PermissionChecker();
private ConcurrentDictionary<User, DateTime> timeBlackList { get; } = new ConcurrentDictionary<User, DateTime>(); //key - sid:command
//value - userid
private ConcurrentDictionary<string, ulong> commandCooldowns = new ConcurrentDictionary<string, ulong>();
private HashSet<ulong> timeBlackList { get; } = new HashSet<ulong>();
static PermissionChecker() { } static PermissionChecker() { }
private PermissionChecker() private PermissionChecker()
@ -22,8 +26,8 @@ namespace NadekoBot.Modules.Permissions.Classes
{ {
while (true) while (true)
{ {
//blacklist is cleared every 1.75 seconds. That is the most time anyone will be blocked //blacklist is cleared every 1.00 seconds. That is the most time anyone will be blocked
await Task.Delay(1750).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
timeBlackList.Clear(); timeBlackList.Clear();
} }
}); });
@ -46,18 +50,26 @@ namespace NadekoBot.Modules.Permissions.Classes
return false; return false;
} }
if (timeBlackList.ContainsKey(user)) if (timeBlackList.Contains(user.Id))
return false; return false;
timeBlackList.TryAdd(user, DateTime.Now);
if (!channel.IsPrivate && !channel.Server.CurrentUser.GetPermissions(channel).SendMessages) if (!channel.IsPrivate && !channel.Server.CurrentUser.GetPermissions(channel).SendMessages)
{ {
return false; 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 try
{ {
@ -75,8 +87,6 @@ namespace NadekoBot.Modules.Permissions.Classes
catch { } catch { }
if (user.Server.Owner.Id == user.Id || (role != null && user.HasRole(role))) if (user.Server.Owner.Id == user.Id || (role != null && user.HasRole(role)))
return true; 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."); throw new Exception($"You don't have the necessary role (**{(perms?.PermissionsControllerRole ?? "Nadeko")}**) to change permissions.");
} }
@ -128,8 +138,7 @@ namespace NadekoBot.Modules.Permissions.Classes
Console.WriteLine($"Exception in canrun: {ex}"); Console.WriteLine($"Exception in canrun: {ex}");
try try
{ {
ServerPermissions perms; if (perms != null && perms.Verbose)
if (PermissionsHandler.PermissionsDict.TryGetValue(user.Server.Id, out perms) && perms.Verbose)
//if verbose - print errors //if verbose - print errors
error = ex.Message; error = ex.Message;
} }
@ -140,5 +149,26 @@ namespace NadekoBot.Modules.Permissions.Classes
return false; 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);
}
});
}
} }
} }

View File

@ -55,9 +55,11 @@ namespace NadekoBot.Modules.Permissions.Classes
if (string.IsNullOrWhiteSpace(commandText)) if (string.IsNullOrWhiteSpace(commandText))
throw new ArgumentNullException(nameof(commandText)); throw new ArgumentNullException(nameof(commandText));
var normalizedCmdTxt = commandText.Trim().ToUpperInvariant();
foreach (var com in NadekoBot.Client.GetService<CommandService>().AllCommands) foreach (var com in NadekoBot.Client.GetService<CommandService>().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; return com.Text;
} }
throw new NullReferenceException("That command does not exist."); throw new NullReferenceException("That command does not exist.");

View File

@ -424,6 +424,21 @@ namespace NadekoBot.Modules.Permissions.Classes
Task.Run(() => WriteServerToJson(serverPerms)); 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) public static void AddFilteredWord(Server server, string word)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -537,6 +552,10 @@ namespace NadekoBot.Modules.Permissions.Classes
public Dictionary<ulong, Permissions> UserPermissions { get; set; } public Dictionary<ulong, Permissions> UserPermissions { get; set; }
public Dictionary<ulong, Permissions> ChannelPermissions { get; set; } public Dictionary<ulong, Permissions> ChannelPermissions { get; set; }
public Dictionary<ulong, Permissions> RolePermissions { get; set; } public Dictionary<ulong, Permissions> RolePermissions { get; set; }
/// <summary>
/// Dictionary of command names with their respective cooldowns
/// </summary>
public ConcurrentDictionary<string, int> CommandCooldowns { get; set; }
public ServerPermissions(ulong id, string name) public ServerPermissions(ulong id, string name)
{ {
@ -549,6 +568,7 @@ namespace NadekoBot.Modules.Permissions.Classes
UserPermissions = new Dictionary<ulong, Permissions>(); UserPermissions = new Dictionary<ulong, Permissions>();
ChannelPermissions = new Dictionary<ulong, Permissions>(); ChannelPermissions = new Dictionary<ulong, Permissions>();
RolePermissions = new Dictionary<ulong, Permissions>(); RolePermissions = new Dictionary<ulong, Permissions>();
CommandCooldowns = new ConcurrentDictionary<string, int>();
Words = new HashSet<string>(); Words = new HashSet<string>();
} }
} }

View File

@ -1,5 +1,6 @@
using Discord.Commands; using Discord.Commands;
using Discord.Modules; using Discord.Modules;
using NadekoBot.Classes;
using NadekoBot.Classes.JSONModels; using NadekoBot.Classes.JSONModels;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Commands; using NadekoBot.Modules.Games.Commands;
@ -776,6 +777,57 @@ namespace NadekoBot.Modules.Permissions
await e.Channel.SendMessage($"`Sucessfully blacklisted server {server.Name}`").ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted server {server.Name}`").ConfigureAwait(false);
}).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);
}
});
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);
});
}); });
} }
} }

View File

@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Pokemon
}); });
cgb.CreateCommand(Prefix + "heal") 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}heal @someone")
.Parameter("target", ParameterType.Unparsed) .Parameter("target", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -242,7 +242,7 @@ namespace NadekoBot.Modules.Pokemon
return; return;
} }
var target = (usr.Id == e.User.Id) ? "yourself" : usr.Name; 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 //healing
targetStats.Hp = targetStats.MaxHp; targetStats.Hp = targetStats.MaxHp;
if (HP < 0) 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); 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; 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 //Actually changing the type here
var preTypes = DbHandler.Instance.GetAllRows<UserPokeTypes>(); var preTypes = DbHandler.Instance.GetAllRows<UserPokeTypes>();
Dictionary<long, int> Dict = preTypes.ToDictionary(x => x.UserId, y => y.Id.Value); Dictionary<long, int> Dict = preTypes.ToDictionary(x => x.UserId, y => y.Id.Value);

View File

@ -49,11 +49,11 @@ namespace NadekoBot.Modules.Searches.Commands
string result = parser.Parse(expression).ToString(); string result = parser.Parse(expression).ToString();
return result; return result;
} }
catch (OverflowException e) catch (OverflowException)
{ {
return $"Overflow error on {expression}"; return $"Overflow error on {expression}";
} }
catch (FormatException e) catch (FormatException)
{ {
return $"\"{expression}\" was not formatted correctly"; return $"\"{expression}\" was not formatted correctly";
} }

View File

@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Searches.Commands
string.Join("\n", JsonConvert.DeserializeObject<Dictionary<string, string>>(await SearchHelper.GetResponseStringAsync("http://memegen.link/templates/")) string.Join("\n", JsonConvert.DeserializeObject<Dictionary<string, string>>(await SearchHelper.GetResponseStringAsync("http://memegen.link/templates/"))
.Select(kvp => Path.GetFileName(kvp.Value)) .Select(kvp => Path.GetFileName(kvp.Value))
.GroupBy(item => (i++) / 4) .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); + $"\n```").ConfigureAwait(false);
}); });

View File

@ -15,6 +15,7 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Web;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
@ -219,6 +220,19 @@ $@"🌍 **Weather for** 【{obj["target"]}】
.ConfigureAwait(false); .ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "google")
.Alias(Prefix + "g")
.Description("Get a google search link for some terms.")
.Parameter("terms", ParameterType.Unparsed)
.Do(async e =>
{
var terms = e.GetArg("terms")?.Trim();
if (string.IsNullOrWhiteSpace(terms))
return;
await e.Channel.SendMessage($"https://google.com/search?q={ HttpUtility.UrlEncode(terms).Replace(' ', '+') }")
.ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "hs") cgb.CreateCommand(Prefix + "hs")
.Description("Searches for a Hearthstone card and shows its image. Takes a while to complete. |~hs Ysera") .Description("Searches for a Hearthstone card and shows its image. Takes a while to complete. |~hs Ysera")
.Parameter("name", ParameterType.Unparsed) .Parameter("name", ParameterType.Unparsed)

View File

@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Translator.Helpers
text = await http.GetStringAsync(url).ConfigureAwait(false); text = await http.GetStringAsync(url).ConfigureAwait(false);
} }
return JArray.Parse(text)[0][0][0].ToString(); return (string.Concat(JArray.Parse(text)[0].Select(x => x[0])));
} }
#endregion #endregion

View File

@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Utility
if (arr.Length == 0) if (arr.Length == 0)
await e.Channel.SendMessage("Nobody. (not 100% sure)").ConfigureAwait(false); await e.Channel.SendMessage("Nobody. (not 100% sure)").ConfigureAwait(false);
else 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") cgb.CreateCommand(Prefix + "inrole")

View File

@ -41,6 +41,7 @@ namespace NadekoBot
public static LocalizedStrings Locale { get; set; } = new LocalizedStrings(); public static LocalizedStrings Locale { get; set; } = new LocalizedStrings();
public static string BotMention { get; set; } = ""; public static string BotMention { get; set; } = "";
public static bool Ready { get; set; } = false; public static bool Ready { get; set; } = false;
public static Action OnReady { get; set; } = delegate { };
private static List<Channel> OwnerPrivateChannels { get; set; } private static List<Channel> OwnerPrivateChannels { get; set; }
@ -196,7 +197,7 @@ namespace NadekoBot
return; return;
} }
#if NADEKO_RELEASE #if NADEKO_RELEASE
await Task.Delay(120000).ConfigureAwait(false); await Task.Delay(90000).ConfigureAwait(false);
#else #else
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
#endif #endif
@ -229,6 +230,7 @@ namespace NadekoBot
}; };
PermissionsHandler.Initialize(); PermissionsHandler.Initialize();
NadekoBot.Ready = true; NadekoBot.Ready = true;
NadekoBot.OnReady();
}); });
Console.WriteLine("Exiting..."); Console.WriteLine("Exiting...");
Console.ReadKey(); Console.ReadKey();
@ -277,5 +279,3 @@ namespace NadekoBot
} }
} }
} }
//95520984584429568 meany

View File

@ -40,10 +40,12 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<Optimize>false</Optimize> <Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;__DEMO__,__DEMO_EXPERIMENTAL__</DefineConstants> <DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<NoWarn>
</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -74,6 +76,46 @@
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PRIVATE|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\PRIVATE\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'NadekoRelease|x64'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;NADEKO_RELEASE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="libvideo, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="libvideo, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VideoLibrary.1.3.3\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\libvideo.dll</HintPath> <HintPath>..\packages\VideoLibrary.1.3.3\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\libvideo.dll</HintPath>

View File

@ -175,7 +175,7 @@ Nadeko Support Server: <https://discord.gg/0ehQwTK2RBjAxzEY>";
public string Conversations { get; set; } = "<@{0}>"; public string Conversations { get; set; } = "<@{0}>";
public string ClashOfClans { get; set; } = ","; public string ClashOfClans { get; set; } = ",";
public string Help { 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 Trello { get; set; } = "trello ";
public string Games { get; set; } = ">"; public string Games { get; set; } = ">";
public string Gambling { get; set; } = "$"; public string Gambling { get; set; } = "$";

View File

@ -7,7 +7,7 @@ namespace NadekoBot.Classes.JSONModels
public class Credentials public class Credentials
{ {
public string Token { get; set; } = ""; 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 BotId { get; set; } = 1231231231231;
public ulong[] OwnerIds { get; set; } = { 123123123123, 5675675679845 }; public ulong[] OwnerIds { get; set; } = { 123123123123, 5675675679845 };
public string GoogleAPIKey { get; set; } = ""; public string GoogleAPIKey { get; set; } = "";

View File

@ -1,6 +1,6 @@
{ {
"Token": "", "Token": "",
"ClientId": "116275390695079945", "ClientId": "170254782546575360",
"BotId": 1231231231231, "BotId": 1231231231231,
"OwnerIds": [ "OwnerIds": [
123123123123, 123123123123,

View File

@ -83,7 +83,7 @@
"Conversations": "<@{0}>", "Conversations": "<@{0}>",
"ClashOfClans": ",", "ClashOfClans": ",",
"Help": "-", "Help": "-",
"Music": "!m", "Music": "!!",
"Trello": "trello ", "Trello": "trello ",
"Games": ">", "Games": ">",
"Gambling": "$", "Gambling": "$",

View File

@ -1,8 +1,8 @@
######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/** ######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 #NadekoBot List Of Commands
Version: `NadekoBot v0.9.6036.32870` Version: `NadekoBot v0.9.6048.2992`
### Help ### Help
Command and aliases | Description | Usage 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. `.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. `.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!** `.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. `.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. `.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. `.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 `.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. `.rsar` | Removes a specified role from the list of self-assignable roles.
`.lsar` | Lists all 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 `.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 `.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: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **Bot Owner Only!** | .acr "hello" I love saying hello to %user% `.addcustreact`, `.acr` | Add a custom reaction. Guide here: <https://github.com/Kwoth/NadekoBot/wiki/Custom-Reactions> **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. `.donators` | List of lovely people who donated to keep this project alive.
`.donadd` | Add a donator to the database. `.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 `.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 ### Utility
@ -145,6 +146,8 @@ Command and aliases | Description | Usage
`;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl #some_channel `;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl #some_channel
`;cubl` | Unblacklists a mentioned channel (#general for example). | ;cubl #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] `;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 ### Conversations
Command and aliases | Description | Usage 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 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 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 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 dump` | Dumps all of the invites it can to dump.txt.** Owner Only.**
`@BotName ab` | Try to get 'abalabahaha' `@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] `$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. `$shuffle`, `$sh` | Reshuffles all cards back into the deck.
`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` `$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 `$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` `$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. `$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` `$$$` | Check how much NadekoFlowers a person has. (Defaults to yourself) | `$$$` or `$$$ @Someone`
`$give` | Give someone a certain amount of NadekoFlowers `$give` | Give someone a certain amount of NadekoFlowers
`$award` | Gives someone a certain amount of flowers. **Bot Owner Only!** | `$award 100 @person` `$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!** `$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` | `$leaderboard`, `$lb` |
### Games ### Games
@ -199,39 +204,39 @@ Command and aliases | Description | Usage
### Music ### 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` `!!next`, `!!n`, `!!skip` | Goes to the next song in the queue. You have to be in the same voice channel as the bot. | `!!n`
`!m stop`, `!m s` | Stops the music and clears the playlist. Stays in the channel. | `!m s` `!!stop`, `!!s` | Stops the music and clears the playlist. Stays in the channel. | `!!s`
`!m destroy`, `!m d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!m d` `!!destroy`, `!!d` | Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) | `!!d`
`!m pause`, `!m p` | Pauses or Unpauses the song. | `!m p` `!!pause`, `!!p` | Pauses or Unpauses the song. | `!!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` `!!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`
`!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` `!!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`
`!m listqueue`, `!m lq` | Lists 15 currently queued songs per page. Default page is 1. | `!m lq` or `!m lq 2` `!!listqueue`, `!!lq` | Lists 15 currently queued songs per page. Default page is 1. | `!!lq` or `!!lq 2`
`!m nowplaying`, `!m np` | Shows the song currently playing. | `!m np` `!!nowplaying`, `!!np` | Shows the song currently playing. | `!!np`
`!m volume`, `!m vol` | Sets the music volume 0-100% | `!m vol 50` `!!volume`, `!!vol` | Sets the music volume 0-100% | `!!vol 50`
`!m defvol`, `!m dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!m dv 80` `!!defvol`, `!!dv` | Sets the default music volume when music playback is started (0-100). Persists through restarts. | `!!dv 80`
`!m mute`, `!m min` | Sets the music volume to 0% | `!m min` `!!mute`, `!!min` | Sets the music volume to 0% | `!!min`
`!m max` | Sets the music volume to 100%. | `!m max` `!!max` | Sets the music volume to 100%. | `!!max`
`!m half` | Sets the music volume to 50%. | `!m half` `!!half` | Sets the music volume to 50%. | `!!half`
`!m shuffle`, `!m sh` | Shuffles the current playlist. | `!m sh` `!!shuffle`, `!!sh` | Shuffles the current playlist. | `!!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` `!!playlist`, `!!pl` | Queues up to 500 songs from a youtube playlist specified by a link, or keywords. | `!!pl playlist link or name`
`!m soundcloudpl`, `!m scpl` | Queue a soundcloud playlist using a link. | `!m scpl https://soundcloud.com/saratology/sets/symphony` `!!soundcloudpl`, `!!scpl` | Queue a soundcloud playlist using a link. | `!!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` `!!localplaylst`, `!!lopl` | Queues all songs from a directory. **Bot Owner Only!** | `!!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` `!!radio`, `!!ra` | Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) | `!!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` `!!local`, `!!lo` | Queues a local file by specifying a full path. **Bot Owner Only!** | `!!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` `!!move`, `!!mv` | Moves the bot to your voice channel. (works only if music is already playing) | `!!mv`
`!m remove`, `!m rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!m rm 5` `!!remove`, `!!rm` | Remove a song by its # in the queue, or 'all' to remove whole queue. | `!!rm 5`
`!m movesong`, `!m ms` | Moves a song from one position to another. | `!m ms` 5>3 `!!movesong`, `!!ms` | Moves a song from one position to another. | `!! 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` `!!setmaxqueue`, `!!smq` | Sets a maximum queue size. Supply 0 or no argument to have no limit. | `!!smq` 50 or `!!smq`
`!m cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!m cleanup` `!!cleanup` | Cleans up hanging voice connections. **Bot Owner Only!** | `!!cleanup`
`!m reptcursong`, `!m rcs` | Toggles repeat of current song. | `!m rcs` `!!reptcursong`, `!!rcs` | Toggles repeat of current song. | `!!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` `!!rpeatplaylst`, `!!rpl` | Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). | `!!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` `!!save` | Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. | `!!save classical1`
`!m load` | Loads a playlist under a certain name. | `!m load classical-1` `!!load` | Loads a playlist under a certain name. | `!!load classical-1`
`!m playlists`, `!m pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!m pls 1` `!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!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` `!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
`!m goto` | Goes to a specific time in seconds in a song. `!!goto` | Goes to a specific time in seconds in a song.
`!m getlink`, `!m gl` | Shows a link to the currently playing song. `!!getlink`, `!!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) `!!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 ### Searches
Command and aliases | Description | Usage 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 `~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 `~ir` | Pulls a random image using a search parameter. | ~ir cute kitten
`~lmgtfy` | Google something for an idiot. `~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 `~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 `~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple
`~#` | Searches Tagdef.com for a hashtag. | ~# ff `~#` | 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` `>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 `>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 `>type` | Get the poketype of the target. | >type @someone
`>settype` | Set your poketype. Costs a NadekoFlower. | >settype fire `>settype` | Set your poketype. Costs a NadekoFlower. | >settype fire
@ -331,16 +337,17 @@ Command and aliases | Description | Usage
`moveto` | Custom reaction. | moveto `moveto` | Custom reaction. | moveto
`comeatmebro` | Custom reaction. | comeatmebro `comeatmebro` | Custom reaction. | comeatmebro
`e` | Custom reaction. | e `e` | Custom reaction. | e
`@BotName insult`, `<@!119777021319577610> insult` | Custom reaction. | %mention% insult `@BotName insult`, `<@!116275390695079945> insult` | Custom reaction. | %mention% insult
`@BotName praise`, `<@!119777021319577610> praise` | Custom reaction. | %mention% praise `@BotName praise`, `<@!116275390695079945> praise` | Custom reaction. | %mention% praise
`@BotName pat`, `<@!119777021319577610> pat` | Custom reaction. | %mention% pat `@BotName pat`, `<@!116275390695079945> pat` | Custom reaction. | %mention% pat
`@BotName cry`, `<@!119777021319577610> cry` | Custom reaction. | %mention% cry `@BotName cry`, `<@!116275390695079945> cry` | Custom reaction. | %mention% cry
`@BotName are you real?`, `<@!119777021319577610> are you real?` | Custom reaction. | %mention% are you real? `@BotName are you real?`, `<@!116275390695079945> are you real?` | Custom reaction. | %mention% are you real?
`@BotName are you there?`, `<@!119777021319577610> are you there?` | Custom reaction. | %mention% are you there? `@BotName are you there?`, `<@!116275390695079945> are you there?` | Custom reaction. | %mention% are you there?
`@BotName draw`, `<@!119777021319577610> draw` | Custom reaction. | %mention% draw `@BotName draw`, `<@!116275390695079945> draw` | Custom reaction. | %mention% draw
`@BotName bb`, `<@!119777021319577610> bb` | Custom reaction. | %mention% bb `@BotName bb`, `<@!116275390695079945> bb` | Custom reaction. | %mention% bb
`@BotName call`, `<@!119777021319577610> call` | Custom reaction. | %mention% call `@BotName call`, `<@!116275390695079945> call` | Custom reaction. | %mention% call
`@BotName disguise`, `<@!119777021319577610> disguise` | Custom reaction. | %mention% disguise `@BotName disguise`, `<@!116275390695079945> disguise` | Custom reaction. | %mention% disguise
`~hentai` | Custom reaction. | ~hentai
### Trello ### Trello
Command and aliases | Description | Usage Command and aliases | Description | Usage

@ -1 +1 @@
Subproject commit 80f9d6f2de25355a245bf93d4019d16e3fd033ca Subproject commit 3e519b5e0b33175e5a5ca247322b7082de484e15