Merge pull request #5 from Kwoth/master

Updating fork
This commit is contained in:
miraai 2016-07-28 20:22:03 +02:00 committed by GitHub
commit 54bed4ecc9
40 changed files with 1233 additions and 630 deletions

3
.gitmodules vendored
View File

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

View File

@ -36,9 +36,7 @@ ________________________________________________________________________________
###### Setting up `ffmpeg` with installer: ###### Setting up `ffmpeg` with installer:
1) Google Account 1) Google Account
2) Soundcloud Account (if you want soundcloud support) 2) Soundcloud Account (if you want soundcloud support)
3) Download installer here: https://goo.gl/lQZnsH (pick the one for your system, either 32 or 64bit and then click 'download')
3) Download installer here: http://luxcaeli.de/owncloud/s/fIxSgh4Nde3Td6e/download
4) Run the installer 4) Run the installer
5) Follow these steps on how to setup API keys: 5) Follow these steps on how to setup API keys:

View File

@ -138,7 +138,7 @@ namespace NadekoBot.Extensions
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="list"></param> /// <param name="list"></param>
public static void Shuffle<T>(this IList<T> list) public static IList<T> Shuffle<T>(this IList<T> list)
{ {
// Thanks to @Joe4Evr for finding a bug in the old version of the shuffle // Thanks to @Joe4Evr for finding a bug in the old version of the shuffle
@ -160,6 +160,7 @@ namespace NadekoBot.Extensions
list[k] = list[n]; list[k] = list[n];
list[n] = value; list[n] = value;
} }
return list;
} }
/// <summary> /// <summary>
@ -303,6 +304,15 @@ namespace NadekoBot.Extensions
public static int GiB(this int value) => value.MiB() * 1024; public static int GiB(this int value) => value.MiB() * 1024;
public static int GB(this int value) => value.MB() * 1000; public static int GB(this int value) => value.MB() * 1000;
public static ulong KiB(this ulong value) => value * 1024;
public static ulong KB(this ulong value) => value * 1000;
public static ulong MiB(this ulong value) => value.KiB() * 1024;
public static ulong MB(this ulong value) => value.KB() * 1000;
public static ulong GiB(this ulong value) => value.MiB() * 1024;
public static ulong GB(this ulong value) => value.MB() * 1000;
public static Stream ToStream(this Image img, System.Drawing.Imaging.ImageFormat format = null) public static Stream ToStream(this Image img, System.Drawing.Imaging.ImageFormat format = null)
{ {
if (format == null) if (format == null)
@ -362,5 +372,7 @@ namespace NadekoBot.Extensions
return sw.BaseStream; return sw.BaseStream;
} }
public static double UnixTimestamp(this DateTime dt) => dt.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
} }
} }

View File

@ -4,8 +4,10 @@ using NadekoBot.Extensions;
using NadekoBot.Modules.Administration.Commands; using NadekoBot.Modules.Administration.Commands;
using NadekoBot.Modules.Music; using NadekoBot.Modules.Music;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Reflection; using System.Reflection;
@ -41,7 +43,23 @@ namespace NadekoBot
var commandService = NadekoBot.Client.GetService<CommandService>(); var commandService = NadekoBot.Client.GetService<CommandService>();
statsStopwatch.Start(); statsStopwatch.Start();
commandService.CommandExecuted += StatsCollector_RanCommand; commandService.CommandExecuted += StatsCollector_RanCommand;
commandService.CommandFinished += CommandService_CommandFinished;
commandService.CommandErrored += (s, e) =>
{
try
{
if (e.ErrorType == CommandErrorType.Exception)
File.AppendAllText("errors.txt", $@"Command: {e.Command}
{e.Exception}
-------------------------------------
");
}
catch {
Console.WriteLine("Command errored errorring");
}
};
Task.Run(StartCollecting); Task.Run(StartCollecting);
@ -97,7 +115,7 @@ namespace NadekoBot
if (e.Channel.IsPrivate) if (e.Channel.IsPrivate)
return; return;
if (e.Channel.Type == ChannelType.Text) if (e.Channel.Type == ChannelType.Text)
VoiceChannelsCount++; TextChannelsCount--;
else if (e.Channel.Type == ChannelType.Voice) else if (e.Channel.Type == ChannelType.Voice)
VoiceChannelsCount--; VoiceChannelsCount--;
} }
@ -106,6 +124,7 @@ namespace NadekoBot
carbonStatusTimer.Elapsed += async (s, e) => await SendUpdateToCarbon().ConfigureAwait(false); carbonStatusTimer.Elapsed += async (s, e) => await SendUpdateToCarbon().ConfigureAwait(false);
carbonStatusTimer.Start(); carbonStatusTimer.Start();
} }
HttpClient carbonClient = new HttpClient(); HttpClient carbonClient = new HttpClient();
private async Task SendUpdateToCarbon() private async Task SendUpdateToCarbon()
{ {
@ -176,9 +195,11 @@ namespace NadekoBot
private async Task StartCollecting() private async Task StartCollecting()
{ {
var statsSw = new Stopwatch();
while (true) while (true)
{ {
await Task.Delay(new TimeSpan(0, 30, 0)).ConfigureAwait(false); await Task.Delay(new TimeSpan(0, 30, 0)).ConfigureAwait(false);
statsSw.Start();
try try
{ {
var onlineUsers = await Task.Run(() => NadekoBot.Client.Servers.Sum(x => x.Users.Count())).ConfigureAwait(false); var onlineUsers = await Task.Run(() => NadekoBot.Client.Servers.Sum(x => x.Users.Count())).ConfigureAwait(false);
@ -195,6 +216,13 @@ namespace NadekoBot
ConnectedServers = connectedServers, ConnectedServers = connectedServers,
DateAdded = DateTime.Now DateAdded = DateTime.Now
}); });
statsSw.Stop();
var clr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"--------------\nCollecting stats finished in {statsSw.Elapsed.TotalSeconds}s\n-------------");
Console.ForegroundColor = clr;
statsSw.Reset();
} }
catch catch
{ {
@ -204,9 +232,21 @@ namespace NadekoBot
} }
} }
private static ConcurrentDictionary<ulong, DateTime> commandTracker = new ConcurrentDictionary<ulong, DateTime>();
private void CommandService_CommandFinished(object sender, CommandEventArgs e)
{
DateTime dt;
if (!commandTracker.TryGetValue(e.Message.Id, out dt))
return;
Console.WriteLine($">>COMMAND ENDED after *{(DateTime.UtcNow - dt).TotalSeconds}s*\nCmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----");
}
private async void StatsCollector_RanCommand(object sender, CommandEventArgs e) private async void StatsCollector_RanCommand(object sender, CommandEventArgs e)
{ {
Console.WriteLine($">> Cmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----"); commandTracker.TryAdd(e.Message.Id, DateTime.UtcNow);
Console.WriteLine($">>COMMAND STARTED\nCmd: {e.Command.Text}\nMsg: {e.Message.Text}\nUsr: {e.User.Name} [{e.User.Id}]\nSrvr: {e.Server?.Name ?? "PRIVATE"} [{e.Server?.Id}]\n-----");
#if !NADEKO_RELEASE
await Task.Run(() => await Task.Run(() =>
{ {
try try
@ -230,6 +270,7 @@ namespace NadekoBot
Console.WriteLine(ex); Console.WriteLine(ex);
} }
}).ConfigureAwait(false); }).ConfigureAwait(false);
#endif
} }
} }
} }

View File

@ -364,9 +364,7 @@ namespace NadekoBot.Classes
} }
var httpResponse = (await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) as HttpWebResponse; var httpResponse = (await httpWebRequest.GetResponseAsync().ConfigureAwait(false)) as HttpWebResponse;
if (httpResponse == null) return "HTTP_RESPONSE_ERROR";
var responseStream = httpResponse.GetResponseStream(); var responseStream = httpResponse.GetResponseStream();
if (responseStream == null) return "RESPONSE_STREAM ERROR";
using (var streamReader = new StreamReader(responseStream)) using (var streamReader = new StreamReader(responseStream))
{ {
var responseText = await streamReader.ReadToEndAsync().ConfigureAwait(false); var responseText = await streamReader.ReadToEndAsync().ConfigureAwait(false);
@ -375,6 +373,7 @@ namespace NadekoBot.Classes
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Shortening of this url failed: " + url);
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
return url; return url;
} }

View File

@ -6,6 +6,8 @@ using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes namespace NadekoBot.Classes
{ {
@ -27,9 +29,12 @@ namespace NadekoBot.Classes
{ {
configs = JsonConvert configs = JsonConvert
.DeserializeObject<ConcurrentDictionary<ulong, ServerSpecificConfig>>( .DeserializeObject<ConcurrentDictionary<ulong, ServerSpecificConfig>>(
File.ReadAllText(filePath), new JsonSerializerSettings() { File.ReadAllText(filePath), new JsonSerializerSettings()
Error = (s,e) => { {
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") { Error = (s, e) =>
{
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels")
{
e.ErrorContext.Handled = true; e.ErrorContext.Handled = true;
} }
} }
@ -52,14 +57,19 @@ namespace NadekoBot.Classes
public ServerSpecificConfig Of(ulong id) => public ServerSpecificConfig Of(ulong id) =>
configs.GetOrAdd(id, _ => new ServerSpecificConfig()); configs.GetOrAdd(id, _ => new ServerSpecificConfig());
private readonly object saveLock = new object(); private readonly SemaphoreSlim saveLock = new SemaphoreSlim(1, 1);
public void Save() public async Task Save()
{ {
lock (saveLock) await saveLock.WaitAsync();
try
{ {
File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented)); File.WriteAllText(filePath, JsonConvert.SerializeObject(configs, Formatting.Indented));
} }
finally
{
saveLock.Release();
}
} }
} }
@ -245,7 +255,7 @@ namespace NadekoBot.Classes
LogserverIgnoreChannels = new ObservableCollection<ulong>(); LogserverIgnoreChannels = new ObservableCollection<ulong>();
} }
public event PropertyChangedEventHandler PropertyChanged = delegate { SpecificConfigurations.Default.Save(); }; public event PropertyChangedEventHandler PropertyChanged = async delegate { await SpecificConfigurations.Default.Save().ConfigureAwait(false); };
private void OnPropertyChanged([CallerMemberName] string propertyName = null) private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{ {

View File

@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Administration
{ {
var conf = SpecificConfigurations.Default.Of(e.Server.Id); var conf = SpecificConfigurations.Default.Of(e.Server.Id);
conf.AutoDeleteMessagesOnCommand = !conf.AutoDeleteMessagesOnCommand; conf.AutoDeleteMessagesOnCommand = !conf.AutoDeleteMessagesOnCommand;
Classes.JSONModels.ConfigHandler.SaveConfig(); await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
if (conf.AutoDeleteMessagesOnCommand) if (conf.AutoDeleteMessagesOnCommand)
await e.Channel.SendMessage("❗`Now automatically deleting successfull command invokations.`"); await e.Channel.SendMessage("❗`Now automatically deleting successfull command invokations.`");
else else
@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "setrole").Alias(Prefix + "sr") cgb.CreateCommand(Prefix + "setrole").Alias(Prefix + "sr")
.Description("Sets a role for a given user. | .sr @User Guest") .Description($"Sets a role for a given user. | `{Prefix}sr @User Guest`")
.Parameter("user_name", ParameterType.Required) .Parameter("user_name", ParameterType.Required)
.Parameter("role_name", ParameterType.Unparsed) .Parameter("role_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles) .AddCheck(SimpleCheckers.CanManageRoles)
@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "removerole").Alias(Prefix + "rr") cgb.CreateCommand(Prefix + "removerole").Alias(Prefix + "rr")
.Description("Removes a role from a given user. | .rr @User Admin") .Description($"Removes a role from a given user. | `{Prefix}rr @User Admin`")
.Parameter("user_name", ParameterType.Required) .Parameter("user_name", ParameterType.Required)
.Parameter("role_name", ParameterType.Unparsed) .Parameter("role_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles) .AddCheck(SimpleCheckers.CanManageRoles)
@ -204,7 +204,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "removeallroles").Alias(Prefix + "rar") cgb.CreateCommand(Prefix + "removeallroles").Alias(Prefix + "rar")
.Description("Removes all roles from a mentioned user. | .rar @User") .Description($"Removes all roles from a mentioned user. | `{Prefix}rar @User`")
.Parameter("user_name", ParameterType.Unparsed) .Parameter("user_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles) .AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e => .Do(async e =>
@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "createrole").Alias(Prefix + "cr") cgb.CreateCommand(Prefix + "createrole").Alias(Prefix + "cr")
.Description("Creates a role with a given name.**Usage**: `.r Awesome Role`") .Description($"Creates a role with a given name. | `{Prefix}cr Awesome Role`")
.Parameter("role_name", ParameterType.Unparsed) .Parameter("role_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles) .AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e => .Do(async e =>
@ -253,7 +253,7 @@ namespace NadekoBot.Modules.Administration
.Parameter("r", ParameterType.Optional) .Parameter("r", ParameterType.Optional)
.Parameter("g", ParameterType.Optional) .Parameter("g", ParameterType.Optional)
.Parameter("b", ParameterType.Optional) .Parameter("b", ParameterType.Optional)
.Description("Set a role's color to the hex or 0-255 rgb color value provided. | `.color Admin 255 200 100` or `.color Admin ffba55`") .Description($"Set a role's color to the hex or 0-255 rgb color value provided. | `{Prefix}rc Admin 255 200 100` or `{Prefix}rc Admin ffba55`")
.Do(async e => .Do(async e =>
{ {
if (!e.User.ServerPermissions.ManageRoles) if (!e.User.ServerPermissions.ManageRoles)
@ -298,7 +298,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "ban").Alias(Prefix + "b") cgb.CreateCommand(Prefix + "ban").Alias(Prefix + "b")
.Parameter("user", ParameterType.Required) .Parameter("user", ParameterType.Required)
.Parameter("msg", ParameterType.Unparsed) .Parameter("msg", ParameterType.Unparsed)
.Description("Bans a user by id or name with an optional message. | .b \"@some Guy\" Your behaviour is toxic.") .Description($"Bans a user by id or name with an optional message. | `{Prefix}b \"@some Guy\" Your behaviour is toxic.`")
.Do(async e => .Do(async e =>
{ {
var msg = e.GetArg("msg"); var msg = e.GetArg("msg");
@ -333,7 +333,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "softban").Alias(Prefix + "sb") cgb.CreateCommand(Prefix + "softban").Alias(Prefix + "sb")
.Parameter("user", ParameterType.Required) .Parameter("user", ParameterType.Required)
.Parameter("msg", ParameterType.Unparsed) .Parameter("msg", ParameterType.Unparsed)
.Description("Bans and then unbans a user by id or name with an optional message. | .sb \"@some Guy\" Your behaviour is toxic.") .Description($"Bans and then unbans a user by id or name with an optional message. | `{Prefix}sb \"@some Guy\" Your behaviour is toxic.`")
.Do(async e => .Do(async e =>
{ {
var msg = e.GetArg("msg"); var msg = e.GetArg("msg");
@ -369,7 +369,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "kick").Alias(Prefix + "k") cgb.CreateCommand(Prefix + "kick").Alias(Prefix + "k")
.Parameter("user") .Parameter("user")
.Parameter("msg", ParameterType.Unparsed) .Parameter("msg", ParameterType.Unparsed)
.Description("Kicks a mentioned user.") .Description($"Kicks a mentioned user. | `{Prefix}k \"@some Guy\" Your behaviour is toxic.`")
.Do(async e => .Do(async e =>
{ {
var msg = e.GetArg("msg"); var msg = e.GetArg("msg");
@ -400,7 +400,7 @@ namespace NadekoBot.Modules.Administration
} }
}); });
cgb.CreateCommand(Prefix + "mute") cgb.CreateCommand(Prefix + "mute")
.Description("Mutes mentioned user or users.") .Description($"Mutes mentioned user or users. | `{Prefix}mute \"@Someguy\"` or `{Prefix}mute \"@Someguy\" \"@Someguy\"`")
.Parameter("throwaway", ParameterType.Unparsed) .Parameter("throwaway", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -426,7 +426,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "unmute") cgb.CreateCommand(Prefix + "unmute")
.Description("Unmutes mentioned user or users.") .Description($"Unmutes mentioned user or users. | `{Prefix}unmute \"@Someguy\"` or `{Prefix}unmute \"@Someguy\" \"@Someguy\"`")
.Parameter("throwaway", ParameterType.Unparsed) .Parameter("throwaway", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -453,7 +453,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "deafen") cgb.CreateCommand(Prefix + "deafen")
.Alias(Prefix + "deaf") .Alias(Prefix + "deaf")
.Description("Deafens mentioned user or users") .Description($"Deafens mentioned user or users | `{Prefix}deaf \"@Someguy\"` or `{Prefix}deaf \"@Someguy\" \"@Someguy\"`")
.Parameter("throwaway", ParameterType.Unparsed) .Parameter("throwaway", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -480,7 +480,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "undeafen") cgb.CreateCommand(Prefix + "undeafen")
.Alias(Prefix + "undef") .Alias(Prefix + "undef")
.Description("Undeafens mentioned user or users") .Description($"Undeafens mentioned user or users | `{Prefix}undef \"@Someguy\"` or `{Prefix}undef \"@Someguy\" \"@Someguy\"`")
.Parameter("throwaway", ParameterType.Unparsed) .Parameter("throwaway", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -507,7 +507,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "delvoichanl") cgb.CreateCommand(Prefix + "delvoichanl")
.Alias(Prefix + "dvch") .Alias(Prefix + "dvch")
.Description("Deletes a voice channel with a given name.") .Description($"Deletes a voice channel with a given name. | `{Prefix}dvch VoiceChannelName`")
.Parameter("channel_name", ParameterType.Required) .Parameter("channel_name", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -530,7 +530,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "creatvoichanl") cgb.CreateCommand(Prefix + "creatvoichanl")
.Alias(Prefix + "cvch") .Alias(Prefix + "cvch")
.Description("Creates a new voice channel with a given name.") .Description($"Creates a new voice channel with a given name. | `{Prefix}cvch VoiceChannelName`")
.Parameter("channel_name", ParameterType.Required) .Parameter("channel_name", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -550,7 +550,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "deltxtchanl") cgb.CreateCommand(Prefix + "deltxtchanl")
.Alias(Prefix + "dtch") .Alias(Prefix + "dtch")
.Description("Deletes a text channel with a given name.") .Description($"Deletes a text channel with a given name. | `{Prefix}dtch TextChannelName`")
.Parameter("channel_name", ParameterType.Required) .Parameter("channel_name", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -572,7 +572,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "creatxtchanl") cgb.CreateCommand(Prefix + "creatxtchanl")
.Alias(Prefix + "ctch") .Alias(Prefix + "ctch")
.Description("Creates a new text channel with a given name.") .Description($"Creates a new text channel with a given name. | `{Prefix}ctch TextChannelName`")
.Parameter("channel_name", ParameterType.Required) .Parameter("channel_name", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -604,7 +604,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "setchanlname") cgb.CreateCommand(Prefix + "setchanlname")
.Alias(Prefix + "schn") .Alias(Prefix + "schn")
.Description("Changed the name of the current channel.") .Description($"Changed the name of the current channel.| `{Prefix}schn NewName`")
.AddCheck(SimpleCheckers.ManageChannels()) .AddCheck(SimpleCheckers.ManageChannels())
.Parameter("name", ParameterType.Unparsed) .Parameter("name", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
@ -636,7 +636,8 @@ namespace NadekoBot.Modules.Administration
Message[] msgs; 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
{ {
msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false)).Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray(); msgs = (await e.Channel.DownloadMessages(100).ConfigureAwait(false));//.Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray();
msgs = msgs.Where(m => m.User.Id == e.Server.CurrentUser.Id).ToArray();
if (!msgs.Any()) if (!msgs.Any())
return; return;
await e.Channel.DeleteMessages(msgs).ConfigureAwait(false); await e.Channel.DeleteMessages(msgs).ConfigureAwait(false);
@ -684,7 +685,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "setname") cgb.CreateCommand(Prefix + "setname")
.Alias(Prefix + "newnm") .Alias(Prefix + "newnm")
.Description("Give the bot a new name. **Bot Owner Only!**") .Description($"Give the bot a new name. **Bot Owner Only!** | {Prefix}newnm BotName")
.Parameter("new_name", ParameterType.Unparsed) .Parameter("new_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -696,7 +697,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "newavatar") cgb.CreateCommand(Prefix + "newavatar")
.Alias(Prefix + "setavatar") .Alias(Prefix + "setavatar")
.Description("Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg`") .Description($"Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `{Prefix}setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg`")
.Parameter("img", ParameterType.Unparsed) .Parameter("img", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -717,7 +718,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "setgame") cgb.CreateCommand(Prefix + "setgame")
.Description("Sets the bots game. **Bot Owner Only!**") .Description($"Sets the bots game. **Bot Owner Only!** | `{Prefix}setgame Playing with kwoth`")
.Parameter("set_game", ParameterType.Unparsed) .Parameter("set_game", ParameterType.Unparsed)
.Do(e => .Do(e =>
{ {
@ -727,7 +728,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "send") cgb.CreateCommand(Prefix + "send")
.Description("Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!`") .Description($"Send a message to someone on a different server through the bot. **Bot Owner Only!** | `{Prefix}send serverid|u:user_id Send this to a user!` or `{Prefix}send serverid|c:channel_id Send this to a channel!`")
.Parameter("ids", ParameterType.Required) .Parameter("ids", ParameterType.Required)
.Parameter("msg", ParameterType.Unparsed) .Parameter("msg", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
@ -775,7 +776,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "mentionrole") cgb.CreateCommand(Prefix + "mentionrole")
.Alias(Prefix + "menro") .Alias(Prefix + "menro")
.Description("Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission.") .Description($"Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. | `{Prefix}menro RoleName`")
.Parameter("roles", ParameterType.Unparsed) .Parameter("roles", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -828,7 +829,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "donadd") cgb.CreateCommand(Prefix + "donadd")
.Description("Add a donator to the database.") .Description($"Add a donator to the database. | `.donadd Donate Amount`")
.Parameter("donator") .Parameter("donator")
.Parameter("amount") .Parameter("amount")
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
@ -854,7 +855,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "announce") cgb.CreateCommand(Prefix + "announce")
.Description($"Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | {Prefix}announce Useless spam") .Description($"Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | `{Prefix}announce Useless spam`")
.Parameter("msg", ParameterType.Unparsed) .Parameter("msg", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -868,7 +869,7 @@ namespace NadekoBot.Modules.Administration
}); });
cgb.CreateCommand(Prefix + "savechat") cgb.CreateCommand(Prefix + "savechat")
.Description("Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150`") .Description($"Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `{Prefix}savechat 150`")
.Parameter("cnt", ParameterType.Required) .Parameter("cnt", ParameterType.Required)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>

View File

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Administration.Commands
NadekoBot.Config.CustomReactions[name].Add(message); NadekoBot.Config.CustomReactions[name].Add(message);
else else
NadekoBot.Config.CustomReactions.Add(name, new System.Collections.Generic.List<string>() { message }); NadekoBot.Config.CustomReactions.Add(name, new System.Collections.Generic.List<string>() { message });
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false); await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"Added {name} : {message}").ConfigureAwait(false); await e.Channel.SendMessage($"Added {name} : {message}").ConfigureAwait(false);
}); });
@ -140,7 +140,7 @@ namespace NadekoBot.Modules.Administration.Commands
index = index - 1; index = index - 1;
NadekoBot.Config.CustomReactions[name][index] = msg; NadekoBot.Config.CustomReactions[name][index] = msg;
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false); await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"Edited response #{index + 1} from `{name}`").ConfigureAwait(false); await e.Channel.SendMessage($"Edited response #{index + 1} from `{name}`").ConfigureAwait(false);
}); });
@ -183,7 +183,7 @@ namespace NadekoBot.Modules.Administration.Commands
NadekoBot.Config.CustomReactions.Remove(name); NadekoBot.Config.CustomReactions.Remove(name);
message = $"Deleted custom reaction: `{name}`"; message = $"Deleted custom reaction: `{name}`";
} }
await Task.Run(() => Classes.JSONModels.ConfigHandler.SaveConfig()).ConfigureAwait(false); await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage(message).ConfigureAwait(false); await e.Channel.SendMessage(message).ConfigureAwait(false);
}); });
} }

