Merge pull request #6 from Kwoth/dev

Updating fork dev
This commit is contained in:
miraai 2016-07-28 20:24:51 +02:00 committed by GitHub
commit 07fd374a1c
45 changed files with 1256 additions and 686 deletions

3
.gitmodules vendored
View File

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

View File

@ -35,11 +35,9 @@ ________________________________________________________________________________
#### Setting Up NadekoBot For Music
###### Setting up `ffmpeg` with installer:
1) Google Account
2) Soundcloud Account (if you want soundcloud support)
3) Download installer here: http://luxcaeli.de/owncloud/s/fIxSgh4Nde3Td6e/download
4) Run the installer
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')
4) Run the installer
5) Follow these steps on how to setup API keys:
- Go to https://console.developers.google.com and log in.

View File

@ -222,6 +222,8 @@ Type/ Copy and hit **Enter**.
Now time to **move bot to background** and to do that, press **CTRL+B+D** (this will ditach the nadeko session using TMUX), and you can finally close PuTTY now.
Copy your CLIENT ID (that's in the same Developer page where you brought your token) and replace `12345678` in this link: `https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303` with it. Go to that link and you will be able to add your bot to your server.
**NOW YOU HAVE YOUR OWN NADEKO BOT** `Thanks to Kwoth <3`
######SOME MORE INFO (JUST TO KNOW):

View File

@ -138,7 +138,7 @@ namespace NadekoBot.Extensions
/// </summary>
/// <typeparam name="T"></typeparam>
/// <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
@ -160,6 +160,7 @@ namespace NadekoBot.Extensions
list[k] = list[n];
list[n] = value;
}
return list;
}
/// <summary>
@ -303,6 +304,15 @@ namespace NadekoBot.Extensions
public static int GiB(this int value) => value.MiB() * 1024;
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)
{
if (format == null)
@ -362,5 +372,7 @@ namespace NadekoBot.Extensions
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.Music;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reflection;
@ -41,7 +43,23 @@ namespace NadekoBot
var commandService = NadekoBot.Client.GetService<CommandService>();
statsStopwatch.Start();
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);
@ -97,7 +115,7 @@ namespace NadekoBot
if (e.Channel.IsPrivate)
return;
if (e.Channel.Type == ChannelType.Text)
VoiceChannelsCount++;
TextChannelsCount--;
else if (e.Channel.Type == ChannelType.Voice)
VoiceChannelsCount--;
}
@ -106,6 +124,7 @@ namespace NadekoBot
carbonStatusTimer.Elapsed += async (s, e) => await SendUpdateToCarbon().ConfigureAwait(false);
carbonStatusTimer.Start();
}
HttpClient carbonClient = new HttpClient();
private async Task SendUpdateToCarbon()
{
@ -176,9 +195,11 @@ namespace NadekoBot
private async Task StartCollecting()
{
var statsSw = new Stopwatch();
while (true)
{
await Task.Delay(new TimeSpan(0, 30, 0)).ConfigureAwait(false);
statsSw.Start();
try
{
var onlineUsers = await Task.Run(() => NadekoBot.Client.Servers.Sum(x => x.Users.Count())).ConfigureAwait(false);
@ -195,6 +216,13 @@ namespace NadekoBot
ConnectedServers = connectedServers,
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
{
@ -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)
{
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(() =>
{
try
@ -230,6 +270,7 @@ namespace NadekoBot
Console.WriteLine(ex);
}
}).ConfigureAwait(false);
#endif
}
}
}
}

View File

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

View File

@ -6,6 +6,8 @@ using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Classes
{
@ -27,9 +29,12 @@ namespace NadekoBot.Classes
{
configs = JsonConvert
.DeserializeObject<ConcurrentDictionary<ulong, ServerSpecificConfig>>(
File.ReadAllText(filePath), new JsonSerializerSettings() {
Error = (s,e) => {
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels") {
File.ReadAllText(filePath), new JsonSerializerSettings()
{
Error = (s, e) =>
{
if (e.ErrorContext.Member.ToString() == "GenerateCurrencyChannels")
{
e.ErrorContext.Handled = true;
}
}
@ -52,14 +57,19 @@ namespace NadekoBot.Classes
public ServerSpecificConfig Of(ulong id) =>
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));
}
finally
{
saveLock.Release();
}
}
}
@ -245,7 +255,7 @@ namespace NadekoBot.Classes
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)
{

View File

@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Administration
{
var conf = SpecificConfigurations.Default.Of(e.Server.Id);
conf.AutoDeleteMessagesOnCommand = !conf.AutoDeleteMessagesOnCommand;
Classes.JSONModels.ConfigHandler.SaveConfig();
await Classes.JSONModels.ConfigHandler.SaveConfig().ConfigureAwait(false);
if (conf.AutoDeleteMessagesOnCommand)
await e.Channel.SendMessage("❗`Now automatically deleting successfull command invokations.`");
else
@ -90,7 +90,7 @@ namespace NadekoBot.Modules.Administration
});
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("role_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles)
@ -133,7 +133,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "removerole").Alias(Prefix + "rr")
.Description("Removes a role from a given user. | .rr @User Admin")
.Description($"Removes a role from a given user. | `{Prefix}rr @User Admin`")
.Parameter("user_name", ParameterType.Required)
.Parameter("role_name", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.CanManageRoles)
@ -204,7 +204,7 @@ namespace NadekoBot.Modules.Administration
});
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)
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>
@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Administration
});
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)
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>
@ -253,7 +253,7 @@ namespace NadekoBot.Modules.Administration
.Parameter("r", ParameterType.Optional)
.Parameter("g", ParameterType.Optional)
.Parameter("b", ParameterType.Optional)
.Description("Set a role's color to the hex or 0-255 rgb color value provided. | `.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 =>
{
if (!e.User.ServerPermissions.ManageRoles)
@ -298,7 +298,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "ban").Alias(Prefix + "b")
.Parameter("user", ParameterType.Required)
.Parameter("msg", ParameterType.Unparsed)
.Description("Bans a user by id or name with an optional message. | .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 =>
{
var msg = e.GetArg("msg");
@ -333,7 +333,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "softban").Alias(Prefix + "sb")
.Parameter("user", ParameterType.Required)
.Parameter("msg", ParameterType.Unparsed)
.Description("Bans and then unbans a user by id or name with an optional message. | .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 =>
{
var msg = e.GetArg("msg");
@ -369,7 +369,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "kick").Alias(Prefix + "k")
.Parameter("user")
.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 =>
{
var msg = e.GetArg("msg");
@ -400,7 +400,7 @@ namespace NadekoBot.Modules.Administration
}
});
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)
.Do(async e =>
{
@ -426,7 +426,7 @@ namespace NadekoBot.Modules.Administration
});
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)
.Do(async e =>
{
@ -453,7 +453,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "deafen")
.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)
.Do(async e =>
{
@ -480,7 +480,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "undeafen")
.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)
.Do(async e =>
{
@ -507,7 +507,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "delvoichanl")
.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)
.Do(async e =>
{
@ -530,7 +530,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "creatvoichanl")
.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)
.Do(async e =>
{
@ -550,7 +550,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "deltxtchanl")
.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)
.Do(async e =>
{
@ -572,7 +572,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "creatxtchanl")
.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)
.Do(async e =>
{
@ -604,7 +604,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "setchanlname")
.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())
.Parameter("name", ParameterType.Unparsed)
.Do(async e =>
@ -636,7 +636,8 @@ namespace NadekoBot.Modules.Administration
Message[] msgs;
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())
return;
await e.Channel.DeleteMessages(msgs).ConfigureAwait(false);
@ -684,7 +685,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "setname")
.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)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -696,7 +697,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "newavatar")
.Alias(Prefix + "setavatar")
.Description("Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner Only!** | `.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)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -717,7 +718,7 @@ namespace NadekoBot.Modules.Administration
});
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)
.Do(e =>
{
@ -727,7 +728,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "send")
.Description("Send a message to someone on a different server through the bot. **Bot Owner Only!** | `.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("msg", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly())
@ -775,7 +776,7 @@ namespace NadekoBot.Modules.Administration
cgb.CreateCommand(Prefix + "mentionrole")
.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)
.Do(async e =>
{
@ -828,7 +829,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "donadd")
.Description("Add a donator to the database.")
.Description($"Add a donator to the database. | `.donadd Donate Amount`")
.Parameter("donator")
.Parameter("amount")
.AddCheck(SimpleCheckers.OwnerOnly())
@ -854,7 +855,7 @@ namespace NadekoBot.Modules.Administration
});
cgb.CreateCommand(Prefix + "announce")
.Description($"Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | {Prefix}announce Useless spam")
.Description($"Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | `{Prefix}announce Useless spam`")
.Parameter("msg", ParameterType.Unparsed)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -868,7 +869,7 @@ namespace NadekoBot.Modules.Administration
});
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)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>

View File

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Administration.Commands
NadekoBot.Config.CustomReactions[name].Add(message);
else
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);
});
@ -140,7 +140,7 @@ namespace NadekoBot.Modules.Administration.Commands
index = index - 1;
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);
});
@ -183,7 +183,7 @@ namespace NadekoBot.Modules.Administration.Commands
NadekoBot.Config.CustomReactions.Remove(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);
});
}

View File