View File

@ -7,8 +7,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Timers; using System.Timers;
using Timer = System.Timers.Timer;
namespace NadekoBot.Modules.Administration.Commands namespace NadekoBot.Modules.Administration.Commands
{ {
@ -36,7 +38,7 @@ namespace NadekoBot.Modules.Administration.Commands
{"%trivia%", () => Games.Commands.TriviaCommands.RunningTrivias.Count.ToString()} {"%trivia%", () => Games.Commands.TriviaCommands.RunningTrivias.Count.ToString()}
}; };
private readonly object playingPlaceholderLock = new object(); private readonly SemaphoreSlim playingPlaceholderLock = new SemaphoreSlim(1,1);
public PlayingRotate(DiscordModule module) : base(module) public PlayingRotate(DiscordModule module) : base(module)
{ {
@ -47,7 +49,9 @@ namespace NadekoBot.Modules.Administration.Commands
{ {
i++; i++;
var status = ""; var status = "";
lock (playingPlaceholderLock) //wtf am i doing, just use a queue ffs
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{ {
if (PlayingPlaceholders.Count == 0 if (PlayingPlaceholders.Count == 0
|| NadekoBot.Config.RotatingStatuses.Count == 0 || NadekoBot.Config.RotatingStatuses.Count == 0
@ -59,6 +63,7 @@ namespace NadekoBot.Modules.Administration.Commands
status = PlayingPlaceholders.Aggregate(status, status = PlayingPlaceholders.Aggregate(status,
(current, kvp) => current.Replace(kvp.Key, kvp.Value())); (current, kvp) => current.Replace(kvp.Key, kvp.Value()));
} }
finally { playingPlaceholderLock.Release(); }
if (string.IsNullOrWhiteSpace(status)) if (string.IsNullOrWhiteSpace(status))
return; return;
await Task.Run(() => { NadekoBot.Client.SetGame(status); }); await Task.Run(() => { NadekoBot.Client.SetGame(status); });
@ -71,14 +76,18 @@ namespace NadekoBot.Modules.Administration.Commands
public Func<CommandEventArgs, Task> DoFunc() => async e => public Func<CommandEventArgs, Task> DoFunc() => async e =>
{ {
lock (playingPlaceholderLock) await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{ {
if (timer.Enabled) if (timer.Enabled)
timer.Stop(); timer.Stop();
else else
timer.Start(); timer.Start();
NadekoBot.Config.IsRotatingStatus = timer.Enabled; NadekoBot.Config.IsRotatingStatus = timer.Enabled;
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
}
finally {
playingPlaceholderLock.Release();
} }
await e.Channel.SendMessage($"❗`Rotating playing status has been {(timer.Enabled ? "enabled" : "disabled")}.`").ConfigureAwait(false); await e.Channel.SendMessage($"❗`Rotating playing status has been {(timer.Enabled ? "enabled" : "disabled")}.`").ConfigureAwait(false);
}; };
@ -102,10 +111,15 @@ namespace NadekoBot.Modules.Administration.Commands
var arg = e.GetArg("text"); var arg = e.GetArg("text");
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
return; return;
lock (playingPlaceholderLock) await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{ {
NadekoBot.Config.RotatingStatuses.Add(arg); NadekoBot.Config.RotatingStatuses.Add(arg);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig();
}
finally
{
playingPlaceholderLock.Release();
} }
await e.Channel.SendMessage("🆗 `Added a new playing string.`").ConfigureAwait(false); await e.Channel.SendMessage("🆗 `Added a new playing string.`").ConfigureAwait(false);
}); });
@ -137,14 +151,15 @@ namespace NadekoBot.Modules.Administration.Commands
var arg = e.GetArg("number"); var arg = e.GetArg("number");
int num; int num;
string str; string str;
lock (playingPlaceholderLock) await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
{ try {
if (!int.TryParse(arg.Trim(), out num) || num <= 0 || num > NadekoBot.Config.RotatingStatuses.Count) if (!int.TryParse(arg.Trim(), out num) || num <= 0 || num > NadekoBot.Config.RotatingStatuses.Count)
return; return;
str = NadekoBot.Config.RotatingStatuses[num - 1]; str = NadekoBot.Config.RotatingStatuses[num - 1];
NadekoBot.Config.RotatingStatuses.RemoveAt(num - 1); NadekoBot.Config.RotatingStatuses.RemoveAt(num - 1);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
} }
finally { playingPlaceholderLock.Release(); }
await e.Channel.SendMessage($"🆗 `Removed playing string #{num}`({str})").ConfigureAwait(false); await e.Channel.SendMessage($"🆗 `Removed playing string #{num}`({str})").ConfigureAwait(false);
}); });
} }

View File