@ -7,8 +7,10 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using Timer = System.Timers.Timer;
namespace NadekoBot.Modules.Administration.Commands
{
@ -36,7 +38,7 @@ namespace NadekoBot.Modules.Administration.Commands
{"%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)
{
@ -47,7 +49,9 @@ namespace NadekoBot.Modules.Administration.Commands
{
i++;
var status = "";
lock (playingPlaceholderLock)
//wtf am i doing, just use a queue ffs
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{
if (PlayingPlaceholders.Count == 0
|| NadekoBot.Config.RotatingStatuses.Count == 0
@ -59,6 +63,7 @@ namespace NadekoBot.Modules.Administration.Commands
status = PlayingPlaceholders.Aggregate(status,
(current, kvp) => current.Replace(kvp.Key, kvp.Value()));
}
finally { playingPlaceholderLock.Release(); }
if (string.IsNullOrWhiteSpace(status))
return;
await Task.Run(() => { NadekoBot.Client.SetGame(status); });
@ -71,14 +76,18 @@ namespace NadekoBot.Modules.Administration.Commands
public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
lock (playingPlaceholderLock)
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{
if (timer.Enabled)
timer.Stop();
else
timer.Start();
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);
};
@ -102,10 +111,15 @@ namespace NadekoBot.Modules.Administration.Commands
var arg = e.GetArg("text");
if (string.IsNullOrWhiteSpace(arg))
return;
lock (playingPlaceholderLock)
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try
{
NadekoBot.Config.RotatingStatuses.Add(arg);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig();
}
finally
{
playingPlaceholderLock.Release();
}
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");
int num;
string str;
lock (playingPlaceholderLock)
{
await playingPlaceholderLock.WaitAsync().ConfigureAwait(false);
try {
if (!int.TryParse(arg.Trim(), out num) || num <= 0 || num > NadekoBot.Config.RotatingStatuses.Count)
return;
str = NadekoBot.Config.RotatingStatuses[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);
});
}

View File

@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Administration.Commands
var config = SpecificConfigurations.Default.Of(e.Server.Id);
var msg = new StringBuilder($"There are `{config.ListOfSelfAssignableRoles.Count}` self assignable roles:\n");
var toRemove = new HashSet<ulong>();
foreach (var roleId in config.ListOfSelfAssignableRoles.OrderBy(r=>r.ToString()))
foreach (var roleId in config.ListOfSelfAssignableRoles.OrderBy(r => r.ToString()))
{
var role = e.Server.GetRole(roleId);
if (role == null)
@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Administration.Commands
cgb.CreateCommand(Module.Prefix + "togglexclsar").Alias(Module.Prefix +"tesar")
cgb.CreateCommand(Module.Prefix + "togglexclsar").Alias(Module.Prefix + "tesar")
.Description("toggle whether the self-assigned roles should be exclusive")
.AddCheck(SimpleCheckers.CanManageRoles)
.Do(async e =>
@ -146,12 +146,13 @@ namespace NadekoBot.Modules.Administration.Commands
{
await e.User.AddRoles(role).ConfigureAwait(false);
}
catch(HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)
catch (HttpException ex) when (ex.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
}
catch (Exception)
catch (Exception ex)
{
await e.Channel.SendMessage($":anger:`I am unable to add that role to you. I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false);
return;
}
var msg = await e.Channel.SendMessage($":ok:You now have {role.Name} role.").ConfigureAwait(false);
await Task.Delay(3000).ConfigureAwait(false);

View File

@ -1,46 +0,0 @@
using Discord;
using Discord.Commands;
using NadekoBot.Classes;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration.Commands
{
internal class VoiceNotificationCommand : DiscordCommand
{
//voicechannel/text channel
private readonly ConcurrentDictionary<Channel, Channel> subscribers = new ConcurrentDictionary<Channel, Channel>();
public Func<CommandEventArgs, Task> DoFunc() => async e =>
{
var arg = e.GetArg("voice_name");
if (string.IsNullOrWhiteSpace("voice_name"))
return;
var voiceChannel = e.Server.FindChannels(arg, ChannelType.Voice).FirstOrDefault();
if (voiceChannel == null)
return;
if (subscribers.ContainsKey(voiceChannel))
{
await e.Channel.SendMessage("`Voice channel notifications disabled.`").ConfigureAwait(false);
return;
}
if (subscribers.TryAdd(voiceChannel, e.Channel))
{
await e.Channel.SendMessage("`Voice channel notifications enabled.`").ConfigureAwait(false);
}
};
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "voicenotif")
.Description("Enables notifications on who joined/left the voice channel. |.voicenotif Karaoke club")
.Parameter("voice_name", ParameterType.Unparsed)
.Do(DoFunc());
}
public VoiceNotificationCommand(DiscordModule module) : base(module) { }
}
}

View File

@ -1,9 +1,8 @@
using Discord.Commands;
using Newtonsoft.Json;
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
//using Manatee.Json.Serialization;
namespace NadekoBot.Classes.ClashOfClans
{
@ -11,16 +10,22 @@ namespace NadekoBot.Classes.ClashOfClans
{
One, Two, Three
}
public enum WarState
{
Started, Ended, Created
}
[System.Serializable]
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 Caller() { }
public Caller(string callUser, DateTime timeAdded, bool baseDestroyed)
{
@ -31,7 +36,7 @@ namespace NadekoBot.Classes.ClashOfClans
public void ResetTime()
{
TimeAdded = DateTime.Now;
TimeAdded = DateTime.UtcNow;
}
public void Destroy()
@ -44,97 +49,84 @@ namespace NadekoBot.Classes.ClashOfClans
{
private static TimeSpan callExpire => new TimeSpan(2, 0, 0);
public string EnemyClan { get; }
public int Size { get; }
public string EnemyClan { get; set; }
public int Size { get; set; }
private Caller[] bases { get; }
private CancellationTokenSource[] baseCancelTokens;
private CancellationTokenSource endTokenSource { get; } = new CancellationTokenSource();
public event Action<string> OnUserTimeExpired = delegate { };
public event Action OnWarEnded = delegate { };
public bool Started { get; set; } = false;
public Caller[] Bases { get; set; }
public WarState WarState { get; set; } = WarState.Created;
//public bool Started { get; set; } = false;
public DateTime StartedAt { get; set; }
//public bool Ended { get; private 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.Size = size;
this.bases = new Caller[size];
this.baseCancelTokens = new CancellationTokenSource[size];
this.Bases = new Caller[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()
{
if (endTokenSource.Token.IsCancellationRequested) return;
endTokenSource.Cancel();
OnWarEnded();
//Ended = true;
WarState = WarState.Ended;
}
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");
if (bases[baseNumber] != null)
if (Bases[baseNumber] != null)
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)
throw new ArgumentException($"@{u} You already claimed a base #{i + 1}. You can't claim a new one.");
if (Bases[i]?.BaseDestroyed == false && Bases[i]?.CallUser == u)
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)
throw new InvalidOperationException();
try
if (WarState == WarState.Started)
throw new InvalidOperationException("War already started");
//if (Started)
// throw new InvalidOperationException();
//Started = true;
WarState = WarState.Started;
StartedAt = DateTime.UtcNow;
foreach (var b in Bases.Where(b => b != null))
{
Started = true;
foreach (var b in bases.Where(b => b != null))
{
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();
b.ResetTime();
}
}
internal int Uncall(string user)
{
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;
bases[i] = null;
if (Bases[i]?.CallUser != user) continue;
Bases[i] = null;
return i;
}
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() =>
$"`{EnemyClan}` ({Size} v {Size})";
@ -143,24 +135,24 @@ namespace NadekoBot.Classes.ClashOfClans
var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{EnemyClan}` ({Size} v {Size}) INFO:**");
if (!Started)
if (WarState == WarState.Created)
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*");
}
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
{
var left = Started ? callExpire - (DateTime.Now - bases[i].TimeAdded) : callExpire;
sb.AppendLine($"`{i + 1}.` ✅ `{bases[i].CallUser}` {left.Hours}h {left.Minutes}m {left.Seconds}s left");
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");
}
}
@ -171,11 +163,11 @@ namespace NadekoBot.Classes.ClashOfClans
internal int FinishClaim(string user, int stars = 3)
{
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;
bases[i].BaseDestroyed = true;
bases[i].Stars = stars;
if (Bases[i]?.BaseDestroyed != false || Bases[i]?.CallUser != user) continue;
Bases[i].BaseDestroyed = true;
Bases[i].Stars = stars;
return i;
}
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base.");

View File

@ -1,12 +1,15 @@
using Discord.Commands;
using Discord.Modules;
using NadekoBot.Classes.ClashOfClans;
using NadekoBot.Modules.Permissions.Classes;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NadekoBot.Modules.Permissions.Classes;
namespace NadekoBot.Modules.ClashOfClans
{
@ -14,69 +17,172 @@ namespace NadekoBot.Modules.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();
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)
{
manager.CreateCommands("", cgb =>
{
cgb.AddCheck(PermissionChecker.Instance);
cgb.AddCheck(PermissionChecker.Instance);
cgb.CreateCommand(Prefix + "createwar")
.Alias(Prefix + "cw")
.Description($"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{Prefix}cw 15 The Enemy Clan")
.Parameter("size")
.Parameter("enemy_clan", ParameterType.Unparsed)
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageChannels)
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");
if (string.IsNullOrWhiteSpace(enemyClan))
{
return;
}
int size;
if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0)
{
await e.Channel.SendMessage("💢🔰 Not a Valid war size").ConfigureAwait(false);
return;
}
var cw = new ClashWar(enemyClan, size, e);
//cw.Start();
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);
//war with the index X started.
});
cgb.CreateCommand(Prefix + "createwar")
.Alias(Prefix + "cw")
.Description($"Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. |{Prefix}cw 15 The Enemy Clan")
.Parameter("size")
.Parameter("enemy_clan", ParameterType.Unparsed)
.Do(async e =>
{
if (!e.User.ServerPermissions.ManageChannels)
return;
var enemyClan = e.GetArg("enemy_clan");
if (string.IsNullOrWhiteSpace(enemyClan))
{
return;
}
int size;
if (!int.TryParse(e.GetArg("size"), out size) || size < 10 || size > 50 || size % 5 != 0)
{
await e.Channel.SendMessage("💢🔰 Not a Valid war size").ConfigureAwait(false);
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 cw = new ClashWar(enemyClan, size, e.Server.Id, e.Channel.Id);
//cw.Start();
wars.Add(cw);
await e.Channel.SendMessage($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false);
Save();
//war with the index X started.
});
cgb.CreateCommand(Prefix + "startwar")
.Alias(Prefix + "sw")
@ -93,14 +199,14 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2];
try
{
var startTask = war.Start();
war.Start();
await e.Channel.SendMessage($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false);
await startTask.ConfigureAwait(false);
}
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")
@ -132,6 +238,7 @@ namespace NadekoBot.Modules.ClashOfClans
}
await e.Channel.SendMessage(sb.ToString()).ConfigureAwait(false);
return;
}
//if number is not null, print the war needed
var warsInfo = GetInfo(e);
@ -173,6 +280,7 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNum - 1);
await e.Channel.SendMessage($"🔰**{usr}** claimed a base #{baseNum} for a war against {war.ShortPrint()}").ConfigureAwait(false);
Save();
}
catch (Exception ex)
{
@ -206,7 +314,7 @@ namespace NadekoBot.Modules.ClashOfClans
cgb.CreateCommand(Prefix + "unclaim")
.Alias(Prefix + "uncall")
.Alias(Prefix + "uc")
.Description($"Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | {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("other_name", ParameterType.Unparsed)
.Do(async e =>
@ -226,6 +334,7 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr);
await e.Channel.SendMessage($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false);
Save();
}
catch (Exception ex)
{
@ -246,12 +355,17 @@ namespace NadekoBot.Modules.ClashOfClans
return;
}
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;
warsInfo.Item1.RemoveAt(warsInfo.Item2);
Save();
});
});
}
#endregion
private async Task FinishClaim(CommandEventArgs e, int stars = 3)
{
@ -271,6 +385,7 @@ namespace NadekoBot.Modules.ClashOfClans
{
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);
Save();
}
catch (Exception ex)
{

View File

@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Conversations
cgb.AddCheck(PermissionChecker.Instance);
cgb.CreateCommand("..")
.Description("Adds a new quote with the specified name (single word) and message (no limit). | .. abc My message")
.Description("Adds a new quote with the specified name (single word) and message (no limit). | `.. abc My message`")
.Parameter("keyword", ParameterType.Required)
.Parameter("text", ParameterType.Unparsed)
.Do(async e =>
@ -54,7 +54,7 @@ namespace NadekoBot.Modules.Conversations
});
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)
.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.DataModels;
using NadekoBot.Extensions;
using NadekoBot.Modules.Gambling.Commands;
using NadekoBot.Modules.Permissions.Classes;
using System;
using System.Linq;
@ -18,6 +19,7 @@ namespace NadekoBot.Modules.Gambling
commands.Add(new DrawCommand(this));
commands.Add(new FlipCoinCommand(this));
commands.Add(new DiceRollCommand(this));
commands.Add(new AnimalRacing(this));
}
public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Gambling;
@ -31,7 +33,7 @@ namespace NadekoBot.Modules.Gambling
commands.ForEach(com => com.Init(cgb));
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)
.Do(async e =>
{
@ -61,7 +63,7 @@ namespace NadekoBot.Modules.Gambling
});
cgb.CreateCommand(Prefix + "give")
.Description(string.Format("Give someone a certain amount of {0}s", NadekoBot.Config.CurrencyName))
.Description(string.Format("Give someone a certain amount of {0}s", NadekoBot.Config.CurrencyName)+ $"|`{Prefix}give 1 \"@SomeGuy\"`")
.Parameter("amount", ParameterType.Required)
.Parameter("receiver", ParameterType.Unparsed)
.Do(async e =>
@ -93,7 +95,7 @@ namespace NadekoBot.Modules.Gambling
});
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())
.Parameter("amount", ParameterType.Required)
.Parameter("receiver", ParameterType.Unparsed)
@ -115,7 +117,7 @@ namespace NadekoBot.Modules.Gambling
});
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())
.Parameter("amount", ParameterType.Required)
.Parameter("rektperson", ParameterType.Unparsed)

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games.Commands
@ -59,7 +60,7 @@ namespace NadekoBot.Modules.Games.Commands
//channelid/messageid pair
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)
{
@ -78,38 +79,47 @@ namespace NadekoBot.Modules.Games.Commands
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);
await Task.Delay(10000).ConfigureAwait(false);
await msg.Delete().ConfigureAwait(false);
ThreadPool.QueueUserWorkItem(async (state) =>
{
try
{
await Task.Delay(10000).ConfigureAwait(false);
await msg.Delete().ConfigureAwait(false);
}
catch { }
});
});
cgb.CreateCommand(Module.Prefix + "plant")
.Description("Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost)")
.Do(e =>
.Do(async e =>
{
lock (locker)
await locker.WaitAsync().ConfigureAwait(false);
try
{
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;
}
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)
{
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;
}
var file = GetRandomCurrencyImagePath();
Message msg;
if (file == null)
msg = e.Channel.SendMessage(NadekoBot.Config.CurrencySign).GetAwaiter().GetResult();
msg = await e.Channel.SendMessage(NadekoBot.Config.CurrencySign).ConfigureAwait(false);
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 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 });
}
finally { locker.Release(); }
});
cgb.CreateCommand(Prefix + "gencurrency")
@ -129,12 +139,12 @@ namespace NadekoBot.Modules.Games.Commands
int 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
{
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 Func<CommandEventArgs, Task> DoFunc()
{
throw new NotImplementedException();
}
internal override void Init(CommandGroupBuilder cgb)
{
cgb.CreateCommand(Module.Prefix + "poll")

View File

@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
{
internal class TriviaGame
{
private readonly object _guessLock = new object();
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1,1);
private Server server { get; }
private Channel channel { get; }
@ -113,7 +113,8 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return;
var guess = false;
lock (_guessLock)
await _guessLock.WaitAsync().ConfigureAwait(false);
try
{
if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested)
{
@ -122,6 +123,7 @@ namespace NadekoBot.Modules.Games.Commands.Trivia
guess = true;
}
}
finally { _guessLock.Release(); }
if (!guess) return;
triviaCancelSource.Cancel();
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));
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)
.Do(async e =>
{
@ -50,7 +50,7 @@ namespace NadekoBot.Modules.Games
});
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)
.Do(async e =>
{
@ -60,14 +60,14 @@ namespace NadekoBot.Modules.Games
try
{
await e.Channel.SendMessage(
$":question: **Question**: `{question}` \n🎱 **8Ball Answers**: `{NadekoBot.Config._8BallResponses[rng.Next(0, NadekoBot.Config._8BallResponses.Length)]}`")
$":question: `Question` __**{question}**__ \n🎱 `8Ball Answers` __**{NadekoBot.Config._8BallResponses[rng.Next(0, NadekoBot.Config._8BallResponses.Length)]}**__")
.ConfigureAwait(false);
}
catch { }
});
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)
.Do(async e =>
{

View File

@ -31,7 +31,7 @@ namespace NadekoBot.Classes.Help.Commands
if (alias != null)
str = $" / `{ com.Aliases.FirstOrDefault()}`";
if (com != null)
await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**",1)}").ConfigureAwait(false);
await e.Channel.SendMessage($@"**__Help for:__ `{com.Text}`**" + str + $"\n**Desc:** {new Regex(@"\|").Replace(com.Description, "\n**Usage:**", 1)}").ConfigureAwait(false);
}).ConfigureAwait(false);
};
public static string HelpString {
@ -49,7 +49,8 @@ namespace NadekoBot.Classes.Help.Commands
{
string helpstr =
$@"######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/**
######You can donate on paypal: `nadekodiscordbot@gmail.com`
######You can donate on patreon: `https://patreon.com/nadekobot`
######or paypal: `nadekodiscordbot@gmail.com`
#NadekoBot List Of Commands
Version: `{NadekoStats.Instance.BotVersion}`";

View File

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

View File

@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Music.Classes
public int BufferSize { get; }
private readonly object readWriteLock = new object();
private readonly SemaphoreSlim readWriteLock = new SemaphoreSlim(1, 1);
public PoopyBuffer(int size)
{
@ -32,51 +32,57 @@ namespace NadekoBot.Modules.Music.Classes
ringBuffer = new byte[size];
}
public int Read(byte[] buffer, int count)
public Task<int> ReadAsync(byte[] buffer, int count)
{
if (buffer.Length < count)
throw new ArgumentException();
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
lock (readWriteLock)
return Task.Run(async () =>
{
//read as much as you can if you're reading too much
if (count > ContentLength)
count = ContentLength;
//if nothing to read, return 0
if (WritePosition == ReadPosition)
return 0;
// if buffer is in the "normal" state, just read
if (WritePosition > ReadPosition)
if (buffer.Length < count)
throw new ArgumentException();
//Console.WriteLine($"***\nRead: {ReadPosition}\nWrite: {WritePosition}\nContentLength:{ContentLength}\n***");
await readWriteLock.WaitAsync().ConfigureAwait(false);
try
{
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
//read as much as you can if you're reading too much
if (count > ContentLength)
count = ContentLength;
//if nothing to read, return 0
if (WritePosition == ReadPosition)
return 0;
// if buffer is in the "normal" state, just read
if (WritePosition > ReadPosition)
{
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally1 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
//else ReadPos <Writepos
// buffer is in its inverted state
// A: if i can read as much as possible without hitting the buffer.length, read that
if (count + ReadPosition <= BufferSize)
{
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally2 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
// B: if i can't read as much, read to the end,
var readNormaly = BufferSize - ReadPosition;
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, readNormaly);
//Console.WriteLine($"Read normaly {count}[{ReadPosition} to {ReadPosition + readNormaly}]");
//then read the remaining amount from the start
var readFromStart = count - readNormaly;
Buffer.BlockCopy(ringBuffer, 0, buffer, readNormaly, readFromStart);
//Console.WriteLine($"Read From start {readFromStart}[{0} to {readFromStart}]");
ReadPosition = readFromStart;
return count;
}
//else ReadPos <Writepos
// buffer is in its inverted state
// A: if i can read as much as possible without hitting the buffer.length, read that
finally { readWriteLock.Release(); }
});
if (count + ReadPosition <= BufferSize)
{
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, count);
ReadPosition += count;
//Console.WriteLine($"Read only normally2 {count}[{ReadPosition - count} to {ReadPosition}]");
return count;
}
// B: if i can't read as much, read to the end,
var readNormaly = BufferSize - ReadPosition;
Buffer.BlockCopy(ringBuffer, ReadPosition, buffer, 0, readNormaly);
//Console.WriteLine($"Read normaly {count}[{ReadPosition} to {ReadPosition + readNormaly}]");
//then read the remaining amount from the start
var readFromStart = count - readNormaly;
Buffer.BlockCopy(ringBuffer, 0, buffer, readNormaly, readFromStart);
//Console.WriteLine($"Read From start {readFromStart}[{0} to {readFromStart}]");
ReadPosition = readFromStart;
return count;
}
}
public async Task WriteAsync(byte[] buffer, int count, CancellationToken cancelToken)
@ -89,32 +95,37 @@ namespace NadekoBot.Modules.Music.Classes
if (cancelToken.IsCancellationRequested)
return;
}
//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
lock (readWriteLock)
await Task.Run(async () =>
{
// if i can just write without hitting buffer.length, do it
if (WritePosition + count < BufferSize)
//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
await readWriteLock.WaitAsync().ConfigureAwait(false);
try
{
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
WritePosition += count;
//Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
return;
// if i can just write without hitting buffer.length, do it
if (WritePosition + count < BufferSize)
{
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, count);
WritePosition += count;
//Console.WriteLine($"Wrote only normally {count}[{WritePosition - count} to {WritePosition}]");
return;
}
// otherwise, i have to write to the end, then write the rest from the start
var wroteNormaly = BufferSize - WritePosition;
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
//Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
var wroteFromStart = count - wroteNormaly;
Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
//Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
WritePosition = wroteFromStart;
}
// otherwise, i have to write to the end, then write the rest from the start
var wroteNormaly = BufferSize - WritePosition;
Buffer.BlockCopy(buffer, 0, ringBuffer, WritePosition, wroteNormaly);
//Console.WriteLine($"Wrote normally {wroteNormaly}[{WritePosition} to {BufferSize}]");
var wroteFromStart = count - wroteNormaly;
Buffer.BlockCopy(buffer, wroteNormaly, ringBuffer, 0, wroteFromStart);
//Console.WriteLine($"and from start {wroteFromStart} [0 to {wroteFromStart}");
WritePosition = wroteFromStart;
}
finally { readWriteLock.Release(); }
});
}
}
}

View File

@ -3,6 +3,7 @@ using NadekoBot.Classes;
using NadekoBot.Extensions;
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
@ -31,9 +32,7 @@ namespace NadekoBot.Modules.Music.Classes
public SongInfo SongInfo { get; }
public string QueuerName { get; set; }
private PoopyBuffer songBuffer { get; set; }
private bool prebufferingComplete { get; set; } = false;
private bool bufferingCompleted { get; set; } = false;
public MusicPlayer MusicPlayer { get; set; }
public string PrettyCurrentTime()
@ -74,7 +73,7 @@ namespace NadekoBot.Modules.Music.Classes
return this;
}
private Task BufferSong(CancellationToken cancelToken) =>
private Task BufferSong(string filename, CancellationToken cancelToken) =>
Task.Factory.StartNew(async () =>
{
Process p = null;
@ -89,40 +88,38 @@ namespace NadekoBot.Modules.Music.Classes
RedirectStandardError = false,
CreateNoWindow = true,
});
const int blockSize = 3840;
var buffer = new byte[blockSize];
var attempt = 0;
while (!cancelToken.IsCancellationRequested)
var prebufferSize = 100ul.MiB();
using (var outStream = new FileStream(filename, FileMode.Append, FileAccess.Write, FileShare.Read))
{
var read = 0;
try
byte[] buffer = new byte[81920];
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)
.ConfigureAwait(false);
await outStream.WriteAsync(buffer, 0, bytesRead, cancelToken).ConfigureAwait(false);
while ((ulong)outStream.Length - bytesSent > prebufferSize)
await Task.Delay(100, cancelToken);
}
catch
{
return;
}
if (read == 0)
if (attempt++ == 50)
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;
}
bufferingCompleted = 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)
{
Console.WriteLine($"Buffering errored: {ex.Message}");
Console.WriteLine($"Buffering stopped: {ex.Message}");
}
finally
{
Console.WriteLine($"Buffering done." + $" [{songBuffer.ContentLength}]");
Console.WriteLine($"Buffering done.");
if (p != null)
{
try
@ -137,53 +134,82 @@ namespace NadekoBot.Modules.Music.Classes
internal async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{
// initialize the buffer here because if this song was playing before (requeued), we must delete old buffer data
songBuffer = new PoopyBuffer(NadekoBot.Config.BufferSize);
var filename = Path.Combine(MusicModule.MusicDataPath, DateTime.Now.UnixTimestamp().ToString());
var bufferTask = BufferSong(cancelToken).ConfigureAwait(false);
var bufferAttempts = 0;
const int waitPerAttempt = 500;
var toAttemptTimes = SongInfo.ProviderType != MusicType.Normal ? 5 : 9;
while (!prebufferingComplete && bufferAttempts++ < toAttemptTimes)
var bufferTask = BufferSong(filename, cancelToken).ConfigureAwait(false);
var inStream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Read, FileShare.Write);
bytesSent = 0;
try
{
await Task.Delay(waitPerAttempt, cancelToken).ConfigureAwait(false);
}
Console.WriteLine($"Prebuffering done? in {waitPerAttempt * bufferAttempts}");
const int blockSize = 3840;
var attempt = 0;
while (!cancelToken.IsCancellationRequested)
{
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
byte[] buffer = new byte[blockSize];
var read = songBuffer.Read(buffer, blockSize);
unchecked
var prebufferingTask = CheckPrebufferingAsync(inStream, cancelToken);
var sw = new Stopwatch();
sw.Start();
var t = await Task.WhenAny(prebufferingTask, Task.Delay(5000, cancelToken));
if (t != prebufferingTask)
{
bytesSent += (ulong)read;
Console.WriteLine("Prebuffering timed out or canceled. Cannot get any data from the stream.");
return;
}
if (read == 0)
if (attempt++ == 20)
{
voiceClient.Wait();
Console.WriteLine($"Song finished. [{songBuffer.ContentLength}]");
break;
}
else
await Task.Delay(100, cancelToken).ConfigureAwait(false);
else
attempt = 0;
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);
while (this.MusicPlayer.Paused)
await Task.Delay(200, cancelToken).ConfigureAwait(false);
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
voiceClient.Send(buffer, 0, read);
const int blockSize = 3840;
var attempt = 0;
byte[] buffer = new byte[blockSize];
while (!cancelToken.IsCancellationRequested)
{
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
var read = inStream.Read(buffer, 0, buffer.Length);
//await inStream.CopyToAsync(voiceClient.OutputStream);
unchecked
{
bytesSent += (ulong)read;
}
if (read == 0)
if (attempt++ == 20)
{
voiceClient.Wait();
break;
}
else
await Task.Delay(100, cancelToken).ConfigureAwait(false);
else
attempt = 0;
while (this.MusicPlayer.Paused)
await Task.Delay(200, cancelToken).ConfigureAwait(false);
buffer = AdjustVolume(buffer, MusicPlayer.Volume);
voiceClient.Send(buffer, 0, read);
}
}
finally
{
await bufferTask;
await Task.Run(() => voiceClient.Clear());
inStream.Dispose();
try { File.Delete(filename); } catch { }
}
Console.WriteLine("Awiting buffer task");
await bufferTask;
Console.WriteLine("Buffer task done.");
voiceClient.Clear();
cancelToken.ThrowIfCancellationRequested();
}
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
private static byte[] AdjustVolume(byte[] audioSamples, float volume)
{
@ -210,6 +236,33 @@ namespace NadekoBot.Modules.Music.Classes
}
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)
{

View File

@ -18,11 +18,16 @@ namespace NadekoBot.Modules.Music
{
internal class MusicModule : DiscordModule
{
public static ConcurrentDictionary<Server, MusicPlayer> MusicPlayers = new ConcurrentDictionary<Server, MusicPlayer>();
public const string MusicDataPath = "data/musicdata";
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;
@ -713,7 +718,7 @@ namespace NadekoBot.Modules.Music
});
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")
.Do(async e =>
{
@ -856,7 +861,7 @@ namespace NadekoBot.Modules.Music
}
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
Task.Run(async () =>
{

View File

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

View File

@ -150,21 +150,21 @@ namespace NadekoBot.Modules.Permissions.Classes
return PermissionBanType.None;
}
private static void WriteServerToJson(ServerPermissions serverPerms)
private static Task WriteServerToJson(ServerPermissions serverPerms) => Task.Run(() =>
{
string pathToFile = $"data/permissions/{serverPerms.Id}.json";
File.WriteAllText(pathToFile,
Newtonsoft.Json.JsonConvert.SerializeObject(serverPerms, Newtonsoft.Json.Formatting.Indented));
}
});
public static void WriteToJson()
public static Task WriteToJson() => Task.Run(() =>
{
Directory.CreateDirectory("data/permissions/");
foreach (var kvp in PermissionsDict)
{
WriteServerToJson(kvp.Value);
}
}
});
public static string GetServerPermissionsRoleName(Server server)
{
@ -174,25 +174,25 @@ namespace NadekoBot.Modules.Permissions.Classes
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,
new ServerPermissions(server.Id, server.Name));
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,
new ServerPermissions(server.Id, server.Name));
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -207,10 +207,10 @@ namespace NadekoBot.Modules.Permissions.Classes
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -225,10 +225,10 @@ namespace NadekoBot.Modules.Permissions.Classes
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -243,10 +243,10 @@ namespace NadekoBot.Modules.Permissions.Classes
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,
new ServerPermissions(server.Id, server.Name));
@ -256,10 +256,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value;
else
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,
new ServerPermissions(server.Id, server.Name));
@ -269,10 +269,10 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value;
else
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;
@ -288,10 +288,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value;
else
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -306,10 +306,10 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value;
else
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -324,10 +324,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value;
else
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -342,10 +342,10 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value;
else
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -360,10 +360,10 @@ namespace NadekoBot.Modules.Permissions.Classes
modules[moduleName] = value;
else
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 serverPerms = PermissionsDict.GetOrAdd(server.Id,
@ -377,19 +377,19 @@ namespace NadekoBot.Modules.Permissions.Classes
commands[commandName] = value;
else
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,
new ServerPermissions(server.Id, server.Name));
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 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[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,
new ServerPermissions(server.Id, server.Name));
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 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[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,
new ServerPermissions(server.Id, server.Name));
@ -436,26 +436,26 @@ namespace NadekoBot.Modules.Permissions.Classes
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,
new ServerPermissions(server.Id, server.Name));
if (serverPerms.Words.Contains(word))
throw new InvalidOperationException("That word is already banned.");
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,
new ServerPermissions(server.Id, server.Name));
if (!serverPerms.Words.Contains(word))
throw new InvalidOperationException("That word is not banned.");
serverPerms.Words.Remove(word);
Task.Run(() => WriteServerToJson(serverPerms));
await WriteServerToJson(serverPerms).ConfigureAwait(false);
}
}
/// <summary>