@ -1,9 +1,8 @@
using Discord.Commands; using Newtonsoft.Json;
using System; using System;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; //using Manatee.Json.Serialization;
using System.Threading.Tasks;
namespace NadekoBot.Classes.ClashOfClans namespace NadekoBot.Classes.ClashOfClans
{ {
@ -11,16 +10,22 @@ namespace NadekoBot.Classes.ClashOfClans
{ {
One, Two, Three One, Two, Three
} }
public enum WarState
{
Started, Ended, Created
}
[System.Serializable]
internal class Caller internal class Caller
{ {
public string CallUser { get; } public string CallUser { get; set; }
public DateTime TimeAdded { get; private set; } public DateTime TimeAdded { get; set; }
public bool BaseDestroyed { get; internal set; } public bool BaseDestroyed { get; set; }
public int Stars { get; set; } = 3; public int Stars { get; set; } = 3;
public Caller() { }
public Caller(string callUser, DateTime timeAdded, bool baseDestroyed) public Caller(string callUser, DateTime timeAdded, bool baseDestroyed)
{ {
@ -31,7 +36,7 @@ namespace NadekoBot.Classes.ClashOfClans
public void ResetTime() public void ResetTime()
{ {
TimeAdded = DateTime.Now; TimeAdded = DateTime.UtcNow;
} }
public void Destroy() public void Destroy()
@ -44,97 +49,84 @@ namespace NadekoBot.Classes.ClashOfClans
{ {
private static TimeSpan callExpire => new TimeSpan(2, 0, 0); private static TimeSpan callExpire => new TimeSpan(2, 0, 0);
public string EnemyClan { get; } public string EnemyClan { get; set; }
public int Size { get; } public int Size { get; set; }
private Caller[] bases { get; } public Caller[] Bases { get; set; }
private CancellationTokenSource[] baseCancelTokens; public WarState WarState { get; set; } = WarState.Created;
private CancellationTokenSource endTokenSource { get; } = new CancellationTokenSource(); //public bool Started { get; set; } = false;
public event Action<string> OnUserTimeExpired = delegate { }; public DateTime StartedAt { get; set; }
public event Action OnWarEnded = delegate { }; //public bool Ended { get; private set; } = false;
public bool Started { get; set; } = false;
public ClashWar(string enemyClan, int size, CommandEventArgs e) public ulong ServerId { get; set; }
public ulong ChannelId { get; set; }
[JsonIgnore]
public Discord.Channel Channel { get; internal set; }
/// <summary>
/// This init is purely for the deserialization
/// </summary>
public ClashWar() { }
public ClashWar(string enemyClan, int size, ulong serverId, ulong channelId)
{ {
this.EnemyClan = enemyClan; this.EnemyClan = enemyClan;
this.Size = size; this.Size = size;
this.bases = new Caller[size]; this.Bases = new Caller[size];
this.baseCancelTokens = new CancellationTokenSource[size]; this.ServerId = serverId;
this.ChannelId = channelId;
this.Channel = NadekoBot.Client.Servers.FirstOrDefault(s => s.Id == serverId)?.TextChannels.FirstOrDefault(c => c.Id == channelId);
} }
internal void End() internal void End()
{ {
if (endTokenSource.Token.IsCancellationRequested) return; //Ended = true;
endTokenSource.Cancel(); WarState = WarState.Ended;
OnWarEnded();
} }
internal void Call(string u, int baseNumber) internal void Call(string u, int baseNumber)
{ {
if (baseNumber < 0 || baseNumber >= bases.Length) if (baseNumber < 0 || baseNumber >= Bases.Length)
throw new ArgumentException("Invalid base number"); throw new ArgumentException("Invalid base number");
if (bases[baseNumber] != null) if (Bases[baseNumber] != null)
throw new ArgumentException("That base is already claimed."); throw new ArgumentException("That base is already claimed.");
for (var i = 0; i < bases.Length; i++) for (var i = 0; i < Bases.Length; i++)
{ {
if (bases[i]?.BaseDestroyed == false && bases[i]?.CallUser == u) if (Bases[i]?.BaseDestroyed == false && Bases[i]?.CallUser == u)
throw new ArgumentException($"@{u} You already claimed a base #{i + 1}. You can't claim a new one."); throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one.");
} }
bases[baseNumber] = new Caller(u.Trim(), DateTime.Now, false); Bases[baseNumber] = new Caller(u.Trim(), DateTime.UtcNow, false);
} }
internal async Task Start() internal void Start()
{ {
if (Started) if (WarState == WarState.Started)
throw new InvalidOperationException(); throw new InvalidOperationException("War already started");
try //if (Started)
{ // throw new InvalidOperationException();
Started = true; //Started = true;
foreach (var b in bases.Where(b => b != null)) WarState = WarState.Started;
StartedAt = DateTime.UtcNow;
foreach (var b in Bases.Where(b => b != null))
{ {
b.ResetTime(); b.ResetTime();
} }
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () => await ClearArray()).ConfigureAwait(false);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
await Task.Delay(new TimeSpan(24, 0, 0), endTokenSource.Token).ConfigureAwait(false);
}
catch { }
finally
{
End();
}
} }
internal int Uncall(string user) internal int Uncall(string user)
{ {
user = user.Trim(); user = user.Trim();
for (var i = 0; i < bases.Length; i++) for (var i = 0; i < Bases.Length; i++)
{ {
if (bases[i]?.CallUser != user) continue; if (Bases[i]?.CallUser != user) continue;
bases[i] = null; Bases[i] = null;
return i; return i;
} }
throw new InvalidOperationException("You are not participating in that war."); throw new InvalidOperationException("You are not participating in that war.");
} }
private async Task ClearArray()
{
while (!endTokenSource.IsCancellationRequested)
{
await Task.Delay(5000).ConfigureAwait(false);
for (var i = 0; i < bases.Length; i++)
{
if (bases[i] == null) continue;
if (!bases[i].BaseDestroyed && DateTime.Now - bases[i].TimeAdded >= callExpire)
{
OnUserTimeExpired(bases[i].CallUser);
bases[i] = null;
}
}
}
}
public string ShortPrint() => public string ShortPrint() =>
$"`{EnemyClan}` ({Size} v {Size})"; $"`{EnemyClan}` ({Size} v {Size})";
@ -143,24 +135,24 @@ namespace NadekoBot.Classes.ClashOfClans
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**"); sb.AppendLine($"🔰**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**");
if (!Started) if (WarState == WarState.Created)
sb.AppendLine("`not started`"); sb.AppendLine("`not started`");
for (var i = 0; i < bases.Length; i++) for (var i = 0; i < Bases.Length; i++)
{ {
if (bases[i] == null) if (Bases[i] == null)
{ {
sb.AppendLine($"`{i + 1}.` ❌*unclaimed*"); sb.AppendLine($"`{i + 1}.` ❌*unclaimed*");
} }
else else
{ {
if (bases[i].BaseDestroyed) if (Bases[i].BaseDestroyed)
{ {
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` {new string('⭐', bases[i].Stars)}"); sb.AppendLine($"`{i + 1}.` ✅ `{Bases[i].CallUser}` {new string('⭐', Bases[i].Stars)}");
} }
else else
{ {
var left = Started ? callExpire - (DateTime.Now - bases[i].TimeAdded) : callExpire; var left = (WarState == WarState.Started) ? callExpire - (DateTime.UtcNow - Bases[i].TimeAdded) : callExpire;
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left"); sb.AppendLine($"`{i + 1}.` ✅ `{Bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
} }
} }
@ -171,11 +163,11 @@ namespace NadekoBot.Classes.ClashOfClans
internal int FinishClaim(string user, int stars = 3) internal int FinishClaim(string user, int stars = 3)
{ {
user = user.Trim(); user = user.Trim();
for (var i = 0; i < bases.Length; i++) for (var i = 0; i < Bases.Length; i++)
{ {
if (bases[i]?.BaseDestroyed != false || bases[i]?.CallUser != user) continue; if (Bases[i]?.BaseDestroyed != false || Bases[i]?.CallUser != user) continue;
bases[i].BaseDestroyed = true; Bases[i].BaseDestroyed = true;
bases[i].Stars = stars; Bases[i].Stars = stars;
return i; return i;
} }
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base."); throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");

View File

@ -1,12 +1,15 @@
using Discord.Commands; using Discord.Commands;
using Discord.Modules; using Discord.Modules;
using NadekoBot.Classes.ClashOfClans; using NadekoBot.Classes.ClashOfClans;
using NadekoBot.Modules.Permissions.Classes;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Modules.Permissions.Classes;
namespace NadekoBot.Modules.ClashOfClans namespace NadekoBot.Modules.ClashOfClans
{ {
@ -14,10 +17,128 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.ClashOfClans; public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.ClashOfClans;
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; } = new ConcurrentDictionary<ulong, List<ClashWar>>(); public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
private readonly object writeLock = new object(); private readonly object writeLock = new object();
public ClashOfClansModule()
{
NadekoBot.OnReady += () => Task.Run(async () =>
{
if (File.Exists("data/clashofclans/wars.json"))
{
try
{
var content = File.ReadAllText("data/clashofclans/wars.json");
var dict = JsonConvert.DeserializeObject<Dictionary<ulong, List<ClashWar>>>(content);
foreach (var cw in dict)
{
cw.Value.ForEach(war =>
{
war.Channel = NadekoBot.Client.GetServer(war.ServerId)?.GetChannel(war.ChannelId);
if (war.Channel == null)
{
cw.Value.Remove(war);
}
}
);
}
//urgh
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(dict);
}
catch (Exception e)
{
Console.WriteLine("Could not load coc wars: " + e.Message);
}
}
//Can't this be disabled if the modules is disabled too :)
var callExpire = new TimeSpan(2, 0, 0);
var warExpire = new TimeSpan(23, 0, 0);
while (true)
{
try
{
var hash = ClashWars.GetHashCode();
foreach (var cw in ClashWars)
{
foreach (var war in cw.Value)
{
await CheckWar(callExpire, war);
}
List<ClashWar> newVal = new List<ClashWar>();
foreach (var w in cw.Value)
{
//We add when A: the war is not ended
if (w.WarState != WarState.Ended)
{
//and B: the war has not expired
if ((w.WarState == WarState.Started && DateTime.UtcNow - w.StartedAt <= warExpire) || w.WarState == WarState.Created)
{
newVal.Add(w);
}
}
}
//var newVal = cw.Value.Where(w => !(w.Ended || DateTime.UtcNow - w.StartedAt >= warExpire)).ToList();
foreach (var exWar in cw.Value.Except(newVal))
{
await exWar.Channel.SendMessage($"War against {exWar.EnemyClan} ({exWar.Size}v{exWar.Size}) has ended");
}
if (newVal.Count == 0)
{
List<ClashWar> obj;
ClashWars.TryRemove(cw.Key, out obj);
}
else
{
ClashWars.AddOrUpdate(cw.Key, newVal, (x, s) => newVal);
}
}
if (hash != ClashWars.GetHashCode()) //something changed
{
Save();
}
}
catch { }
await Task.Delay(5000);
}
});
}
private static void Save()
{
try
{
Directory.CreateDirectory("data/clashofclans");
File.WriteAllText("data/clashofclans/wars.json", JsonConvert.SerializeObject(ClashWars, Formatting.Indented));
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private static async Task CheckWar(TimeSpan callExpire, ClashWar war)
{
var Bases = war.Bases;
for (var i = 0; i < Bases.Length; i++)
{
if (Bases[i] == null) continue;
if (!Bases[i].BaseDestroyed && DateTime.UtcNow - Bases[i].TimeAdded >= callExpire)
{
await war.Channel.SendMessage($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false);
Bases[i] = null;
}
}
}
#region commands
public override void Install(ModuleManager manager) public override void Install(ModuleManager manager)
{ {
manager.CreateCommands("", cgb => manager.CreateCommands("", cgb =>
@ -34,13 +155,6 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
if (!e.User.ServerPermissions.ManageChannels) if (!e.User.ServerPermissions.ManageChannels)
return; return;
List<ClashWar> wars;
if (!ClashWars.TryGetValue(e.Server.Id, out wars))
{
wars = new List<ClashWar>();
if (!ClashWars.TryAdd(e.Server.Id, wars))
return;
}
var enemyClan = e.GetArg("enemy_clan"); var enemyClan = e.GetArg("enemy_clan");
if (string.IsNullOrWhiteSpace(enemyClan)) if (string.IsNullOrWhiteSpace(enemyClan))
{ {
@ -52,29 +166,21 @@ namespace NadekoBot.Modules.ClashOfClans
await e.Channel.SendMessage("💢🔰 Not a Valid war size").ConfigureAwait(false); await e.Channel.SendMessage("💢🔰 Not a Valid war size").ConfigureAwait(false);
return; return;
} }
var cw = new ClashWar(enemyClan, size, e); List<ClashWar> wars;
if (!ClashWars.TryGetValue(e.Server.Id, out wars))
{
wars = new List<ClashWar>();
if (!ClashWars.TryAdd(e.Server.Id, wars))
return;
}
var cw = new ClashWar(enemyClan, size, e.Server.Id, e.Channel.Id);
//cw.Start(); //cw.Start();
wars.Add(cw); wars.Add(cw);
cw.OnUserTimeExpired += async (u) =>
{
try
{
await
e.Channel.SendMessage(
$"❗🔰**Claim from @{u} for a war against {cw.ShortPrint()} has expired.**")
.ConfigureAwait(false);
}
catch { }
};
cw.OnWarEnded += async () =>
{
try
{
await e.Channel.SendMessage($"❗🔰**War against {cw.ShortPrint()} ended.**").ConfigureAwait(false);
}
catch { }
};
await e.Channel.SendMessage($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); await e.Channel.SendMessage($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
Save();
//war with the index X started. //war with the index X started.
}); });
@ -93,14 +199,14 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
try try
{ {
var startTask = war.Start(); war.Start();
await e.Channel.SendMessage($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false); await e.Channel.SendMessage($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
await startTask.ConfigureAwait(false);
} }
catch catch
{ {
await e.Channel.SendMessage($"🔰**WAR AGAINST {war.ShortPrint()} IS ALREADY STARTED**").ConfigureAwait(false); await e.Channel.SendMessage($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false);
} }
Save();
}); });
cgb.CreateCommand(Prefix + "listwar") cgb.CreateCommand(Prefix + "listwar")
@ -132,6 +238,7 @@ namespace NadekoBot.Modules.ClashOfClans
} }
await e.Channel.SendMessage(sb.ToString()).ConfigureAwait(false); await e.Channel.SendMessage(sb.ToString()).ConfigureAwait(false);
return; return;
} }
//if number is not null, print the war needed //if number is not null, print the war needed
var warsInfo = GetInfo(e); var warsInfo = GetInfo(e);
@ -173,6 +280,7 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNum - 1); war.Call(usr, baseNum - 1);
await e.Channel.SendMessage($"🔰**{usr}** claimed a base #{baseNum} for a war against {war.ShortPrint()}").ConfigureAwait(false); await e.Channel.SendMessage($"🔰**{usr}** claimed a base #{baseNum} for a war against {war.ShortPrint()}").ConfigureAwait(false);
Save();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -206,7 +314,7 @@ namespace NadekoBot.Modules.ClashOfClans
cgb.CreateCommand(Prefix + "unclaim") cgb.CreateCommand(Prefix + "unclaim")
.Alias(Prefix + "uncall") .Alias(Prefix + "uncall")
.Alias(Prefix + "uc") .Alias(Prefix + "uc")
.Description($"Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | {Prefix}uc [war_number] [optional_other_name]") .Description($"Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim | {Prefix}uc [war_number] [optional_other_name]")
.Parameter("number", ParameterType.Required) .Parameter("number", ParameterType.Required)
.Parameter("other_name", ParameterType.Unparsed) .Parameter("other_name", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
@ -226,6 +334,7 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr); var baseNumber = war.Uncall(usr);
await e.Channel.SendMessage($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false); await e.Channel.SendMessage($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
Save();
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -246,12 +355,17 @@ namespace NadekoBot.Modules.ClashOfClans
return; return;
} }
warsInfo.Item1[warsInfo.Item2].End(); warsInfo.Item1[warsInfo.Item2].End();
await e.Channel.SendMessage($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false);
var size = warsInfo.Item1[warsInfo.Item2].Size; var size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2); warsInfo.Item1.RemoveAt(warsInfo.Item2);
Save();
}); });
}); });
} }
#endregion
private async Task FinishClaim(CommandEventArgs e, int stars = 3) private async Task FinishClaim(CommandEventArgs e, int stars = 3)
{ {
@ -271,6 +385,7 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
var baseNum = war.FinishClaim(usr, stars); var baseNum = war.FinishClaim(usr, stars);
await e.Channel.SendMessage($"❗🔰{e.User.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); await e.Channel.SendMessage($"❗🔰{e.User.Mention} **DESTROYED** a base #{baseNum + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false);
Save();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Conversations
cgb.AddCheck(PermissionChecker.Instance); cgb.AddCheck(PermissionChecker.Instance);
cgb.CreateCommand("..") cgb.CreateCommand("..")
.Description("Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message") .Description("Adds a new quote with the specified name (single word) and message (no limit). | `.. abc My message`")
.Parameter("keyword", ParameterType.Required) .Parameter("keyword", ParameterType.Required)
.Parameter("text", ParameterType.Unparsed) .Parameter("text", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
@ -54,7 +54,7 @@ namespace NadekoBot.Modules.Conversations
}); });
cgb.CreateCommand("...") cgb.CreateCommand("...")
.Description("Shows a random quote with a specified name. | .. abc") .Description("Shows a random quote with a specified name. | `... abc`")
.Parameter("keyword", ParameterType.Required) .Parameter("keyword", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {

View File

@ -0,0 +1,289 @@
using NadekoBot.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using System.Collections.Concurrent;
using Discord;
using NadekoBot.Extensions;
using System.Threading;
namespace NadekoBot.Modules.Gambling.Commands
{
class AnimalRacing : DiscordCommand
{
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces = new ConcurrentDictionary<ulong, AnimalRace>();
public AnimalRacing(DiscordModule module) : base(module)
{
}
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Prefix + "race")
.Description("Starts a new animal race.")
.Do(e => {
var ar = new AnimalRace(e.Server.Id, e.Channel);
if (ar.Fail)
{
return;
}
});
cgb.CreateCommand(Prefix + "joinrace")
.Alias(Prefix + "jr")
.Description("Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`")
.Parameter("amount", ParameterType.Optional)
.Do(async e => {
int amount;
if (!int.TryParse(e.GetArg("amount"), out amount) || amount < 0)
amount = 0;
var userFlowers = GamblingModule.GetUserFlowers(e.User.Id);
if (userFlowers < amount)
{
await e.Channel.SendMessage($"{e.User.Mention} You don't have enough {NadekoBot.Config.CurrencyName}s. You only have {userFlowers}{NadekoBot.Config.CurrencySign}.").ConfigureAwait(false);
return;
}
if (amount > 0)
await FlowersHandler.RemoveFlowers(e.User, "BetRace", (int)amount, true).ConfigureAwait(false);
AnimalRace ar;
if (!AnimalRaces.TryGetValue(e.Server.Id, out ar)) {
await e.Channel.SendMessage("No race exists on this server");
}
await ar.JoinRace(e.User, amount);
});
}
public class AnimalRace
{
private ConcurrentQueue<string> animals = new ConcurrentQueue<string>(NadekoBot.Config.RaceAnimals.Shuffle());
public bool Fail { get; internal set; }
public List<Participant> participants = new List<Participant>();
private ulong serverId;
private int messagesSinceGameStarted = 0;
public Channel raceChannel { get; set; }
public bool Started { get; private set; } = false;
public AnimalRace(ulong serverId, Channel ch)
{
this.serverId = serverId;
this.raceChannel = ch;
if (!AnimalRaces.TryAdd(serverId, this))
{
Fail = true;
return;
}
var cancelSource = new CancellationTokenSource();
var token = cancelSource.Token;
var fullgame = CheckForFullGameAsync(token);
Task.Run(async () =>
{
try
{
await raceChannel.SendMessage($"🏁`Race is starting in 20 seconds or when the room is full. Type $jr to join the race.`");
var t = await Task.WhenAny(Task.Delay(20000, token), fullgame);
Started = true;
cancelSource.Cancel();
if (t == fullgame)
{
await raceChannel.SendMessage("🏁`Race full, starting right now!`");
}
else if (participants.Count > 1)
{
await raceChannel.SendMessage("🏁`Game starting with " + participants.Count + " praticipants.`");
}
else
{
await raceChannel.SendMessage("🏁`Race failed to start since there was not enough participants.`");
var p = participants.FirstOrDefault();
if (p != null)
await FlowersHandler.AddFlowersAsync(p.User, "BetRace", p.AmountBet, true).ConfigureAwait(false);
End();
return;
}
await Task.Run(StartRace);
End();
}
catch { }
});
}
private void End()
{
AnimalRace throwaway;
AnimalRaces.TryRemove(serverId, out throwaway);
}
private async Task StartRace() {
var rng = new Random();
Participant winner = null;
Message msg = null;
int place = 1;
try
{
NadekoBot.Client.MessageReceived += Client_MessageReceived;
while (!participants.All(p => p.Total >= 60))
{
//update the state
participants.ForEach(p =>
{
p.Total += 1 + rng.Next(0, 10);
if (p.Total > 60)
{
p.Total = 60;
if (winner == null)
{
winner = p;
}
if (p.Place == 0)
p.Place = place++;
}
});
//draw the state
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|
{String.Join("\n", participants.Select(p => $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}"))}
|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚|";
if (msg == null || messagesSinceGameStarted >= 10) // also resend the message if channel was spammed
{
if(msg != null)
try { await msg.Delete(); } catch { }
msg = await raceChannel.SendMessage(text);
messagesSinceGameStarted = 0;
}
else
await msg.Edit(text);
await Task.Delay(2500);
}
}
finally
{
NadekoBot.Client.MessageReceived -= Client_MessageReceived;
}
if (winner.AmountBet > 0)
{
var wonAmount = winner.AmountBet * (participants.Count - 1);
await FlowersHandler.AddFlowersAsync(winner.User, "Won a Race", wonAmount).ConfigureAwait(false);
await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{NadekoBot.Config.CurrencySign}!**");
}
else
{
await raceChannel.SendMessage($"🏁 {winner.User.Mention} as {winner.Animal} **Won the race!**");
}
}
private void Client_MessageReceived(object sender, MessageEventArgs e)
{
if (e.Message.IsAuthor || e.Channel.IsPrivate || e.Channel != raceChannel)
return;
messagesSinceGameStarted++;
}
private async Task CheckForFullGameAsync(CancellationToken cancelToken) {
while (animals.Count > 0)
{
await Task.Delay(100,cancelToken);
}
}
public async Task<bool> JoinRace(User u, int amount = 0)
{
var animal = "";
if (!animals.TryDequeue(out animal))
{
await raceChannel.SendMessage($"{u.Mention} `There is no running race on this server.`");
return false;
}
var p = new Participant(u, animal, amount);
if (participants.Contains(p))
{
await raceChannel.SendMessage($"{u.Mention} `You already joined this race.`");
return false;
}
if (Started)
{
await raceChannel.SendMessage($"{u.Mention} `Race is already started`");
return false;
}
participants.Add(p);
await raceChannel.SendMessage($"{u.Mention} **joined the race as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {NadekoBot.Config.CurrencyName.SnPl(amount)}!**" : "**"));
return true;
}
}
public class Participant
{
public User User { get; set; }
public string Animal { get; set; }
public int AmountBet { get; set; }
public float Coeff { get; set; }
public int Total { get; set; }
public int Place { get; set; } = 0;
public Participant(User u, string a, int amount)
{
this.User = u;
this.Animal = a;
this.AmountBet = amount;
}
public override int GetHashCode()
{
return User.GetHashCode();
}
public override bool Equals(object obj)
{
var p = obj as Participant;
return p == null?
false:
p.User == User;
}
public override string ToString()
{
var str = new string('‣', Total) + Animal;
if (Place == 0)
return str;
if (Place == 1)
{
return str + "🏆";
}
else if (Place == 2)
{
return str + "`2nd`";
}
else if (Place == 3)
{
return str + "`3rd`";
}
else {
return str + $"`{Place}th`";
}
}
}
}
}

View File

@ -4,6 +4,7 @@ using Discord.Modules;
using NadekoBot.Classes; using NadekoBot.Classes;
using NadekoBot.DataModels; using NadekoBot.DataModels;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Gambling.Commands;
using NadekoBot.Modules.Permissions.Classes; using NadekoBot.Modules.Permissions.Classes;
using System; using System;
using System.Linq; using System.Linq;
@ -18,6 +19,7 @@ namespace NadekoBot.Modules.Gambling
commands.Add(new DrawCommand(this)); commands.Add(new DrawCommand(this));
commands.Add(new FlipCoinCommand(this)); commands.Add(new FlipCoinCommand(this));
commands.Add(new DiceRollCommand(this)); commands.Add(new DiceRollCommand(this));
commands.Add(new AnimalRacing(this));
} }
public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Gambling; public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Gambling;
@ -31,7 +33,7 @@ namespace NadekoBot.Modules.Gambling
commands.ForEach(com => com.Init(cgb)); commands.ForEach(com => com.Init(cgb));
cgb.CreateCommand(Prefix + "raffle") cgb.CreateCommand(Prefix + "raffle")
.Description("Prints a name and ID of a random user from the online list from the (optional) role.") .Description($"Prints a name and ID of a random user from the online list from the (optional) role. | `{Prefix}raffle` or `{Prefix}raffle RoleName")
.Parameter("role", ParameterType.Optional) .Parameter("role", ParameterType.Optional)
.Do(async e => .Do(async e =>
{ {
@ -93,7 +95,7 @@ namespace NadekoBot.Modules.Gambling
}); });
cgb.CreateCommand(Prefix + "award") cgb.CreateCommand(Prefix + "award")
.Description("Gives someone a certain amount of flowers. **Bot Owner Only!** | `$award 100 @person`") .Description($"Gives someone a certain amount of flowers. **Bot Owner Only!** | `{Prefix}award 100 @person`")
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Parameter("amount", ParameterType.Required) .Parameter("amount", ParameterType.Required)
.Parameter("receiver", ParameterType.Unparsed) .Parameter("receiver", ParameterType.Unparsed)
@ -115,7 +117,7 @@ namespace NadekoBot.Modules.Gambling
}); });
cgb.CreateCommand(Prefix + "take") cgb.CreateCommand(Prefix + "take")
.Description("Takes a certain amount of flowers from someone. **Bot Owner Only!**") .Description($"Takes a certain amount of flowers from someone. **Bot Owner Only!** | `{Prefix}take 1 \"@someguy\"`")
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Parameter("amount", ParameterType.Required) .Parameter("amount", ParameterType.Required)
.Parameter("rektperson", ParameterType.Unparsed) .Parameter("rektperson", ParameterType.Unparsed)

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands namespace NadekoBot.Modules.Games.Commands
@ -59,7 +60,7 @@ namespace NadekoBot.Modules.Games.Commands
//channelid/messageid pair //channelid/messageid pair
ConcurrentDictionary<ulong, IEnumerable<Message>> plantedFlowerChannels = new ConcurrentDictionary<ulong, IEnumerable<Message>>(); ConcurrentDictionary<ulong, IEnumerable<Message>> plantedFlowerChannels = new ConcurrentDictionary<ulong, IEnumerable<Message>>();
private object locker = new object(); private SemaphoreSlim locker = new SemaphoreSlim(1,1);
internal override void Init(CommandGroupBuilder cgb) internal override void Init(CommandGroupBuilder cgb)
{ {
@ -78,38 +79,47 @@ namespace NadekoBot.Modules.Games.Commands
await FlowersHandler.AddFlowersAsync(e.User, "Picked a flower.", 1, true).ConfigureAwait(false); await FlowersHandler.AddFlowersAsync(e.User, "Picked a flower.", 1, true).ConfigureAwait(false);
var 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);
ThreadPool.QueueUserWorkItem(async (state) =>
{
try
{
await Task.Delay(10000).ConfigureAwait(false); await Task.Delay(10000).ConfigureAwait(false);
await msg.Delete().ConfigureAwait(false); await msg.Delete().ConfigureAwait(false);
}
catch { }
});
}); });
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(e => .Do(async e =>
{ {
lock (locker) await locker.WaitAsync().ConfigureAwait(false);
try
{ {
if (plantedFlowerChannels.ContainsKey(e.Channel.Id)) if (plantedFlowerChannels.ContainsKey(e.Channel.Id))
{ {
e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel."); await e.Channel.SendMessage($"There is already a {NadekoBot.Config.CurrencyName} in this channel.").ConfigureAwait(false);
return; return;
} }
var removed = FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).GetAwaiter().GetResult(); var removed = await FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).ConfigureAwait(false);
if (!removed) if (!removed)
{ {
e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").Wait(); await e.Channel.SendMessage($"You don't have any {NadekoBot.Config.CurrencyName}s.").ConfigureAwait(false);
return; return;
} }
var file = GetRandomCurrencyImagePath(); var file = GetRandomCurrencyImagePath();
Message msg; Message msg;
if (file == null) if (file == null)
msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult(); msg = await e.Channel.SendMessage(NadekoBot.Config.CurrencySign).ConfigureAwait(false);
else else
msg = e.Channel.SendFile(file).GetAwaiter().GetResult(); msg = await e.Channel.SendFile(file).ConfigureAwait(false);
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]); 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(); 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").ConfigureAwait(false);
plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 }); plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 });
} }
finally { locker.Release(); }
}); });
cgb.CreateCommand(Prefix + "gencurrency") cgb.CreateCommand(Prefix + "gencurrency")
@ -129,12 +139,12 @@ namespace NadekoBot.Modules.Games.Commands
int throwaway; int throwaway;
if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway)) if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway))
{ {
await e.Channel.SendMessage("`Currency generation disabled on this channel.`"); await e.Channel.SendMessage("`Currency generation disabled on this channel.`").ConfigureAwait(false);
} }
else else
{ {
if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd)) if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd))
await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`"); await e.Channel.SendMessage($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`").ConfigureAwait(false);
} }
}); });
} }