View File

@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var chan = string.IsNullOrWhiteSpace(chanStr)
? e.Channel
: 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.")
.ConfigureAwait(false);
return;
@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Permissions.Commands
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.")
.ConfigureAwait(false);
@ -102,7 +102,7 @@ namespace NadekoBot.Modules.Permissions.Commands
try
{
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.")
.ConfigureAwait(false);

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var chan = string.IsNullOrWhiteSpace(chanStr)
? e.Channel
: 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);
return;
}
@ -76,7 +76,7 @@ namespace NadekoBot.Modules.Permissions.Commands
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);
}
@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var word = e.GetArg("word");
if (string.IsNullOrWhiteSpace(word))
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);
}
@ -120,7 +120,7 @@ namespace NadekoBot.Modules.Permissions.Commands
var word = e.GetArg("word");
if (string.IsNullOrWhiteSpace(word))
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);
}
@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Permissions.Commands
try
{
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.")
.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);
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);
});
@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Permissions
var args = arg.Split('~').Select(a => a.Trim()).ToArray();
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;
}
try
@ -79,12 +79,12 @@ namespace NadekoBot.Modules.Permissions
var fromRole = PermissionHelper.ValidateRole(e.Server, args[0]);
var toRole = PermissionHelper.ValidateRole(e.Server, args[1]);
PermissionsHandler.CopyRolePermissions(fromRole, toRole);
await e.Channel.SendMessage($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**.");
await PermissionsHandler.CopyRolePermissions(fromRole, toRole).ConfigureAwait(false);
await e.Channel.SendMessage($"Copied permission settings from **{fromRole.Name}** to **{toRole.Name}**.").ConfigureAwait(false);
}
catch (Exception ex)
{
await e.Channel.SendMessage($"💢{ex.Message}");
await e.Channel.SendMessage($"💢{ex.Message}").ConfigureAwait(false);
}
});
cgb.CreateCommand(Prefix + "chnlpermscopy")
@ -107,8 +107,8 @@ namespace NadekoBot.Modules.Permissions
var fromChannel = PermissionHelper.ValidateChannel(e.Server, args[0]);
var toChannel = PermissionHelper.ValidateChannel(e.Server, args[1]);
PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel);
await e.Channel.SendMessage($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**.");
await PermissionsHandler.CopyChannelPermissions(fromChannel, toChannel).ConfigureAwait(false);
await e.Channel.SendMessage($"Copied permission settings from **{fromChannel.Name}** to **{toChannel.Name}**.").ConfigureAwait(false);
}
catch (Exception ex)
{
@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Permissions
var args = arg.Split('~').Select(a => a.Trim()).ToArray();
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;
}
try
@ -135,8 +135,8 @@ namespace NadekoBot.Modules.Permissions
var fromUser = PermissionHelper.ValidateUser(e.Server, args[0]);
var toUser = PermissionHelper.ValidateUser(e.Server, args[1]);
PermissionsHandler.CopyUserPermissions(fromUser, toUser);
await e.Channel.SendMessage($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**.");
await PermissionsHandler.CopyUserPermissions(fromUser, toUser).ConfigureAwait(false);
await e.Channel.SendMessage($"Copied permission settings from **{fromUser.ToString()}**to * *{toUser.ToString()}**.").ConfigureAwait(false);
}
catch (Exception ex)
{
@ -146,13 +146,13 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "verbose")
.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)
.Do(async e =>
{
var arg = e.GetArg("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);
});
@ -169,7 +169,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "roleperms")
.Alias(Prefix + "rp")
.Description("Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole")
.Description($"Shows banned permissions for a certain role. No argument means for everyone. | `{Prefix}rp AwesomeRole`")
.Parameter("role", ParameterType.Unparsed)
.Do(async e =>
{
@ -195,7 +195,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "chnlperms")
.Alias(Prefix + "cp")
.Description("Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev")
.Description($"Shows banned permissions for a certain channel. No argument means for this channel. | `{Prefix}cp #dev`")
.Parameter("channel", ParameterType.Unparsed)
.Do(async e =>
{
@ -220,7 +220,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "userperms")
.Alias(Prefix + "up")
.Description("Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth")
.Description($"Shows banned permissions for a certain user. No argument means for yourself. | `{Prefix}up Kwoth`")
.Parameter("user", ParameterType.Unparsed)
.Do(async e =>
{
@ -246,7 +246,7 @@ namespace NadekoBot.Modules.Permissions
.Alias(Prefix + "sm")
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Description("Sets a module's permission at the server level. | ;sm \"module name\" enable")
.Description($"Sets a module's permission at the server level. | `{Prefix}sm \"module name\" enable`")
.Do(async e =>
{
try
@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Permissions
var module = PermissionHelper.ValidateModule(e.GetArg("module"));
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);
}
catch (ArgumentException exArg)
@ -270,7 +270,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "srvrcmd").Alias(Prefix + "sc")
.Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Description("Sets a command's permission at the server level. | ;sc \"command name\" disable")
.Description($"Sets a command's permission at the server level. | `{Prefix}sc \"command name\" disable`")
.Do(async e =>
{
try
@ -278,7 +278,7 @@ namespace NadekoBot.Modules.Permissions
var command = PermissionHelper.ValidateCommand(e.GetArg("command"));
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);
}
catch (ArgumentException exArg)
@ -295,7 +295,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed)
.Description("Sets a module's permission at the role level. | ;rm \"module name\" enable MyRole")
.Description($"Sets a module's permission at the role level. | `{Prefix}rm \"module name\" enable MyRole`")
.Do(async e =>
{
try
@ -307,7 +307,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
@ -315,7 +315,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
}
@ -333,7 +333,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed)
.Description("Sets a command's permission at the role level. | ;rc \"command name\" disable MyRole")
.Description($"Sets a command's permission at the role level. | `{Prefix}rc \"command name\" disable MyRole`")
.Do(async e =>
{
try
@ -345,7 +345,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
@ -353,7 +353,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
}
@ -371,7 +371,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed)
.Description("Sets a module's permission at the channel level. | ;cm \"module name\" enable SomeChannel")
.Description($"Sets a module's permission at the channel level. | `{Prefix}cm \"module name\" enable SomeChannel`")
.Do(async e =>
{
try
@ -383,20 +383,20 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
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);
}
else
{
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);
}
}
@ -414,7 +414,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed)
.Description("Sets a command's permission at the channel level. | ;cc \"command name\" enable SomeChannel")
.Description($"Sets a command's permission at the channel level. | `{Prefix}cc \"command name\" enable SomeChannel`")
.Do(async e =>
{
try
@ -426,7 +426,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
@ -434,7 +434,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
}
}
@ -452,7 +452,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("user", ParameterType.Unparsed)
.Description("Sets a module's permission at the user level. | ;um \"module name\" enable SomeUsername")
.Description($"Sets a module's permission at the user level. | `{Prefix}um \"module name\" enable SomeUsername`")
.Do(async e =>
{
try
@ -461,7 +461,7 @@ namespace NadekoBot.Modules.Permissions
var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
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);
}
catch (ArgumentException exArg)
@ -478,7 +478,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("command", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("user", ParameterType.Unparsed)
.Description("Sets a command's permission at the user level. | ;uc \"command name\" enable SomeUsername")
.Description($"Sets a command's permission at the user level. | `{Prefix}uc \"command name\" enable SomeUsername`")
.Do(async e =>
{
try
@ -487,7 +487,7 @@ namespace NadekoBot.Modules.Permissions
var state = PermissionHelper.ValidateBool(e.GetArg("bool"));
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);
}
catch (ArgumentException exArg)
@ -502,7 +502,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allsrvrmdls").Alias(Prefix + "asm")
.Parameter("bool", ParameterType.Required)
.Description("Sets permissions for all modules at the server level. | ;asm [enable/disable]")
.Description($"Sets permissions for all modules at the server level. | `{Prefix}asm [enable/disable]`")
.Do(async e =>
{
try
@ -511,7 +511,7 @@ namespace NadekoBot.Modules.Permissions
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);
}
@ -528,7 +528,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allsrvrcmds").Alias(Prefix + "asc")
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Description("Sets permissions for all commands from a certain module at the server level. | ;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 =>
{
try
@ -538,7 +538,7 @@ namespace NadekoBot.Modules.Permissions
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);
}
@ -555,7 +555,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allchnlmdls").Alias(Prefix + "acm")
.Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed)
.Description("Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel")
.Description($"Sets permissions for all modules at the channel level. | `{Prefix}acm [enable/disable] SomeChannel`")
.Do(async e =>
{
try
@ -565,7 +565,7 @@ namespace NadekoBot.Modules.Permissions
var channel = string.IsNullOrWhiteSpace(chArg) ? e.Channel : PermissionHelper.ValidateChannel(e.Server, chArg);
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);
@ -584,7 +584,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("channel", ParameterType.Unparsed)
.Description("Sets permissions for all commands from a certain module at the channel level. | ;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 =>
{
try
@ -594,7 +594,7 @@ namespace NadekoBot.Modules.Permissions
var channel = PermissionHelper.ValidateChannel(e.Server, e.GetArg("channel"));
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);
}
@ -611,7 +611,7 @@ namespace NadekoBot.Modules.Permissions
cgb.CreateCommand(Prefix + "allrolemdls").Alias(Prefix + "arm")
.Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed)
.Description("Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole")
.Description($"Sets permissions for all modules at the role level. | `{Prefix}arm [enable/disable] MyRole`")
.Do(async e =>
{
try
@ -620,7 +620,7 @@ namespace NadekoBot.Modules.Permissions
var role = PermissionHelper.ValidateRole(e.Server, e.GetArg("role"));
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);
@ -639,7 +639,7 @@ namespace NadekoBot.Modules.Permissions
.Parameter("module", ParameterType.Required)
.Parameter("bool", ParameterType.Required)
.Parameter("role", ParameterType.Unparsed)
.Description("Sets permissions for all commands from a certain module at the role level. | ;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 =>
{
try
@ -652,7 +652,7 @@ namespace NadekoBot.Modules.Permissions
{
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);
@ -663,7 +663,7 @@ namespace NadekoBot.Modules.Permissions
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);
}
@ -679,7 +679,7 @@ namespace NadekoBot.Modules.Permissions
});
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)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -689,13 +689,13 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedUsers.Any()) return;
var usr = e.Message.MentionedUsers.First();
NadekoBot.Config.UserBlacklist.Add(usr.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted user {usr.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false);
});
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)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -707,7 +707,7 @@ namespace NadekoBot.Modules.Permissions
if (NadekoBot.Config.UserBlacklist.Contains(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);
}
else
@ -718,7 +718,7 @@ namespace NadekoBot.Modules.Permissions
});
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)
.Do(async e =>
{
@ -727,13 +727,13 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedChannels.Any()) return;
var ch = e.Message.MentionedChannels.First();
NadekoBot.Config.UserBlacklist.Add(ch.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false);
});
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)
.Do(async e =>
{
@ -742,13 +742,13 @@ namespace NadekoBot.Modules.Permissions
if (!e.Message.MentionedChannels.Any()) return;
var ch = e.Message.MentionedChannels.First();
NadekoBot.Config.UserBlacklist.Remove(ch.Id);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
await e.Channel.SendMessage($"`Sucessfully blacklisted channel {ch.Name}`").ConfigureAwait(false);
}).ConfigureAwait(false);
});
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)
.AddCheck(SimpleCheckers.OwnerOnly())
.Do(async e =>
@ -767,7 +767,7 @@ namespace NadekoBot.Modules.Permissions
}
var serverId = server.Id;
NadekoBot.Config.ServerBlacklist.Add(serverId);
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
//cleanup trivias and typeracing
Modules.Games.Commands.Trivia.TriviaGame 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)");
PermissionsHandler.SetCommandCooldown(e.Server, command, secs);
await PermissionsHandler.SetCommandCooldown(e.Server, command, secs).ConfigureAwait(false);
if(secs == 0)
await e.Channel.SendMessage($"Command **{command}** has no coooldown now.").ConfigureAwait(false);
else