View File

@ -15,11 +15,6 @@ namespace NadekoBot.Modules.Games.Commands
public static ConcurrentDictionary<Server, Poll> ActivePolls = new ConcurrentDictionary<Server, Poll>(); public static ConcurrentDictionary<Server, Poll> ActivePolls = new ConcurrentDictionary<Server, Poll>();
public Func<CommandEventArgs, Task> DoFunc()
{
throw new NotImplementedException();
}
internal override void Init(CommandGroupBuilder cgb) internal override void Init(CommandGroupBuilder cgb)
{ {
cgb.CreateCommand(Module.Prefix + "poll") cgb.CreateCommand(Module.Prefix + "poll")

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
{ {
internal class TriviaGame internal class TriviaGame
{ {
private readonly object _guessLock = new object(); private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1,1);
private Server server { get; } private Server server { get; }
private Channel channel { get; } private Channel channel { get; }
@ -113,7 +113,8 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return; if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return;
var guess = false; var guess = false;
lock (_guessLock) await _guessLock.WaitAsync().ConfigureAwait(false);
try
{ {
if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested) if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested)
{ {
@ -122,6 +123,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
guess = true; guess = true;
} }
} }
finally { _guessLock.Release(); }
if (!guess) return; if (!guess) return;
triviaCancelSource.Cancel(); triviaCancelSource.Cancel();
await channel.SendMessage($"☑️ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); await channel.SendMessage($"☑️ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);

View File

@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Games
commands.ForEach(cmd => cmd.Init(cgb)); commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand(Prefix + "choose") cgb.CreateCommand(Prefix + "choose")
.Description("Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more") .Description($"Chooses a thing from a list of things | `{Prefix}choose Get up;Sleep;Sleep more`")
.Parameter("list", ParameterType.Unparsed) .Parameter("list", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Games
}); });
cgb.CreateCommand(Prefix + "8ball") cgb.CreateCommand(Prefix + "8ball")
.Description("Ask the 8ball a yes/no question.") .Description($"Ask the 8ball a yes/no question. | `{Prefix}8ball should i do something`")
.Parameter("question", ParameterType.Unparsed) .Parameter("question", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Games
}); });
cgb.CreateCommand(Prefix + "rps") cgb.CreateCommand(Prefix + "rps")
.Description("Play a game of rocket paperclip scissors with Nadeko. | >rps scissors") .Description($"Play a game of rocket paperclip scissors with Nadeko. | `{Prefix}rps scissors`")
.Parameter("input", ParameterType.Required) .Parameter("input", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {

View File

@ -113,17 +113,10 @@ namespace NadekoBot.Modules.Music.Classes
if (CurrentSong == null) if (CurrentSong == null)
continue; continue;
try
{
OnStarted(this, CurrentSong); OnStarted(this, CurrentSong);
await CurrentSong.Play(audioClient, cancelToken); await CurrentSong.Play(audioClient, cancelToken);
}
catch (OperationCanceledException)
{
Console.WriteLine("Song canceled");
SongCancelSource = new CancellationTokenSource();
cancelToken = SongCancelSource.Token;
}
OnCompleted(this, CurrentSong); OnCompleted(this, CurrentSong);
if (RepeatPlaylist) if (RepeatPlaylist)
@ -135,8 +128,14 @@ namespace NadekoBot.Modules.Music.Classes
} }
finally finally
{ {
await Task.Delay(300).ConfigureAwait(false); if (!cancelToken.IsCancellationRequested)
{
SongCancelSource.Cancel();
}
SongCancelSource = new CancellationTokenSource();
cancelToken = SongCancelSource.Token;
CurrentSong = null; CurrentSong = null;
await Task.Delay(300).ConfigureAwait(false);
} }
} }
} }

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Music.Classes
public int BufferSize { get; } public int BufferSize { get; }
private readonly object readWriteLock = new object(); private readonly SemaphoreSlim readWriteLock = new SemaphoreSlim(1, 1);
public PoopyBuffer(int size) public PoopyBuffer(int size)
{ {
@ -32,12 +32,15 @@ namespace NadekoBot.Modules.Music.Classes
ringBuffer = new byte[size]; ringBuffer = new byte[size];
} }
public int Read(byte[] buffer, int count) public Task<int> ReadAsync(byte[] buffer, int count)
{
return Task.Run(async () =>
{ {
if (buffer.Length < count) if (buffer.Length < count)
throw new ArgumentException(); throw new ArgumentException();
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***"); //Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
lock (readWriteLock) await readWriteLock.WaitAsync().ConfigureAwait(false);
try
{ {
//read as much as you can if you're reading too much //read as much as you can if you're reading too much
if (count > ContentLength) if (count > ContentLength)
@ -77,6 +80,9 @@ namespace NadekoBot.Modules.Music.Classes
ReadPosition = readFromStart; ReadPosition = readFromStart;
return count; return count;
} }
finally { readWriteLock.Release(); }
});
} }
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken) public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken)
@ -89,9 +95,12 @@ namespace NadekoBot.Modules.Music.Classes
if (cancelToken.IsCancellationRequested) if (cancelToken.IsCancellationRequested)
return; return;
} }
await Task.Run(async () =>
{
//the while above assures that i cannot write past readposition with my write, so i don't have to check //the while above assures that i cannot write past readposition with my write, so i don't have to check
// *unless its multithreaded or task is not awaited // *unless its multithreaded or task is not awaited
lock (readWriteLock) await readWriteLock.WaitAsync().ConfigureAwait(false);
try
{ {
// if i can just write without hitting buffer.length, do it // if i can just write without hitting buffer.length, do it
if (WritePosition + count < BufferSize) if (WritePosition + count < BufferSize)
@ -115,6 +124,8 @@ namespace NadekoBot.Modules.Music.Classes
WritePosition = wroteFromStart; WritePosition = wroteFromStart;
} }
finally { readWriteLock.Release(); }
});
} }
} }
} }

View File