View File

@ -30,8 +30,6 @@ namespace NadekoBot.Modules.Searches.Commands
}
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();
public LoLCommands(DiscordModule module) : base(module)
@ -42,7 +40,6 @@ namespace NadekoBot.Modules.Searches.Commands
{
try
{
lock (cacheLock)
CachedChampionImages = CachedChampionImages
.Where(kvp => DateTime.Now - kvp.Value.AddedAt > new TimeSpan(1, 0, 0))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value);
@ -87,16 +84,14 @@ namespace NadekoBot.Modules.Searches.Commands
var resolvedRole = role;
var name = e.GetArg("champ").Replace(" ", "").ToLower();
CachedChampion champ = null;
lock (cacheLock)
{
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
}
if (champ != null)
{
champ.ImageStream.Position = 0;
await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
return;
}
if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
if (champ != null)
{
champ.ImageStream.Position = 0;
await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
return;
}
var allData = JArray.Parse(await Classes.SearchHelper.GetResponseStringAsync($"http://api.champion.gg/champion/{name}?api_key={NadekoBot.Creds.LOLAPIKey}").ConfigureAwait(false));
JToken data = null;
if (role != null)
@ -121,17 +116,13 @@ namespace NadekoBot.Modules.Searches.Commands
role = allData[0]["role"].ToString();
resolvedRole = ResolvePos(role);
}
lock (cacheLock)
{
CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ);
}
if (champ != null)
{
Console.WriteLine("Sending lol image from cache.");
champ.ImageStream.Position = 0;
await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
return;
}
if(CachedChampionImages.TryGetValue(name + "_" + resolvedRole, out champ))
if (champ != null)
{
champ.ImageStream.Position = 0;
await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false);
return;
}
//name = data["title"].ToString();
// get all possible roles, and "select" the shown one
var roles = new string[allData.Count];

View File

@ -68,7 +68,7 @@ namespace NadekoBot.Modules.Searches.Commands
}
}
catch { }
ConfigHandler.SaveConfig();
await ConfigHandler.SaveConfig().ConfigureAwait(false);
};
checkTimer.Start();
}
@ -254,7 +254,7 @@ namespace NadekoBot.Modules.Searches.Commands
}
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);
});

View File

@ -48,7 +48,7 @@ namespace NadekoBot.Modules.Searches
commands.ForEach(cmd => cmd.Init(cgb));
cgb.CreateCommand(Prefix + "we")
.Description($"Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | {Prefix}we Moscow RF")
.Description($"Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | `{Prefix}we Moscow RF`")
.Parameter("city", ParameterType.Required)
.Parameter("country", ParameterType.Required)
.Do(async e =>
@ -69,7 +69,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "yt")
.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 =>
{
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -106,7 +106,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "imdb")
.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 =>
{
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -130,7 +130,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "mang")
.Alias(Prefix + "manga").Alias(Prefix + "mq")
.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 =>
{
if (!(await SearchHelper.ValidateQuery(e.Channel, e.GetArg("query")).ConfigureAwait(false))) return;
@ -157,8 +157,16 @@ $@"🌍 **Weather for** 【{obj["target"]}】
.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")
.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)
.Do(async e =>
{
@ -184,7 +192,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -211,7 +219,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
cgb.CreateCommand(Prefix + "lmgtfy")
.Description("Google something for an idiot.")
.Description($"Google something for an idiot. | `{Prefix}lmgtfy query`")
.Parameter("ffs", ParameterType.Unparsed)
.Do(async e =>
{
@ -222,7 +230,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "google")
.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)
.Do(async e =>
{
@ -234,7 +242,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -275,7 +283,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -304,7 +312,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
// thanks to Blaubeerwald
cgb.CreateCommand(Prefix + "#")
.Description("Searches Tagdef.com for a hashtag. |~# ff")
.Description($"Searches Tagdef.com for a hashtag. | `{Prefix}# ff`")
.Parameter("query", ParameterType.Unparsed)
.Do(async e =>
{
@ -389,7 +397,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -406,7 +414,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -418,7 +426,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
cgb.CreateCommand(Prefix + "safebooru")
.Description("Shows a random image from safebooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~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)
.Do(async e =>
{
@ -431,7 +439,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -445,7 +453,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
});
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)
.Do(async e =>
{
@ -470,7 +478,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
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)
.Do(async e =>
{
@ -494,7 +502,7 @@ $@"🌍 **Weather for** 【{obj["target"]}】
cgb.CreateCommand(Prefix + "av")
.Alias(Prefix + "avatar")
.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 =>
{
var usr = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault();

View File

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

View File

@ -71,7 +71,7 @@ namespace NadekoBot.Modules.Trello
cgb.CreateCommand(Prefix + "bind")
.Description("Bind a trello bot to a single channel. " +
"You will receive notifications from your board when something is added or edited." +
" | bind [board_id]")
$" | `{Prefix}bind [board_id]`")
.Parameter("board_id", Discord.Commands.ParameterType.Required)
.Do(async e =>
{
@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Trello
});
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)
.Do(async e =>
{

View File

@ -31,7 +31,7 @@ namespace NadekoBot.Modules.Utility
commands.ForEach(cmd => cmd.Init(cgb));
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)
.Do(async e =>
{
@ -52,16 +52,15 @@ namespace NadekoBot.Modules.Utility
});
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)
.Do(async e =>
{
await Task.Run(async () =>
{
if (!e.User.ServerPermissions.MentionEveryone) return;
var arg = e.GetArg("roles").Split(',').Select(r => r.Trim());
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();
if (role == null) continue;
@ -71,6 +70,11 @@ namespace NadekoBot.Modules.Utility
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);
await
e.Channel.Send(curstr.Substring(0,
@ -110,7 +114,7 @@ namespace NadekoBot.Modules.Utility
});
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)
.Do(async e =>
{
@ -122,11 +126,11 @@ namespace NadekoBot.Modules.Utility
});
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));
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));
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);
});
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;
}
#if NADEKO_RELEASE
await Task.Delay(90000).ConfigureAwait(false);
await Task.Delay(100000).ConfigureAwait(false);
#else
await Task.Delay(1000).ConfigureAwait(false);
#endif

View File

@ -46,6 +46,7 @@
<Prefer32Bit>true</Prefer32Bit>
<NoWarn>
</NoWarn>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
@ -55,6 +56,7 @@
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'PRIVATE|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
@ -78,13 +80,14 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
<Optimize>false</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
@ -115,6 +118,7 @@
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="libvideo, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
@ -186,6 +190,7 @@
<Compile Include="Classes\FlowersHandler.cs" />
<Compile Include="Modules\Conversations\Commands\RipCommand.cs" />
<Compile Include="Modules\CustomReactions\CustomReactions.cs" />
<Compile Include="Modules\Gambling\Commands\AnimalRacing.cs" />
<Compile Include="Modules\Music\Classes\PlaylistFullException.cs" />
<Compile Include="Modules\Programming\Commands\HaskellRepl.cs" />
<Compile Include="Modules\Programming\ProgrammingModule.cs" />
@ -197,6 +202,7 @@
<Compile Include="Modules\Searches\Commands\OsuCommands.cs" />
<Compile Include="Modules\Searches\Commands\PokemonSearchCommands.cs" />
<Compile Include="Modules\Utility\UtilityModule.cs" />
<Compile Include="_Models\DataModels\TestDataModel.cs" />
<Compile Include="_Models\DataModels\Incident.cs" />
<Compile Include="_Models\JSONModels\AnimeResult.cs" />
<Compile Include="_Models\JSONModels\Configuration.cs" />
@ -259,7 +265,6 @@
<Compile Include="Modules\Gambling\DrawCommand.cs" />
<Compile Include="Modules\Gambling\FlipCoinCommand.cs" />
<Compile Include="Modules\Help\Commands\HelpCommand.cs" />
<Compile Include="Modules\Administration\Commands\VoiceNotificationCommand.cs" />
<Compile Include="Modules\Administration\Commands\VoicePlusTextCommand.cs" />
<Compile Include="Modules\Administration\AdministrationModule.cs" />
<Compile Include="Modules\Conversations\Conversations.cs" />

View File

@ -0,0 +1,8 @@
namespace NadekoBot.DataModels
{
internal class TestDataModel : IDataModel
{
public long TestNumber { get; set; }
public string TestString { get; set; }
}
}

View File

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

View File

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

View File

@ -1,8 +1,9 @@
######For more information and how to setup your own NadekoBot, go to: **http://github.com/Kwoth/NadekoBot/**
######You can donate on paypal: `nadekodiscordbot@gmail.com`
######You can donate on patreon: `https://patreon.com/nadekobot`
######or paypal: `nadekodiscordbot@gmail.com`
#NadekoBot List Of Commands
Version: `NadekoBot v0.9.6048.2992`
Version: `NadekoBot v0.9.6053.28429`
### Help
Command and aliases | Description | Usage
----------------|--------------|-------
@ -57,38 +58,38 @@ Command and aliases | Description | Usage
`.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.
`.restart` | Restarts the bot. Might not work. **Bot Owner Only**
`.setrole`, `.sr` | Sets a role for a given user. | .sr @User Guest
`.removerole`, `.rr` | Removes a role from a given user. | .rr @User Admin
`.setrole`, `.sr` | Sets a role for a given user. | `.sr @User Guest`
`.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`
`.removeallroles`, `.rar` | Removes all roles from a mentioned user. | .rar @User
`.createrole`, `.cr` | Creates a role with a given name. | `.r 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`
`.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.
`.kick`, `.k` | Kicks a mentioned user.
`.mute` | Mutes mentioned user or users.
`.unmute` | Unmutes mentioned user or users.
`.deafen`, `.deaf` | Deafens mentioned user or users
`.undeafen`, `.undef` | Undeafens mentioned user or users
`.delvoichanl`, `.dvch` | Deletes a voice channel with a given name.
`.creatvoichanl`, `.cvch` | Creates a new voice channel with a given name.
`.deltxtchanl`, `.dtch` | Deletes a text channel with a given name.
`.creatxtchanl`, `.ctch` | Creates a new text channel with a given name.
`.removeallroles`, `.rar` | Removes all roles from a mentioned user. | `.rar @User`
`.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. | `.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.`
`.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. | `.k "@some Guy" Your behaviour is toxic.`
`.mute` | Mutes mentioned user or users. | `.mute "@Someguy"` or `.mute "@Someguy" "@Someguy"`
`.unmute` | Unmutes mentioned user or users. | `.unmute "@Someguy"` or `.unmute "@Someguy" "@Someguy"`
`.deafen`, `.deaf` | Deafens mentioned user or users | `.deaf "@Someguy"` or `.deaf "@Someguy" "@Someguy"`
`.undeafen`, `.undef` | Undeafens mentioned user or users | `.undef "@Someguy"` or `.undef "@Someguy" "@Someguy"`
`.delvoichanl`, `.dvch` | Deletes a voice channel with a given name. | `.dvch VoiceChannelName`
`.creatvoichanl`, `.cvch` | Creates a new voice channel with a given name. | `.cvch VoiceChannelName`
`.deltxtchanl`, `.dtch` | Deletes a text channel with a given name. | `.dtch TextChannelName`
`.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`
`.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!**
`.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!**
`.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`
`.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!`
`.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!**
`.donators` | List of lovely people who donated to keep this project alive.
`.donadd` | Add a donator to the database.
`.announce` | Sends a message to all servers' general channel bot is connected to.**Bot Owner Only!** | .announce Useless spam
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.chatsave 150`
`.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`
`.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner Only** | `.savechat 150`
### Utility
Command and aliases | Description | Usage
@ -98,15 +99,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
`.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
`.whoplays` | Shows a list of users who are playing the specified game.
`.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server.
`.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. 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.
`.stats` | Shows some basic stats for Nadeko.
`.dysyd` | Shows some basic stats for Nadeko.
`.userid`, `.uid` | Shows user ID.
`.channelid`, `.cid` | Shows current channel ID.
`.serverid`, `.sid` | Shows current server ID.
`.userid`, `.uid` | Shows user ID. | `.uid` or `.uid "@SomeGuy"
`.channelid`, `.cid` | Shows current channel ID. | `.cid`
`.serverid`, `.sid` | Shows current server ID. | `.sid`
`.roles` | List all roles on this server or a single user if specified.
`.channeltopic`, `.ct` | Sends current channel's topic as a message. | `.ct`
### Permissions
Command and aliases | Description | Usage
@ -122,38 +124,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`
`;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`
`;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.
`;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | ;rp AwesomeRole
`;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | ;cp #dev
`;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | ;up Kwoth
`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | ;sm "module name" enable
`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | ;sc "command name" disable
`;rolemdl`, `;rm` | Sets a module's permission at the role level. | ;rm "module name" enable MyRole
`;rolecmd`, `;rc` | Sets a command's permission at the role level. | ;rc "command name" disable MyRole
`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | ;cm "module name" enable SomeChannel
`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | ;cc "command name" enable SomeChannel
`;usrmdl`, `;um` | Sets a module's permission at the user level. | ;um "module name" enable SomeUsername
`;usrcmd`, `;uc` | Sets a command's permission at the user level. | ;uc "command name" enable SomeUsername
`;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | ;asm [enable/disable]
`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | ;asc "module name" [enable/disable]
`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | ;acm [enable/disable] SomeChannel
`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | ;acc "module name" [enable/disable] SomeChannel
`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | ;arm [enable/disable] MyRole
`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | ;arc "module name" [enable/disable] MyRole
`;ubl` | Blacklists a mentioned user. | ;ubl [user_mention]
`;uubl` | Unblacklists a mentioned user. | ;uubl [user_mention]
`;cbl` | Blacklists a mentioned channel (#general for example). | ;cbl #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]
`;roleperms`, `;rp` | Shows banned permissions for a certain role. No argument means for everyone. | `;rp AwesomeRole`
`;chnlperms`, `;cp` | Shows banned permissions for a certain channel. No argument means for this channel. | `;cp #dev`
`;userperms`, `;up` | Shows banned permissions for a certain user. No argument means for yourself. | `;up Kwoth`
`;srvrmdl`, `;sm` | Sets a module's permission at the server level. | `;sm "module name" enable`
`;srvrcmd`, `;sc` | Sets a command's permission at the server level. | `;sc "command name" disable`
`;rolemdl`, `;rm` | Sets a module's permission at the role level. | `;rm "module name" enable MyRole`
`;rolecmd`, `;rc` | Sets a command's permission at the role level. | `;rc "command name" disable MyRole`
`;chnlmdl`, `;cm` | Sets a module's permission at the channel level. | `;cm "module name" enable SomeChannel`
`;chnlcmd`, `;cc` | Sets a command's permission at the channel level. | `;cc "command name" enable SomeChannel`
`;usrmdl`, `;um` | Sets a module's permission at the user level. | `;um "module name" enable SomeUsername`
`;usrcmd`, `;uc` | Sets a command's permission at the user level. | `;uc "command name" enable SomeUsername`
`;allsrvrmdls`, `;asm` | Sets permissions for all modules at the server level. | `;asm [enable/disable]`
`;allsrvrcmds`, `;asc` | Sets permissions for all commands from a certain module at the server level. | `;asc "module name" [enable/disable]`
`;allchnlmdls`, `;acm` | Sets permissions for all modules at the channel level. | `;acm [enable/disable] SomeChannel`
`;allchnlcmds`, `;acc` | Sets permissions for all commands from a certain module at the channel level. | `;acc "module name" [enable/disable] SomeChannel`
`;allrolemdls`, `;arm` | Sets permissions for all modules at the role level. | `;arm [enable/disable] MyRole`
`;allrolecmds`, `;arc` | Sets permissions for all commands from a certain module at the role level. | `;arc "module name" [enable/disable] MyRole`
`;ubl` | Blacklists a mentioned user. | `;ubl [user_mention]`
`;uubl` | Unblacklists a mentioned user. | `;uubl [user_mention]`
`;cbl` | Blacklists a mentioned channel (#general for example). | `;cbl #some_channel`
`;cubl` | Unblacklists a mentioned channel (#general for example). | `;cubl #some_channel`
`;sbl` | Blacklists a server by a name or id (#general for example). **BOT OWNER ONLY** | `;sbl [servername/serverid]`
`;cmdcooldown`, `;cmdcd` | Sets a cooldown per user for a command. Set 0 to clear. | `;cmdcd "some cmd" 5`
`;allcmdcooldowns`, `;acmdcds` | Shows a list of all commands and their respective cooldowns.
### Conversations
Command and aliases | Description | Usage
----------------|--------------|-------
`..` | 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
`..` | 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`
`..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 die` | Works only for the owner. Shuts the bot down.
@ -173,11 +175,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
`$rolluo` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice (unordered). If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | $roll or $roll 7 or $roll 3d5
`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15`
`$raffle` | Prints a name and ID of a random user from the online list from the (optional) role.
`$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`
`$give` | Give someone a certain amount of NadekoFlowers
`$give` | Give someone a certain amount of NadekoFlowers|`$give 1 "@SomeGuy"`
`$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
`$leaderboard`, `$lb` |
@ -196,9 +200,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)
`>gencurrency`, `>gc` | Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a NadekoFlower. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `>gc` or `>gc 60`
`>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | >leet 3 Hello
`>choose` | Chooses a thing from a list of things | >choose Get up;Sleep;Sleep more
`>8ball` | Ask the 8ball a yes/no question.
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | >rps scissors
`>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more`
`>8ball` | Ask the 8ball a yes/no question. | `>8ball should i do something`
`>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors`
`>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows`
### Music
@ -234,7 +238,7 @@ Command and aliases | Description | Usage
`!!load` | Loads a playlist under a certain name. | `!!load classical-1`
`!!playlists`, `!!pls` | Lists all playlists. Paginated. 20 per page. Default page is 0. | `!!pls 1`
`!!deleteplaylist`, `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5`
`!!goto` | Goes to a specific time in seconds in a song.
`!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30`
`!!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)
@ -262,41 +266,42 @@ Command and aliases | Description | Usage
`~pokemonability`, `~pokeab` | Searches for a pokemon ability.
`~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/
`~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"`
`~we` | Shows weather data for a specified city and a country. BOTH ARE REQUIRED. Use country abbrevations. | ~we Moscow RF
`~yt` | Searches youtubes and shows the first result
`~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 query`
`~ani`, `~anime`, `~aq` | Queries anilist for an anime and shows the first result.
`~imdb` | Queries imdb for movies or series, show first result.
`~mang`, `~manga`, `~mq` | Queries anilist for a manga and shows the 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. | `~mq query`
`~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
`~ir` | Pulls a random image using a search parameter. | ~ir cute kitten
`~lmgtfy` | Google something for an idiot.
`~google`, `~g` | Get a google search link for some terms.
`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | ~hs Ysera
`~ud` | Searches Urban Dictionary for a word. | ~ud Pineapple
`~#` | Searches Tagdef.com for a hashtag. | ~# ff
`~randomdog`, `~woof` | Shows a random dog image.
`~i` | Pulls the first image found using a search parameter. Use ~ir for different results. | `~i cute kitten`
`~ir` | Pulls a random image using a search parameter. | `~ir cute kitten`
`~lmgtfy` | Google something for an idiot. | `~lmgtfy query`
`~google`, `~g` | Get a google search link for some terms. | `~google query`
`~hs` | Searches for a Hearthstone card and shows its image. Takes a while to complete. | `~hs Ysera`
`~ud` | Searches Urban Dictionary for a word. | `~ud Pineapple`
`~#` | Searches Tagdef.com for a hashtag. | `~# ff`
`~quote` | Shows a random quote.
`~catfact` | Shows a random catfact from <http://catfacts-api.appspot.com/api/facts>
`~yomama`, `~ym` | Shows a random joke from <http://api.yomomma.info/>
`~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>
`~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.
`~revimg` | Returns a google reverse image search for an image from a 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
`~wiki` | Gives you back a wikipedia link
`~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. | `~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`
`~wiki` | Gives you back a wikipedia link | `~wiki query`
`~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.
`~av`, `~avatar` | Shows a mentioned person's avatar. | ~av @X
`~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`
### NSFW
Command and aliases | Description | Usage
----------------|--------------|-------
`~hentai` | Shows a random NSFW hentai image from gelbooru and danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~hentai yuri+kissing
`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preffered. (multiple tags are appended with +) | ~danbooru yuri+kissing
`~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
`~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
`~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`
`~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`
`~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.
`~boobs` | Real adult content.
`~butts`, `~ass`, `~butt` | Real adult content.
@ -311,7 +316,7 @@ Command and aliases | Description | Usage
`,claimfinish`, `,cf`, `,cf3`, `,claimfinish3` | Finish your claim with 3 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
`,claimfinish2`, `,cf2` | Finish your claim with 2 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
`,claimfinish1`, `,cf1` | Finish your claim with 1 stars if you destroyed a base. Optional second argument finishes for someone else. | ,cf [war_number] [optional_other_name]
`,unclaim`, `,uncall`, `,uc` | Removes your claim from a certain war. Optional second argument denotes a person in whos place to unclaim | ,uc [war_number] [optional_other_name]
`,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]
### Pokegame
@ -326,7 +331,7 @@ Command and aliases | Description | Usage
### Translator
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.
### Customreactions
@ -352,7 +357,7 @@ Command and aliases | Description | Usage
### Trello
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 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