@ -3,6 +3,7 @@ using NadekoBot.Classes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@ -31,9 +32,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; set; } private bool bufferingCompleted { get; set; } = false;
private bool prebufferingComplete { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; } public MusicPlayer MusicPlayer { get; set; }
public string PrettyCurrentTime() public string PrettyCurrentTime()
@ -74,7 +73,7 @@ namespace NadekoBot.Modules.Music.Classes
return this; return this;
} }
private Task BufferSong(CancellationToken cancelToken) => private Task BufferSong(string filename, CancellationToken cancelToken) =>
Task.Factory.StartNew(async () => Task.Factory.StartNew(async () =>
{ {
Process p = null; Process p = null;
@ -89,40 +88,38 @@ namespace NadekoBot.Modules.Music.Classes
RedirectStandardError = false, RedirectStandardError = false,
CreateNoWindow = true, CreateNoWindow = true,
}); });
const int blockSize = 3840; var prebufferSize = 100ul.MiB();
var buffer = new byte[blockSize]; using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
var attempt = 0;
while (!cancelToken.IsCancellationRequested)
{ {
var read = 0; byte[] buffer = new byte[81920];
try int bytesRead;
while ((bytesRead = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancelToken).ConfigureAwait(false)) != 0)
{ {
read = await p.StandardOutput.BaseStream.ReadAsync(buffer, 0, blockSize, cancelToken) await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
.ConfigureAwait(false); while ((ulong)outStream.Length - bytesSent > prebufferSize)
await Task.Delay(100, cancelToken);
} }
catch
{
return;
} }
if (read == 0)
if (attempt++ == 50) bufferingCompleted = true;
break;
else
await Task.Delay(100, cancelToken).ConfigureAwait(false);
else
attempt = 0;
await songBuffer.WriteAsync(buffer, read, cancelToken).ConfigureAwait(false);
if (songBuffer.ContentLength > 2.MB())
prebufferingComplete = true;
} }
catch (System.ComponentModel.Win32Exception) {
var oldclr = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(@"You have not properly installed or configured FFMPEG.
Please install and configure FFMPEG to play music.
Check the guides for your platform on how to setup ffmpeg correctly:
Windows Guide: https://goo.gl/SCv72y
Linux Guide: https://goo.gl/rRhjCp");
Console.ForegroundColor = oldclr;
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Buffering errored: {ex.Message}"); Console.WriteLine($"Buffering stopped: {ex.Message}");
} }
finally finally
{ {
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]"); Console.WriteLine($"Buffering done.");
if (p != null) if (p != null)
{ {
try try
@ -137,25 +134,41 @@ 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 var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
songBuffer = new PoopyBuffer(NadekoBot.Config.BufferSize);
var bufferTask = BufferSong(cancelToken).ConfigureAwait(false); var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false);
var bufferAttempts = 0;
const int waitPerAttempt = 500; var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes) bytesSent = 0;
try
{ {
await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false); var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
var sw = new Stopwatch();
sw.Start();
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
if (t != prebufferingTask)
{
Console.WriteLine("Prebuffering timed out or canceled. Cannot get any data from the stream.");
return;
} }
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}"); else if(prebufferingTask.IsCanceled)
{
Console.WriteLine("Prebuffering timed out. Cannot get any data from the stream.");
return;
}
sw.Stop();
Console.WriteLine("Prebuffering successfully completed in "+ sw.Elapsed);
const int blockSize = 3840; const int blockSize = 3840;
var attempt = 0; var attempt = 0;
byte[] buffer = new byte[blockSize];
while (!cancelToken.IsCancellationRequested) while (!cancelToken.IsCancellationRequested)
{ {
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
byte[] buffer = new byte[blockSize]; var read = inStream.Read(buffer, 0, buffer.Length);
var read = songBuffer.Read(buffer, blockSize); //await inStream.CopyToAsync(voiceClient.OutputStream);
unchecked unchecked
{ {
bytesSent += (ulong)read; bytesSent += (ulong)read;
@ -164,7 +177,6 @@ namespace NadekoBot.Modules.Music.Classes
if (attempt++ == 20) if (attempt++ == 20)
{ {
voiceClient.Wait(); voiceClient.Wait();
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
break; break;
} }
else else
@ -174,16 +186,30 @@ namespace NadekoBot.Modules.Music.Classes
while (this.MusicPlayer.Paused) while (this.MusicPlayer.Paused)
await Task.Delay(200, cancelToken).ConfigureAwait(false); await Task.Delay(200, cancelToken).ConfigureAwait(false);
buffer = AdjustVolume(buffer, MusicPlayer.Volume); buffer = AdjustVolume(buffer, MusicPlayer.Volume);
voiceClient.Send(buffer, 0, read); voiceClient.Send(buffer, 0, read);
} }
Console.WriteLine("Awiting buffer task"); }
finally
{
await bufferTask; await bufferTask;
Console.WriteLine("Buffer task done."); await Task.Run(() => voiceClient.Clear());
voiceClient.Clear(); inStream.Dispose();
cancelToken.ThrowIfCancellationRequested(); try { File.Delete(filename); } catch { }
}
} }
private async Task CheckPrebufferingAsync(Stream inStream, CancellationToken cancelToken)
{
while (!bufferingCompleted && inStream.Length < 2.MiB())
{
await Task.Delay(100, cancelToken);
}
Console.WriteLine("Buffering successfull");
}
/*
//stackoverflow ftw //stackoverflow ftw
private static byte[] AdjustVolume(byte[] audioSamples, float volume) private static byte[] AdjustVolume(byte[] audioSamples, float volume)
{ {
@ -210,6 +236,33 @@ namespace NadekoBot.Modules.Music.Classes
} }
return array; return array;
} }
*/
//aidiakapi ftw
public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume)
{
Contract.Requires(audioSamples != null);
Contract.Requires(audioSamples.Length % 2 == 0);
Contract.Requires(volume >= 0f && volume <= 1f);
Contract.Assert(BitConverter.IsLittleEndian);
if (Math.Abs(volume - 1f) < 0.0001f) return audioSamples;
// 16-bit precision for the multiplication
int volumeFixed = (int)Math.Round(volume * 65536d);
int count = audioSamples.Length / 2;
fixed (byte* srcBytes = audioSamples)
{
short* src = (short*)srcBytes;
for (int i = count; i != 0; i--, src++)
*src = (short)(((*src) * volumeFixed) >> 16);
}
return audioSamples;
}
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal) public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
{ {

View File

@ -18,11 +18,16 @@ namespace NadekoBot.Modules.Music
{ {
internal class MusicModule : DiscordModule internal class MusicModule : DiscordModule
{ {
public static ConcurrentDictionary<Server, MusicPlayer> MusicPlayers = new ConcurrentDictionary<Server, MusicPlayer>(); public static ConcurrentDictionary<Server, MusicPlayer> MusicPlayers = new ConcurrentDictionary<Server, MusicPlayer>();
public const string MusicDataPath = "data/musicdata";
public MusicModule() public MusicModule()
{ {
//it can fail if its currenctly opened or doesn't exist. Either way i don't care
try { Directory.Delete(MusicDataPath, true); } catch { }
Directory.CreateDirectory(MusicDataPath);
} }
public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Music; public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Music;
@ -713,7 +718,7 @@ namespace NadekoBot.Modules.Music
}); });
cgb.CreateCommand(Prefix + "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. | {Prefix}goto 30")
.Parameter("time") .Parameter("time")
.Do(async e => .Do(async e =>
{ {
@ -856,7 +861,7 @@ namespace NadekoBot.Modules.Music
} }
if (!silent) if (!silent)
{ {
var queuedMessage = await textCh.SendMessage($"🎵`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count}`").ConfigureAwait(false); var queuedMessage = await textCh.SendMessage($"🎵`Queued`{resolvedSong.PrettyName} **at** `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
Task.Run(async () => Task.Run(async () =>
{ {

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.NSFW
cgb.AddCheck(PermissionChecker.Instance); cgb.AddCheck(PermissionChecker.Instance);
cgb.CreateCommand(Prefix + "hentai") cgb.CreateCommand(Prefix + "hentai")
.Description("Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~hentai yuri+kissing") .Description($"Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `{Prefix}hentai yuri+kissing`")
.Parameter("tag", ParameterType.Unparsed) .Parameter("tag", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -39,7 +39,7 @@ namespace NadekoBot.Modules.NSFW
await e.Channel.SendMessage("`No results.`"); await e.Channel.SendMessage("`No results.`");
}); });
cgb.CreateCommand(Prefix + "danbooru") cgb.CreateCommand(Prefix + "danbooru")
.Description("Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~danbooru yuri+kissing") .Description($"Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `{Prefix}danbooru yuri+kissing`")
.Parameter("tag", ParameterType.Unparsed) .Parameter("tag", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -51,7 +51,7 @@ namespace NadekoBot.Modules.NSFW
await e.Channel.SendMessage(link).ConfigureAwait(false); await e.Channel.SendMessage(link).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "gelbooru") cgb.CreateCommand(Prefix + "gelbooru")
.Description("Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~gelbooru yuri+kissing") .Description($"Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `{Prefix}gelbooru yuri+kissing`")
.Parameter("tag", ParameterType.Unparsed) .Parameter("tag", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -64,7 +64,7 @@ namespace NadekoBot.Modules.NSFW
}); });
cgb.CreateCommand(Prefix + "rule34") cgb.CreateCommand(Prefix + "rule34")
.Description("Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~rule34 yuri+kissing") .Description($"Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `{Prefix}rule34 yuri+kissing`")
.Parameter("tag", ParameterType.Unparsed) .Parameter("tag", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -76,7 +76,7 @@ namespace NadekoBot.Modules.NSFW
await e.Channel.SendMessage(link).ConfigureAwait(false); await e.Channel.SendMessage(link).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "e621") cgb.CreateCommand(Prefix + "e621")
.Description("Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | ~e621 yuri kissing") .Description($"Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | `{Prefix}e621 yuri kissing`")
.Parameter("tag", ParameterType.Unparsed) .Parameter("tag", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {

View File

@ -150,21 +150,21 @@ namespace NadekoBot.Modules.Permissions.Classes
return PermissionBanType.None; return PermissionBanType.None;
} }
private static void WriteServerToJson(ServerPermissions serverPerms) private static Task WriteServerToJson(ServerPermissions serverPerms) => Task.Run(() =>
{ {
string pathToFile = $"data/permissions/{serverPerms.Id}.json"; string pathToFile = $"data/permissions/{serverPerms.Id}.json";
File.WriteAllText(pathToFile, File.WriteAllText(pathToFile,
Newtonsoft.Json.JsonConvert.SerializeObject(serverPerms, Newtonsoft.Json.Formatting.Indented)); Newtonsoft.Json.JsonConvert.SerializeObject(serverPerms, Newtonsoft.Json.Formatting.Indented));
} });
public static void WriteToJson() public static Task WriteToJson() => Task.Run(() =>
{ {
Directory.CreateDirectory("data/permissions/"); Directory.CreateDirectory("data/permissions/");
foreach (var kvp in PermissionsDict) foreach (var kvp in PermissionsDict)
{ {
WriteServerToJson(kvp.Value); WriteServerToJson(kvp.Value);
} }
} });
public static string GetServerPermissionsRoleName(Server server) public static string GetServerPermissionsRoleName(Server server)
{ {
@ -174,25 +174,25 @@ namespace NadekoBot.Modules.Permissions.Classes
return serverPerms.PermissionsControllerRole; return serverPerms.PermissionsControllerRole;
} }
internal static void SetPermissionsRole(Server server, string roleName) internal static async Task SetPermissionsRole(Server server, string roleName)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
serverPerms.PermissionsControllerRole = roleName; serverPerms.PermissionsControllerRole = roleName;
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
internal static void SetVerbosity(Server server, bool val) internal static async Task SetVerbosity(Server server, bool val)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
serverPerms.Verbose = val; serverPerms.Verbose = val;
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
internal static void CopyRolePermissions(Role fromRole, Role toRole) internal static async Task CopyRolePermissions(Role fromRole, Role toRole)
{ {
var server = fromRole.Server; var server = fromRole.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -207,10 +207,10 @@ namespace NadekoBot.Modules.Permissions.Classes
to.CopyFrom(from); to.CopyFrom(from);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
internal static void CopyChannelPermissions(Channel fromChannel, Channel toChannel) internal static async Task CopyChannelPermissions(Channel fromChannel, Channel toChannel)
{ {
var server = fromChannel.Server; var server = fromChannel.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -225,10 +225,10 @@ namespace NadekoBot.Modules.Permissions.Classes
to.CopyFrom(from); to.CopyFrom(from);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
internal static void CopyUserPermissions(User fromUser, User toUser) internal static async Task CopyUserPermissions(User fromUser, User toUser)
{ {
var server = fromUser.Server; var server = fromUser.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -243,10 +243,10 @@ namespace NadekoBot.Modules.Permissions.Classes
to.CopyFrom(from); to.CopyFrom(from);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetServerModulePermission(Server server, string moduleName, bool value) public static async Task SetServerModulePermission(Server server, string moduleName, bool value)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
@ -256,10 +256,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value; modules[moduleName] = value;
else else
modules.TryAdd(moduleName, value); modules.TryAdd(moduleName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetServerCommandPermission(Server server, string commandName, bool value) public static async Task SetServerCommandPermission(Server server, string commandName, bool value)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
@ -269,10 +269,10 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value; commands[commandName] = value;
else else
commands.TryAdd(commandName, value); commands.TryAdd(commandName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetChannelModulePermission(Channel channel, string moduleName, bool value) public static async Task SetChannelModulePermission(Channel channel, string moduleName, bool value)
{ {
var server = channel.Server; var server = channel.Server;
@ -288,10 +288,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value; modules[moduleName] = value;
else else
modules.TryAdd(moduleName, value); modules.TryAdd(moduleName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetChannelCommandPermission(Channel channel, string commandName, bool value) public static async Task SetChannelCommandPermission(Channel channel, string commandName, bool value)
{ {
var server = channel.Server; var server = channel.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -306,10 +306,10 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value; commands[commandName] = value;
else else
commands.TryAdd(commandName, value); commands.TryAdd(commandName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetRoleModulePermission(Role role, string moduleName, bool value) public static async Task SetRoleModulePermission(Role role, string moduleName, bool value)
{ {
var server = role.Server; var server = role.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -324,10 +324,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value; modules[moduleName] = value;
else else
modules.TryAdd(moduleName, value); modules.TryAdd(moduleName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetRoleCommandPermission(Role role, string commandName, bool value) public static async Task SetRoleCommandPermission(Role role, string commandName, bool value)
{ {
var server = role.Server; var server = role.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -342,10 +342,10 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value; commands[commandName] = value;
else else
commands.TryAdd(commandName, value); commands.TryAdd(commandName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetUserModulePermission(User user, string moduleName, bool value) public static async Task SetUserModulePermission(User user, string moduleName, bool value)
{ {
var server = user.Server; var server = user.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -360,10 +360,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value; modules[moduleName] = value;
else else
modules.TryAdd(moduleName, value); modules.TryAdd(moduleName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetUserCommandPermission(User user, string commandName, bool value) public static async Task SetUserCommandPermission(User user, string commandName, bool value)
{ {
var server = user.Server; var server = user.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -377,19 +377,19 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value; commands[commandName] = value;
else else
commands.TryAdd(commandName, value); commands.TryAdd(commandName, value);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetServerWordPermission(Server server, bool value) public static async Task SetServerWordPermission(Server server, bool value)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
serverPerms.Permissions.FilterWords = value; serverPerms.Permissions.FilterWords = value;
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetChannelWordPermission(Channel channel, bool value) public static async Task SetChannelWordPermission(Channel channel, bool value)
{ {
var server = channel.Server; var server = channel.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -399,19 +399,19 @@ namespace NadekoBot.Modules.Permissions.Classes
serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name));
serverPerms.ChannelPermissions[channel.Id].FilterWords = value; serverPerms.ChannelPermissions[channel.Id].FilterWords = value;
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetServerFilterInvitesPermission(Server server, bool value) public static async Task SetServerFilterInvitesPermission(Server server, bool value)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
serverPerms.Permissions.FilterInvites = value; serverPerms.Permissions.FilterInvites = value;
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetChannelFilterInvitesPermission(Channel channel, bool value) public static async Task SetChannelFilterInvitesPermission(Channel channel, bool value)
{ {
var server = channel.Server; var server = channel.Server;
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -421,10 +421,10 @@ namespace NadekoBot.Modules.Permissions.Classes
serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name)); serverPerms.ChannelPermissions.Add(channel.Id, new Permissions(channel.Name));
serverPerms.ChannelPermissions[channel.Id].FilterInvites = value; serverPerms.ChannelPermissions[channel.Id].FilterInvites = value;
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void SetCommandCooldown(Server server, string commandName, int value) public static async Task SetCommandCooldown(Server server, string commandName, int value)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
@ -436,26 +436,26 @@ namespace NadekoBot.Modules.Permissions.Classes
serverPerms.CommandCooldowns.AddOrUpdate(commandName, value, (str, v) => value); serverPerms.CommandCooldowns.AddOrUpdate(commandName, value, (str, v) => value);
} }
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void AddFilteredWord(Server server, string word) public static async Task AddFilteredWord(Server server, string word)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
if (serverPerms.Words.Contains(word)) if (serverPerms.Words.Contains(word))
throw new InvalidOperationException("That word is already banned."); throw new InvalidOperationException("That word is already banned.");
serverPerms.Words.Add(word); serverPerms.Words.Add(word);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
public static void RemoveFilteredWord(Server server, string word) public static async Task RemoveFilteredWord(Server server, string word)
{ {
var serverPerms = PermissionsDict.GetOrAdd(server.Id, var serverPerms = PermissionsDict.GetOrAdd(server.Id,
new ServerPermissions(server.Id, server.Name)); new ServerPermissions(server.Id, server.Name));
if (!serverPerms.Words.Contains(word)) if (!serverPerms.Words.Contains(word))
throw new InvalidOperationException("That word is not banned."); throw new InvalidOperationException("That word is not banned.");
serverPerms.Words.Remove(word); serverPerms.Words.Remove(word);
Task.Run(() => WriteServerToJson(serverPerms)); await WriteServerToJson(serverPerms).ConfigureAwait(false);
} }
} }
/// <summary> /// <summary>

View File

@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var chan = string.IsNullOrWhiteSpace(chanStr) var chan = string.IsNullOrWhiteSpace(chanStr)
? e.Channel ? e.Channel
: PermissionHelper.ValidateChannel(e.Server, chanStr); : PermissionHelper.ValidateChannel(e.Server, chanStr);
PermissionsHandler.SetChannelFilterInvitesPermission(chan, state); await PermissionsHandler.SetChannelFilterInvitesPermission(chan, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.") await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.")
.ConfigureAwait(false); .ConfigureAwait(false);
return; return;
@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Permissions.Commands
foreach (var curChannel in e.Server.TextChannels) foreach (var curChannel in e.Server.TextChannels)
{ {
PermissionsHandler.SetChannelFilterInvitesPermission(curChannel, state); await PermissionsHandler.SetChannelFilterInvitesPermission(curChannel, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.") await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.")
.ConfigureAwait(false); .ConfigureAwait(false);
@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Permissions.Commands
try try
{ {
var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
PermissionsHandler.SetServerFilterInvitesPermission(e.Server, state); await PermissionsHandler.SetServerFilterInvitesPermission(e.Server, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for this server.") await e.Channel.SendMessage($"Invite Filter has been **{(state ? "enabled" : "disabled")}** for this server.")
.ConfigureAwait(false); .ConfigureAwait(false);

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var chan = string.IsNullOrWhiteSpace(chanStr) var chan = string.IsNullOrWhiteSpace(chanStr)
? e.Channel ? e.Channel
: PermissionHelper.ValidateChannel(e.Server, chanStr); : PermissionHelper.ValidateChannel(e.Server, chanStr);
PermissionsHandler.SetChannelWordPermission(chan, state); await PermissionsHandler.SetChannelWordPermission(chan, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.").ConfigureAwait(false); await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **{chan.Name}** channel.").ConfigureAwait(false);
return; return;
} }
@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Permissions.Commands
foreach (var curChannel in e.Server.TextChannels) foreach (var curChannel in e.Server.TextChannels)
{ {
PermissionsHandler.SetChannelWordPermission(curChannel, state); await PermissionsHandler.SetChannelWordPermission(curChannel, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.").ConfigureAwait(false); await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** for **ALL** channels.").ConfigureAwait(false);
} }
@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var word = e.GetArg("word"); var word = e.GetArg("word");
if (string.IsNullOrWhiteSpace(word)) if (string.IsNullOrWhiteSpace(word))
return; return;
PermissionsHandler.AddFilteredWord(e.Server, word.ToLowerInvariant().Trim()); await PermissionsHandler.AddFilteredWord(e.Server, word.ToLowerInvariant().Trim()).ConfigureAwait(false);
await e.Channel.SendMessage($"Successfully added new filtered word.").ConfigureAwait(false); await e.Channel.SendMessage($"Successfully added new filtered word.").ConfigureAwait(false);
} }
@ -120,7 +120,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var word = e.GetArg("word"); var word = e.GetArg("word");
if (string.IsNullOrWhiteSpace(word)) if (string.IsNullOrWhiteSpace(word))
return; return;
PermissionsHandler.RemoveFilteredWord(e.Server, word.ToLowerInvariant().Trim()); await PermissionsHandler.RemoveFilteredWord(e.Server, word.ToLowerInvariant().Trim()).ConfigureAwait(false);
await e.Channel.SendMessage($"Successfully removed filtered word.").ConfigureAwait(false); await e.Channel.SendMessage($"Successfully removed filtered word.").ConfigureAwait(false);
} }
@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Permissions.Commands
try try
{ {
var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
PermissionsHandler.SetServerWordPermission(e.Server, state); await PermissionsHandler.SetServerWordPermission(e.Server, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** on this server.") await e.Channel.SendMessage($"Word filtering has been **{(state ? "enabled" : "disabled")}** on this server.")
.ConfigureAwait(false); .ConfigureAwait(false);

View File

@ -55,7 +55,7 @@ namespace NadekoBot.Modules.Permissions
await e.Channel.SendMessage($"Role `{arg}` probably doesn't exist. Create the role with that name first.").ConfigureAwait(false); await e.Channel.SendMessage($"Role `{arg}` probably doesn't exist. Create the role with that name first.").ConfigureAwait(false);
return; return;
} }
PermissionsHandler.SetPermissionsRole(e.Server, role.Name); await PermissionsHandler.SetPermissionsRole(e.Server, role.Name).ConfigureAwait(false);
await e.Channel.SendMessage($"Role `{role.Name}` is now required in order to change permissions.").ConfigureAwait(false); await e.Channel.SendMessage($"Role `{role.Name}` is now required in order to change permissions.").ConfigureAwait(false);
}); });
@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Permissions
var args = arg.Split('~').Select(a => a.Trim()).ToArray(); var args = arg.Split('~').Select(a => a.Trim()).ToArray();
if (args.Length > 2) if (args.Length > 2)
{ {
await e.Channel.SendMessage("💢Invalid number of '~'s in the argument."); await e.Channel.SendMessage("💢Invalid number of '~'s in the argument.").ConfigureAwait(false);
return; return;
} }
try try
@ -79,12 +79,12 @@ namespace NadekoBot.Modules.Permissions
var fromRole = PermissionHelper.ValidateRole(e.Server, args[0]); var fromRole = PermissionHelper.ValidateRole(e.Server, args[0]);
var toRole = PermissionHelper.ValidateRole(e.Server, args[1]); var toRole = PermissionHelper.ValidateRole(e.Server, args[1]);
PermissionsHandler.CopyRolePermissions(fromRole, toRole); await PermissionsHandler.CopyRolePermissions(fromRole, toRole).ConfigureAwait(false);
await e.Channel.SendMessage($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**."); await e.Channel.SendMessage($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await e.Channel.SendMessage($"💢{ex.Message}"); await e.Channel.SendMessage($"💢{ex.Message}").ConfigureAwait(false);
} }
}); });
cgb.CreateCommand(Prefix + "chnlpermscopy") cgb.CreateCommand(Prefix + "chnlpermscopy")
@ -107,8 +107,8 @@ namespace NadekoBot.Modules.Permissions
var fromChannel = PermissionHelper.ValidateChannel(e.Server, args[0]); var fromChannel = PermissionHelper.ValidateChannel(e.Server, args[0]);
var toChannel = PermissionHelper.ValidateChannel(e.Server, args[1]); var toChannel = PermissionHelper.ValidateChannel(e.Server, args[1]);
PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel); await PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel).ConfigureAwait(false);
await e.Channel.SendMessage($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**."); await e.Channel.SendMessage($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Permissions
var args = arg.Split('~').Select(a => a.Trim()).ToArray(); var args = arg.Split('~').Select(a => a.Trim()).ToArray();
if (args.Length > 2) if (args.Length > 2)
{ {
await e.Channel.SendMessage("💢Invalid number of '~'s in the argument."); await e.Channel.SendMessage("💢Invalid number of '~'s in the argument.").ConfigureAwait(false);
return; return;
} }
try try
@ -135,8 +135,8 @@ namespace NadekoBot.Modules.Permissions
var fromUser = PermissionHelper.ValidateUser(e.Server, args[0]); var fromUser = PermissionHelper.ValidateUser(e.Server, args[0]);
var toUser = PermissionHelper.ValidateUser(e.Server, args[1]); var toUser = PermissionHelper.ValidateUser(e.Server, args[1]);
PermissionsHandler.CopyUserPermissions(fromUser, toUser); await PermissionsHandler.CopyUserPermissions(fromUser, toUser).ConfigureAwait(false);
await e.Channel.SendMessage($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**."); await e.Channel.SendMessage($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**.").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -146,13 +146,13 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "verbose") cgb.CreateCommand(Prefix + "verbose")
.Alias(Prefix + "v") .Alias(Prefix + "v")
.Description("Sets whether to show when a command/module is blocked. | ;verbose true") .Description($"Sets whether to show when a command/module is blocked. | `{Prefix}verbose true`")
.Parameter("arg", ParameterType.Required) .Parameter("arg", ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
var arg = e.GetArg("arg"); var arg = e.GetArg("arg");
var val = PermissionHelper.ValidateBool(arg); var val = PermissionHelper.ValidateBool(arg);
PermissionsHandler.SetVerbosity(e.Server, val); await PermissionsHandler.SetVerbosity(e.Server, val).ConfigureAwait(false);
await e.Channel.SendMessage($"Verbosity set to {val}.").ConfigureAwait(false); await e.Channel.SendMessage($"Verbosity set to {val}.").ConfigureAwait(false);
}); });
@ -169,7 +169,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "roleperms") cgb.CreateCommand(Prefix + "roleperms")
.Alias(Prefix + "rp") .Alias(Prefix + "rp")
.Description("Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole") .Description($"Shows banned permissions for a certain role. No argument means for everyone. | `{Prefix}rp AwesomeRole`")
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -195,7 +195,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "chnlperms") cgb.CreateCommand(Prefix + "chnlperms")
.Alias(Prefix + "cp") .Alias(Prefix + "cp")
.Description("Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev") .Description($"Shows banned permissions for a certain channel. No argument means for this channel. | `{Prefix}cp #dev`")
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -220,7 +220,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "userperms") cgb.CreateCommand(Prefix + "userperms")
.Alias(Prefix + "up") .Alias(Prefix + "up")
.Description("Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth") .Description($"Shows banned permissions for a certain user. No argument means for yourself. | `{Prefix}up Kwoth`")
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -246,7 +246,7 @@ namespace NadekoBot.Modules.Permissions
.Alias(Prefix + "sm") .Alias(Prefix + "sm")
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Description("Sets a module's permission at the server level. | ;sm \"module name\" enable") .Description($"Sets a module's permission at the server level. | `{Prefix}sm \"module name\" enable`")
.Do(async e => .Do(async e =>
{ {
try try
@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Permissions
var module = PermissionHelper.ValidateModule(e.GetArg("module")); var module = PermissionHelper.ValidateModule(e.GetArg("module"));
var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
PermissionsHandler.SetServerModulePermission(e.Server, module, state); await PermissionsHandler.SetServerModulePermission(e.Server, module, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false);
} }
catch (ArgumentException exArg) catch (ArgumentException exArg)
@ -270,7 +270,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "srvrcmd").Alias(Prefix + "sc") cgb.CreateCommand(Prefix + "srvrcmd").Alias(Prefix + "sc")
.Parameter("command", ParameterType.Required) .Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Description("Sets a command's permission at the server level. | ;sc \"command name\" disable") .Description($"Sets a command's permission at the server level. | `{Prefix}sc \"command name\" disable`")
.Do(async e => .Do(async e =>
{ {
try try
@ -278,7 +278,7 @@ namespace NadekoBot.Modules.Permissions
var command = PermissionHelper.ValidateCommand(e.GetArg("command")); var command = PermissionHelper.ValidateCommand(e.GetArg("command"));
var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
PermissionsHandler.SetServerCommandPermission(e.Server, command, state); await PermissionsHandler.SetServerCommandPermission(e.Server, command, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false);
} }
catch (ArgumentException exArg) catch (ArgumentException exArg)
@ -295,7 +295,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.Description("Sets a module's permission at the role level. | ;rm \"module name\" enable MyRole") .Description($"Sets a module's permission at the role level. | `{Prefix}rm \"module name\" enable MyRole`")
.Do(async e => .Do(async e =>
{ {
try try
@ -307,7 +307,7 @@ namespace NadekoBot.Modules.Permissions
{ {
foreach (var role in e.Server.Roles) foreach (var role in e.Server.Roles)
{ {
PermissionsHandler.SetRoleModulePermission(role, module, state); await PermissionsHandler.SetRoleModulePermission(role, module, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false);
} }
@ -315,7 +315,7 @@ namespace NadekoBot.Modules.Permissions
{ {
var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role"));
PermissionsHandler.SetRoleModulePermission(role, module, state); await PermissionsHandler.SetRoleModulePermission(role, module, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false);
} }
} }
@ -333,7 +333,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("command", ParameterType.Required) .Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.Description("Sets a command's permission at the role level. | ;rc \"command name\" disable MyRole") .Description($"Sets a command's permission at the role level. | `{Prefix}rc \"command name\" disable MyRole`")
.Do(async e => .Do(async e =>
{ {
try try
@ -345,7 +345,7 @@ namespace NadekoBot.Modules.Permissions
{ {
foreach (var role in e.Server.Roles) foreach (var role in e.Server.Roles)
{ {
PermissionsHandler.SetRoleCommandPermission(role, command, state); await PermissionsHandler.SetRoleCommandPermission(role, command, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **ALL** roles.").ConfigureAwait(false);
} }
@ -353,7 +353,7 @@ namespace NadekoBot.Modules.Permissions
{ {
var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role"));
PermissionsHandler.SetRoleCommandPermission(role, command, state); await PermissionsHandler.SetRoleCommandPermission(role, command, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false);
} }
} }
@ -371,7 +371,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Description("Sets a module's permission at the channel level. | ;cm \"module name\" enable SomeChannel") .Description($"Sets a module's permission at the channel level. | `{Prefix}cm \"module name\" enable SomeChannel`")
.Do(async e => .Do(async e =>
{ {
try try
@ -383,20 +383,20 @@ namespace NadekoBot.Modules.Permissions
{ {
foreach (var channel in e.Server.TextChannels) foreach (var channel in e.Server.TextChannels)
{ {
PermissionsHandler.SetChannelModulePermission(channel, module, state); await PermissionsHandler.SetChannelModulePermission(channel, module, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false);
} }
else if (string.IsNullOrWhiteSpace(channelArg)) else if (string.IsNullOrWhiteSpace(channelArg))
{ {
PermissionsHandler.SetChannelModulePermission(e.Channel, module, state); await PermissionsHandler.SetChannelModulePermission(e.Channel, module, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{e.Channel.Name}** channel.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{e.Channel.Name}** channel.").ConfigureAwait(false);
} }
else else
{ {
var channel = PermissionHelper.ValidateChannel(e.Server, channelArg); var channel = PermissionHelper.ValidateChannel(e.Server, channelArg);
PermissionsHandler.SetChannelModulePermission(channel, module, state); await PermissionsHandler.SetChannelModulePermission(channel, module, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false);
} }
} }
@ -414,7 +414,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("command", ParameterType.Required) .Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Description("Sets a command's permission at the channel level. | ;cc \"command name\" enable SomeChannel") .Description($"Sets a command's permission at the channel level. | `{Prefix}cc \"command name\" enable SomeChannel`")
.Do(async e => .Do(async e =>
{ {
try try
@ -426,7 +426,7 @@ namespace NadekoBot.Modules.Permissions
{ {
foreach (var channel in e.Server.TextChannels) foreach (var channel in e.Server.TextChannels)
{ {
PermissionsHandler.SetChannelCommandPermission(channel, command, state); await PermissionsHandler.SetChannelCommandPermission(channel, command, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** on **ALL** channels.").ConfigureAwait(false);
} }
@ -434,7 +434,7 @@ namespace NadekoBot.Modules.Permissions
{ {
var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel")); var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel"));
PermissionsHandler.SetChannelCommandPermission(channel, command, state); await PermissionsHandler.SetChannelCommandPermission(channel, command, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false);
} }
} }
@ -452,7 +452,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.Description("Sets a module's permission at the user level. | ;um \"module name\" enable SomeUsername") .Description($"Sets a module's permission at the user level. | `{Prefix}um \"module name\" enable SomeUsername`")
.Do(async e => .Do(async e =>
{ {
try try
@ -461,7 +461,7 @@ namespace NadekoBot.Modules.Permissions
var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
var user = PermissionHelper.ValidateUser(e.Server, e.GetArg("user")); var user = PermissionHelper.ValidateUser(e.Server, e.GetArg("user"));
PermissionsHandler.SetUserModulePermission(user, module, state); await PermissionsHandler.SetUserModulePermission(user, module, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false); await e.Channel.SendMessage($"Module **{module}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false);
} }
catch (ArgumentException exArg) catch (ArgumentException exArg)
@ -478,7 +478,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("command", ParameterType.Required) .Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.Description("Sets a command's permission at the user level. | ;uc \"command name\" enable SomeUsername") .Description($"Sets a command's permission at the user level. | `{Prefix}uc \"command name\" enable SomeUsername`")
.Do(async e => .Do(async e =>
{ {
try try
@ -487,7 +487,7 @@ namespace NadekoBot.Modules.Permissions
var state = PermissionHelper.ValidateBool(e.GetArg("bool")); var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
var user = PermissionHelper.ValidateUser(e.Server, e.GetArg("user")); var user = PermissionHelper.ValidateUser(e.Server, e.GetArg("user"));
PermissionsHandler.SetUserCommandPermission(user, command, state); await PermissionsHandler.SetUserCommandPermission(user, command, state).ConfigureAwait(false);
await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has been **{(state ? "enabled" : "disabled")}** for user **{user.Name}**.").ConfigureAwait(false);
} }
catch (ArgumentException exArg) catch (ArgumentException exArg)
@ -502,7 +502,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allsrvrmdls").Alias(Prefix + "asm") cgb.CreateCommand(Prefix + "allsrvrmdls").Alias(Prefix + "asm")
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Description("Sets permissions for all modules at the server level. | ;asm [enable/disable]") .Description($"Sets permissions for all modules at the server level. | `{Prefix}asm [enable/disable]`")
.Do(async e => .Do(async e =>
{ {
try try
@ -511,7 +511,7 @@ namespace NadekoBot.Modules.Permissions
foreach (var module in NadekoBot.Client.GetService<ModuleService>().Modules) foreach (var module in NadekoBot.Client.GetService<ModuleService>().Modules)
{ {
PermissionsHandler.SetServerModulePermission(e.Server, module.Name, state); await PermissionsHandler.SetServerModulePermission(e.Server, module.Name, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false);
} }
@ -528,7 +528,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allsrvrcmds").Alias(Prefix + "asc") cgb.CreateCommand(Prefix + "allsrvrcmds").Alias(Prefix + "asc")
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Description("Sets permissions for all commands from a certain module at the server level. | ;asc \"module name\" [enable/disable]") .Description($"Sets permissions for all commands from a certain module at the server level. | `{Prefix}asc \"module name\" [enable/disable]`")
.Do(async e => .Do(async e =>
{ {
try try
@ -538,7 +538,7 @@ namespace NadekoBot.Modules.Permissions
foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module)) foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module))
{ {
PermissionsHandler.SetServerCommandPermission(e.Server, command.Text, state); await PermissionsHandler.SetServerCommandPermission(e.Server, command.Text, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false); await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** on this server.").ConfigureAwait(false);
} }
@ -555,7 +555,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allchnlmdls").Alias(Prefix + "acm") cgb.CreateCommand(Prefix + "allchnlmdls").Alias(Prefix + "acm")
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Description("Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel") .Description($"Sets permissions for all modules at the channel level. | `{Prefix}acm [enable/disable] SomeChannel`")
.Do(async e => .Do(async e =>
{ {
try try
@ -565,7 +565,7 @@ namespace NadekoBot.Modules.Permissions
var channel = string.IsNullOrWhiteSpace(chArg) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chArg); var channel = string.IsNullOrWhiteSpace(chArg) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chArg);
foreach (var module in NadekoBot.Client.GetService<ModuleService>().Modules) foreach (var module in NadekoBot.Client.GetService<ModuleService>().Modules)
{ {
PermissionsHandler.SetChannelModulePermission(channel, module.Name, state); await PermissionsHandler.SetChannelModulePermission(channel, module.Name, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false);
@ -584,7 +584,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Description("Sets permissions for all commands from a certain module at the channel level. | ;acc \"module name\" [enable/disable] SomeChannel") .Description($"Sets permissions for all commands from a certain module at the channel level. | `{Prefix}acc \"module name\" [enable/disable] SomeChannel`")
.Do(async e => .Do(async e =>
{ {
try try
@ -594,7 +594,7 @@ namespace NadekoBot.Modules.Permissions
var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel")); var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel"));
foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module)) foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module))
{ {
PermissionsHandler.SetChannelCommandPermission(channel, command.Text, state); await PermissionsHandler.SetChannelCommandPermission(channel, command.Text, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false); await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{channel.Name}** channel.").ConfigureAwait(false);
} }
@ -611,7 +611,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allrolemdls").Alias(Prefix + "arm") cgb.CreateCommand(Prefix + "allrolemdls").Alias(Prefix + "arm")
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.Description("Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole") .Description($"Sets permissions for all modules at the role level. | `{Prefix}arm [enable/disable] MyRole`")
.Do(async e => .Do(async e =>
{ {
try try
@ -620,7 +620,7 @@ namespace NadekoBot.Modules.Permissions
var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role")); var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role"));
foreach (var module in NadekoBot.Client.GetService<ModuleService>().Modules) foreach (var module in NadekoBot.Client.GetService<ModuleService>().Modules)
{ {
PermissionsHandler.SetRoleModulePermission(role, module.Name, state); await PermissionsHandler.SetRoleModulePermission(role, module.Name, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); await e.Channel.SendMessage($"All modules have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false);
@ -639,7 +639,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required) .Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required) .Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed) .Parameter("role", ParameterType.Unparsed)
.Description("Sets permissions for all commands from a certain module at the role level. | ;arc \"module name\" [enable/disable] MyRole") .Description($"Sets permissions for all commands from a certain module at the role level. | `{Prefix}arc \"module name\" [enable/disable] MyRole`")
.Do(async e => .Do(async e =>
{ {
try try
@ -652,7 +652,7 @@ namespace NadekoBot.Modules.Permissions
{ {
foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module)) foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module))
{ {
PermissionsHandler.SetRoleCommandPermission(role, command.Text, state); await PermissionsHandler.SetRoleCommandPermission(role, command.Text, state).ConfigureAwait(false);
} }
} }
await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **all roles** role.").ConfigureAwait(false); await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **all roles** role.").ConfigureAwait(false);
@ -663,7 +663,7 @@ namespace NadekoBot.Modules.Permissions
foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module)) foreach (var command in NadekoBot.Client.GetService<CommandService>().AllCommands.Where(c => c.Category == module))
{ {
PermissionsHandler.SetRoleCommandPermission(role, command.Text, state); await PermissionsHandler.SetRoleCommandPermission(role, command.Text, state).ConfigureAwait(false);
} }
await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false); await e.Channel.SendMessage($"All commands from the **{module}** module have been **{(state ? "enabled" : "disabled")}** for **{role.Name}** role.").ConfigureAwait(false);
} }
@ -679,7 +679,7 @@ namespace NadekoBot.Modules.Permissions
}); });
cgb.CreateCommand(Prefix + "ubl") cgb.CreateCommand(Prefix + "ubl")
.Description("Blacklists a mentioned user. | ;ubl [user_mention]") .Description($"Blacklists a mentioned user. | `{Prefix}ubl [user_mention]`")
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -689,13 +689,13 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedUsers.Any()) return; if (!e.Message.MentionedUsers.Any()) return;
var usr = e.Message.MentionedUsers.First(); var usr = e.Message.MentionedUsers.First();
NadekoBot.Config.UserBlacklist.Add(usr.Id); NadekoBot.Config.UserBlacklist.Add(usr.Id);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted user {usr.Name}`").ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted user {usr.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false); }).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "uubl") cgb.CreateCommand(Prefix + "uubl")
.Description($"Unblacklists a mentioned user. | {Prefix}uubl [user_mention]") .Description($"Unblacklists a mentioned user. | `{Prefix}uubl [user_mention]`")
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -707,7 +707,7 @@ namespace NadekoBot.Modules.Permissions
if (NadekoBot.Config.UserBlacklist.Contains(usr.Id)) if (NadekoBot.Config.UserBlacklist.Contains(usr.Id))
{ {
NadekoBot.Config.UserBlacklist.Remove(usr.Id); NadekoBot.Config.UserBlacklist.Remove(usr.Id);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully unblacklisted user {usr.Name}`").ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully unblacklisted user {usr.Name}`").ConfigureAwait(false);
} }
else else
@ -718,7 +718,7 @@ namespace NadekoBot.Modules.Permissions
}); });
cgb.CreateCommand(Prefix + "cbl") cgb.CreateCommand(Prefix + "cbl")
.Description("Blacklists a mentioned channel (#general for example). | ;cbl #some_channel") .Description($"Blacklists a mentioned channel (#general for example). | `{Prefix}cbl #some_channel`")
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -727,13 +727,13 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedChannels.Any()) return; if (!e.Message.MentionedChannels.Any()) return;
var ch = e.Message.MentionedChannels.First(); var ch = e.Message.MentionedChannels.First();
NadekoBot.Config.UserBlacklist.Add(ch.Id); NadekoBot.Config.UserBlacklist.Add(ch.Id);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false); }).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "cubl") cgb.CreateCommand(Prefix + "cubl")
.Description("Unblacklists a mentioned channel (#general for example). | ;cubl #some_channel") .Description($"Unblacklists a mentioned channel (#general for example). | `{Prefix}cubl #some_channel`")
.Parameter("channel", ParameterType.Unparsed) .Parameter("channel", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -742,13 +742,13 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedChannels.Any()) return; if (!e.Message.MentionedChannels.Any()) return;
var ch = e.Message.MentionedChannels.First(); var ch = e.Message.MentionedChannels.First();
NadekoBot.Config.UserBlacklist.Remove(ch.Id); NadekoBot.Config.UserBlacklist.Remove(ch.Id);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false); await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false); }).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "sbl") cgb.CreateCommand(Prefix + "sbl")
.Description("Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | ;sbl [servername/serverid]") .Description($"Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | `{Prefix}sbl [servername/serverid]`")
.Parameter("server", ParameterType.Unparsed) .Parameter("server", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly()) .AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e => .Do(async e =>
@ -767,7 +767,7 @@ namespace NadekoBot.Modules.Permissions
} }
var serverId = server.Id; var serverId = server.Id;
NadekoBot.Config.ServerBlacklist.Add(serverId); NadekoBot.Config.ServerBlacklist.Add(serverId);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
//cleanup trivias and typeracing //cleanup trivias and typeracing
Modules.Games.Commands.Trivia.TriviaGame trivia; Modules.Games.Commands.Trivia.TriviaGame trivia;
TriviaCommands.RunningTrivias.TryRemove(serverId, out trivia); TriviaCommands.RunningTrivias.TryRemove(serverId, out trivia);
@ -795,7 +795,7 @@ namespace NadekoBot.Modules.Permissions
throw new ArgumentOutOfRangeException("secs", "Invalid second parameter. (Must be a number between 0 and 3600)"); throw new ArgumentOutOfRangeException("secs", "Invalid second parameter. (Must be a number between 0 and 3600)");
PermissionsHandler.SetCommandCooldown(e.Server, command, secs); await PermissionsHandler.SetCommandCooldown(e.Server, command, secs).ConfigureAwait(false);
if(secs == 0) if(secs == 0)
await e.Channel.SendMessage($"Command **{command}** has no coooldown now.").ConfigureAwait(false); await e.Channel.SendMessage($"Command **{command}** has no coooldown now.").ConfigureAwait(false);
else else

View File

@ -30,8 +30,6 @@ namespace NadekoBot.Modules.Searches.Commands
} }
private static Dictionary<string, CachedChampion> CachedChampionImages = new Dictionary<string, CachedChampion>(); private static Dictionary<string, CachedChampion> CachedChampionImages = new Dictionary<string, CachedChampion>();
private readonly object cacheLock = new object();
private System.Timers.Timer clearTimer { get; } = new System.Timers.Timer(); private System.Timers.Timer clearTimer { get; } = new System.Timers.Timer();
public LoLCommands(DiscordModule module) : base(module) public LoLCommands(DiscordModule module) : base(module)
@ -42,7 +40,6 @@ namespace NadekoBot.Modules.Searches.Commands
{ {
try try
{ {
lock (cacheLock)
CachedChampionImages = CachedChampionImages CachedChampionImages = CachedChampionImages
.Where(kvp => DateTime.Now - kvp.Value.AddedAt > new TimeSpan(1, 0, 0)) .Where(kvp => DateTime.Now - kvp.Value.AddedAt > new TimeSpan(1, 0, 0))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value); .ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
@ -87,10 +84,8 @@ namespace NadekoBot.Modules.Searches.Commands
var resolvedRole = role; var resolvedRole = role;
var name = e.GetArg("champ").Replace(" ", "").ToLower(); var name = e.GetArg("champ").Replace(" ", "").ToLower();
CachedChampion champ = null; CachedChampion champ = null;
lock (cacheLock)
{ if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
}
if (champ != null) if (champ != null)
{ {
champ.ImageStream.Position = 0; champ.ImageStream.Position = 0;
@ -121,13 +116,9 @@ namespace NadekoBot.Modules.Searches.Commands
role = allData[0]["role"].ToString(); role = allData[0]["role"].ToString();
resolvedRole = ResolvePos(role); resolvedRole = ResolvePos(role);
} }
lock (cacheLock) if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
{
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
}
if (champ != null) if (champ != null)
{ {
Console.WriteLine("Sending lol image from cache.");
champ.ImageStream.Position = 0; champ.ImageStream.Position = 0;
await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
return; return;

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Searches.Commands
} }
} }
catch { } catch { }
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
}; };
checkTimer.Start(); checkTimer.Start();
} }
@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Searches.Commands
} }
config.ObservingStreams.Remove(toRemove); config.ObservingStreams.Remove(toRemove);
ConfigHandler.SaveConfig(); await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($":ok: Removed `{toRemove.Username}`'s stream from notifications.").ConfigureAwait(false); await e.Channel.SendMessage($":ok: Removed `{toRemove.Username}`'s stream from notifications.").ConfigureAwait(false);
}); });

View File

@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Searches
commands.ForEach(cmd => cmd.Init(cgb)); commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand(Prefix + "we") cgb.CreateCommand(Prefix + "we")
.Description($"Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | {Prefix}we Moscow RF") .Description($"Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | `{Prefix}we Moscow RF`")
.Parameter("city", ParameterType.Required) .Parameter("city", ParameterType.Required)
.Parameter("country", ParameterType.Required) .Parameter("country", ParameterType.Required)
.Do(async e => .Do(async e =>
@ -69,7 +69,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "yt") cgb.CreateCommand(Prefix + "yt")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Description("Searches youtubes and shows the first result") .Description($"Searches youtubes and shows the first result | `{Prefix}yt query`")
.Do(async e => .Do(async e =>
{ {
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return; if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -106,7 +106,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "imdb") cgb.CreateCommand(Prefix + "imdb")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Description("Queries imdb for movies or series, show first result.") .Description($"Queries imdb for movies or series, show first result. | `{Prefix}imdb query`")
.Do(async e => .Do(async e =>
{ {
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return; if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -130,7 +130,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "mang") cgb.CreateCommand(Prefix + "mang")
.Alias(Prefix + "manga").Alias(Prefix + "mq") .Alias(Prefix + "manga").Alias(Prefix + "mq")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Description("Queries anilist for a manga and shows the first result.") .Description($"Queries anilist for a manga and shows the first result. | `{Prefix}mq query`")
.Do(async e => .Do(async e =>
{ {
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return; if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -157,8 +157,16 @@ $@"🌍 **Weather for** 【{obj["target"]}】
.ConfigureAwait(false); .ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "randomdog")
.Alias(Prefix + "woof")
.Description("Shows a random dog image.")
.Do(async e =>
{
await e.Channel.SendMessage("http://random.dog/" + await SearchHelper.GetResponseStringAsync("http://random.dog/woof").ConfigureAwait(false)).ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "i") cgb.CreateCommand(Prefix + "i")
.Description("Pulls the first image found using a search parameter. Use ~ir for different results. | ~i cute kitten") .Description($"Pulls the first image found using a search parameter. Use ~ir for different results. | `{Prefix}i cute kitten`")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -184,7 +192,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "ir") cgb.CreateCommand(Prefix + "ir")
.Description("Pulls a random image using a search parameter. | ~ir cute kitten") .Description($"Pulls a random image using a search parameter. | `{Prefix}ir cute kitten`")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -211,7 +219,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "lmgtfy") cgb.CreateCommand(Prefix + "lmgtfy")
.Description("Google something for an idiot.") .Description($"Google something for an idiot. | `{Prefix}lmgtfy query`")
.Parameter("ffs", ParameterType.Unparsed) .Parameter("ffs", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -222,7 +230,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "google") cgb.CreateCommand(Prefix + "google")
.Alias(Prefix + "g") .Alias(Prefix + "g")
.Description("Get a google search link for some terms.") .Description($"Get a google search link for some terms. | `{Prefix}google query`")
.Parameter("terms", ParameterType.Unparsed) .Parameter("terms", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -234,7 +242,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
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. | `{Prefix}hs Ysera`")
.Parameter("name", ParameterType.Unparsed) .Parameter("name", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -275,7 +283,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "ud") cgb.CreateCommand(Prefix + "ud")
.Description("Searches Urban Dictionary for a word. |~ud Pineapple") .Description($"Searches Urban Dictionary for a word. | `{Prefix}ud Pineapple`")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -304,7 +312,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
// thanks to Blaubeerwald // thanks to Blaubeerwald
cgb.CreateCommand(Prefix + "#") cgb.CreateCommand(Prefix + "#")
.Description("Searches Tagdef.com for a hashtag. |~# ff") .Description($"Searches Tagdef.com for a hashtag. | `{Prefix}# ff`")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -389,7 +397,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "revav") cgb.CreateCommand(Prefix + "revav")
.Description("Returns a google reverse image search for someone's avatar.") .Description($"Returns a google reverse image search for someone's avatar. | `{Prefix}revav \"@SomeGuy\"")
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -406,7 +414,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "revimg") cgb.CreateCommand(Prefix + "revimg")
.Description("Returns a google reverse image search for an image from a link.") .Description($"Returns a google reverse image search for an image from a link. | `{Prefix}revav Image link`")
.Parameter("image", ParameterType.Unparsed) .Parameter("image", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -418,7 +426,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "safebooru") cgb.CreateCommand(Prefix + "safebooru")
.Description("Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~safebooru yuri+kissing") .Description($"Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `{Prefix}safebooru yuri+kissing`")
.Parameter("tag", ParameterType.Unparsed) .Parameter("tag", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -431,7 +439,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "wiki") cgb.CreateCommand(Prefix + "wiki")
.Description("Gives you back a wikipedia link") .Description($"Gives you back a wikipedia link | `{Prefix}wiki query`")
.Parameter("query", ParameterType.Unparsed) .Parameter("query", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -445,7 +453,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
}); });
cgb.CreateCommand(Prefix + "clr") cgb.CreateCommand(Prefix + "clr")
.Description("Shows you what color corresponds to that hex. | `~clr 00ff00`") .Description($"Shows you what color corresponds to that hex. | `{Prefix}clr 00ff00`")
.Parameter("color", ParameterType.Unparsed) .Parameter("color", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -470,7 +478,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "videocall") cgb.CreateCommand(Prefix + "videocall")
.Description("Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message.") .Description($"Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `{Prefix}videocall \"@SomeGuy\"`")
.Parameter("arg", ParameterType.Unparsed) .Parameter("arg", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -494,7 +502,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "av") cgb.CreateCommand(Prefix + "av")
.Alias(Prefix + "avatar") .Alias(Prefix + "avatar")
.Parameter("mention", ParameterType.Required) .Parameter("mention", ParameterType.Required)
.Description("Shows a mentioned person's avatar. | ~av @X") .Description($"Shows a mentioned person's avatar. | `{Prefix}av @X`")
.Do(async e => .Do(async e =>
{ {
var usr = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault(); var usr = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault();

View File

@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Translator
{ {
cgb.CreateCommand(Module.Prefix + "translate") cgb.CreateCommand(Module.Prefix + "translate")
.Alias(Module.Prefix + "trans") .Alias(Module.Prefix + "trans")
.Description($"Translates from>to text. From the given language to the destiation language. | {Module.Prefix}trans en>fr Hello") .Description($"Translates from>to text. From the given language to the destiation language. | `{Module.Prefix}trans en>fr Hello`")
.Parameter("langs", ParameterType.Required) .Parameter("langs", ParameterType.Required)
.Parameter("text", ParameterType.Unparsed) .Parameter("text", ParameterType.Unparsed)
.Do(TranslateFunc()); .Do(TranslateFunc());

View File

@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Trello
cgb.CreateCommand(Prefix + "bind") cgb.CreateCommand(Prefix + "bind")
.Description("Bind a trello bot to a single channel. " + .Description("Bind a trello bot to a single channel. " +
"You will receive notifications from your board when something is added or edited." + "You will receive notifications from your board when something is added or edited." +
" | bind [board_id]") $" | `{Prefix}bind [board_id]`")
.Parameter("board_id", Discord.Commands.ParameterType.Required) .Parameter("board_id", Discord.Commands.ParameterType.Required)
.Do(async e => .Do(async e =>
{ {
@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Trello
}); });
cgb.CreateCommand(Prefix + "cards") cgb.CreateCommand(Prefix + "cards")
.Description("Lists all cards from the supplied list. You can supply either a name or an index.") .Description($"Lists all cards from the supplied list. You can supply either a name or an index. | `{Prefix}cards index`")
.Parameter("list_name", Discord.Commands.ParameterType.Unparsed) .Parameter("list_name", Discord.Commands.ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {

View File

@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Utility
commands.ForEach(cmd => cmd.Init(cgb)); commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand(Prefix + "whoplays") cgb.CreateCommand(Prefix + "whoplays")
.Description("Shows a list of users who are playing the specified game.") .Description($"Shows a list of users who are playing the specified game. | `{Prefix}whoplays Overwatch`")
.Parameter("game", ParameterType.Unparsed) .Parameter("game", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -52,16 +52,15 @@ namespace NadekoBot.Modules.Utility
}); });
cgb.CreateCommand(Prefix + "inrole") cgb.CreateCommand(Prefix + "inrole")
.Description("Lists every person from the provided role or roles (separated by a ',') on this server.") .Description($"Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `{Prefix}inrole Role`")
.Parameter("roles", ParameterType.Unparsed) .Parameter("roles", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
await Task.Run(async () => await Task.Run(async () =>
{ {
if (!e.User.ServerPermissions.MentionEveryone) return;
var arg = e.GetArg("roles").Split(',').Select(r => r.Trim()); var arg = e.GetArg("roles").Split(',').Select(r => r.Trim());
string send = $"`Here is a list of users in a specfic role:`"; string send = $"`Here is a list of users in a specfic role:`";
foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str))) foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@everyone" && str != "everyone"))
{ {
var role = e.Server.FindRoles(roleStr).FirstOrDefault(); var role = e.Server.FindRoles(roleStr).FirstOrDefault();
if (role == null) continue; if (role == null) continue;
@ -71,6 +70,11 @@ namespace NadekoBot.Modules.Utility
while (send.Length > 2000) while (send.Length > 2000)
{ {
if (!e.User.ServerPermissions.ManageMessages)
{
await e.Channel.SendMessage($"{e.User.Mention} you are not allowed to use this command on roles with a lot of users in them to prevent abuse.");
return;
}
var curstr = send.Substring(0, 2000); var curstr = send.Substring(0, 2000);
await await
e.Channel.Send(curstr.Substring(0, e.Channel.Send(curstr.Substring(0,
@ -110,7 +114,7 @@ namespace NadekoBot.Modules.Utility
}); });
cgb.CreateCommand(Prefix + "userid").Alias(Prefix + "uid") cgb.CreateCommand(Prefix + "userid").Alias(Prefix + "uid")
.Description("Shows user ID.") .Description($"Shows user ID. | `{Prefix}uid` or `{Prefix}uid \"@SomeGuy\"")
.Parameter("user", ParameterType.Unparsed) .Parameter("user", ParameterType.Unparsed)
.Do(async e => .Do(async e =>
{ {
@ -122,11 +126,11 @@ namespace NadekoBot.Modules.Utility
}); });
cgb.CreateCommand(Prefix + "channelid").Alias(Prefix + "cid") cgb.CreateCommand(Prefix + "channelid").Alias(Prefix + "cid")
.Description("Shows current channel ID.") .Description($"Shows current channel ID. | `{Prefix}cid`")
.Do(async e => await e.Channel.SendMessage("This channel's ID is " + e.Channel.Id).ConfigureAwait(false)); .Do(async e => await e.Channel.SendMessage("This channel's ID is " + e.Channel.Id).ConfigureAwait(false));
cgb.CreateCommand(Prefix + "serverid").Alias(Prefix + "sid") cgb.CreateCommand(Prefix + "serverid").Alias(Prefix + "sid")
.Description("Shows current server ID.") .Description($"Shows current server ID. | `{Prefix}sid`")
.Do(async e => await e.Channel.SendMessage("This server's ID is " + e.Server.Id).ConfigureAwait(false)); .Do(async e => await e.Channel.SendMessage("This server's ID is " + e.Server.Id).ConfigureAwait(false));
cgb.CreateCommand(Prefix + "roles") cgb.CreateCommand(Prefix + "roles")
@ -144,6 +148,18 @@ namespace NadekoBot.Modules.Utility
} }
await e.Channel.SendMessage("`List of roles:` \n• " + string.Join("\n• ", e.Server.Roles)).ConfigureAwait(false); await e.Channel.SendMessage("`List of roles:` \n• " + string.Join("\n• ", e.Server.Roles)).ConfigureAwait(false);
}); });
cgb.CreateCommand(Prefix + "channeltopic")
.Alias(Prefix + "ct")
.Description($"Sends current channel's topic as a message. | `{Prefix}ct`")
.Do(async e =>
{
var topic = e.Channel.Topic;
if (string.IsNullOrWhiteSpace(topic))
return;
await e.Channel.SendMessage(topic).ConfigureAwait(false);
});
}); });
} }
} }

View File

@ -197,7 +197,7 @@ namespace NadekoBot
return; return;
} }
#if NADEKO_RELEASE #if NADEKO_RELEASE
await Task.Delay(90000).ConfigureAwait(false); await Task.Delay(100000).ConfigureAwait(false);
#else #else
await Task.Delay(1000).ConfigureAwait(false); await Task.Delay(1000).ConfigureAwait(false);
#endif #endif

View File

@ -46,6 +46,7 @@
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<NoWarn> <NoWarn>
</NoWarn> </NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -55,6 +56,7 @@
<DefineConstants>TRACE</DefineConstants> <DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PRIVATE|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PRIVATE|AnyCPU'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
@ -78,13 +80,14 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols> <DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath> <OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants> <DefineConstants>TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget> <PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<Optimize>false</Optimize>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath> <OutputPath>bin\x64\Release\</OutputPath>
@ -115,6 +118,7 @@
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup> </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">
@ -186,6 +190,7 @@
<Compile Include="Classes\FlowersHandler.cs" /> <Compile Include="Classes\FlowersHandler.cs" />
<Compile Include="Modules\Conversations\Commands\RipCommand.cs" /> <Compile Include="Modules\Conversations\Commands\RipCommand.cs" />
<Compile Include="Modules\CustomReactions\CustomReactions.cs" /> <Compile Include="Modules\CustomReactions\CustomReactions.cs" />
<Compile Include="Modules\Gambling\Commands\AnimalRacing.cs" />
<Compile Include="Modules\Music\Classes\PlaylistFullException.cs" /> <Compile Include="Modules\Music\Classes\PlaylistFullException.cs" />
<Compile Include="Modules\Programming\Commands\HaskellRepl.cs" /> <Compile Include="Modules\Programming\Commands\HaskellRepl.cs" />
<Compile Include="Modules\Programming\ProgrammingModule.cs" /> <Compile Include="Modules\Programming\ProgrammingModule.cs" />

View File

@ -4,6 +4,8 @@ using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes.JSONModels namespace NadekoBot.Classes.JSONModels
{ {
@ -88,6 +90,17 @@ namespace NadekoBot.Classes.JSONModels
public bool IsRotatingStatus { get; set; } = false; public bool IsRotatingStatus { get; set; } = false;
public int BufferSize { get; set; } = 4.MiB(); public int BufferSize { get; set; } = 4.MiB();
public string[] RaceAnimals { get; internal set; } = {
"🐼",
"🐻",
"🐧",
"🐨",
"🐬",
"🐞",
"🦀",
"🦄" };
[JsonIgnore]
public List<Quote> Quotes { get; set; } = new List<Quote>(); public List<Quote> Quotes { get; set; } = new List<Quote>();
[JsonIgnore] [JsonIgnore]
@ -187,13 +200,17 @@ Nadeko Support Server: <https://discord.gg/0ehQwTK2RBjAxzEY>";
public static class ConfigHandler public static class ConfigHandler
{ {
private static readonly object configLock = new object(); private static readonly SemaphoreSlim configLock = new SemaphoreSlim(1, 1);
public static void SaveConfig() public static async Task SaveConfig()
{ {
lock (configLock) await configLock.WaitAsync();
try
{ {
File.WriteAllText("data/config.json", JsonConvert.SerializeObject(NadekoBot.Config, Formatting.Indented)); File.WriteAllText("data/config.json", JsonConvert.SerializeObject(NadekoBot.Config, Formatting.Indented));
} }
finally {
configLock.Release();
}
} }
public static bool IsBlackListed(MessageEventArgs evArgs) => IsUserBlacklisted(evArgs.User.Id) || public static bool IsBlackListed(MessageEventArgs evArgs) => IsUserBlacklisted(evArgs.User.Id) ||

View File

@ -4,7 +4,16 @@
"ForwardToAllOwners": false, "ForwardToAllOwners": false,
"IsRotatingStatus": false, "IsRotatingStatus": false,
"BufferSize": 4194304, "BufferSize": 4194304,
"Quotes": [], "RaceAnimals": [
"🐼",
"🐻",
"🐧",
"🐨",
"🐬",
"🐞",
"🦀",
"🦄"
],
"RemindMessageFormat": "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗", "RemindMessageFormat": "❗⏰**I've been told to remind you to '%message%' now by %user%.**⏰❗",
"CustomReactions": { "CustomReactions": {
"\\o\\": [ "\\o\\": [

View File

@ -2,7 +2,7 @@
######You can donate on paypal: `nadekodiscordbot@gmail.com` ######You can donate on paypal: `nadekodiscordbot@gmail.com`
#NadekoBot List Of Commands #NadekoBot List Of Commands
Version: `NadekoBot v0.9.6048.2992` Version: `NadekoBot v0.9.6051.26856`
### Help ### Help
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
@ -57,38 +57,38 @@ Command and aliases | Description | Usage
`.listallincidents`, `.lain` | Sends you a file containing all incidents and flags them as read. `.listallincidents`, `.lain` | Sends you a file containing all incidents and flags them as read.
`.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only. `.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. Server Manager Only.
`.restart` | Restarts the bot. Might not work. **Bot Owner Only** `.restart` | Restarts the bot. Might not work. **Bot Owner Only**
`.setrole`, `.sr` | Sets a role for a given user. | .sr @User Guest `.setrole`, `.sr` | Sets a role for a given user. | `.sr @User Guest`
`.removerole`, `.rr` | Removes a role from a given user. | .rr @User Admin `.removerole`, `.rr` | Removes a role from a given user. | `.rr @User Admin`
`.renamerole`, `.renr` | Renames a role. Role you are renaming must be lower than bot's highest role. | `.renr "First role" SecondRole` `.renamerole`, `.renr` | Renames a role. Role you are renaming must be lower than bot's highest role. | `.renr "First role" SecondRole`
`.removeallroles`, `.rar` | Removes all roles from a mentioned user. | .rar @User `.removeallroles`, `.rar` | Removes all roles from a mentioned user. | `.rar @User`
`.createrole`, `.cr` | Creates a role with a given name. | `.r Awesome Role` `.createrole`, `.cr` | Creates a role with a given name. | `.cr Awesome Role`
`.rolecolor`, `.rc` | Set a role's color to the hex or 0-255 rgb color value provided. | `.color Admin 255 200 100` or `.color Admin ffba55` `.rolecolor`, `.rc` | Set a role's color to the hex or 0-255 rgb color value provided. | `.rc Admin 255 200 100` or `.rc Admin ffba55`
`.ban`, `.b` | Bans a user by id or name with an optional message. | .b "@some Guy" Your behaviour is toxic. `.ban`, `.b` | Bans a user by id or name with an optional message. | `.b "@some Guy" Your behaviour is toxic.`
`.softban`, `.sb` | Bans and then unbans a user by id or name with an optional message. | .sb "@some Guy" Your behaviour is toxic. `.softban`, `.sb` | Bans and then unbans a user by id or name with an optional message. | `.sb "@some Guy" Your behaviour is toxic.`
`.kick`, `.k` | Kicks a mentioned user. `.kick`, `.k` | Kicks a mentioned user. | `.k "@some Guy" Your behaviour is toxic.`
`.mute` | Mutes mentioned user or users. `.mute` | Mutes mentioned user or users. | `.mute "@Someguy"` or `.mute "@Someguy" "@Someguy"`
`.unmute` | Unmutes mentioned user or users. `.unmute` | Unmutes mentioned user or users. | `.unmute "@Someguy"` or `.unmute "@Someguy" "@Someguy"`
`.deafen`, `.deaf` | Deafens mentioned user or users `.deafen`, `.deaf` | Deafens mentioned user or users | `.deaf "@Someguy"` or `.deaf "@Someguy" "@Someguy"`
`.undeafen`, `.undef` | Undeafens mentioned user or users `.undeafen`, `.undef` | Undeafens mentioned user or users | `.undef "@Someguy"` or `.undef "@Someguy" "@Someguy"`
`.delvoichanl`, `.dvch` | Deletes a voice channel with a given name. `.delvoichanl`, `.dvch` | Deletes a voice channel with a given name. | `.dvch VoiceChannelName`
`.creatvoichanl`, `.cvch` | Creates a new voice channel with a given name. `.creatvoichanl`, `.cvch` | Creates a new voice channel with a given name. | `.cvch VoiceChannelName`
`.deltxtchanl`, `.dtch` | Deletes a text channel with a given name. `.deltxtchanl`, `.dtch` | Deletes a text channel with a given name. | `.dtch TextChannelName`
`.creatxtchanl`, `.ctch` | Creates a new text channel with a given name. `.creatxtchanl`, `.ctch` | Creates a new text channel with a given name. | `.ctch TextChannelName`
`.settopic`, `.st` | Sets a topic on the current channel. | `.st My new topic` `.settopic`, `.st` | Sets a topic on the current channel. | `.st My new topic`
`.setchanlname`, `.schn` | Changed the name of the current channel. `.setchanlname`, `.schn` | Changed the name of the current channel.| `.schn NewName`
`.heap` | Shows allocated memory - **Bot Owner Only!** `.heap` | Shows allocated memory - **Bot Owner Only!**
`.prune`, `.clr` | `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X` `.prune`, `.clr` | `.prune` removes all nadeko's messages in the last 100 messages.`.prune X` removes last X messages from the channel (up to 100)`.prune @Someone` removes all Someone's messages in the last 100 messages.`.prune @Someone X` removes last X 'Someone's' messages in the channel. | `.prune` or `.prune 5` or `.prune @Someone` or `.prune @Someone X`
`.die` | Shuts the bot down and notifies users about the restart. **Bot Owner Only!** `.die` | Shuts the bot down and notifies users about the restart. **Bot Owner Only!**
`.setname`, `.newnm` | Give the bot a new name. **Bot Owner Only!** `.setname`, `.newnm` | Give the bot a new name. **Bot Owner Only!** | .newnm BotName
`.newavatar`, `.setavatar` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg` `.newavatar`, `.setavatar` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.setavatar https://i.ytimg.com/vi/WDudkR1eTMM/maxresdefault.jpg`
`.setgame` | Sets the bots game. **Bot Owner Only!** `.setgame` | Sets the bots game. **Bot Owner Only!** | `.setgame Playing with kwoth`
`.send` | Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!` `.send` | Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.send serverid|u:user_id Send this to a user!` or `.send serverid|c:channel_id Send this to a channel!`
`.mentionrole`, `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. `.mentionrole`, `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. | `.menro RoleName`
`.unstuck` | Clears the message queue. **Bot Owner Only!** `.unstuck` | Clears the message queue. **Bot Owner Only!**
`.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. | `.donadd Donate Amount`
`.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`
`.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** | `.savechat 150`
### Utility ### Utility
Command and aliases | Description | Usage Command and aliases | Description | Usage
@ -98,15 +98,16 @@ Command and aliases | Description | Usage
`.serverinfo`, `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | .sinfo Some Server `.serverinfo`, `.sinfo` | Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. | .sinfo Some Server
`.channelinfo`, `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | .cinfo #some-channel `.channelinfo`, `.cinfo` | Shows info about the channel. If no channel is supplied, it defaults to current one. | .cinfo #some-channel
`.userinfo`, `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | .uinfo @SomeUser `.userinfo`, `.uinfo` | Shows info about the user. If no user is supplied, it defaults a user running the command. | .uinfo @SomeUser
`.whoplays` | Shows a list of users who are playing the specified game. `.whoplays` | Shows a list of users who are playing the specified game. | `.whoplays Overwatch`
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. `.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role`
`.checkmyperms` | Checks your userspecific permissions on this channel. `.checkmyperms` | Checks your userspecific permissions on this channel.
`.stats` | Shows some basic stats for Nadeko. `.stats` | Shows some basic stats for Nadeko.
`.dysyd` | Shows some basic stats for Nadeko. `.dysyd` | Shows some basic stats for Nadeko.
`.userid`, `.uid` | Shows user ID. `.userid`, `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy"
`.channelid`, `.cid` | Shows current channel ID. `.channelid`, `.cid` | Shows current channel ID. | `.cid`
`.serverid`, `.sid` | Shows current server ID. `.serverid`, `.sid` | Shows current server ID. | `.sid`
`.roles` | List all roles on this server or a single user if specified. `.roles` | List all roles on this server or a single user if specified.
`.channeltopic`, `.ct` | Sends current channel's topic as a message. | `.ct`
### Permissions ### Permissions
Command and aliases | Description | Usage Command and aliases | Description | Usage
@ -122,38 +123,38 @@ Command and aliases | Description | Usage
`;rolepermscopy`, `;rpc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;rpc Some Role ~ Some other role` `;rolepermscopy`, `;rpc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;rpc Some Role ~ Some other role`
`;chnlpermscopy`, `;cpc` | Copies BOT PERMISSIONS (not discord permissions) from one channel to another. | `;cpc Some Channel ~ Some other channel` `;chnlpermscopy`, `;cpc` | Copies BOT PERMISSIONS (not discord permissions) from one channel to another. | `;cpc Some Channel ~ Some other channel`
`;usrpermscopy`, `;upc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;upc @SomeUser ~ @SomeOtherUser` `;usrpermscopy`, `;upc` | Copies BOT PERMISSIONS (not discord permissions) from one role to another. | `;upc @SomeUser ~ @SomeOtherUser`
`;verbose`, `;v` | Sets whether to show when a command/module is blocked. | ;verbose true `;verbose`, `;v` | Sets whether to show when a command/module is blocked. | `;verbose true`
`;srvrperms`, `;sp` | Shows banned permissions for this server. `;srvrperms`, `;sp` | Shows banned permissions for this server.
`;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole `;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | `;rp AwesomeRole`
`;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev `;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | `;cp #dev`
`;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth `;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | `;up Kwoth`
`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | ;sm "module name" enable `;srvrmdl`, `;sm` | Sets a module's permission at the server level. | `;sm "module name" enable`
`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | ;sc "command name" disable `;srvrcmd`, `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable`
`;rolemdl`, `;rm` | Sets a module's permission at the role level. | ;rm "module name" enable MyRole `;rolemdl`, `;rm` | Sets a module's permission at the role level. | `;rm "module name" enable MyRole`
`;rolecmd`, `;rc` | Sets a command's permission at the role level. | ;rc "command name" disable MyRole `;rolecmd`, `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole`
`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | ;cm "module name" enable SomeChannel `;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | `;cm "module name" enable SomeChannel`
`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | ;cc "command name" enable SomeChannel `;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel`
`;usrmdl`, `;um` | Sets a module's permission at the user level. | ;um "module name" enable SomeUsername `;usrmdl`, `;um` | Sets a module's permission at the user level. | `;um "module name" enable SomeUsername`
`;usrcmd`, `;uc` | Sets a command's permission at the user level. | ;uc "command name" enable SomeUsername `;usrcmd`, `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername`
`;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | ;asm [enable/disable] `;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | `;asm [enable/disable]`
`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | ;asc "module name" [enable/disable] `;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | `;asc "module name" [enable/disable]`
`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel `;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | `;acm [enable/disable] SomeChannel`
`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | ;acc "module name" [enable/disable] SomeChannel `;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | `;acc "module name" [enable/disable] SomeChannel`
`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole `;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | `;arm [enable/disable] MyRole`
`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | ;arc "module name" [enable/disable] MyRole `;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | `;arc "module name" [enable/disable] MyRole`
`;ubl` | Blacklists a mentioned user. | ;ubl [user_mention] `;ubl` | Blacklists a mentioned user. | `;ubl [user_mention]`
`;uubl` | Unblacklists a mentioned user. | ;uubl [user_mention] `;uubl` | Unblacklists a mentioned user. | `;uubl [user_mention]`
`;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` `;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. `;allcmdcooldowns`, `;acmdcds` | Shows a list of all commands and their respective cooldowns.
### Conversations ### Conversations
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`..` | Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message `..` | Adds a new quote with the specified name (single word) and message (no limit). | `.. abc My message`
`...` | Shows a random quote with a specified name. | .. abc `...` | Shows a random quote with a specified name. | `... abc`
`..qdel`, `..quotedelete` | Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. | `..qdel abc` `..qdel`, `..quotedelete` | Deletes all quotes with the specified keyword. You have to either be bot owner or the creator of the quote to delete it. | `..qdel abc`
`@BotName rip` | Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000 `@BotName rip` | Shows a grave image of someone with a start year | @NadekoBot rip @Someone 2000
`@BotName die` | Works only for the owner. Shuts the bot down. `@BotName die` | Works only for the owner. Shuts the bot down.
@ -173,11 +174,13 @@ Command and aliases | Description | Usage
`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $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 `$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. `$race` | Starts a new animal race.
`$joinrace`, `$jr` | Joins a new race. You can specify an amount of flowers for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5`
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName
`$$$` | 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!** | `$take 1 "@someguy"`
`$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 `$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` |
@ -196,9 +199,9 @@ Command and aliases | Description | Usage
`>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) `>plant` | Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)
`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60` `>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60`
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello `>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello
`>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more `>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more`
`>8ball` | Ask the 8ball a yes/no question. `>8ball` | Ask the 8ball a yes/no question. | `>8ball should i do something`
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | >rps scissors `>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors`
`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows` `>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows`
### Music ### Music
@ -234,7 +237,7 @@ Command and aliases | Description | Usage
`!!load` | Loads a playlist under a certain name. | `!!load classical-1` `!!load` | Loads a playlist under a certain name. | `!!load classical-1`
`!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1` `!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1`
`!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5` `!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
`!!goto` | Goes to a specific time in seconds in a song. `!!goto` | Goes to a specific time in seconds in a song. | !!goto 30
`!!getlink`, `!!gl` | Shows a link to the currently playing song. `!!getlink`, `!!gl` | Shows a link to the currently playing song.
`!!autoplay`, `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) `!!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)
@ -262,41 +265,42 @@ Command and aliases | Description | Usage
`~pokemonability`, `~pokeab` | Searches for a pokemon ability. `~pokemonability`, `~pokeab` | Searches for a pokemon ability.
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ `~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/
`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` `~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"`
`~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | ~we Moscow RF `~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | `~we Moscow RF`
`~yt` | Searches youtubes and shows the first result `~yt` | Searches youtubes and shows the first result | `~yt query`
`~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result. `~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result.
`~imdb` | Queries imdb for movies or series, show first result. `~imdb` | Queries imdb for movies or series, show first result. | `~imdb query`
`~mang`, `~manga`, `~mq` | Queries anilist for a manga and shows the first result. `~mang`, `~manga`, `~mq` | Queries anilist for a manga and shows the first result. | `~mq query`
`~randomcat`, `~meow` | Shows a random cat image. `~randomcat`, `~meow` | Shows a random cat image.
`~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | ~i cute kitten `~randomdog`, `~woof` | Shows a random dog image.
`~ir` | Pulls a random image using a search parameter. | ~ir cute kitten `~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | `~i cute kitten`
`~lmgtfy` | Google something for an idiot. `~ir` | Pulls a random image using a search parameter. | `~ir cute kitten`
`~google`, `~g` | Get a google search link for some terms. `~lmgtfy` | Google something for an idiot. | `~lmgtfy query`
`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | ~hs Ysera `~google`, `~g` | Get a google search link for some terms. | `~google query`
`~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple `~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
`~#` | Searches Tagdef.com for a hashtag. | ~# ff `~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
`~#` | Searches Tagdef.com for a hashtag. | `~# ff`
`~quote` | Shows a random quote. `~quote` | Shows a random quote.
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> `~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts>
`~yomama`, `~ym` | Shows a random joke from <http://api.yomomma.info/> `~yomama`, `~ym` | Shows a random joke from <http://api.yomomma.info/>
`~randjoke`, `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random> `~randjoke`, `~rj` | Shows a random joke from <http://tambal.azurewebsites.net/joke/random>
`~chucknorris`, `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> `~chucknorris`, `~cn` | Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random>
`~magicitem`, `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> `~magicitem`, `~mi` | Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items>
`~revav` | Returns a google reverse image search for someone's avatar. `~revav` | Returns a google reverse image search for someone's avatar. | `~revav "@SomeGuy"
`~revimg` | Returns a google reverse image search for an image from a link. `~revimg` | Returns a google reverse image search for an image from a link. | `~revav Image link`
`~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~safebooru yuri+kissing `~safebooru` | Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~safebooru yuri+kissing`
`~wiki` | Gives you back a wikipedia link `~wiki` | Gives you back a wikipedia link | `~wiki query`
`~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00` `~clr` | Shows you what color corresponds to that hex. | `~clr 00ff00`
`~videocall` | Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message. `~videocall` | Creates a private <http://www.appear.in> video call link for you and other mentioned people. The link is sent to mentioned people via a private message. | `~videocall "@SomeGuy"`
`~av`, `~avatar` | Shows a mentioned person's avatar. | ~av @X `~av`, `~avatar` | Shows a mentioned person's avatar. | `~av @X`
### NSFW ### NSFW
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`~hentai` | Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~hentai yuri+kissing `~hentai` | Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~hentai yuri+kissing`
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~danbooru yuri+kissing `~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~danbooru yuri+kissing`
`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~gelbooru yuri+kissing `~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~gelbooru yuri+kissing`
`~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~rule34 yuri+kissing `~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | `~rule34 yuri+kissing`
`~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | ~e621 yuri kissing `~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preffered. Use spaces for multiple tags. | `~e621 yuri kissing`
`~cp` | We all know where this will lead you to. `~cp` | We all know where this will lead you to.
`~boobs` | Real adult content. `~boobs` | Real adult content.
`~butts`, `~ass`, `~butt` | Real adult content. `~butts`, `~ass`, `~butt` | Real adult content.
@ -305,13 +309,13 @@ Command and aliases | Description | Usage
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`,createwar`, `,cw` | Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. | ,cw 15 The Enemy Clan `,createwar`, `,cw` | Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. | ,cw 15 The Enemy Clan
`,startwar`, `,sw` | Starts a war with a given number. `,startwar`, `,sw` | Starts a war with a given number. | `,sw 1`
`,listwar`, `,lw` | Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | ,lw [war_number] or ,lw `,listwar`, `,lw` | Shows the active war claims by a number. Shows all wars in a short way if no number is specified. | ,lw [war_number] or ,lw
`,claim`, `,call`, `,c` | Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | ,call [war_number] [base_number] [optional_other_name] `,claim`, `,call`, `,c` | Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. | ,call [war_number] [base_number] [optional_other_name]
`,claimfinish`, `,cf`, `,cf3`, `,claimfinish3` | Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] `,claimfinish`, `,cf`, `,cf3`, `,claimfinish3` | Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
`,claimfinish2`, `,cf2` | Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] `,claimfinish2`, `,cf2` | Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
`,claimfinish1`, `,cf1` | Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name] `,claimfinish1`, `,cf1` | Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
`,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | ,uc [war_number] [optional_other_name] `,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim | ,uc [war_number] [optional_other_name]
`,endwar`, `,ew` | Ends the war with a given index. | ,ew [war_number] `,endwar`, `,ew` | Ends the war with a given index. | ,ew [war_number]
### Pokegame ### Pokegame
@ -326,7 +330,7 @@ Command and aliases | Description | Usage
### Translator ### Translator
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`~translate`, `~trans` | Translates from>to text. From the given language to the destiation language. | ~trans en>fr Hello `~translate`, `~trans` | Translates from>to text. From the given language to the destiation language. | `~trans en>fr Hello`
`~translangs` | List the valid languages for translation. `~translangs` | List the valid languages for translation.
### Customreactions ### Customreactions
@ -352,7 +356,7 @@ Command and aliases | Description | Usage
### Trello ### Trello
Command and aliases | Description | Usage Command and aliases | Description | Usage
----------------|--------------|------- ----------------|--------------|-------
`trello bind` | Bind a trello bot to a single channel. You will receive notifications from your board when something is added or edited. | bind [board_id] `trello bind` | Bind a trello bot to a single channel. You will receive notifications from your board when something is added or edited. | `trello bind [board_id]`
`trello unbind` | Unbinds a bot from the channel and board. `trello unbind` | Unbinds a bot from the channel and board.
`trello lists`, `trello list` | Lists all lists yo ;) `trello lists`, `trello list` | Lists all lists yo ;)
`trello cards` | Lists all cards from the supplied list. You can supply either a name or an index. `trello cards` | Lists all cards from the supplied list. You can supply either a name or an index. | `trello cards index`

@ -1 +1 @@
Subproject commit 3e519b5e0b33175e5a5ca247322b7082de484e15 Subproject commit 3a33731135f1b7dd2efdb51b16158c84bb22da66

1
ffmpeg-installer Submodule

@ -0,0 +1 @@
Subproject commit d593fe3a86be7da9e4177865446f2f5ca58b6be4