From 630c71de6303230d75ad53ac8c6b93760b285696 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Feb 2017 17:50:14 +0100 Subject: [PATCH 001/256] Download users back? --- Discord.Net | 2 +- src/NadekoBot/NadekoBot.cs | 3 +- src/NadekoBot/ShardedDiscordClient.cs | 199 -------------------------- 3 files changed, 3 insertions(+), 201 deletions(-) delete mode 100644 src/NadekoBot/ShardedDiscordClient.cs diff --git a/Discord.Net b/Discord.Net index 80384323..b57d5d73 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit 80384323790471d254c7db5c237a49dc62624378 +Subproject commit b57d5d738921021f6e366ab6b4eb13cd0501959d diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 02c60720..a6842797 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -67,7 +67,8 @@ namespace NadekoBot MessageCacheSize = 10, LogLevel = LogSeverity.Warning, TotalShards = Credentials.TotalShards, - ConnectionTimeout = int.MaxValue + ConnectionTimeout = int.MaxValue, + AlwaysDownloadUsers = true, }); #if GLOBAL_NADEKO Client.Log += Client_Log; diff --git a/src/NadekoBot/ShardedDiscordClient.cs b/src/NadekoBot/ShardedDiscordClient.cs deleted file mode 100644 index daa5276e..00000000 --- a/src/NadekoBot/ShardedDiscordClient.cs +++ /dev/null @@ -1,199 +0,0 @@ -using Discord; -using Discord.WebSocket; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using NLog; -using System.Diagnostics; - -namespace NadekoBot -{ - public class ShardedDiscordClient - { - private DiscordSocketConfig discordSocketConfig; - private Logger _log { get; } - - public event Action UserJoined = delegate { }; - public event Action MessageReceived = delegate { }; - public event Action UserLeft = delegate { }; - public event Action UserUpdated = delegate { }; - public event Action GuildUserUpdated = delegate { }; - public event Action, SocketMessage> MessageUpdated = delegate { }; - public event Action> MessageDeleted = delegate { }; - public event Action UserBanned = delegate { }; - public event Action UserUnbanned = delegate { }; - public event Action, SocketUser, SocketPresence, SocketPresence> UserPresenceUpdated = delegate { }; - public event Action UserVoiceStateUpdated = delegate { }; - public event Action ChannelCreated = delegate { }; - public event Action ChannelDestroyed = delegate { }; - public event Action ChannelUpdated = delegate { }; - public event Action, SocketReaction> ReactionAdded = delegate { }; - public event Action, SocketReaction> ReactionRemoved = delegate { }; - public event Action> ReactionsCleared = delegate { }; - - public event Action JoinedGuild = delegate { }; - public event Action LeftGuild = delegate { }; - - public event Action Disconnected = delegate { }; - public event Action Connected = delegate { }; - - private uint _connectedCount = 0; - private uint _downloadedCount = 0; - - private int _guildCount = 0; - - private IReadOnlyList Clients { get; } - - public ShardedDiscordClient(DiscordSocketConfig discordSocketConfig) - { - _log = LogManager.GetCurrentClassLogger(); - this.discordSocketConfig = discordSocketConfig; - - var clientList = new List(); - for (int i = 0; i < discordSocketConfig.TotalShards; i++) - { - discordSocketConfig.ShardId = i; - var client = new DiscordSocketClient(discordSocketConfig); - clientList.Add(client); - client.UserJoined += arg1 => { UserJoined(arg1); return Task.CompletedTask; }; - client.MessageReceived += arg1 => - { - if (arg1.Author == null || arg1.Author.IsBot) - return Task.CompletedTask; - MessageReceived(arg1); - return Task.CompletedTask; - }; - client.UserLeft += arg1 => { UserLeft(arg1); return Task.CompletedTask; }; - client.UserUpdated += (arg1, gu2) => { UserUpdated(arg1, gu2); return Task.CompletedTask; }; - client.GuildMemberUpdated += (arg1, arg2) => { GuildUserUpdated(arg1, arg2); return Task.CompletedTask; }; - client.MessageUpdated += (arg1, m2) => { MessageUpdated(arg1, m2); return Task.CompletedTask; }; - client.MessageDeleted += (arg1, arg2) => { MessageDeleted(arg1, arg2); return Task.CompletedTask; }; - client.UserBanned += (arg1, arg2) => { UserBanned(arg1, arg2); return Task.CompletedTask; }; - client.UserUnbanned += (arg1, arg2) => { UserUnbanned(arg1, arg2); return Task.CompletedTask; }; - client.UserPresenceUpdated += (arg1, arg2, arg3, arg4) => { UserPresenceUpdated(arg1, arg2, arg3, arg4); return Task.CompletedTask; }; - client.UserVoiceStateUpdated += (arg1, arg2, arg3) => { UserVoiceStateUpdated(arg1, arg2, arg3); return Task.CompletedTask; }; - client.ChannelCreated += arg => { ChannelCreated(arg); return Task.CompletedTask; }; - client.ChannelDestroyed += arg => { ChannelDestroyed(arg); return Task.CompletedTask; }; - client.ChannelUpdated += (arg1, arg2) => { ChannelUpdated(arg1, arg2); return Task.CompletedTask; }; - client.JoinedGuild += (arg1) => { JoinedGuild(arg1); ++_guildCount; return Task.CompletedTask; }; - client.LeftGuild += (arg1) => { LeftGuild(arg1); --_guildCount; return Task.CompletedTask; }; - client.ReactionAdded += (arg1, arg2, arg3) => { ReactionAdded(arg1, arg2, arg3); return Task.CompletedTask; }; - client.ReactionRemoved += (arg1, arg2, arg3) => { ReactionRemoved(arg1, arg2, arg3); return Task.CompletedTask; }; - client.ReactionsCleared += (arg1, arg2) => { ReactionsCleared(arg1, arg2); return Task.CompletedTask; }; - - _log.Info($"Shard #{i} initialized."); -#if GLOBAL_NADEKO - client.Log += Client_Log; -#endif - var j = i; - client.Disconnected += (ex) => - { - _log.Error("Shard #{0} disconnected", j); - _log.Error(ex, ex?.Message ?? "No error"); - return Task.CompletedTask; - }; - } - - Clients = clientList.AsReadOnly(); - } - - private Task Client_Log(LogMessage arg) - { - _log.Warn(arg.Message); - _log.Error(arg.Exception); - return Task.CompletedTask; - } - - public DiscordSocketClient MainClient => - Clients[0]; - - public SocketSelfUser CurrentUser => - Clients[0].CurrentUser; - - public IEnumerable GetGuilds() => - Clients.SelectMany(c => c.Guilds); - - public int GetGuildsCount() => - _guildCount; - - public SocketGuild GetGuild(ulong id) - { - foreach (var c in Clients) - { - var g = c.GetGuild(id); - if (g != null) - return g; - } - return null; - } - - public Task GetDMChannelAsync(ulong channelId) => - Clients[0].GetDMChannelAsync(channelId); - - internal async Task LoginAsync(TokenType tokenType, string token) - { - foreach (var c in Clients) - { - await c.LoginAsync(tokenType, token).ConfigureAwait(false); - _log.Info($"Shard #{c.ShardId} logged in."); - } - } - - internal async Task ConnectAsync() - { - - foreach (var c in Clients) - { - try - { - var sw = Stopwatch.StartNew(); - await c.ConnectAsync().ConfigureAwait(false); - sw.Stop(); - _log.Info($"Shard #{c.ShardId} connected after {sw.Elapsed.TotalSeconds:F2}s ({++_connectedCount}/{Clients.Count})"); - _guildCount += c.Guilds.Count; - } - catch - { - _log.Error($"Shard #{c.ShardId} FAILED CONNECTING."); - try { await c.ConnectAsync().ConfigureAwait(false); } - catch (Exception ex2) - { - _log.Error($"Shard #{c.ShardId} FAILED CONNECTING TWICE."); - _log.Error(ex2); - } - } - } - Connected(); - } - - internal Task DownloadAllUsersAsync() => - Task.WhenAll(Clients.Select(async c => - { - var sw = Stopwatch.StartNew(); - await c.DownloadAllUsersAsync().ConfigureAwait(false); - sw.Stop(); - _log.Info($"Shard #{c.ShardId} downloaded {c.Guilds.Sum(g => g.Users.Count)} users after {sw.Elapsed.TotalSeconds:F2}s ({++_downloadedCount}/{Clients.Count})."); - })); - - public Task SetGame(string game) => Task.WhenAll(Clients.Select(ms => ms.SetGameAsync(game))); - - - public Task SetStream(string name, string url) => Task.WhenAll(Clients.Select(ms => ms.SetGameAsync(name, url, StreamType.Twitch))); - - //public Task SetStatus(SettableUserStatus status) => Task.WhenAll(Clients.Select(ms => ms.SetStatusAsync(SettableUserStatusToUserStatus(status)))); - } - - public enum SettableUserStatus - { - Online = 1, - On = 1, - Invisible = 2, - Invis = 2, - Idle = 3, - Afk = 3, - Dnd = 4, - DoNotDisturb = 4, - Busy = 4, - } -} \ No newline at end of file From 0a280f9b5b73126690ee5d6f24620a911ed7b642 Mon Sep 17 00:00:00 2001 From: samvaio Date: Thu, 2 Feb 2017 03:52:16 +0530 Subject: [PATCH 002/256] linux guide updated --- docs/guides/Linux Guide.md | 289 +++++++++++++++++++++++-------------- 1 file changed, 178 insertions(+), 111 deletions(-) diff --git a/docs/guides/Linux Guide.md b/docs/guides/Linux Guide.md index 5fee13a3..b14351bc 100644 --- a/docs/guides/Linux Guide.md +++ b/docs/guides/Linux Guide.md @@ -8,11 +8,11 @@ Assuming you have followed the link above to setup an account and Droplet with 6 **Go through this whole guide before setting up Nadeko** -#### Prerequisites +####Prerequisites - Download [PuTTY](http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html) -- Download [CyberDuck](https://cyberduck.io) or [WinSCP](https://winscp.net/eng/download.php) +- Download [WinSCP](https://winscp.net/eng/download.php) *(optional)* -#### Follow these steps +####Starting up - **Open PuTTY.exe** that you downloaded before, and paste or enter your `IP address` and then click **Open**. If you entered your Droplets IP address correctly, it should show **login as:** in a newly opened window. @@ -24,7 +24,162 @@ If you entered your Droplets IP address correctly, it should show **login as:** **NOTE:** Copy the commands, and just paste them using **mouse single right-click.** -####Installing Git +####Creating and Inviting bot + +- Read here how to [create a DiscordBot application](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#creating-discordbot-application) +- [Visual Invite Guide](http://discord.kongslien.net/guide.html) **(Note: Client ID is your Bot ID)** +- Copy your `Client ID` from your [applications page](https://discordapp.com/developers/applications/me). +- Replace the **12345678** in this link: +`https://discordapp.com/oauth2/authorize?client_id=`12345678`&scope=bot&permissions=66186303` + with your `Client ID` +- The link should now look like this: +`https://discordapp.com/oauth2/authorize?client_id=`**YOUR_CLENT_ID_HERE**`&scope=bot&permissions=66186303` +- Go to the newly created link and pick the server we created, and click `Authorize` +- The bot should have been added to your server. + +####Getting NadekoBot +#####Part I +Use the following command to get and run `linuxAIO.sh` +(Remember **Do Not** rename the file **linuxAIO.sh**) + +`cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh` + +You should see these following options after using the above command: + +``` +1. Download Dev Build (Latest) +2. Download Stable Build +3. Run Nadeko (Normally) +4. Run Nadeko with Auto Restart (Run Nadeko normally before using this.) +5. Auto-Install Prerequisites (for Ubuntu and Debian) +6. Set up credentials.json (if you have downloaded the bot already) +7. To exit +``` +#####Part II +**If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed, Press `5` and press `enter` key, then `y` when you see the following: +``` +Welcome to NadekoBot Auto Prerequisites Installer. +Would you like to continue? +``` +That will install all the prerequisites your system need to run NadekoBot. + +If you prefer to install them [manually](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#installing-manually-optional), click on the link. *(Optional)* + +Once *prerequisites* finish installing. +#####Part III +Choose either +`1` to get the **most updated build of NadekoBot** +or +`2` to get the **previously stable build of NadekoBot** +and then press `enter` key. + +Once Installation is completed you should see the options again. + +Next, check out: +#####Part IV +If you prefer to do this step [manually](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-sftp), and skip other parts click on the link. *(Optional)* + +- [1. Setting up credentials.json](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-credentialsjson) +- [2. To Get the Google API](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-nadekobot-for-music) +- [3. JSON Explanations for other APIs](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) + +You will need the following for the next step: +![botimg](https://cdn.discordapp.com/attachments/251504306010849280/276455844223123457/Capture.PNG) + +- **Bot's Client ID** and **Bot's ID** (both are same) [(*required)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file) +- **Bot's Token** (not client secret) [(*required)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file) +- Your **Discord userID** [(*required)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file) +- **Google Api Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-nadekobot-for-music) +- **LoL Api Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) +- **Mashape Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) +- **Osu Api Key** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) +- **Sound Cloud Client Id** [(optional)](http://nadekobot.readthedocs.io/en/latest/JSON%20Explanations/) + +Once you have acquired them, press `6` to **Set up credentials.json** + +You will be asked to enter the required informations, just follow the on-screen instructions and enter whats asked. +*i.e* If you are asked **Bot's Token**, then just copy and paste the `token` and press `enter` key. + +If you want to skip any optional informations, just press `enter` key without typing/pasting anything. +Once done, +#####Part V +You should see the options again within the **tmux session** named *nadako*. +Next, press `3` to **Run Nadeko (Normally)** +Check in your discord server if your new bot is working properly. +#####Part VI +If the bot is working properly in your server, type `.die` to shut down the bot. +You should be back to the options again, +from the options choose `4` to **Run Nadeko with Auto Restart.** + +It will show you more options: +``` +1. Run Auto Restart normally without Updating. +2. Auto Restart and Update with Dev Build (latest) +3. Auto Restart and Update with Stable Build +4. Exit +``` +Choose anything you like and once the bot's back online again in your server, close the **PuTTY**. +Done, You now have your own **NadekoBot**. + + +[To **Restart** your **NadekoBot** anytime later.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) + +####Running NadekoBot + +**Create a new Session:** + +- `tmux new -s nadeko` + +The above command will create a new session named **nadeko** *(you can replace “nadeko” with anything you prefer and remember its your session name)* so you can run the bot in background without having to keep the PuTTY running. + +**Next, we need to run `linuxAIO.sh` in order to get the latest running scripts with patches:** + +- `cd ~ && bash linuxAIO.sh` + +From the options, + +Choose `3` To Run the bot normally. +**NOTE:** With option `3` (Running Normally), if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) in discord. The bot will shut down and will stay offline until you manually run it again. (best if you want to check the bot.) + +Choose `4` To Run the bot with Auto Restart. +**NOTE:** With option `4` (Running with Auto Restart), bot will auto run if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) making the command `.die` to function as restart. + +See how that happens: + +![img9](https://cdn.discordapp.com/attachments/251504306010849280/251506312893038592/die_explaination.gif) + +**Remember** that, while running with Auto Restart, you will need to [close the tmux session](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) to stop the bot completely. + +**Now check your Discord, the bot should be online** + +Next to **move the bot to background** and to do that, press **CTRL+B+D** (this will detach the nadeko session using TMUX), and you can finally close PuTTY now. + +####Restarting Nadeko + +**Restarting Nadeko with the Server:** + +Open **PuTTY** and login as you have before, type `reboot` and hit Enter. + +**Restarting Manually:** + +- Kill your previous session, check with `tmux ls` +- `tmux kill-session -t nadeko` (don't forget to replace "nadeko" to what ever you named your bot's session) +- [Run the bot again.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot) + +####Updating Nadeko + +- Connect to the terminal through PuTTY. +- `tmux kill-session -t nadeko` (don't forget to replace **nadeko** in the command with the name of your bot's session) +- Make sure the bot is **not** running. +- `tmux new -s nadeko` (**nadeko** is the name of the session) +- `cd ~ && bash linuxAIO.sh` +- Choose either `1` or `2` to update the bot with **latest build** or **stable build** respectively. +- Choose either `3` or `4` to run the bot again with **normally** or **auto restart** respectively. +- Done. You can close PuTTY now. + +####Installing Manually (Optional) + +#####Installing Git ![img1](https://cdn.discordapp.com/attachments/251504306010849280/251504416019054592/git.gif) @@ -38,14 +193,14 @@ CentOS: **NOTE:** If the command is not being initiated, hit **Enter** -####Installing .NET Core SDK +#####Installing .NET Core SDK ![img2](https://cdn.discordapp.com/attachments/251504306010849280/251504746987388938/dotnet.gif) Go to [this link](https://www.microsoft.com/net/core#ubuntu) (for Ubuntu) or to [this link](https://www.microsoft.com/net/core#linuxcentos) (for CentOS) provided by microsoft for instructions on how to get the most up to date version of the dotnet core sdk! Make sure that you're on the correct page for your distribution of linux as the guides are different for the various distributions -We'll go over the steps here for Ubuntu 16.04 anyway (these will **only** work on Ubuntu 16.04), accurate as of 25/11/2016 +We'll go over the steps here for Ubuntu 16.04 anyway (these will **only** work on Ubuntu 16.04), accurate as of 3/2/2017 ``` sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list' @@ -55,7 +210,7 @@ sudo apt-get update && sudo apt-get install dotnet-dev-1.0.0-preview2.1-003177 - **NOTE:** .NET CORE SDK only supports 64-bit Linux Operating Systems (Raspberry Pis are not supported because of this) -####Installing Opus Voice Codec and libsodium +#####Installing Opus Voice Codec and libsodium ![img3](https://cdn.discordapp.com/attachments/251504306010849280/251505294654308353/libopus.gif) @@ -67,7 +222,7 @@ CentOS: `yum -y install opus opus-devel` -####Installing FFMPEG +#####Installing FFMPEG ![img4](https://cdn.discordapp.com/attachments/251504306010849280/251505443111829505/ffmpeg.gif) @@ -101,7 +256,7 @@ echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sou sudo apt-get update && sudo apt-get install ffmpeg -y ``` -####Installing TMUX +#####Installing TMUX ![img5](https://cdn.discordapp.com/attachments/251504306010849280/251505519758409728/tmux.gif) @@ -113,33 +268,7 @@ Centos: `yum -y install tmux` -####Getting NadekoBot - -Use the following command to get and run `linuxAIO.sh`: -(Remember **DO NOT** rename the file `linuxAIO.sh`) - -`cd ~ && wget -N https://github.com/Kwoth/NadekoBot-BashScript/raw/master/linuxAIO.sh && bash linuxAIO.sh` - -Follow the on screen instructions: - -1. To Get the latest build. (most recent updates) -2. To Get the stable build. - -Choose either `1` or `2` then press `enter` key. -Once Installation is completed you should see the options again. -Next, choose `5` to exit. - -####Creating and Inviting bot - -- Read here how to [create a DiscordBot application](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#creating-discordbot-application) -- [Visual Invite Guide](http://discord.kongslien.net/guide.html) *NOTE: Client ID is your Bot ID* -- Copy your `Client ID` from your [applications page](https://discordapp.com/developers/applications/me). -- Replace the `12345678` in this link `https://discordapp.com/oauth2/authorize?client_id=12345678&scope=bot&permissions=66186303` with your `Client ID`. -- The link should now look like this: `https://discordapp.com/oauth2/authorize?client_id=**YOUR_CLENT_ID_HERE**&scope=bot&permissions=66186303`. -- Go to the newly created link and pick the server we created, and click `Authorize`. -- The bot should have been added to your server. - -####Guide for Advance Users +####Guide for Advance Users (Optional) **Skip this step if you are a Regular User or New to Linux.** @@ -158,23 +287,23 @@ Next, choose `5` to exit. ####Setting up SFTP -- Open **CyberDuck** -- Click on **Open Connection** (top-left corner), a new window should appear. -- You should see **FTP (File Transfer Protocol)** in drop-down. -- Change it to **SFTP (SSH File Transfer Protocol)** -- Now, in **Server:** paste or type in your `Digital Ocean Droplets IP address`, leave `Port: 22` (no need to change it) +- Open **WinSCP** +- Click on **New Site** (top-left corner). +- On the right-hand side, you should see **File Protocol** above a drop-down selection menu. +- Select **SFTP** *(SSH File Transfer Protocol)* if its not already selected. +- Now, in **Host name:** paste or type in your `Digital Ocean Droplets IP address` and leave `Port: 22` (no need to change it). - In **Username:** type `root` - In **Password:** type `the new root password (you changed at the start)` -- Click on **Connect** -- It should show you the NadekoBot folder which was created by git earlier +- Click on **Login**, it should connect. +- It should show you the NadekoBot folder which was created by git earlier on the right-hand side window. - Open that folder, then open the `src` folder, followed by another `NadekoBot` folder and you should see `credentials.json` there. ####Setting up credentials.json - Copy the `credentials.json` to desktop - EDIT it as it is guided here: [Setting up credentials.json](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-credentialsjson-file) -- Paste/put it back in the folder once done. `(Using CyberDuck/WinSCP)` -- **If** you already have Nadeko 1.0 setup and have `credentials.json` and `NadekoBot.db`, you can just copy and paste the `credentials.json` to `NadekoBot/src/NadekoBot` and `NadekoBot.db` to `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data` using CyberDuck. +- Paste/put it back in the folder once done. `(Using WinSCP)` +- **If** you already have Nadeko 1.0 setup and have `credentials.json` and `NadekoBot.db`, you can just copy and paste the `credentials.json` to `NadekoBot/src/NadekoBot` and `NadekoBot.db` to `NadekoBot/src/NadekoBot/bin/Release/netcoreapp1.0/data` using WinSCP. - **If** you have Nadeko 0.9x follow the [Upgrading Guide](http://nadekobot.readthedocs.io/en/latest/guides/Upgrading%20Guide/) ####Setting up Music @@ -183,80 +312,18 @@ To set up Nadeko for music and Google API Keys, follow [Setting up NadekoBot for Once done, go back to **PuTTY** -####Running NadekoBot +####Some more Info -**Create a new Session:** - -- `tmux new -s nadeko` - -The above command will create a new session named **nadeko** *(you can replace “nadeko” with anything you prefer and remember its your session name)* so you can run the bot in background without having to keep the PuTTY running. - -**Next, we need to run `linuxAIO.sh` in order to get the latest running scripts with patches:** - -- `cd ~ && bash linuxAIO.sh` - -From the options, - -Choose `3` To Run the bot normally. -**NOTE:** With option `3` (Running Normally), if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) in discord. The bot will shut down and will stay offline until you manually run it again. (best if you want to check the bot.) - -Choose `4` To Run the bot with Auto Restart. -**NOTE:** With option `4` (Running with Auto Restart), bot will auto run if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) making the command `.die` to function as restart. - -See how that happens: - -![img9](https://cdn.discordapp.com/attachments/251504306010849280/251506312893038592/die_explaination.gif) - -**Remember** that, while running with Auto Restart, you will need to [close the tmux session](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) to stop the bot completely. - -**Now check your Discord, the bot should be online** - -Next to **move the bot to background** and to do that, press **CTRL+B+D** (this will detach the nadeko session using TMUX), and you can finally close PuTTY now. - -####Some more Info (just in case) - -**Info about tmux:** +#####Info about tmux - If you want to **see the sessions** after logging back again, type `tmux ls`, and that will give you the list of sessions running. - If you want to **switch to/ see that session**, type `tmux a -t nadeko` (**nadeko** is the name of the session we created before so, replace **“nadeko”** with the session name you created.) - If you want to **kill** NadekoBot **session**, type `tmux kill-session -t nadeko` -**If you are running Ubuntu 16.10, and having trouble installing .NET Core:** - -- Go to [Download Page for libicu55_55.1-7_amd64.deb](http://packages.ubuntu.com/en/xenial/amd64/libicu55/download) -- Copy the link with a download option closest to you -- `wget ` *e.g.* `wget http://mirrors.kernel.org/ubuntu/pool/main/i/icu/libicu55_55.1-7_amd64.deb` (make sure it is downloaded) -- Install with: `dpkg –i libicu55_55.1-7_amd64.deb` -- Now go back and install the .NET Core - -####Restarting Nadeko - -**Restarting Nadeko with the Server:** - -Open **PuTTY** and login as you have before, type `reboot` and hit Enter. - -**Restarting Manually:** - -- Kill your previous session, check with `tmux ls` -- `tmux kill-session -t nadeko` (don't forget to replace "nadeko" to what ever you named your bot's session) -- [Run the bot again.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot) - -####Updating Nadeko - -- Connect to the terminal through PuTTY. -- `tmux kill-session -t nadeko` (don't forget to replace **nadeko** in the command with the name of your bot's session) -- Make sure the bot is **not** running. -- `tmux new -s nadeko` (**nadeko** is the name of the session) -- `cd ~ && bash linuxAIO.sh` -- Choose either `1` or `2` to update the bot with **latest build** or **stable build** respectively. -- Choose either `3` or `4` to run the bot again with **normally** or **auto restart** respectively. -- Done. You can close PuTTY now. - -####Alternative way to Install +#####Alternative way to Install If the [Nadeko installer](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#getting-nadekobot) shows any kind error, check if you have the `linuxAIO.sh` file and make sure its not renamed or if you want to manually install the bot. Use the following command(s): - ![img6](https://cdn.discordapp.com/attachments/251504306010849280/251505587089571850/getting_nadeko.gif) `cd ~ && curl -L https://github.com/Kwoth/NadekoBot-BashScript/raw/master/nadeko_installer.sh | sh` @@ -264,7 +331,7 @@ If the [Nadeko installer](http://nadekobot.readthedocs.io/en/latest/guides/Linux **OR** ``` -cd ~ && git clone -b 1.0 --recursive --depth 1 https://github.com/Kwoth/NadekoBot.git +cd ~ && git clone -b dev --recursive --depth 1 https://github.com/Kwoth/NadekoBot.git cd ~/NadekoBot/discord.net/src/Discord.Net && dotnet restore && cd ../Discord.Net.Commands && dotnet restore && cd ../../../src/NadekoBot/ && dotnet restore && dotnet build --configuration Release ``` From a9ab99a29bd38105362df914ad3861f72c83ae54 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 03:21:02 +0100 Subject: [PATCH 003/256] Compile error --- .../Modules/Administration/Commands/SelfCommands.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 13817a65..a56ee402 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -184,6 +184,14 @@ namespace NadekoBot.Modules.Administration return UserStatus.Online; } + + public enum SettableUserStatus + { + Online, + Invisible, + Idle, + Dnd + } } } } From 258b90e48278ac68de8e51dd54644f4027292b4d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 03:21:32 +0100 Subject: [PATCH 004/256] upped version --- src/NadekoBot/Services/Impl/StatsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index ab72769d..d8995c59 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Services.Impl private DiscordShardedClient client; private DateTime started; - public const string BotVersion = "1.1.5a"; + public const string BotVersion = "1.1.6"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net"; From aef7302513030c6e09eee663d9029597284294d8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 04:55:51 +0100 Subject: [PATCH 005/256] log for public bot --- Discord.Net | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Discord.Net b/Discord.Net index b57d5d73..979eecab 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit b57d5d738921021f6e366ab6b4eb13cd0501959d +Subproject commit 979eecabfe0654db0be27cba71ecb398848f2c0c From e7fcac3c8fd9374c13644387f365e54a3d0b92de Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 04:58:39 +0100 Subject: [PATCH 006/256] gelbooru and danbooru back on public bot --- src/NadekoBot/Modules/NSFW/NSFW.cs | 43 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 266ffa68..cd87454a 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -140,24 +140,6 @@ namespace NadekoBot.Modules.NSFW } } - - [NadekoCommand, Usage, Description, Aliases] - public async Task Danbooru([Remainder] string tag = null) - { - tag = tag?.Trim() ?? ""; - - var url = await GetDanbooruImageLink(tag).ConfigureAwait(false); - - if (url == null) - await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.").ConfigureAwait(false); - else - await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithDescription(Context.User.Mention + " " + tag) - .WithImageUrl(url) - .WithFooter(efb => efb.WithText("Danbooru"))) - .ConfigureAwait(false); - } - [NadekoCommand, Usage, Description, Aliases] public Task Yandere([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere); @@ -166,10 +148,6 @@ namespace NadekoBot.Modules.NSFW public Task Konachan([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan); - [NadekoCommand, Usage, Description, Aliases] - public Task Gelbooru([Remainder] string tag = null) - => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru); - [NadekoCommand, Usage, Description, Aliases] public Task Rule34([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34); @@ -191,6 +169,27 @@ namespace NadekoBot.Modules.NSFW .ConfigureAwait(false); } #endif + [NadekoCommand, Usage, Description, Aliases] + public async Task Danbooru([Remainder] string tag = null) + { + tag = tag?.Trim() ?? ""; + + var url = await GetDanbooruImageLink(tag).ConfigureAwait(false); + + if (url == null) + await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.").ConfigureAwait(false); + else + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithDescription(Context.User.Mention + " " + tag) + .WithImageUrl(url) + .WithFooter(efb => efb.WithText("Danbooru"))) + .ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + public Task Gelbooru([Remainder] string tag = null) + => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru); + [NadekoCommand, Usage, Description, Aliases] public async Task Cp() { From 3488eb8152ea136f7d9bc5141f98b8e21f6f7517 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 05:02:37 +0100 Subject: [PATCH 007/256] compile error --- src/NadekoBot/Modules/NSFW/NSFW.cs | 43 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index cd87454a..9698379a 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -186,6 +186,28 @@ namespace NadekoBot.Modules.NSFW .ConfigureAwait(false); } + public static Task GetDanbooruImageLink(string tag) => Task.Run(async () => + { + try + { + using (var http = new HttpClient()) + { + http.AddFakeHeaders(); + var data = await http.GetStreamAsync("https://danbooru.donmai.us/posts.xml?limit=100&tags=" + tag).ConfigureAwait(false); + var doc = new XmlDocument(); + doc.Load(data); + var nodes = doc.GetElementsByTagName("file-url"); + + var node = nodes[new NadekoRandom().Next(0, nodes.Count)]; + return "https://danbooru.donmai.us" + node.InnerText; + } + } + catch + { + return null; + } + }); + [NadekoCommand, Usage, Description, Aliases] public Task Gelbooru([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru); @@ -232,27 +254,6 @@ namespace NadekoBot.Modules.NSFW } } #if !GLOBAL_NADEKO - public static Task GetDanbooruImageLink(string tag) => Task.Run(async () => - { - try - { - using (var http = new HttpClient()) - { - http.AddFakeHeaders(); - var data = await http.GetStreamAsync("https://danbooru.donmai.us/posts.xml?limit=100&tags=" + tag).ConfigureAwait(false); - var doc = new XmlDocument(); - doc.Load(data); - var nodes = doc.GetElementsByTagName("file-url"); - - var node = nodes[new NadekoRandom().Next(0, nodes.Count)]; - return "https://danbooru.donmai.us" + node.InnerText; - } - } - catch - { - return null; - } - }); public static Task GetE621ImageLink(string tag) => Task.Run(async () => From a0e23b4c2cf0a0552eed78447d729eaaabd11c7a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 05:08:28 +0100 Subject: [PATCH 008/256] Fixed public bot connection logging --- Discord.Net | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Discord.Net b/Discord.Net index 979eecab..0fd1e70a 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit 979eecabfe0654db0be27cba71ecb398848f2c0c +Subproject commit 0fd1e70a22612ff9fa697dace96f22780080b01f From dfbabea8b904ec980f9277e57dcc8096123170ea Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 14:16:28 +0100 Subject: [PATCH 009/256] performance improvements? --- .../Modules/Administration/Administration.cs | 28 ++-- src/NadekoBot/Services/CommandHandler.cs | 137 +++++++++--------- 2 files changed, 87 insertions(+), 78 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 45e99b8e..cf92fb4b 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -38,20 +38,24 @@ namespace NadekoBot.Modules.Administration } - private static async Task DelMsgOnCmd_Handler(SocketUserMessage msg, CommandInfo cmd) + private static Task DelMsgOnCmd_Handler(SocketUserMessage msg, CommandInfo cmd) { - try + var _ = Task.Run(async () => { - var channel = msg.Channel as SocketTextChannel; - if (channel == null) - return; - if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune") - await msg.DeleteAsync().ConfigureAwait(false); - } - catch (Exception ex) - { - _log.Warn(ex, "Delmsgoncmd errored..."); - } + try + { + var channel = msg.Channel as SocketTextChannel; + if (channel == null) + return; + if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune") + await msg.DeleteAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + _log.Warn(ex, "Delmsgoncmd errored..."); + } + }); + return Task.CompletedTask; } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 09080f12..8cc68001 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -105,9 +105,8 @@ namespace NadekoBot.Services BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id); const float oneThousandth = 1.0f / 1000; - private async Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int ticks) + private Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int ticks) { - await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); _log.Info("Command Executed after {4}s\n\t" + "User: {0}\n\t" + "Server: {1}\n\t" + @@ -118,6 +117,7 @@ namespace NadekoBot.Services (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content, // {3} ticks * oneThousandth); + return Task.CompletedTask; } private void LogErroredExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int ticks) @@ -184,93 +184,98 @@ namespace NadekoBot.Services return false; } - private async Task MessageReceivedHandler(SocketMessage msg) + private Task MessageReceivedHandler(SocketMessage msg) { - try + var _ = Task.Run(async () => { - if (msg.Author.IsBot || !NadekoBot.Ready) //no bots, wait until bot connected and initialized - return; + try + { + if (msg.Author.IsBot || !NadekoBot.Ready) //no bots, wait until bot connected and initialized + return; - var execTime = Environment.TickCount; + var execTime = Environment.TickCount; - var usrMsg = msg as SocketUserMessage; - if (usrMsg == null) //has to be an user message, not system/other messages. - return; + var usrMsg = msg as SocketUserMessage; + if (usrMsg == null) //has to be an user message, not system/other messages. + return; #if !GLOBAL_NADEKO - // track how many messagges each user is sending - UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); + // track how many messagges each user is sending + UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); #endif - var channel = msg.Channel as SocketTextChannel; - var guild = channel?.Guild; + var channel = msg.Channel as SocketTextChannel; + var guild = channel?.Guild; - if (guild != null && guild.OwnerId != msg.Author.Id) - { - if (await InviteFiltered(guild, usrMsg).ConfigureAwait(false)) + if (guild != null && guild.OwnerId != msg.Author.Id) + { + if (await InviteFiltered(guild, usrMsg).ConfigureAwait(false)) + return; + + if (await WordFiltered(guild, usrMsg).ConfigureAwait(false)) + return; + } + + if (IsBlacklisted(guild, usrMsg)) return; - if (await WordFiltered(guild, usrMsg).ConfigureAwait(false)) + var cleverBotRan = await Task.Run(() => TryRunCleverbot(usrMsg, guild)).ConfigureAwait(false); + if (cleverBotRan) return; - } - if (IsBlacklisted(guild, usrMsg)) - return; + // maybe this message is a custom reaction + // todo log custom reaction executions. return struct with info + var crExecuted = await Task.Run(() => CustomReactions.TryExecuteCustomReaction(usrMsg)).ConfigureAwait(false); + if (crExecuted) //if it was, don't execute the command + return; - var cleverBotRan = await Task.Run(() => TryRunCleverbot(usrMsg, guild)).ConfigureAwait(false); - if (cleverBotRan) - return; + string messageContent = usrMsg.Content; - // maybe this message is a custom reaction - // todo log custom reaction executions. return struct with info - var crExecuted = await Task.Run(() => CustomReactions.TryExecuteCustomReaction(usrMsg)).ConfigureAwait(false); - if (crExecuted) //if it was, don't execute the command - return; + // execute the command and measure the time it took + var exec = await Task.Run(() => ExecuteCommand(new CommandContext(_client, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best)).ConfigureAwait(false); + execTime = Environment.TickCount - execTime; - string messageContent = usrMsg.Content; - - // execute the command and measure the time it took - var exec = await Task.Run(() => ExecuteCommand(new CommandContext(_client, usrMsg), messageContent, DependencyMap.Empty, MultiMatchHandling.Best)).ConfigureAwait(false); - execTime = Environment.TickCount - execTime; - - if (exec.Result.IsSuccess) - { - await LogSuccessfulExecution(usrMsg, exec, channel, execTime).ConfigureAwait(false); - } - else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) - { - LogErroredExecution(usrMsg, exec, channel, execTime); - if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception) + if (exec.Result.IsSuccess) { - if (exec.PermissionCache != null && exec.PermissionCache.Verbose) - try { await msg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { } + await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); + await LogSuccessfulExecution(usrMsg, exec, channel, execTime).ConfigureAwait(false); + } + else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) + { + LogErroredExecution(usrMsg, exec, channel, execTime); + if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception) + { + if (exec.PermissionCache != null && exec.PermissionCache.Verbose) + try { await msg.Channel.SendMessageAsync("⚠️ " + exec.Result.ErrorReason).ConfigureAwait(false); } catch { } + } + } + else + { + if (msg.Channel is IPrivateChannel) + { + // rofl, gotta do this to prevent dm help message being sent to + // users who are voting on private polls (sending a number in a DM) + int vote; + if (int.TryParse(msg.Content, out vote)) return; + + await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); + + await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false); + } } } - else + catch (Exception ex) { - if (msg.Channel is IPrivateChannel) + _log.Warn("Error in CommandHandler"); + _log.Warn(ex); + if (ex.InnerException != null) { - // rofl, gotta do this to prevent dm help message being sent to - // users who are voting on private polls (sending a number in a DM) - int vote; - if (int.TryParse(msg.Content, out vote)) return; - - await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); - - await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false); + _log.Warn("Inner Exception of the error in CommandHandler"); + _log.Warn(ex.InnerException); } } - } - catch (Exception ex) - { - _log.Warn("Error in CommandHandler"); - _log.Warn(ex); - if (ex.InnerException != null) - { - _log.Warn("Inner Exception of the error in CommandHandler"); - _log.Warn(ex.InnerException); - } - } + }); + return Task.CompletedTask; } public Task ExecuteCommandAsync(CommandContext context, int argPos, IDependencyMap dependencyMap = null, MultiMatchHandling multiMatchHandling = MultiMatchHandling.Exception) From 0728486f3e68a49aee68c0a7ed93bdd5ffe31a6d Mon Sep 17 00:00:00 2001 From: samvaio Date: Thu, 2 Feb 2017 21:57:46 +0530 Subject: [PATCH 010/256] fixed typo :/ --- docs/guides/Linux Guide.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/docs/guides/Linux Guide.md b/docs/guides/Linux Guide.md index b14351bc..8a6cab9f 100644 --- a/docs/guides/Linux Guide.md +++ b/docs/guides/Linux Guide.md @@ -56,7 +56,7 @@ You should see these following options after using the above command: 7. To exit ``` #####Part II -**If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed, Press `5` and press `enter` key, then `y` when you see the following: +**If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed, Press `5` and `enter` key, then `y` when you see the following: ``` Welcome to NadekoBot Auto Prerequisites Installer. Would you like to continue? @@ -97,18 +97,19 @@ You will need the following for the next step: Once you have acquired them, press `6` to **Set up credentials.json** -You will be asked to enter the required informations, just follow the on-screen instructions and enter whats asked. -*i.e* If you are asked **Bot's Token**, then just copy and paste the `token` and press `enter` key. +You will be asked to enter the required informations, just follow the on-screen instructions and enter the required information. +*i.e* If you are asked **Bot's Token**, then just copy and paste or type the **Bot's Token** and press `enter` key. -If you want to skip any optional informations, just press `enter` key without typing/pasting anything. +(If you want to skip any optional infos, just press `enter` key without typing/pasting anything.) Once done, #####Part V -You should see the options again within the **tmux session** named *nadako*. +You should see the options again within the **`tmux` session** named `nadeko` *(remember this one)* Next, press `3` to **Run Nadeko (Normally)** Check in your discord server if your new bot is working properly. #####Part VI -If the bot is working properly in your server, type `.die` to shut down the bot. -You should be back to the options again, +If your bot is working properly in your server, type `.die` to shut down the bot. + +You should be back to the options screen again on **PuTTY**, from the options choose `4` to **Run Nadeko with Auto Restart.** It will show you more options: @@ -118,11 +119,12 @@ It will show you more options: 3. Auto Restart and Update with Stable Build 4. Exit ``` -Choose anything you like and once the bot's back online again in your server, close the **PuTTY**. -Done, You now have your own **NadekoBot**. +Choose anything you like and once the bot's back online again in your server, close the **PuTTY**. + +**Done**, You now have your own **NadekoBot**. -[To **Restart** your **NadekoBot** anytime later.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) +[Check this when you need to **restart** your **NadekoBot** anytime later along with tmux session.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) ####Running NadekoBot @@ -156,6 +158,11 @@ Next to **move the bot to background** and to do that, press **CTRL+B+D** (this ####Restarting Nadeko +**Restarting NadekoBot:** + +**If** you have chosen option `4` to **Run Nadeko with Auto Restart** from Nadeko's `linuxAIO.sh` *[(you got it from this step)](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#getting-nadekobot)* +You can simply type `.die` in the server you have your NadekoBot to make her restart. + **Restarting Nadeko with the Server:** Open **PuTTY** and login as you have before, type `reboot` and hit Enter. @@ -168,14 +175,14 @@ Open **PuTTY** and login as you have before, type `reboot` and hit Enter. ####Updating Nadeko -- Connect to the terminal through PuTTY. +- Connect to the terminal through **PuTTY**. - `tmux kill-session -t nadeko` (don't forget to replace **nadeko** in the command with the name of your bot's session) - Make sure the bot is **not** running. - `tmux new -s nadeko` (**nadeko** is the name of the session) - `cd ~ && bash linuxAIO.sh` - Choose either `1` or `2` to update the bot with **latest build** or **stable build** respectively. - Choose either `3` or `4` to run the bot again with **normally** or **auto restart** respectively. -- Done. You can close PuTTY now. +- Done. You can close **PuTTY** now. ####Installing Manually (Optional) From e3b6c8dffb8c003287f135f752c5d242c773068b Mon Sep 17 00:00:00 2001 From: samvaio Date: Thu, 2 Feb 2017 22:15:06 +0530 Subject: [PATCH 011/256] added lil more info --- docs/guides/Linux Guide.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guides/Linux Guide.md b/docs/guides/Linux Guide.md index 8a6cab9f..8e01383e 100644 --- a/docs/guides/Linux Guide.md +++ b/docs/guides/Linux Guide.md @@ -55,7 +55,7 @@ You should see these following options after using the above command: 6. Set up credentials.json (if you have downloaded the bot already) 7. To exit ``` -#####Part II +#####Part II (Optional) **If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed, Press `5` and `enter` key, then `y` when you see the following: ``` Welcome to NadekoBot Auto Prerequisites Installer. @@ -76,8 +76,8 @@ and then press `enter` key. Once Installation is completed you should see the options again. Next, check out: -#####Part IV -If you prefer to do this step [manually](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-sftp), and skip other parts click on the link. *(Optional)* +#####Part IV (Optional) +If you prefer to skip this step and want to do it [manually](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-sftp) or already have the `credentials.json` file, click on the link. *(Optional)* - [1. Setting up credentials.json](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#setting-up-credentialsjson) - [2. To Get the Google API](http://nadekobot.readthedocs.io/en/latest/guides/Windows%20Guide/#setting-up-nadekobot-for-music) From 9791b5b9e367311ad719671b78da1380ed27755b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 21:00:38 +0100 Subject: [PATCH 012/256] v+t slower but much better and reliable. Need to check ratelimits before speeding it up. --- .../Commands/ProtectionCommands.cs | 109 ++++++----- .../Commands/VoicePlusTextCommands.cs | 182 ++++++++++++------ src/NadekoBot/NadekoBot.cs | 2 +- 3 files changed, 185 insertions(+), 108 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs index e37f4850..3e3b217a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -105,74 +105,85 @@ namespace NadekoBot.Modules.Administration antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam }); } - NadekoBot.Client.MessageReceived += async (imsg) => + NadekoBot.Client.MessageReceived += (imsg) => { + var msg = imsg as IUserMessage; + if (msg == null || msg.Author.IsBot) + return Task.CompletedTask; - try + var channel = msg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; + var _ = Task.Run(async () => { - var msg = imsg as IUserMessage; - if (msg == null || msg.Author.IsBot) - return; - - var channel = msg.Channel as ITextChannel; - if (channel == null) - return; - AntiSpamStats spamSettings; - if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || - spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore() - { - ChannelId = channel.Id - })) - return; - - var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content), - (id, old) => { old.ApplyNextMessage(msg.Content); return old; }); - - if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold) + try { - if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) + AntiSpamStats spamSettings; + if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || + spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore() + { + ChannelId = channel.Id + })) + return; + + var stats = spamSettings.UserStats.AddOrUpdate(msg.Author.Id, new UserSpamStats(msg.Content), + (id, old) => + { + old.ApplyNextMessage(msg.Content); return old; + }); + + if (stats.Count >= spamSettings.AntiSpamSettings.MessageThreshold) { - await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author) - .ConfigureAwait(false); + if (spamSettings.UserStats.TryRemove(msg.Author.Id, out stats)) + { + await PunishUsers(spamSettings.AntiSpamSettings.Action, ProtectionType.Spamming, (IGuildUser)msg.Author) + .ConfigureAwait(false); + } } } - } - catch { } + catch { } + }); + return Task.CompletedTask; }; - NadekoBot.Client.UserJoined += async (usr) => + NadekoBot.Client.UserJoined += (usr) => { - try + if (usr.IsBot) + return Task.CompletedTask; + AntiRaidStats settings; + if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) + return Task.CompletedTask; + if (!settings.RaidUsers.Add(usr)) + return Task.CompletedTask; + + var _ = Task.Run(async () => { - if (usr.IsBot) - return; - AntiRaidStats settings; - if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) - return; - if (!settings.RaidUsers.Add(usr)) - return; - - ++settings.UsersCount; - - if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold) + try { - var users = settings.RaidUsers.ToArray(); - settings.RaidUsers.Clear(); + ++settings.UsersCount; + + if (settings.UsersCount >= settings.AntiRaidSettings.UserThreshold) + { + var users = settings.RaidUsers.ToArray(); + settings.RaidUsers.Clear(); + + await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false); + } + await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false); + + settings.RaidUsers.TryRemove(usr); + --settings.UsersCount; - await PunishUsers(settings.AntiRaidSettings.Action, ProtectionType.Raiding, users).ConfigureAwait(false); } - await Task.Delay(1000 * settings.AntiRaidSettings.Seconds).ConfigureAwait(false); - - settings.RaidUsers.TryRemove(usr); - --settings.UsersCount; - - } - catch { } + catch { } + }); + return Task.CompletedTask; }; } private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus) { + _log.Warn($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild"); foreach (var gu in gus) { switch (action) diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs index 9d92ff98..3b68f36f 100644 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -7,9 +7,11 @@ using NadekoBot.Services; using NLog; using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; +using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Administration @@ -22,6 +24,8 @@ namespace NadekoBot.Modules.Administration private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); private static ConcurrentHashSet voicePlusTextCache { get; } + + private static ConcurrentDictionary guildLockObjects = new ConcurrentDictionary(); static VoicePlusTextCommands() { var _log = LogManager.GetCurrentClassLogger(); @@ -36,78 +40,119 @@ namespace NadekoBot.Modules.Administration _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); } - private static async Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after) + private static Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after) { var user = (iuser as SocketGuildUser); var guild = user?.Guild; if (guild == null) - return; + return Task.CompletedTask; - try + var botUserPerms = guild.CurrentUser.GuildPermissions; + + if (before.VoiceChannel == after.VoiceChannel) + return Task.CompletedTask; + + if (!voicePlusTextCache.Contains(guild.Id)) + return Task.CompletedTask; + + var _ = Task.Run(async () => { - var botUserPerms = guild.CurrentUser.GuildPermissions; - - if (before.VoiceChannel == after.VoiceChannel) return; - - if (!voicePlusTextCache.Contains(guild.Id)) - return; - - if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles) + try { + + if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles) + { + try + { + await guild.Owner.SendErrorAsync( + "⚠️ I don't have **manage server** and/or **manage channels** permission," + + $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); + } + catch { } + using (var uow = DbHandler.UnitOfWork()) + { + uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; + voicePlusTextCache.TryRemove(guild.Id); + await uow.CompleteAsync().ConfigureAwait(false); + } + return; + } + + var semaphore = guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1)); + try { - await guild.Owner.SendErrorAsync( - "⚠️ I don't have **manage server** and/or **manage channels** permission," + - $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); - } - catch { } - using (var uow = DbHandler.UnitOfWork()) - { - uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; - voicePlusTextCache.TryRemove(guild.Id); - await uow.CompleteAsync().ConfigureAwait(false); - } - return; - } + await semaphore.WaitAsync().ConfigureAwait(false); + var beforeVch = before.VoiceChannel; + if (beforeVch != null) + { + var beforeRoleName = GetRoleName(beforeVch); + var beforeRole = guild.Roles.FirstOrDefault(x => x.Name == beforeRoleName); + if (beforeRole != null) + try + { + _log.Warn("Removing role " + beforeRoleName + " from user " + user.Username); + await user.RemoveRolesAsync(beforeRole).ConfigureAwait(false); + await Task.Delay(200).ConfigureAwait(false); + } + catch (Exception ex) + { + _log.Warn(ex); + } + } + var afterVch = after.VoiceChannel; + if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id) + { + var roleName = GetRoleName(afterVch); + IRole roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName); + if (roleToAdd == null) + roleToAdd = await guild.CreateRoleAsync(roleName).ConfigureAwait(false); - var beforeVch = before.VoiceChannel; - if (beforeVch != null) - { - var textChannel = guild.TextChannels.Where(t => t.Name == GetChannelName(beforeVch.Name).ToLowerInvariant()).FirstOrDefault(); - if (textChannel != null) - await textChannel.AddPermissionOverwriteAsync(user, - new OverwritePermissions(readMessages: PermValue.Deny, - sendMessages: PermValue.Deny)).ConfigureAwait(false); - } - var afterVch = after.VoiceChannel; - if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id) - { - ITextChannel textChannel = guild.TextChannels - .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) - .FirstOrDefault(); - if (textChannel == null) - { - textChannel = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); - await textChannel.AddPermissionOverwriteAsync(guild.EveryoneRole, - new OverwritePermissions(readMessages: PermValue.Deny, - sendMessages: PermValue.Deny)).ConfigureAwait(false); + ITextChannel textChannel = guild.TextChannels + .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) + .FirstOrDefault(); + if (textChannel == null) + { + var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); + + try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch { } + await Task.Delay(50).ConfigureAwait(false); + await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions( + readMessages: PermValue.Allow, + sendMessages: PermValue.Allow)) + .ConfigureAwait(false); + await Task.Delay(50).ConfigureAwait(false); + await created.AddPermissionOverwriteAsync(guild.EveryoneRole, new OverwritePermissions( + readMessages: PermValue.Deny, + sendMessages: PermValue.Deny)) + .ConfigureAwait(false); + await Task.Delay(50).ConfigureAwait(false); + } + _log.Warn("Adding role " + roleToAdd.Name + " to user " + user.Username); + await user.AddRolesAsync(roleToAdd).ConfigureAwait(false); + } + } + finally + { + semaphore.Release(); } - await textChannel.AddPermissionOverwriteAsync(user, - new OverwritePermissions(readMessages: PermValue.Allow, - sendMessages: PermValue.Allow)).ConfigureAwait(false); } - } - catch (Exception ex) - { - Console.WriteLine(ex); - } + catch (Exception ex) + { + _log.Warn(ex); + } + }); + return Task.CompletedTask; } private static string GetChannelName(string voiceName) => channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; + private static string GetRoleName(IVoiceChannel ch) => + "nvoice-" + ch.Id; + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] @@ -127,7 +172,7 @@ namespace NadekoBot.Modules.Administration { try { - await Context.Channel.SendErrorAsync("⚠️ You are enabling this feature and **I do not have ADMINISTRATOR permissions**. " + + await Context.Channel.SendErrorAsync("⚠️ You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. " + "`This may cause some issues, and you will have to clean up text channels yourself afterwards.`"); } catch { } @@ -147,6 +192,13 @@ namespace NadekoBot.Modules.Administration foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice"))) { try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } + await Task.Delay(500).ConfigureAwait(false); + } + + foreach (var role in guild.Roles.Where(c => c.Name.StartsWith("nvoice-"))) + { + try { await role.DeleteAsync().ConfigureAwait(false); } catch { } + await Task.Delay(500).ConfigureAwait(false); } await Context.Channel.SendConfirmAsync("ℹ️ Successfuly **removed** voice + text feature.").ConfigureAwait(false); return; @@ -163,7 +215,9 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageRoles)] + //[RequireBotPermission(GuildPermission.ManageRoles)] public async Task CleanVPlusT() { var guild = Context.Guild; @@ -174,15 +228,27 @@ namespace NadekoBot.Modules.Administration return; } - var allTxtChannels = (await guild.GetTextChannelsAsync()).Where(c => c.Name.EndsWith("-voice")); - var validTxtChannelNames = (await guild.GetVoiceChannelsAsync()).Select(c => GetChannelName(c.Name).ToLowerInvariant()); + var textChannels = await guild.GetTextChannelsAsync().ConfigureAwait(false); + var voiceChannels = await guild.GetVoiceChannelsAsync().ConfigureAwait(false); - var invalidTxtChannels = allTxtChannels.Where(c => !validTxtChannelNames.Contains(c.Name)); + var boundTextChannels = textChannels.Where(c => c.Name.EndsWith("-voice")); + var validTxtChannelNames = new HashSet(voiceChannels.Select(c => GetChannelName(c.Name).ToLowerInvariant())); + var invalidTxtChannels = boundTextChannels.Where(c => !validTxtChannelNames.Contains(c.Name)); foreach (var c in invalidTxtChannels) { try { await c.DeleteAsync().ConfigureAwait(false); } catch { } - await Task.Delay(500); + await Task.Delay(500).ConfigureAwait(false); + } + + var boundRoles = guild.Roles.Where(r => r.Name.StartsWith("nvoice-")); + var validRoleNames = new HashSet(voiceChannels.Select(c => GetRoleName(c).ToLowerInvariant())); + var invalidRoles = boundRoles.Where(r => !validRoleNames.Contains(r.Name)); + + foreach (var r in invalidRoles) + { + try { await r.DeleteAsync().ConfigureAwait(false); } catch { } + await Task.Delay(500).ConfigureAwait(false); } await Context.Channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false); diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index a6842797..ba411cea 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -113,7 +113,7 @@ namespace NadekoBot await CommandHandler.StartHandling().ConfigureAwait(false); - await CommandService.AddModulesAsync(this.GetType().GetTypeInfo().Assembly).ConfigureAwait(false); + var _ = await Task.Run(() => CommandService.AddModulesAsync(this.GetType().GetTypeInfo().Assembly)).ConfigureAwait(false); #if !GLOBAL_NADEKO await CommandService.AddModuleAsync().ConfigureAwait(false); #endif From 4a3d66e712505aaf2417459d992e33dfb7b0b1ca Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 21:59:01 +0100 Subject: [PATCH 013/256] Images service. Preloads images bot uses. Holds heads and tails images only for now. --- .../Gambling/Commands/FlipCoinCommand.cs | 30 +++++++----- src/NadekoBot/NadekoBot.cs | 5 ++ src/NadekoBot/Services/IImagesService.cs | 17 +++++++ src/NadekoBot/Services/Impl/ImagesService.cs | 46 +++++++++++++++++++ 4 files changed, 86 insertions(+), 12 deletions(-) create mode 100644 src/NadekoBot/Services/IImagesService.cs create mode 100644 src/NadekoBot/Services/Impl/ImagesService.cs diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 85092dc7..74e201c0 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -16,32 +16,38 @@ namespace NadekoBot.Modules.Gambling [Group] public class FlipCoinCommands : ModuleBase { + private readonly IImagesService _images; + private static NadekoRandom rng { get; } = new NadekoRandom(); - private const string headsPath = "data/images/coins/heads.png"; - private const string tailsPath = "data/images/coins/tails.png"; - + + public FlipCoinCommands() + { + //todo DI in the future, can't atm + this._images = NadekoBot.Images; + } + [NadekoCommand, Usage, Description, Aliases] public async Task Flip(int count = 1) { if (count == 1) { if (rng.Next(0, 2) == 1) - await Context.Channel.SendFileAsync(File.Open(headsPath, FileMode.OpenOrCreate), "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(_images.Heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); else - await Context.Channel.SendFileAsync(File.Open(tailsPath, FileMode.OpenOrCreate), "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(_images.Tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); return; } if (count > 10 || count < 1) { - await Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`"); + await Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`").ConfigureAwait(false); return; } var imgs = new Image[count]; for (var i = 0; i < count; i++) { imgs[i] = rng.Next(0, 10) < 5 ? - new Image(File.OpenRead(headsPath)) : - new Image(File.OpenRead(tailsPath)); + new Image(_images.Heads) : + new Image(_images.Tails); } await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } @@ -70,15 +76,15 @@ namespace NadekoBot.Modules.Gambling var isHeads = guessStr == "HEADS" || guessStr == "H"; bool result = false; - string imgPathToSend; + Stream imageToSend; if (rng.Next(0, 2) == 1) { - imgPathToSend = headsPath; + imageToSend = _images.Heads; result = true; } else { - imgPathToSend = tailsPath; + imageToSend = _images.Tails; } string str; @@ -93,7 +99,7 @@ namespace NadekoBot.Modules.Gambling str = $"{Context.User.Mention}`Better luck next time.`"; } - await Context.Channel.SendFileAsync(File.Open(imgPathToSend, FileMode.OpenOrCreate), new FileInfo(imgPathToSend).Name, str).ConfigureAwait(false); + await Context.Channel.SendFileAsync(imageToSend, "result.png", str).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index ba411cea..a32abc73 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -33,6 +33,7 @@ namespace NadekoBot public static GoogleApiService Google { get; private set; } public static StatsService Stats { get; private set; } + public static IImagesService Images { get; private set; } public static ConcurrentDictionary ModulePrefixes { get; private set; } public static bool Ready { get; private set; } @@ -68,8 +69,11 @@ namespace NadekoBot LogLevel = LogSeverity.Warning, TotalShards = Credentials.TotalShards, ConnectionTimeout = int.MaxValue, +#if !GLOBAL_NADEKO AlwaysDownloadUsers = true, +#endif }); + #if GLOBAL_NADEKO Client.Log += Client_Log; #endif @@ -82,6 +86,7 @@ namespace NadekoBot Google = new GoogleApiService(); CommandHandler = new CommandHandler(Client, CommandService); Stats = new StatsService(Client, CommandHandler); + Images = await ImagesService.Create().ConfigureAwait(false); ////setup DI //var depMap = new DependencyMap(); diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs new file mode 100644 index 00000000..b00b4a4a --- /dev/null +++ b/src/NadekoBot/Services/IImagesService.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services +{ + public interface IImagesService + { + Stream Heads { get; } + Stream Tails { get; } + + Task Reload(); + } +} diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs new file mode 100644 index 00000000..e57636c1 --- /dev/null +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -0,0 +1,46 @@ +using NLog; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Impl +{ + public class ImagesService : IImagesService + { + private readonly Logger _log; + + private const string headsPath = "data/images/coins/heads.png"; + private const string tailsPath = "data/images/coins/tails.png"; + + private byte[] heads; + public Stream Heads => new MemoryStream(heads, false); + + private byte[] tails; + public Stream Tails => new MemoryStream(tails, false); + + private ImagesService() + { + _log = LogManager.GetCurrentClassLogger(); + } + + public static async Task Create() + { + var srvc = new ImagesService(); + await srvc.Reload().ConfigureAwait(false); + return srvc; + } + + public Task Reload() => Task.Run(() => + { + _log.Info("Loading images..."); + var sw = Stopwatch.StartNew(); + heads = File.ReadAllBytes(headsPath); + tails = File.ReadAllBytes(tailsPath); + _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); + }); + } +} \ No newline at end of file From 22f7443f0b1fe3b2b447afec7fd9641339556594 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 2 Feb 2017 22:05:53 +0100 Subject: [PATCH 014/256] punishments and v+t info is info now, not warning --- .../Modules/Administration/Commands/ProtectionCommands.cs | 2 +- .../Modules/Administration/Commands/VoicePlusTextCommands.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs index 3e3b217a..7639a4dc 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -183,7 +183,7 @@ namespace NadekoBot.Modules.Administration private static async Task PunishUsers(PunishmentAction action, ProtectionType pt, params IGuildUser[] gus) { - _log.Warn($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild"); + _log.Info($"[{pt}] - Punishing [{gus.Length}] users with [{action}] in {gus[0].Guild.Name} guild"); foreach (var gu in gus) { switch (action) diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs index 3b68f36f..d0c905bb 100644 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -93,7 +93,7 @@ namespace NadekoBot.Modules.Administration if (beforeRole != null) try { - _log.Warn("Removing role " + beforeRoleName + " from user " + user.Username); + _log.Info("Removing role " + beforeRoleName + " from user " + user.Username); await user.RemoveRolesAsync(beforeRole).ConfigureAwait(false); await Task.Delay(200).ConfigureAwait(false); } From a728e6f670d6556768918918d1e887fc84faaee2 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 3 Feb 2017 00:22:29 +0100 Subject: [PATCH 015/256] Currency images are preloaded now too.(>plant/>gc) --- .../Games/Commands/PlantAndPickCommands.cs | 16 +++++----- src/NadekoBot/Services/IImagesService.cs | 3 ++ src/NadekoBot/Services/Impl/ImagesService.cs | 29 +++++++++++++++--- src/NadekoBot/data/currency_images/img2.jpg | Bin 230256 -> 33845 bytes 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 6b5da1b1..0fe72678 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -93,10 +93,10 @@ namespace NadekoBot.Modules.Games { firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!"; } - var file = GetRandomCurrencyImagePath(); + var file = GetRandomCurrencyImage(); var sent = await channel.SendFileAsync( - File.Open(file, FileMode.OpenOrCreate), - new FileInfo(file).Name, + file.Item2, + file.Item1, $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") .ConfigureAwait(false); @@ -159,7 +159,7 @@ namespace NadekoBot.Modules.Games return; } - var file = GetRandomCurrencyImagePath(); + var file = GetRandomCurrencyImage(); IUserMessage msg; var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); @@ -170,7 +170,7 @@ namespace NadekoBot.Modules.Games } else { - msg = await Context.Channel.SendFileAsync(File.Open(file, FileMode.OpenOrCreate), new FileInfo(file).Name, msgToSend).ConfigureAwait(false); + msg = await Context.Channel.SendFileAsync(file.Item2, file.Item1, msgToSend).ConfigureAwait(false); } var msgs = new IUserMessage[amount]; @@ -220,10 +220,12 @@ namespace NadekoBot.Modules.Games } } - private static string GetRandomCurrencyImagePath() + private static Tuple GetRandomCurrencyImage() { var rng = new NadekoRandom(); - return Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault(); + var images = NadekoBot.Images.CurrencyImages; + + return images[rng.Next(0, images.Count)]; } int GetRandomNumber() diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index b00b4a4a..192ee842 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Text; @@ -12,6 +13,8 @@ namespace NadekoBot.Services Stream Heads { get; } Stream Tails { get; } + IImmutableList> CurrencyImages { get; } + Task Reload(); } } diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index e57636c1..f63e2eb5 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -1,6 +1,8 @@ using NLog; using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; @@ -16,11 +18,18 @@ namespace NadekoBot.Services.Impl private const string headsPath = "data/images/coins/heads.png"; private const string tailsPath = "data/images/coins/tails.png"; + private const string currencyImagesPath = "data/currency_images"; + private byte[] heads; public Stream Heads => new MemoryStream(heads, false); private byte[] tails; public Stream Tails => new MemoryStream(tails, false); + //todo tuple + private IReadOnlyDictionary currencyImages; + public IImmutableList> CurrencyImages => + currencyImages.Select(x => new Tuple(x.Key, (Stream)new MemoryStream(x.Value, false))) + .ToImmutableArray(); private ImagesService() { @@ -36,11 +45,21 @@ namespace NadekoBot.Services.Impl public Task Reload() => Task.Run(() => { - _log.Info("Loading images..."); - var sw = Stopwatch.StartNew(); - heads = File.ReadAllBytes(headsPath); - tails = File.ReadAllBytes(tailsPath); - _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); + try + { + _log.Info("Loading images..."); + var sw = Stopwatch.StartNew(); + heads = File.ReadAllBytes(headsPath); + tails = File.ReadAllBytes(tailsPath); + + currencyImages = Directory.GetFiles(currencyImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); + _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); + } + catch (Exception ex) + { + _log.Error(ex); + throw; + } }); } } \ No newline at end of file diff --git a/src/NadekoBot/data/currency_images/img2.jpg b/src/NadekoBot/data/currency_images/img2.jpg index 5697f8bbf5319a59a7b0494ba2c57fd942a87426..cb22be2c9270022ae11c4d95b17b4bac0c16a219 100644 GIT binary patch delta 29564 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 C$!b&p delta 227522 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 Date: Fri, 3 Feb 2017 09:54:22 +0100 Subject: [PATCH 016/256] .reloadimages owner-only command added. --- .../Administration/Commands/SelfCommands.cs | 18 ++++ .../Games/Commands/PlantAndPickCommands.cs | 98 ++++++++++--------- .../Resources/CommandStrings.Designer.cs | 27 +++++ src/NadekoBot/Resources/CommandStrings.resx | 9 ++ 4 files changed, 107 insertions(+), 45 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index a56ee402..b2af48ab 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -3,6 +3,7 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using System; +using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; @@ -168,6 +169,23 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false); } + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [OwnerOnly] + public async Task ReloadImages() + { + var channel = (ITextChannel)Context.Channel; + + var msg = await Context.Channel.SendMessageAsync("Reloading Images...").ConfigureAwait(false); + var sw = Stopwatch.StartNew(); + await NadekoBot.Images.Reload().ConfigureAwait(false); + sw.Stop(); + await msg.ModifyAsync(x => + { + x.Content = "✅ Images reloaded."; + }).ConfigureAwait(false); + } + private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) { switch (sus) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 0fe72678..ab40b511 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -51,62 +51,70 @@ namespace NadekoBot.Modules.Games .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId))); } - private static async Task PotentialFlowerGeneration(SocketMessage imsg) + private static Task PotentialFlowerGeneration(SocketMessage imsg) { - try + var msg = imsg as SocketUserMessage; + if (msg == null || msg.IsAuthor() || msg.Author.IsBot) + return Task.CompletedTask; + + var channel = imsg.Channel as ITextChannel; + if (channel == null) + return Task.CompletedTask; + + if (!generationChannels.Contains(channel.Id)) + return Task.CompletedTask; + + var _ = Task.Run(async () => { - var msg = imsg as SocketUserMessage; - if (msg == null || msg.IsAuthor() || msg.Author.IsBot) - return; - - var channel = imsg.Channel as ITextChannel; - if (channel == null) - return; - - if (!generationChannels.Contains(channel.Id)) - return; - - var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); - var rng = new NadekoRandom(); - - if (DateTime.Now - TimeSpan.FromSeconds(NadekoBot.BotConfig.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again - return; - - var num = rng.Next(1, 101) + NadekoBot.BotConfig.CurrencyGenerationChance * 100; - - if (num > 100) + try { - lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); + var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); + var rng = new NadekoRandom(); - var dropAmount = NadekoBot.BotConfig.CurrencyDropAmount; + //todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup: + if (DateTime.Now - TimeSpan.FromSeconds(NadekoBot.BotConfig.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again + return; - if (dropAmount > 0) + var num = rng.Next(1, 101) + NadekoBot.BotConfig.CurrencyGenerationChance * 100; + + if (num > 100) { - var msgs = new IUserMessage[dropAmount]; + lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); - string firstPart; - if (dropAmount == 1) + var dropAmount = NadekoBot.BotConfig.CurrencyDropAmount; + + if (dropAmount > 0) { - firstPart = $"A random { NadekoBot.BotConfig.CurrencyName } appeared!"; - } - else - { - firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!"; - } - var file = GetRandomCurrencyImage(); - var sent = await channel.SendFileAsync( - file.Item2, - file.Item1, - $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") - .ConfigureAwait(false); + var msgs = new IUserMessage[dropAmount]; - msgs[0] = sent; + string firstPart; + if (dropAmount == 1) + { + firstPart = $"A random { NadekoBot.BotConfig.CurrencyName } appeared!"; + } + else + { + firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!"; + } + var file = GetRandomCurrencyImage(); + var sent = await channel.SendFileAsync( + file.Item2, + file.Item1, + $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") + .ConfigureAwait(false); - plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; }); + msgs[0] = sent; + + plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; }); + } } } - } - catch { } + catch (Exception ex) + { + _log.Warn(ex); + } + }); + return Task.CompletedTask; } [NadekoCommand, Usage, Description, Aliases] @@ -162,7 +170,7 @@ namespace NadekoBot.Modules.Games var file = GetRandomCurrencyImage(); IUserMessage msg; var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); - + var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; if (file == null) { diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index e89ba368..634991da 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -5621,6 +5621,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to reloadimages. + /// + public static string reloadimages_cmd { + get { + return ResourceManager.GetString("reloadimages_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Reloads images bot is using. Safe to use even when bot is being used heavily.. + /// + public static string reloadimages_desc { + get { + return ResourceManager.GetString("reloadimages_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}reloadimages`. + /// + public static string reloadimages_usage { + get { + return ResourceManager.GetString("reloadimages_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to remind. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 7ee5714b..9b0b01be 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3042,4 +3042,13 @@ `{0}smch` + + reloadimages + + + Reloads images bot is using. Safe to use even when bot is being used heavily. + + + `{0}reloadimages` + \ No newline at end of file From 0b9c52fc63533841d697baf8e2e67b9d7f6e07dd Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 3 Feb 2017 09:56:27 +0100 Subject: [PATCH 017/256] commandlist update --- docs/Commands List.md | 301 +++++++++++++++++++++--------------------- 1 file changed, 152 insertions(+), 149 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index ed71757c..d73852f3 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -18,56 +18,6 @@ You can support the project on patreon: or paypa ### Administration Command and aliases | Description | Usage ----------------|--------------|------- -`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.voice+text` -`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t` -`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30` -`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet` -`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.` -`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm` -`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`. -`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye` -`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` -`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30` -`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` -`.die` | Shuts the bot down. **Bot Owner only.** | `.die` -`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` -`.setstatus` | Sets the bot's status. (Online/Idle/Dnd/Invisible) **Bot Owner only.** | `.setstatus Idle` -`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` -`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` -`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` -`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message` -`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam` -`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm` -`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer` -`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar` -`.lsar` | Lists all self-assignable roles. | `.lsar` -`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar` -`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer` -`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer` -`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode` -`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick` -`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban` -`.antispamignore` | Toggles whether antispam ignores current channel. Antispam must be enabled. | `.antispamignore` -`.antilist` `.antilst` | Shows currently enabled protection features. | `.antilist` -`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl` -`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued% **Bot Owner only.** | `.adpl` -`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl` -`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl` -`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced` -`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone` -`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone` -`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone` -`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone` -`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` -`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` -`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` -`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` -`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` -`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` -`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned` -`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` -`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` -`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable `.resetperms` | Resets BOT's permissions module on this server to the default value. **Requires Administrator server permission.** | `.resetperms` `.delmsgoncmd` | Toggles the automatic deletion of user's successful command message to prevent chat flood. **Requires Administrator server permission.** | `.delmsgoncmd` `.setrole` `.sr` | Sets a role for a given user. **Requires ManageRoles server permission.** | `.sr @User Guest` @@ -91,6 +41,57 @@ Command and aliases | Description | Usage `.mentionrole` `.menro` | Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. **Requires MentionEveryone server permission.** | `.menro RoleName` `.donators` | List of lovely people who donated to keep this project alive. | `.donators` `.donadd` | Add a donator to the database. **Bot Owner only.** | `.donadd Donate Amount` +`.autoassignrole` `.aar` | Automaticaly assigns a specified role to every user who joins the server. **Requires ManageRoles server permission.** | `.aar` to disable, `.aar Role Name` to enable +`.fwmsgs` | Toggles forwarding of non-command messages sent to bot's DM to the bot owners **Bot Owner only.** | `.fwmsgs` +`.fwtoall` | Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json **Bot Owner only.** | `.fwtoall` +`.logserver` | Enables or Disables ALL log events. If enabled, all log events will log to this channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logserver enable` or `.logserver disable` +`.logignore` | Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. **Requires Administrator server permission.** **Bot Owner only.** | `.logignore` +`.logevents` | Shows a list of all events you can subscribe to with `.log` **Requires Administrator server permission.** **Bot Owner only.** | `.logevents` +`.log` | Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `.logevents` to see a list of all events you can subscribe to. **Requires Administrator server permission.** **Bot Owner only.** | `.log userpresence` or `.log userbanned` +`.migratedata` | Migrate data from old bot configuration **Bot Owner only.** | `.migratedata` +`.setmuterole` | Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. **Requires ManageRoles server permission.** | `.setmuterole Silenced` +`.mute` | Mutes a mentioned user both from speaking and chatting. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.mute @Someone` +`.unmute` | Unmutes a mentioned user previously muted with `.mute` command. **Requires ManageRoles server permission.** **Requires MuteMembers server permission.** | `.unmute @Someone` +`.chatmute` | Prevents a mentioned user from chatting in text channels. **Requires ManageRoles server permission.** | `.chatmute @Someone` +`.chatunmute` | Removes a mute role previously set on a mentioned user with `.chatmute` which prevented him from chatting in text channels. **Requires ManageRoles server permission.** | `.chatunmute @Someone` +`.voicemute` | Prevents a mentioned user from speaking in voice channels. **Requires MuteMembers server permission.** | `.voicemute @Someone` +`.voiceunmute` | Gives a previously voice-muted user a permission to speak. **Requires MuteMembers server permission.** | `.voiceunmute @Someguy` +`.rotateplaying` `.ropl` | Toggles rotation of playing status of the dynamic strings you previously specified. **Bot Owner only.** | `.ropl` +`.addplaying` `.adpl` | Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds% **Bot Owner only.** | `.adpl` +`.listplaying` `.lipl` | Lists all playing statuses with their corresponding number. **Bot Owner only.** | `.lipl` +`.removeplaying` `.rmpl` `.repl` | Removes a playing string on a given number. **Bot Owner only.** | `.rmpl` +`.antiraid` | Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) **Requires Administrator server permission.** | `.antiraid 5 20 Kick` +`.antispam` | Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. **Requires Administrator server permission.** | `.antispam 3 Mute` or `.antispam 4 Kick` or `.antispam 6 Ban` +`.antispamignore` | Toggles whether antispam ignores current channel. Antispam must be enabled. | `.antispamignore` +`.antilist` `.antilst` | Shows currently enabled protection features. | `.antilist` +`.slowmode` | Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. **Requires ManageMessages server permission.** | `.slowmode 1 5` or `.slowmode` +`.adsarm` | Toggles the automatic deletion of confirmations for .iam and .iamn commands. **Requires ManageMessages server permission.** | `.adsarm` +`.asar` | Adds a role to the list of self-assignable roles. **Requires ManageRoles server permission.** | `.asar Gamer` +`.rsar` | Removes a specified role from the list of self-assignable roles. **Requires ManageRoles server permission.** | `.rsar` +`.lsar` | Lists all self-assignable roles. | `.lsar` +`.togglexclsar` `.tesar` | Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) **Requires ManageRoles server permission.** | `.tesar` +`.iam` | Adds a role to you that you choose. Role must be on a list of self-assignable roles. | `.iam Gamer` +`.iamnot` `.iamn` | Removes a role to you that you choose. Role must be on a list of self-assignable roles. | `.iamn Gamer` +`.leave` | Makes Nadeko leave the server. Either name or id required. **Bot Owner only.** | `.leave 123123123331` +`.die` | Shuts the bot down. **Bot Owner only.** | `.die` +`.setname` `.newnm` | Gives the bot a new name. **Bot Owner only.** | `.newnm BotName` +`.setstatus` | Sets the bot's status. (Online/Idle/Dnd/Invisible) **Bot Owner only.** | `.setstatus Idle` +`.setavatar` `.setav` | Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. **Bot Owner only.** | `.setav http://i.imgur.com/xTG3a1I.jpg` +`.setgame` | Sets the bots game. **Bot Owner only.** | `.setgame with snakes` +`.setstream` | Sets the bots stream. First argument is the twitch link, second argument is stream name. **Bot Owner only.** | `.setstream TWITCHLINK Hello` +`.send` | Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. **Bot Owner only.** | `.send serverid|c:channelid message` or `.send serverid|u:userid message` +`.announce` | Sends a message to all servers' general channel bot is connected to. **Bot Owner only.** | `.announce Useless spam` +`.reloadimages` | Reloads images bot is using. Safe to use even when bot is being used heavily. **Bot Owner only.** | `.reloadimages` +`.greetdel` `.grdel` | Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.greetdel 0` or `.greetdel 30` +`.greet` | Toggles anouncements on the current channel when someone joins the server. **Requires ManageServer server permission.** | `.greet` +`.greetmsg` | Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. **Requires ManageServer server permission.** | `.greetmsg Welcome, %user%.` +`.greetdm` | Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). **Requires ManageServer server permission.** | `.greetdm` +`.greetdmmsg` | Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. **Requires ManageServer server permission.** | `.greetdmmsg Welcome to the server, %user%`. +`.bye` | Toggles anouncements on the current channel when someone leaves the server. **Requires ManageServer server permission.** | `.bye` +`.byemsg` | Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. **Requires ManageServer server permission.** | `.byemsg %user% has left.` +`.byedel` | Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. **Requires ManageServer server permission.** | `.byedel 0` or `.byedel 30` +`.voice+text` `.v+t` | Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. **Requires ManageRoles server permission.** **Requires ManageChannels server permission.** | `.v+t` +`.cleanvplust` `.cv+t` | Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. **Requires ManageChannels server permission.** **Requires ManageRoles server permission.** | `.cleanv+t` ###### [Back to TOC](#table-of-contents) @@ -125,24 +126,6 @@ Command and aliases | Description | Usage ### Gambling Command and aliases | Description | Usage ----------------|--------------|------- -`$claimwaifu` `$claim` | Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `$affinity` towards you. | `$claim 50 @Himesama` -`$divorce` | Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. | `$divorce @CheatingSloot` -`$affinity` | Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `$claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. | `$affinity @MyHusband` or `$affinity` -`$waifus` `$waifulb` | Shows top 9 waifus. | `$waifus` -`$waifuinfo` `$waifustats` | Shows waifu stats for a target person. Defaults to you if no user is provided. | `$waifuinfo @MyCrush` or `$waifuinfo` -`$slotstats` | Shows the total stats of the slot command for this bot's session. **Bot Owner only.** | `$slotstats` -`$slottest` | Tests to see how much slots payout for X number of plays. **Bot Owner only.** | `$slottest 1000` -`$slot` | Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. | `$slot 5` -`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` -`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.8x the currency you've bet. | `$bf 5 heads` or `$bf 3 t` -`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` -`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh` -`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` -`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` -`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` -`$startevent` | Starts one of the events seen on public nadeko. **Bot Owner only.** | `$startevent flowerreaction` -`$race` | Starts a new animal race. | `$race` -`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` `$raffle` | Prints a name and ID of a random user from the online list from the (optional) role. | `$raffle` or `$raffle RoleName` `$cash` `$$$` | Check how much currency a person has. (Defaults to yourself) | `$$$` or `$$$ @SomeGuy` `$give` | Give someone a certain amount of currency. | `$give 1 "@SomeGuy"` @@ -150,36 +133,54 @@ Command and aliases | Description | Usage `$take` | Takes a certain amount of currency from someone. **Bot Owner only.** | `$take 1 "@someguy"` `$betroll` `$br` | Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10. | `$br 5` `$leaderboard` `$lb` | Displays bot currency leaderboard. | `$lb` +`$race` | Starts a new animal race. | `$race` +`$joinrace` `$jr` | Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. | `$jr` or `$jr 5` +`$startevent` | Starts one of the events seen on public nadeko. **Bot Owner only.** | `$startevent flowerreaction` +`$roll` | Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. | `$roll` or `$roll 7` or `$roll 3d5` or `$roll 5dF` +`$rolluo` | Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. | `$rolluo` or `$rolluo 7` or `$rolluo 3d5` +`$nroll` | Rolls in a given range. | `$nroll 5` (rolls 0-5) or `$nroll 5-15` +`$draw` | Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. | `$draw` or `$draw 5` +`$shuffle` `$sh` | Reshuffles all cards back into the deck. | `$sh` +`$flip` | Flips coin(s) - heads or tails, and shows an image. | `$flip` or `$flip 3` +`$betflip` `$bf` | Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner. | `$bf 5 heads` or `$bf 3 t` +`$slotstats` | Shows the total stats of the slot command for this bot's session. **Bot Owner only.** | `$slotstats` +`$slottest` | Tests to see how much slots payout for X number of plays. **Bot Owner only.** | `$slottest 1000` +`$slot` | Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. | `$slot 5` +`$claimwaifu` `$claim` | Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `$affinity` towards you. | `$claim 50 @Himesama` +`$divorce` | Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. | `$divorce @CheatingSloot` +`$affinity` | Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `$claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. | `$affinity @MyHusband` or `$affinity` +`$waifus` `$waifulb` | Shows top 9 waifus. | `$waifus` +`$waifuinfo` `$waifustats` | Shows waifu stats for a target person. Defaults to you if no user is provided. | `$waifuinfo @MyCrush` or `$waifuinfo` ###### [Back to TOC](#table-of-contents) ### Games Command and aliases | Description | Usage ----------------|--------------|------- -`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint` -`>tl` | Shows a current trivia leaderboard. | `>tl` -`>tq` | Quits current trivia after current question. | `>tq` -`>typestart` | Starts a typing contest. | `>typestart` -`>typestop` | Stops a typing contest on the current channel. | `>typestop` -`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords` -`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3` -`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` -`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` -`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` -`>pollstats` | Shows the poll results without stopping the poll on this server. **Requires ManageMessages server permission.** | `>pollstats` -`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` -`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` -`>plant` | Spend a unit of currency to plant it in this channel. (If bot is restarted or crashes, the currency will be lost) | `>plant` -`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` -`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` -`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` -`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot` -`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` `>choose` | Chooses a thing from a list of things | `>choose Get up;Sleep;Sleep more` `>8ball` | Ask the 8ball a yes/no question. | `>8ball should I do something` `>rps` | Play a game of rocket paperclip scissors with Nadeko. | `>rps scissors` `>linux` | Prints a customizable Linux interjection | `>linux Spyware Windows` `>leet` | Converts a text to leetspeak with 6 (1-6) severity levels | `>leet 3 Hello` +`>acrophobia` `>acro` | Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) | `>acro` or `>acro 30` +`>cleverbot` | Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. **Requires ManageMessages server permission.** | `>cleverbot` +`>hangmanlist` | Shows a list of hangman term types. | `> hangmanlist` +`>hangman` | Starts a game of hangman in the channel. Use `>hangmanlist` to see a list of available term types. Defaults to 'all'. | `>hangman` or `>hangman movies` +`>pick` | Picks the currency planted in this channel. 60 seconds cooldown. | `>pick` +`>plant` | Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) | `>plant` or `>plant 5` +`>gencurrency` `>gc` | Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) **Requires ManageMessages server permission.** | `>gc` +`>poll` | Creates a poll which requires users to send the number of the voting option to the bot. **Requires ManageMessages server permission.** | `>poll Question?;Answer1;Answ 2;A_3` +`>publicpoll` `>ppoll` | Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. **Requires ManageMessages server permission.** | `>ppoll Question?;Answer1;Answ 2;A_3` +`>pollstats` | Shows the poll results without stopping the poll on this server. **Requires ManageMessages server permission.** | `>pollstats` +`>pollend` | Stops active poll on this server and prints the results in this channel. **Requires ManageMessages server permission.** | `>pollend` +`>typestart` | Starts a typing contest. | `>typestart` +`>typestop` | Stops a typing contest on the current channel. | `>typestop` +`>typeadd` | Adds a new article to the typing contest. **Bot Owner only.** | `>typeadd wordswords` +`>typelist` | Lists added typing articles with their IDs. 15 per page. | `>typelist` or `>typelist 3` +`>typedel` | Deletes a typing article given the ID. **Bot Owner only.** | `>typedel 3` +`>trivia` `>t` | Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. | `>t` or `>t 5 nohint` +`>tl` | Shows a current trivia leaderboard. | `>tl` +`>tq` | Quits current trivia after current question. | `>tq` ###### [Back to TOC](#table-of-contents) @@ -227,6 +228,7 @@ Command and aliases | Description | Usage `!!deleteplaylist` `!!delpls` | Deletes a saved playlist. Only if you made it or if you are the bot owner. | `!!delpls animu-5` `!!goto` | Goes to a specific time in seconds in a song. | `!!goto 30` `!!autoplay` `!!ap` | Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) | `!!ap` +`!!setmusicchannel` `!!smch` | Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. **Requires ManageMessages server permission.** | `!!smch` ###### [Back to TOC](#table-of-contents) @@ -236,12 +238,12 @@ Command and aliases | Description | Usage `~hentai` | Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. | `~hentai yuri` `~autohentai` | Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. **Requires ManageMessages channel permission.** | `~autohentai 30 yuri|tail|long_hair` or `~autohentai` `~hentaibomb` | Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. | `~hentaibomb yuri` -`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing` `~yandere` | Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~yandere tag1+tag2` `~konachan` | Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. | `~konachan yuri` -`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing` `~rule34` | Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~rule34 yuri+kissing` `~e621` | Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. | `~e621 yuri kissing` +`~danbooru` | Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~danbooru yuri+kissing` +`~gelbooru` | Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) | `~gelbooru yuri+kissing` `~cp` | We all know where this will lead you to. | `~cp` `~boobs` | Real adult content. | `~boobs` `~butts` `~ass` `~butt` | Real adult content. | `~butts` or `~ass` @@ -251,18 +253,6 @@ Command and aliases | Description | Usage ### Permissions Command and aliases | Description | Usage ----------------|--------------|------- -`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi` -`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi` -`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw` -`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` -`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` -`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` -`;cmdcosts` | Shows a list of command costs. Paginated with 9 command per page. | `;cmdcosts` or `;cmdcosts 2` -`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` -`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` -`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` -`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312` -`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer` `;verbose` `;v` | Sets whether to show when a command/module is blocked. | `;verbose true` `;permrole` `;pr` | Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. | `;pr role` `;listperms` `;lp` | Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. | `;lp` or `;lp 3` @@ -280,6 +270,18 @@ Command and aliases | Description | Usage `;allrolemdls` `;arm` | Enable or disable all modules for a specific role. | `;arm [enable/disable] MyRole` `;allusrmdls` `;aum` | Enable or disable all modules for a specific user. | `;aum enable @someone` `;allsrvrmdls` `;asm` | Enable or disable all modules for your server. | `;asm [enable/disable]` +`;ubl` | Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. **Bot Owner only.** | `;ubl add @SomeUser` or `;ubl rem 12312312313` +`;cbl` | Either [add]s or [rem]oves a channel specified by an ID from a blacklist. **Bot Owner only.** | `;cbl rem 12312312312` +`;sbl` | Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. **Bot Owner only.** | `;sbl add 12312321312` or `;sbl rem SomeTrashServer` +`;cmdcooldown` `;cmdcd` | Sets a cooldown per user for a command. Set to 0 to remove the cooldown. | `;cmdcd "some cmd" 5` +`;allcmdcooldowns` `;acmdcds` | Shows a list of all commands and their respective cooldowns. | `;acmdcds` +`;cmdcosts` | Shows a list of command costs. Paginated with 9 command per page. | `;cmdcosts` or `;cmdcosts 2` +`;srvrfilterinv` `;sfi` | Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. | `;sfi` +`;chnlfilterinv` `;cfi` | Toggles automatic deleting of invites posted in the channel. Does not negate the ;srvrfilterinv enabled setting. Does not affect Bot Owner. | `;cfi` +`;srvrfilterwords` `;sfw` | Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. | `;sfw` +`;chnlfilterwords` `;cfw` | Toggles automatic deleting of messages containing banned words on the channel. Does not negate the ;srvrfilterwords enabled setting. Does not affect bot owner. | `;cfw` +`;fw` | Adds or removes (if it exists) a word from the list of filtered words. Use`;sfw` or `;cfw` to toggle filtering. | `;fw poop` +`;lstfilterwords` `;lfw` | Shows a list of filtered words. | `;lfw` ###### [Back to TOC](#table-of-contents) @@ -297,32 +299,6 @@ Command and aliases | Description | Usage ### Searches Command and aliases | Description | Usage ----------------|--------------|------- -`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest` -`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello` -`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del` -`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value. -`~translangs` | Lists the valid languages for translation. | `~translangs` -`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer` -`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer` -`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer` -`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls` -`~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy` -`~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer` -`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon` -`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow` -`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist` -`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400` -`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016` -`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` -`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712` -`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` -`~yomama` `~ym` | Shows a random joke from | `~ym` -`~randjoke` `~rj` | Shows a random joke from | `~rj` -`~chucknorris` `~cn` | Shows a random chucknorris joke from | `~cn` -`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke` -`~magicitem` `~mi` | Shows a random magicitem from | `~mi` -`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol` -`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin` `~weather` `~we` | Shows weather data for a specified city. You can also specify a country after a comma. | `~we Moscow, RU` `~youtube` `~yt` | Searches youtubes and shows the first result | `~yt query` `~imdb` `~omdb` | Queries omdb for movies or series, show first result. | `~imdb Batman vs Superman` @@ -353,34 +329,40 @@ Command and aliases | Description | Usage `~lolban` | Shows top banned champions ordered by ban rate. | `~lolban` `~memelist` | Pulls a list of memes you can use with `~memegen` from http://memegen.link/templates/ | `~memelist` `~memegen` | Generates a meme from memelist with top and bottom text. | `~memegen biw "gets iced coffee" "in the winter"` +`~mal` | Shows basic info from myanimelist profile. | `~mal straysocks` +`~anime` `~ani` `~aq` | Queries anilist for an anime and shows the first result. | `~ani aquarion evol` +`~manga` `~mang` `~mq` | Queries anilist for a manga and shows the first result. | `~mq Shingeki no kyojin` +`~yomama` `~ym` | Shows a random joke from | `~ym` +`~randjoke` `~rj` | Shows a random joke from | `~rj` +`~chucknorris` `~cn` | Shows a random chucknorris joke from | `~cn` +`~wowjoke` | Get one of Kwoth's penultimate WoW jokes. | `~wowjoke` +`~magicitem` `~mi` | Shows a random magicitem from | `~mi` +`~osu` | Shows osu stats for a player. | `~osu Name` or `~osu Name taiko` +`~osub` | Shows information about an osu beatmap. | `~osub https://osu.ppy.sh/s/127712` +`~osu5` | Displays a user's top 5 plays. | `~osu5 Name` +`~overwatch` `~ow` | Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` | `~ow us Battletag#1337` or `~overwatch eu Battletag#2016` +`~placelist` | Shows the list of available tags for the `~place` command. | `~placelist` +`~place` | Shows a placeholder image of a given tag. Use `~placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. | `~place Cage` or `~place steven 500 400` +`~pokemon` `~poke` | Searches for a pokemon. | `~poke Sylveon` +`~pokemonability` `~pokeab` | Searches for a pokemon ability. | `~pokeab overgrow` +`~hitbox` `~hb` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~hitbox SomeStreamer` +`~twitch` `~tw` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~twitch SomeStreamer` +`~beam` `~bm` | Notifies this channel when a certain user starts streaming. **Requires ManageMessages server permission.** | `~beam SomeStreamer` +`~liststreams` `~ls` | Lists all streams you are following on this server. | `~ls` +`~removestream` `~rms` | Removes notifications of a certain streamer from a certain platform on this channel. **Requires ManageMessages server permission.** | `~rms Twitch SomeGuy` or `~rms Beam SomeOtherGuy` +`~checkstream` `~cs` | Checks if a user is online on a certain streaming platform. | `~cs twitch MyFavStreamer` +`~translate` `~trans` | Translates from>to text. From the given language to the destination language. | `~trans en>fr Hello` +`~autotrans` `~at` | Starts automatic translation of all messages by users who set their `~atl` in this channel. You can set "del" argument to automatically delete all translated user messages. **Requires Administrator server permission.** **Bot Owner only.** | `~at` or `~at del` +`~autotranslang` `~atl` | `~atl en>fr` | Sets your source and target language to be used with `~at`. Specify no arguments to remove previously set value. +`~translangs` | Lists the valid languages for translation. | `~translangs` +`~xkcd` | Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. | `~xkcd` or `~xkcd 1400` or `~xkcd latest` ###### [Back to TOC](#table-of-contents) ### Utility Command and aliases | Description | Usage ----------------|--------------|------- -`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist` -`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000` -`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` -`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!` -`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. -`...` | Shows a random quote with a specified name. | `... abc` -`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` -`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` -`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` -`.repeatinvoke` `.repinv` | Immediately shows the repeat message on a certain index and restarts its timer. **Requires ManageMessages server permission.** | `.repinv 1` -`.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` -`.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` -`.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` -`.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` -`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc` -`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere` -`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` -`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` -`.calcops` | Shows all available operations in .calc command | `.calcops` -`.rotaterolecolor` `.rrc` | Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. **Bot Owner only.** | `.rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `.rrc 0 MyLsdRole` +`.rotaterolecolor` `.rrc` | Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. **Requires ManageRoles server permission.** **Bot Owner only.** | `.rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `.rrc 0 MyLsdRole` `.togethertube` `.totube` | Creates a new room on and shows the link in the chat. | `.totube` `.whosplaying` `.whpl` | Shows a list of users who are playing the specified game. | `.whpl Overwatch` `.inrole` | Lists every person from the provided role or roles (separated by a ',') on this server. If the list is too long for 1 message, you must have Manage Messages permission. | `.inrole Role` @@ -396,3 +378,24 @@ Command and aliases | Description | Usage `.listservers` | Lists servers the bot is on with some basic info. 15 per page. **Bot Owner only.** | `.listservers 3` `.savechat` | Saves a number of messages to a text file and sends it to you. **Bot Owner only.** | `.savechat 150` `.activity` | Checks for spammers. **Bot Owner only.** | `.activity` +`.calculate` `.calc` | Evaluate a mathematical expression. | `.calc 1+1` +`.calcops` | Shows all available operations in .calc command | `.calcops` +`.scsc` | Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. **Bot Owner only.** | `.scsc` +`.jcsc` | Joins current channel to an instance of cross server channel using the token. **Requires ManageServer server permission.** | `.jcsc TokenHere` +`.lcsc` | Leaves Cross server channel instance from this channel. **Requires ManageServer server permission.** | `.lcsc` +`.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` +`.repeatinvoke` `.repinv` | Immediately shows the repeat message on a certain index and restarts its timer. **Requires ManageMessages server permission.** | `.repinv 1` +`.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` +`.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` +`.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` +`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`...` | Shows a random quote with a specified name. | `... abc` +`..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` +`.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` +`.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` +`.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` +`.remindtemplate` | Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. **Bot Owner only.** | `.remindtemplate %user%, do %message%!` +`.convertlist` | List of the convertible dimensions and currencies. | `.convertlist` +`.convert` | Convert quantities. Use `.convertlist` to see supported dimensions and currencies. | `.convert m km 1000` From 9d5c977b485fc76f2c4b030c210bfc95219556b4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 3 Feb 2017 09:59:18 +0100 Subject: [PATCH 018/256] .reloadimages can be ran in DM now --- src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index b2af48ab..e787b83a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -170,12 +170,9 @@ namespace NadekoBot.Modules.Administration } [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] [OwnerOnly] public async Task ReloadImages() { - var channel = (ITextChannel)Context.Channel; - var msg = await Context.Channel.SendMessageAsync("Reloading Images...").ConfigureAwait(false); var sw = Stopwatch.StartNew(); await NadekoBot.Images.Reload().ConfigureAwait(false); From 8791bc55f508523f04406801db8fa47051b0988f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 3 Feb 2017 10:01:29 +0100 Subject: [PATCH 019/256] Just a bit better formatting. --- .../Modules/Administration/Commands/SelfCommands.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index e787b83a..137fa10c 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -173,14 +173,10 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task ReloadImages() { - var msg = await Context.Channel.SendMessageAsync("Reloading Images...").ConfigureAwait(false); var sw = Stopwatch.StartNew(); await NadekoBot.Images.Reload().ConfigureAwait(false); sw.Stop(); - await msg.ModifyAsync(x => - { - x.Content = "✅ Images reloaded."; - }).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync("Images Reloaded").ConfigureAwait(false); } private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) From f850d62c89d9051ad5342d0928c2e4bc4f23cc1c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 3 Feb 2017 11:36:58 +0100 Subject: [PATCH 020/256] sneaky game will say a number of rewarded users at the end --- .../Modules/Gambling/Commands/CurrencyEvents.cs | 4 +++- .../Games/Commands/PlantAndPickCommands.cs | 2 +- src/NadekoBot/Services/IImagesService.cs | 3 ++- src/NadekoBot/Services/Impl/ImagesService.cs | 17 ++++++++++++----- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index 927f8a72..bea309af 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -78,6 +78,7 @@ namespace NadekoBot.Modules.Gambling _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; } + var game = NadekoBot.Client.Game?.Name; await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName) .ConfigureAwait(false); try @@ -94,10 +95,11 @@ namespace NadekoBot.Modules.Gambling await Task.Delay(num * 1000); NadekoBot.Client.MessageReceived -= SneakyGameMessageReceivedEventHandler; + var cnt = _sneakyGameAwardedUsers.Count; _sneakyGameAwardedUsers.Clear(); _secretCode = String.Empty; - await NadekoBot.Client.SetGameAsync($"SneakyGame event ended.") + await NadekoBot.Client.SetGameAsync($"SneakyGame event ended. {cnt} users received a reward.") .ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index ab40b511..0ae16be6 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -231,7 +231,7 @@ namespace NadekoBot.Modules.Games private static Tuple GetRandomCurrencyImage() { var rng = new NadekoRandom(); - var images = NadekoBot.Images.CurrencyImages; + var images = NadekoBot.Images.Currency; return images[rng.Next(0, images.Count)]; } diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index 192ee842..7d98cd06 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -13,7 +13,8 @@ namespace NadekoBot.Services Stream Heads { get; } Stream Tails { get; } - IImmutableList> CurrencyImages { get; } + IImmutableList> Currency { get; } + IImmutableList> Dice { get; } Task Reload(); } diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index f63e2eb5..80898dc1 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -19,16 +19,22 @@ namespace NadekoBot.Services.Impl private const string tailsPath = "data/images/coins/tails.png"; private const string currencyImagesPath = "data/currency_images"; + private const string diceImagesPath = "data/images/dice"; private byte[] heads; public Stream Heads => new MemoryStream(heads, false); private byte[] tails; public Stream Tails => new MemoryStream(tails, false); - //todo tuple - private IReadOnlyDictionary currencyImages; - public IImmutableList> CurrencyImages => - currencyImages.Select(x => new Tuple(x.Key, (Stream)new MemoryStream(x.Value, false))) + //todo C#7 + private IReadOnlyDictionary currency; + public IImmutableList> Currency => + currency.Select(x => new Tuple(x.Key, new MemoryStream(x.Value, false))) + .ToImmutableArray(); + + private IReadOnlyDictionary dice; + public IImmutableList> Dice => + dice.Select(x => new Tuple(x.Key, new MemoryStream(x.Value, false))) .ToImmutableArray(); private ImagesService() @@ -52,7 +58,8 @@ namespace NadekoBot.Services.Impl heads = File.ReadAllBytes(headsPath); tails = File.ReadAllBytes(tailsPath); - currencyImages = Directory.GetFiles(currencyImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); + currency = Directory.GetFiles(currencyImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); + dice = Directory.GetFiles(diceImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); } catch (Exception ex) From 5fba2de3a6da273ec5cadbe073e06ae0675c6b8d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 4 Feb 2017 06:44:27 +0100 Subject: [PATCH 021/256] Don't download users. --- src/NadekoBot/NadekoBot.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index a32abc73..49d0d5c4 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -70,7 +70,7 @@ namespace NadekoBot TotalShards = Credentials.TotalShards, ConnectionTimeout = int.MaxValue, #if !GLOBAL_NADEKO - AlwaysDownloadUsers = true, + //AlwaysDownloadUsers = true, #endif }); From d39e544567c9bc5d3c85fff121d6310a4af9e597 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 4 Feb 2017 09:34:51 +0100 Subject: [PATCH 022/256] Images service almost done. Slots images moved to /data/images/slots currency_images renamed to currency and moved to data/images/ --- .../DataStructures/DisposableImutableList.cs | 82 ++++++++++++++++++ .../Administration/Commands/SelfCommands.cs | 6 +- .../Gambling/Commands/DiceRollCommand.cs | 42 ++++----- .../Gambling/Commands/FlipCoinCommand.cs | 38 +++++--- .../Modules/Gambling/Commands/Slots.cs | 60 +++---------- .../Games/Commands/PlantAndPickCommands.cs | 33 +++---- src/NadekoBot/Services/IImagesService.cs | 17 ++-- src/NadekoBot/Services/Impl/ImagesService.cs | 71 ++++++++++----- src/NadekoBot/_Extensions/Extensions.cs | 5 +- .../currency}/img1.jpg | Bin .../currency}/img2.jpg | Bin .../currency}/img3.jpg | Bin .../data/{ => images}/slots/background.png | Bin .../data/{ => images}/slots/emojis/0.png | Bin .../data/{ => images}/slots/emojis/1.png | Bin .../data/{ => images}/slots/emojis/2.png | Bin .../data/{ => images}/slots/emojis/3.png | Bin .../data/{ => images}/slots/emojis/4.png | Bin .../data/{ => images}/slots/emojis/5.png | Bin .../{slots => images/slots/numbers}/0.png | Bin .../{slots => images/slots/numbers}/1.png | Bin .../{slots => images/slots/numbers}/2.png | Bin .../{slots => images/slots/numbers}/3.png | Bin .../{slots => images/slots/numbers}/4.png | Bin .../{slots => images/slots/numbers}/5.png | Bin .../{slots => images/slots/numbers}/6.png | Bin .../{slots => images/slots/numbers}/7.png | Bin .../{slots => images/slots/numbers}/8.png | Bin .../{slots => images/slots/numbers}/9.png | Bin 29 files changed, 228 insertions(+), 126 deletions(-) create mode 100644 src/NadekoBot/DataStructures/DisposableImutableList.cs rename src/NadekoBot/data/{currency_images => images/currency}/img1.jpg (100%) rename src/NadekoBot/data/{currency_images => images/currency}/img2.jpg (100%) rename src/NadekoBot/data/{currency_images => images/currency}/img3.jpg (100%) rename src/NadekoBot/data/{ => images}/slots/background.png (100%) rename src/NadekoBot/data/{ => images}/slots/emojis/0.png (100%) rename src/NadekoBot/data/{ => images}/slots/emojis/1.png (100%) rename src/NadekoBot/data/{ => images}/slots/emojis/2.png (100%) rename src/NadekoBot/data/{ => images}/slots/emojis/3.png (100%) rename src/NadekoBot/data/{ => images}/slots/emojis/4.png (100%) rename src/NadekoBot/data/{ => images}/slots/emojis/5.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/0.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/1.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/2.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/3.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/4.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/5.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/6.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/7.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/8.png (100%) rename src/NadekoBot/data/{slots => images/slots/numbers}/9.png (100%) diff --git a/src/NadekoBot/DataStructures/DisposableImutableList.cs b/src/NadekoBot/DataStructures/DisposableImutableList.cs new file mode 100644 index 00000000..71bda6de --- /dev/null +++ b/src/NadekoBot/DataStructures/DisposableImutableList.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.DataStructures +{ + + public static class DisposableReadOnlyListExtensions + { + public static IDisposableReadOnlyList AsDisposable(this IReadOnlyList arr) where T : IDisposable + => new DisposableReadOnlyList(arr); + + public static IDisposableReadOnlyList> AsDisposable(this IReadOnlyList> arr) where TValue : IDisposable + => new DisposableReadOnlyList(arr); + } + + public interface IDisposableReadOnlyList : IReadOnlyList, IDisposable + { + } + + public class DisposableReadOnlyList : IDisposableReadOnlyList + where T : IDisposable + { + private readonly IReadOnlyList _arr; + + public int Count => _arr.Count; + + public T this[int index] => _arr[index]; + + public DisposableReadOnlyList(IReadOnlyList arr) + { + this._arr = arr; + } + + public IEnumerator GetEnumerator() + => _arr.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => _arr.GetEnumerator(); + + public void Dispose() + { + foreach (var item in _arr) + { + item.Dispose(); + } + } + } + + public class DisposableReadOnlyList : IDisposableReadOnlyList> + where U : IDisposable + { + private readonly IReadOnlyList> _arr; + + public int Count => _arr.Count; + + KeyValuePair IReadOnlyList>.this[int index] => _arr[index]; + + public DisposableReadOnlyList(IReadOnlyList> arr) + { + this._arr = arr; + } + + public IEnumerator> GetEnumerator() => + _arr.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => + _arr.GetEnumerator(); + + public void Dispose() + { + foreach (var item in _arr) + { + item.Value.Dispose(); + } + } + } +} diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 137fa10c..80356f73 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -173,10 +173,8 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task ReloadImages() { - var sw = Stopwatch.StartNew(); - await NadekoBot.Images.Reload().ConfigureAwait(false); - sw.Stop(); - await Context.Channel.SendConfirmAsync("Images Reloaded").ConfigureAwait(false); + var time = await NadekoBot.Images.Reload().ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Images loaded after {time.TotalSeconds:F3}s!").ConfigureAwait(false); } private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 01283c83..4616aa91 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -34,15 +34,11 @@ namespace NadekoBot.Modules.Gambling var num2 = gen % 10; var imageStream = await Task.Run(() => { - try - { - var ms = new MemoryStream(); - new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); - ms.Position = 0; - return ms; - } - catch { return new MemoryStream(); } - }); + var ms = new MemoryStream(); + new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); + ms.Position = 0; + return ms; + }).ConfigureAwait(false); await Context.Channel.SendFileAsync(imageStream, "dice.png", $"{Context.User.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false); } @@ -82,7 +78,7 @@ namespace NadekoBot.Modules.Gambling await InternallDndRoll(arg, false).ConfigureAwait(false); } - private async Task InternalRoll( int num, bool ordered) + private async Task InternalRoll(int num, bool ordered) { if (num < 1 || num > 30) { @@ -209,20 +205,24 @@ namespace NadekoBot.Modules.Gambling private Image GetDice(int num) { - const string pathToImage = "data/images/dice"; - if (num != 10) + if (num < 0 || num > 10) + throw new ArgumentOutOfRangeException(nameof(num)); + + if (num == 10) { - using (var stream = File.OpenRead(Path.Combine(pathToImage, $"{num}.png"))) - return new Image(stream); + var images = NadekoBot.Images.Dice; + using (var imgOneStream = images[1].Value.ToStream()) + using (var imgZeroStream = images[0].Value.ToStream()) + { + Image imgOne = new Image(imgOneStream); + Image imgZero = new Image(imgZeroStream); + + return new[] { imgOne, imgZero }.Merge(); + } } - - using (var one = File.OpenRead(Path.Combine(pathToImage, "1.png"))) - using (var zero = File.OpenRead(Path.Combine(pathToImage, "0.png"))) + using (var die = NadekoBot.Images.Dice[num].Value.ToStream()) { - Image imgOne = new Image(one); - Image imgZero = new Image(zero); - - return new[] { imgOne, imgZero }.Merge(); + return new Image(die); } } } diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 74e201c0..6ccfe786 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -5,6 +5,7 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; using System; +using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Image = ImageSharp.Image; @@ -32,9 +33,19 @@ namespace NadekoBot.Modules.Gambling if (count == 1) { if (rng.Next(0, 2) == 1) - await Context.Channel.SendFileAsync(_images.Heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + { + using (var heads = _images.Heads.ToStream()) + { + await Context.Channel.SendFileAsync(heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + } + } else - await Context.Channel.SendFileAsync(_images.Tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + { + using (var tails = _images.Tails.ToStream()) + { + await Context.Channel.SendFileAsync(tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + } + } return; } if (count > 10 || count < 1) @@ -43,13 +54,17 @@ namespace NadekoBot.Modules.Gambling return; } var imgs = new Image[count]; - for (var i = 0; i < count; i++) + using (var heads = _images.Heads.ToStream()) + using(var tails = _images.Tails.ToStream()) { - imgs[i] = rng.Next(0, 10) < 5 ? - new Image(_images.Heads) : - new Image(_images.Tails); + for (var i = 0; i < count; i++) + { + imgs[i] = rng.Next(0, 10) < 5 ? + new Image(heads) : + new Image(tails); + } + await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } - await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -74,9 +89,10 @@ namespace NadekoBot.Modules.Gambling //heads = true //tails = false + //todo this seems stinky, no time to look at it right now var isHeads = guessStr == "HEADS" || guessStr == "H"; bool result = false; - Stream imageToSend; + IEnumerable imageToSend; if (rng.Next(0, 2) == 1) { imageToSend = _images.Heads; @@ -98,8 +114,10 @@ namespace NadekoBot.Modules.Gambling { str = $"{Context.User.Mention}`Better luck next time.`"; } - - await Context.Channel.SendFileAsync(imageToSend, "result.png", str).ConfigureAwait(false); + using (var toSend = imageToSend.ToStream()) + { + await Context.Channel.SendFileAsync(toSend, "result.png", str).ConfigureAwait(false); + } } } } diff --git a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs index e1671a6d..445d6c48 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs @@ -21,52 +21,19 @@ namespace NadekoBot.Modules.Gambling private static int totalBet = 0; private static int totalPaidOut = 0; - private const string backgroundPath = "data/slots/background.png"; - - private static readonly byte[] backgroundBuffer; - private static readonly byte[][] numbersBuffer = new byte[10][]; - private static readonly byte[][] emojiBuffer; - const int alphaCutOut = byte.MaxValue / 3; - static Slots() - { - backgroundBuffer = File.ReadAllBytes(backgroundPath); - - for (int i = 0; i < 10; i++) - { - numbersBuffer[i] = File.ReadAllBytes("data/slots/" + i + ".png"); - } - int throwaway; - var emojiFiles = Directory.GetFiles("data/slots/emojis/", "*.png") - .Where(f => int.TryParse(Path.GetFileNameWithoutExtension(f), out throwaway)) - .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) - .ToArray(); - - emojiBuffer = new byte[emojiFiles.Length][]; - for (int i = 0; i < emojiFiles.Length; i++) - { - emojiBuffer[i] = File.ReadAllBytes(emojiFiles[i]); - } - } - - - private static MemoryStream InternalGetStream(string path) - { - var ms = new MemoryStream(); - using (var fs = File.Open(path, FileMode.Open)) - { - fs.CopyTo(ms); - fs.Flush(); - } - ms.Position = 0; - return ms; - } - //here is a payout chart //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg //thanks to judge for helping me with this + private readonly IImagesService _images; + + public Slots() + { + this._images = NadekoBot.Images; + } + public class SlotMachine { public const int MaxValue = 5; @@ -154,7 +121,7 @@ namespace NadekoBot.Modules.Gambling var sb = new StringBuilder(); const int bet = 1; int payout = 0; - foreach (var key in dict.Keys.OrderByDescending(x=>x)) + foreach (var key in dict.Keys.OrderByDescending(x => x)) { sb.AppendLine($"x{key} occured {dict[key]} times. {dict[key] * 1.0f / tests * 100}%"); payout += key * dict[key]; @@ -164,6 +131,7 @@ namespace NadekoBot.Modules.Gambling } static HashSet runningUsers = new HashSet(); + [NadekoCommand, Usage, Description, Aliases] public async Task Slot(int amount = 0) { @@ -189,7 +157,7 @@ namespace NadekoBot.Modules.Gambling return; } Interlocked.Add(ref totalBet, amount); - using (var bgFileStream = new MemoryStream(backgroundBuffer)) + using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream()) { var bgImage = new ImageSharp.Image(bgFileStream); @@ -199,7 +167,7 @@ namespace NadekoBot.Modules.Gambling { for (int i = 0; i < 3; i++) { - using (var file = new MemoryStream(emojiBuffer[numbers[i]])) + using (var file = _images.SlotEmojis[numbers[i]].ToStream()) { var randomImage = new ImageSharp.Image(file); using (var toAdd = randomImage.Lock()) @@ -226,7 +194,7 @@ namespace NadekoBot.Modules.Gambling do { var digit = printWon % 10; - using (var fs = new MemoryStream(numbersBuffer[digit])) + using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream()) { var img = new ImageSharp.Image(fs); using (var pixels = img.Lock()) @@ -251,7 +219,7 @@ namespace NadekoBot.Modules.Gambling do { var digit = printAmount % 10; - using (var fs = new MemoryStream(numbersBuffer[digit])) + using (var fs = _images.SlotNumbers[digit].ToStream()) { var img = new ImageSharp.Image(fs); using (var pixels = img.Lock()) @@ -301,4 +269,4 @@ namespace NadekoBot.Modules.Gambling } } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 0ae16be6..1c8671f8 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -10,6 +10,7 @@ using NLog; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.IO; using System.Linq; @@ -97,13 +98,16 @@ namespace NadekoBot.Modules.Games firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!"; } var file = GetRandomCurrencyImage(); - var sent = await channel.SendFileAsync( - file.Item2, - file.Item1, - $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") - .ConfigureAwait(false); + using (var fileStream = file.Value.ToStream()) + { + var sent = await channel.SendFileAsync( + fileStream, + file.Key, + $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") + .ConfigureAwait(false); - msgs[0] = sent; + msgs[0] = sent; + } plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; }); } @@ -167,18 +171,15 @@ namespace NadekoBot.Modules.Games return; } - var file = GetRandomCurrencyImage(); - IUserMessage msg; + var imgData = GetRandomCurrencyImage(); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; - if (file == null) + + IUserMessage msg; + using (var toSend = imgData.Value.ToStream()) { - msg = await Context.Channel.SendConfirmAsync(NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); - } - else - { - msg = await Context.Channel.SendFileAsync(file.Item2, file.Item1, msgToSend).ConfigureAwait(false); + msg = await Context.Channel.SendFileAsync(toSend, imgData.Key, msgToSend).ConfigureAwait(false); } var msgs = new IUserMessage[amount]; @@ -228,12 +229,12 @@ namespace NadekoBot.Modules.Games } } - private static Tuple GetRandomCurrencyImage() + private static KeyValuePair> GetRandomCurrencyImage() { var rng = new NadekoRandom(); var images = NadekoBot.Images.Currency; - return images[rng.Next(0, images.Count)]; + return images[rng.Next(0, images.Length)]; } int GetRandomNumber() diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index 7d98cd06..81e21907 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -1,4 +1,5 @@ -using System; +using NadekoBot.DataStructures; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.IO; @@ -10,12 +11,16 @@ namespace NadekoBot.Services { public interface IImagesService { - Stream Heads { get; } - Stream Tails { get; } + ImmutableArray Heads { get; } + ImmutableArray Tails { get; } - IImmutableList> Currency { get; } - IImmutableList> Dice { get; } + ImmutableArray>> Currency { get; } + ImmutableArray>> Dice { get; } - Task Reload(); + ImmutableArray SlotBackground { get; } + ImmutableArray> SlotEmojis { get; } + ImmutableArray> SlotNumbers { get; } + + Task Reload(); } } diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index 80898dc1..02bcb1cd 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -1,4 +1,5 @@ -using NLog; +using NadekoBot.DataStructures; +using NLog; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -15,27 +16,30 @@ namespace NadekoBot.Services.Impl { private readonly Logger _log; - private const string headsPath = "data/images/coins/heads.png"; - private const string tailsPath = "data/images/coins/tails.png"; + private const string basePath = "data/images/"; - private const string currencyImagesPath = "data/currency_images"; - private const string diceImagesPath = "data/images/dice"; + private const string headsPath = basePath + "coins/heads.png"; + private const string tailsPath = basePath + "coins/tails.png"; - private byte[] heads; - public Stream Heads => new MemoryStream(heads, false); + private const string currencyImagesPath = basePath + "currency"; + private const string diceImagesPath = basePath + "dice"; + + private const string slotBackgroundPath = basePath + "slots/background.png"; + private const string slotNumbersPath = basePath + "slots/numbers/"; + private const string slotEmojisPath = basePath + "slots/emojis/"; + + + public ImmutableArray Heads { get; private set; } + public ImmutableArray Tails { get; private set; } - private byte[] tails; - public Stream Tails => new MemoryStream(tails, false); //todo C#7 - private IReadOnlyDictionary currency; - public IImmutableList> Currency => - currency.Select(x => new Tuple(x.Key, new MemoryStream(x.Value, false))) - .ToImmutableArray(); + public ImmutableArray>> Currency { get; private set; } - private IReadOnlyDictionary dice; - public IImmutableList> Dice => - dice.Select(x => new Tuple(x.Key, new MemoryStream(x.Value, false))) - .ToImmutableArray(); + public ImmutableArray>> Dice { get; private set; } + + public ImmutableArray SlotBackground { get; private set; } + public ImmutableArray> SlotNumbers { get; private set; } + public ImmutableArray> SlotEmojis { get; private set; } private ImagesService() { @@ -49,18 +53,41 @@ namespace NadekoBot.Services.Impl return srvc; } - public Task Reload() => Task.Run(() => + public Task Reload() => Task.Run(() => { try { _log.Info("Loading images..."); var sw = Stopwatch.StartNew(); - heads = File.ReadAllBytes(headsPath); - tails = File.ReadAllBytes(tailsPath); + Heads = File.ReadAllBytes(headsPath).ToImmutableArray(); + Tails = File.ReadAllBytes(tailsPath).ToImmutableArray(); - currency = Directory.GetFiles(currencyImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); - dice = Directory.GetFiles(diceImagesPath).ToDictionary(x => Path.GetFileName(x), x => File.ReadAllBytes(x)); + Currency = Directory.GetFiles(currencyImagesPath) + .Select(x => new KeyValuePair>( + Path.GetFileName(x), + File.ReadAllBytes(x).ToImmutableArray())) + .ToImmutableArray(); + + Dice = Directory.GetFiles(diceImagesPath) + .OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x))) + .Select(x => new KeyValuePair>(x, + File.ReadAllBytes(x).ToImmutableArray())) + .ToImmutableArray(); + + SlotBackground = File.ReadAllBytes(slotBackgroundPath).ToImmutableArray(); + + SlotNumbers = Directory.GetFiles(slotNumbersPath) + .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) + .Select(x => File.ReadAllBytes(x).ToImmutableArray()) + .ToImmutableArray(); + + SlotEmojis = Directory.GetFiles(slotEmojisPath) + .Select(x => File.ReadAllBytes(x).ToImmutableArray()) + .ToImmutableArray(); + + sw.Stop(); _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); + return sw.Elapsed; } catch (Exception ex) { diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 56479a6f..4c32a542 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -20,7 +20,10 @@ namespace NadekoBot.Extensions { private const string arrow_left = "⬅"; private const string arrow_right = "➡"; - + + public static Stream ToStream(this IEnumerable bytes, bool canWrite = false) + => new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); + /// /// danny kamisama /// diff --git a/src/NadekoBot/data/currency_images/img1.jpg b/src/NadekoBot/data/images/currency/img1.jpg similarity index 100% rename from src/NadekoBot/data/currency_images/img1.jpg rename to src/NadekoBot/data/images/currency/img1.jpg diff --git a/src/NadekoBot/data/currency_images/img2.jpg b/src/NadekoBot/data/images/currency/img2.jpg similarity index 100% rename from src/NadekoBot/data/currency_images/img2.jpg rename to src/NadekoBot/data/images/currency/img2.jpg diff --git a/src/NadekoBot/data/currency_images/img3.jpg b/src/NadekoBot/data/images/currency/img3.jpg similarity index 100% rename from src/NadekoBot/data/currency_images/img3.jpg rename to src/NadekoBot/data/images/currency/img3.jpg diff --git a/src/NadekoBot/data/slots/background.png b/src/NadekoBot/data/images/slots/background.png similarity index 100% rename from src/NadekoBot/data/slots/background.png rename to src/NadekoBot/data/images/slots/background.png diff --git a/src/NadekoBot/data/slots/emojis/0.png b/src/NadekoBot/data/images/slots/emojis/0.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/0.png rename to src/NadekoBot/data/images/slots/emojis/0.png diff --git a/src/NadekoBot/data/slots/emojis/1.png b/src/NadekoBot/data/images/slots/emojis/1.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/1.png rename to src/NadekoBot/data/images/slots/emojis/1.png diff --git a/src/NadekoBot/data/slots/emojis/2.png b/src/NadekoBot/data/images/slots/emojis/2.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/2.png rename to src/NadekoBot/data/images/slots/emojis/2.png diff --git a/src/NadekoBot/data/slots/emojis/3.png b/src/NadekoBot/data/images/slots/emojis/3.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/3.png rename to src/NadekoBot/data/images/slots/emojis/3.png diff --git a/src/NadekoBot/data/slots/emojis/4.png b/src/NadekoBot/data/images/slots/emojis/4.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/4.png rename to src/NadekoBot/data/images/slots/emojis/4.png diff --git a/src/NadekoBot/data/slots/emojis/5.png b/src/NadekoBot/data/images/slots/emojis/5.png similarity index 100% rename from src/NadekoBot/data/slots/emojis/5.png rename to src/NadekoBot/data/images/slots/emojis/5.png diff --git a/src/NadekoBot/data/slots/0.png b/src/NadekoBot/data/images/slots/numbers/0.png similarity index 100% rename from src/NadekoBot/data/slots/0.png rename to src/NadekoBot/data/images/slots/numbers/0.png diff --git a/src/NadekoBot/data/slots/1.png b/src/NadekoBot/data/images/slots/numbers/1.png similarity index 100% rename from src/NadekoBot/data/slots/1.png rename to src/NadekoBot/data/images/slots/numbers/1.png diff --git a/src/NadekoBot/data/slots/2.png b/src/NadekoBot/data/images/slots/numbers/2.png similarity index 100% rename from src/NadekoBot/data/slots/2.png rename to src/NadekoBot/data/images/slots/numbers/2.png diff --git a/src/NadekoBot/data/slots/3.png b/src/NadekoBot/data/images/slots/numbers/3.png similarity index 100% rename from src/NadekoBot/data/slots/3.png rename to src/NadekoBot/data/images/slots/numbers/3.png diff --git a/src/NadekoBot/data/slots/4.png b/src/NadekoBot/data/images/slots/numbers/4.png similarity index 100% rename from src/NadekoBot/data/slots/4.png rename to src/NadekoBot/data/images/slots/numbers/4.png diff --git a/src/NadekoBot/data/slots/5.png b/src/NadekoBot/data/images/slots/numbers/5.png similarity index 100% rename from src/NadekoBot/data/slots/5.png rename to src/NadekoBot/data/images/slots/numbers/5.png diff --git a/src/NadekoBot/data/slots/6.png b/src/NadekoBot/data/images/slots/numbers/6.png similarity index 100% rename from src/NadekoBot/data/slots/6.png rename to src/NadekoBot/data/images/slots/numbers/6.png diff --git a/src/NadekoBot/data/slots/7.png b/src/NadekoBot/data/images/slots/numbers/7.png similarity index 100% rename from src/NadekoBot/data/slots/7.png rename to src/NadekoBot/data/images/slots/numbers/7.png diff --git a/src/NadekoBot/data/slots/8.png b/src/NadekoBot/data/images/slots/numbers/8.png similarity index 100% rename from src/NadekoBot/data/slots/8.png rename to src/NadekoBot/data/images/slots/numbers/8.png diff --git a/src/NadekoBot/data/slots/9.png b/src/NadekoBot/data/images/slots/numbers/9.png similarity index 100% rename from src/NadekoBot/data/slots/9.png rename to src/NadekoBot/data/images/slots/numbers/9.png From cfb874475f28fb706338a6e6c215ef44543e3396 Mon Sep 17 00:00:00 2001 From: samvaio Date: Sat, 4 Feb 2017 14:19:35 +0530 Subject: [PATCH 023/256] changed a lil --- docs/guides/Linux Guide.md | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/docs/guides/Linux Guide.md b/docs/guides/Linux Guide.md index 8e01383e..1ba7e0c0 100644 --- a/docs/guides/Linux Guide.md +++ b/docs/guides/Linux Guide.md @@ -103,26 +103,12 @@ You will be asked to enter the required informations, just follow the on-screen (If you want to skip any optional infos, just press `enter` key without typing/pasting anything.) Once done, #####Part V -You should see the options again within the **`tmux` session** named `nadeko` *(remember this one)* +You should see the options again. Next, press `3` to **Run Nadeko (Normally)** Check in your discord server if your new bot is working properly. #####Part VI -If your bot is working properly in your server, type `.die` to shut down the bot. - -You should be back to the options screen again on **PuTTY**, -from the options choose `4` to **Run Nadeko with Auto Restart.** - -It will show you more options: -``` -1. Run Auto Restart normally without Updating. -2. Auto Restart and Update with Dev Build (latest) -3. Auto Restart and Update with Stable Build -4. Exit -``` -Choose anything you like and once the bot's back online again in your server, close the **PuTTY**. - -**Done**, You now have your own **NadekoBot**. - +If your bot is working properly in your server, type `.die` to **shut down the bot**, then press `7` to **exit**. +Next, [Run your bot again with **tmux**.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#running-nadekobot) [Check this when you need to **restart** your **NadekoBot** anytime later along with tmux session.](http://nadekobot.readthedocs.io/en/latest/guides/Linux%20Guide/#restarting-nadeko) @@ -138,12 +124,19 @@ The above command will create a new session named **nadeko** *(you can replace - `cd ~ && bash linuxAIO.sh` -From the options, +**From the options,** -Choose `3` To Run the bot normally. +Choose `3` to **Run NadekoBot normally.** **NOTE:** With option `3` (Running Normally), if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) in discord. The bot will shut down and will stay offline until you manually run it again. (best if you want to check the bot.) -Choose `4` To Run the bot with Auto Restart. +Choose `4` to **Run NadekoBot with Auto Restart.** +It will show you more options: +``` +1. Run Auto Restart normally without Updating. +2. Auto Restart and Update with Dev Build (latest) +3. Auto Restart and Update with Stable Build +4. Exit +``` **NOTE:** With option `4` (Running with Auto Restart), bot will auto run if you use `.die` [command](http://nadekobot.readthedocs.io/en/latest/Commands%20List/#administration) making the command `.die` to function as restart. See how that happens: @@ -154,7 +147,7 @@ See how that happens: **Now check your Discord, the bot should be online** -Next to **move the bot to background** and to do that, press **CTRL+B+D** (this will detach the nadeko session using TMUX), and you can finally close PuTTY now. +Next to **move the bot to background** and to do that, press **CTRL+B+D** (that will detach the nadeko session using TMUX) and you can finally close **PuTTY** if you want. ####Restarting Nadeko From 1fe9f596fdcc3c64af47a1394f41662f405397ef Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 4 Feb 2017 18:19:03 +0100 Subject: [PATCH 024/256] .shardstats added --- src/NadekoBot/Modules/Utility/Utility.cs | 35 ++++++++++++++++--- .../Resources/CommandStrings.Designer.cs | 27 ++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 +++++ 3 files changed, 67 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 1c1af1e0..ddb34011 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -279,11 +279,41 @@ namespace NadekoBot.Modules.Utility await Context.Channel.SendConfirmAsync($"{Context.User.Mention} https://discord.gg/{invite.Code}"); } + [NadekoCommand, Usage, Description, Aliases] + public async Task ShardStats(int page = 1) + { + page -= 1; + if (page < 0) + return; + + var shards = NadekoBot.Client.Shards.Skip(page * 25).Take(25); + + var info = string.Join("\n", + shards.Select(x => $"Shard **#{x.ShardId.ToString()}** is in {Format.Bold(x.ConnectionState.ToString())} state with {Format.Bold(x.Guilds.Count.ToString())} servers")); + + if (string.IsNullOrWhiteSpace(info)) + info = "No shard stats on this page."; + + await Context.Channel.EmbedAsync(new EmbedBuilder() + .WithOkColor() + .WithTitle("Shard Stats - page " + (page + 1)) + .WithDescription(info)) + .ConfigureAwait(false); + } + [NadekoCommand, Usage, Description, Aliases] public async Task Stats() { var stats = NadekoBot.Stats; + var shardId = Context.Guild != null + ? NadekoBot.Client.GetShardIdFor(Context.Guild.Id) + : 0; + var footer = $"Shard {shardId} | {NadekoBot.Client.Shards.Count} total shards"; +#if !GLOBAL_NADEKO + footer += $" | Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."; +#endif + await Context.Channel.EmbedAsync( new EmbedBuilder().WithOkColor() .WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}") @@ -298,10 +328,7 @@ namespace NadekoBot.Modules.Utility .AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)) -#if !GLOBAL_NADEKO - .WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued.")) -#endif - ); + .WithFooter(efb => efb.WithText(footer))); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 634991da..0a7ff768 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -7025,6 +7025,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to shardstats. + /// + public static string shardstats_cmd { + get { + return ResourceManager.GetString("shardstats_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stats for shards. Paginated with 25 shards per page.. + /// + public static string shardstats_desc { + get { + return ResourceManager.GetString("shardstats_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}shardstats` or `{0}shardstats 2`. + /// + public static string shardstats_usage { + get { + return ResourceManager.GetString("shardstats_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to shorten. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 9b0b01be..d6074104 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3051,4 +3051,13 @@ `{0}reloadimages` + + shardstats + + + Stats for shards. Paginated with 25 shards per page. + + + `{0}shardstats` or `{0}shardstats 2` + \ No newline at end of file From 58f23ff40b72fcfb97e674d17e2e38a7518ea2a8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 4 Feb 2017 18:45:07 +0100 Subject: [PATCH 025/256] .connectshard added --- .../Administration/Commands/SelfCommands.cs | 23 ++++++++++++++++ .../Resources/CommandStrings.Designer.cs | 27 +++++++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 +++++++ 3 files changed, 59 insertions(+) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 80356f73..fb316beb 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -16,6 +16,29 @@ namespace NadekoBot.Modules.Administration [Group] class SelfCommands : ModuleBase { + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task ConnectShard(int shardid) + { + var shard = NadekoBot.Client.GetShard(shardid); + + if (shard == null) + { + await Context.Channel.SendErrorAsync("No shard by that id found.").ConfigureAwait(false); + return; + } + try + { + await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnecting.").ConfigureAwait(false); + await shard.ConnectAsync().ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnected.").ConfigureAwait(false); + } + catch (Exception ex) + { + _log.Warn(ex); + } + } + [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] public async Task Leave([Remainder] string guildStr) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 0a7ff768..2be25567 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -1868,6 +1868,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to connectshard. + /// + public static string connectshard_cmd { + get { + return ResourceManager.GetString("connectshard_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors.. + /// + public static string connectshard_desc { + get { + return ResourceManager.GetString("connectshard_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}connectshard 2`. + /// + public static string connectshard_usage { + get { + return ResourceManager.GetString("connectshard_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to convert. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index d6074104..a0cd8924 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3060,4 +3060,13 @@ `{0}shardstats` or `{0}shardstats 2` + + connectshard + + + Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors. + + + `{0}connectshard 2` + \ No newline at end of file From 1e5b7df47f5e29dfd5dff6b9aa3f8976b5de5a7b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 5 Feb 2017 06:36:07 +0100 Subject: [PATCH 026/256] `.shardid guildid` added, .ropl fixed --- Discord.Net | 2 +- .../Commands/PlayingRotateCommands.cs | 19 ++++++++++++------- src/NadekoBot/Modules/Utility/Utility.cs | 8 ++++++++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Discord.Net b/Discord.Net index 0fd1e70a..9ce5c475 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit 0fd1e70a22612ff9fa697dace96f22780080b01f +Subproject commit 9ce5c4757efc6cb6bb8959e851abcdcbe03217be diff --git a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs index 7fff2dc6..371a6d6b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs @@ -24,7 +24,12 @@ namespace NadekoBot.Modules.Administration public static List RotatingStatusMessages { get; } public static bool RotatingStatuses { get; private set; } = false; private static Timer _t { get; } - + + private class TimerState + { + public int Index { get; set; } = 0; + } + static PlayingRotateCommands() { _log = LogManager.GetCurrentClassLogger(); @@ -34,21 +39,21 @@ namespace NadekoBot.Modules.Administration - _t = new Timer(async (_) => + _t = new Timer(async (objState) => { - var index = 0; try { + var state = (TimerState)objState; if (!RotatingStatuses) return; else { - if (index >= RotatingStatusMessages.Count) - index = 0; + if (state.Index >= RotatingStatusMessages.Count) + state.Index = 0; if (!RotatingStatusMessages.Any()) return; - var status = RotatingStatusMessages[index++].Status; + var status = RotatingStatusMessages[state.Index++].Status; if (string.IsNullOrWhiteSpace(status)) return; PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value())); @@ -68,7 +73,7 @@ namespace NadekoBot.Modules.Administration { _log.Warn("Rotating playing status errored.\n" + ex); } - }, null, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); + }, new TimerState(), TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)); } public static Dictionary> PlayingPlaceholders { get; } = diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index ddb34011..01f91b67 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -301,6 +301,14 @@ namespace NadekoBot.Modules.Utility .ConfigureAwait(false); } + [NadekoCommand, Usage, Description, Aliases] + public async Task ShardId(ulong guildid) + { + var shardId = NadekoBot.Client.GetShardIdFor(guildid); + + await Context.Channel.SendConfirmAsync($"ShardId for **{guildid}** with {NadekoBot.Client.Shards.Count} total shards", shardId.ToString()).ConfigureAwait(false); + } + [NadekoCommand, Usage, Description, Aliases] public async Task Stats() { From 22eb42ff8f7ff43fee4b96962b6468a1bc08bacb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 5 Feb 2017 07:52:39 +0100 Subject: [PATCH 027/256] !!smq can now be used on it's own and act like !!smq 0 (reset max queue) --- src/NadekoBot/Modules/Music/Music.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 5ad9918c..c6746996 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -556,7 +556,7 @@ namespace NadekoBot.Modules.Music [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task SetMaxQueue(uint size) + public async Task SetMaxQueue(uint size = 0) { MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) From 871237af4e2e8fbd23460128f8e3f06ca941784f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 5 Feb 2017 09:09:18 +0100 Subject: [PATCH 028/256] $slot cooldown reduced to 2 seconds. .v+t roles now have no permissions --- .../Modules/Administration/Commands/VoicePlusTextCommands.cs | 2 +- src/NadekoBot/Modules/Gambling/Commands/Slots.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs index d0c905bb..392b4e88 100644 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -108,7 +108,7 @@ namespace NadekoBot.Modules.Administration var roleName = GetRoleName(afterVch); IRole roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName); if (roleToAdd == null) - roleToAdd = await guild.CreateRoleAsync(roleName).ConfigureAwait(false); + roleToAdd = await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false); ITextChannel textChannel = guild.TextChannels .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) diff --git a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs index 445d6c48..989d3789 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs @@ -262,7 +262,7 @@ namespace NadekoBot.Modules.Gambling { var t = Task.Run(async () => { - await Task.Delay(3000); + await Task.Delay(2000); runningUsers.Remove(Context.User.Id); }); } From 7e79dd416320a2cf20966daa6186841f0adbc308 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 5 Feb 2017 10:06:36 +0100 Subject: [PATCH 029/256] $waifuinfo will update target user's username and discrimnator in case they changed --- .../Modules/Gambling/Commands/WaifuClaimCommands.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index f5a2a454..f04ebda8 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -403,6 +403,7 @@ namespace NadekoBot.Modules.Gambling x.UpdateType == WaifuUpdateType.Claimed && x.New == null); if (w == null) + { uow.Waifus.Add(w = new WaifuInfo() { Affinity = null, @@ -410,6 +411,10 @@ namespace NadekoBot.Modules.Gambling Price = 1, Waifu = uow.DiscordUsers.GetOrCreate(target), }); + } + + w.Waifu.Username = target.Username; + w.Waifu.Discriminator = target.Discriminator; await uow.CompleteAsync().ConfigureAwait(false); } From 3388005edbf0705a83d612f7908344ae04f243a9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 5 Feb 2017 19:15:05 +0100 Subject: [PATCH 030/256] just a bit better console output --- .../Resources/CommandStrings.Designer.cs | 27 +++++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 9 ++++++ src/NadekoBot/Services/CommandHandler.cs | 29 ++++++++++++++----- 3 files changed, 57 insertions(+), 8 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 2be25567..6c880516 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -7052,6 +7052,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to shardid. + /// + public static string shardid_cmd { + get { + return ResourceManager.GetString("shardid_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shows which shard is a certain guild on, by guildid.. + /// + public static string shardid_desc { + get { + return ResourceManager.GetString("shardid_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}shardid 117523346618318850`. + /// + public static string shardid_usage { + get { + return ResourceManager.GetString("shardid_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to shardstats. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index a0cd8924..0f03e989 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3069,4 +3069,13 @@ `{0}connectshard 2` + + shardid + + + Shows which shard is a certain guild on, by guildid. + + + `{0}shardid 117523346618318850` + \ No newline at end of file diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 8cc68001..ac0a5b80 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -105,9 +105,9 @@ namespace NadekoBot.Services BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id); const float oneThousandth = 1.0f / 1000; - private Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int ticks) + private Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int exec1, int exec2, int exec3, int total) { - _log.Info("Command Executed after {4}s\n\t" + + _log.Info("Command Executed after {4}/{5}/{6}/{7}s\n\t" + "User: {0}\n\t" + "Server: {1}\n\t" + "Channel: {2}\n\t" + @@ -116,13 +116,17 @@ namespace NadekoBot.Services (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content, // {3} - ticks * oneThousandth); + exec1 * oneThousandth, // {4} + exec2 * oneThousandth, // {5} + exec3 * oneThousandth, // {6} + total * oneThousandth // {7} + ); return Task.CompletedTask; } - private void LogErroredExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int ticks) + private void LogErroredExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int exec1, int exec2, int exec3, int total) { - _log.Warn("Command Errored after {5}s\n\t" + + _log.Warn("Command Errored after {5}/{6}/{7}/{8}s\n\t" + "User: {0}\n\t" + "Server: {1}\n\t" + "Channel: {2}\n\t" + @@ -133,7 +137,10 @@ namespace NadekoBot.Services (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content,// {3} exec.Result.ErrorReason, // {4} - ticks * oneThousandth // {5} + exec1 * oneThousandth, // {5} + exec2 * oneThousandth, // {6} + exec3 * oneThousandth, // {7} + total * oneThousandth // {8} ); } @@ -219,16 +226,22 @@ namespace NadekoBot.Services if (IsBlacklisted(guild, usrMsg)) return; + var exec1 = Environment.TickCount - execTime; + var cleverBotRan = await Task.Run(() => TryRunCleverbot(usrMsg, guild)).ConfigureAwait(false); if (cleverBotRan) return; + var exec2 = Environment.TickCount - execTime; + // maybe this message is a custom reaction // todo log custom reaction executions. return struct with info var crExecuted = await Task.Run(() => CustomReactions.TryExecuteCustomReaction(usrMsg)).ConfigureAwait(false); if (crExecuted) //if it was, don't execute the command return; + var exec3 = Environment.TickCount - execTime; + string messageContent = usrMsg.Content; // execute the command and measure the time it took @@ -238,11 +251,11 @@ namespace NadekoBot.Services if (exec.Result.IsSuccess) { await CommandExecuted(usrMsg, exec.CommandInfo).ConfigureAwait(false); - await LogSuccessfulExecution(usrMsg, exec, channel, execTime).ConfigureAwait(false); + await LogSuccessfulExecution(usrMsg, exec, channel, exec1, exec2, exec3, execTime).ConfigureAwait(false); } else if (!exec.Result.IsSuccess && exec.Result.Error != CommandError.UnknownCommand) { - LogErroredExecution(usrMsg, exec, channel, execTime); + LogErroredExecution(usrMsg, exec, channel, exec1, exec2, exec3, execTime); if (guild != null && exec.CommandInfo != null && exec.Result.Error == CommandError.Exception) { if (exec.PermissionCache != null && exec.PermissionCache.Verbose) From fbd32a679728fef0752393214654da80713be65a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 5 Feb 2017 19:47:36 +0100 Subject: [PATCH 031/256] disabled %rnduser% completely due to performance issues --- .../Modules/CustomReactions/Extensions.cs | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/NadekoBot/Modules/CustomReactions/Extensions.cs b/src/NadekoBot/Modules/CustomReactions/Extensions.cs index ff20fa89..a0b91962 100644 --- a/src/NadekoBot/Modules/CustomReactions/Extensions.cs +++ b/src/NadekoBot/Modules/CustomReactions/Extensions.cs @@ -23,17 +23,24 @@ namespace NadekoBot.Modules.CustomReactions {"%mention%", (ctx) => { return $"<@{NadekoBot.Client.CurrentUser.Id}>"; } }, {"%user%", (ctx) => { return ctx.Author.Mention; } }, {"%rnduser%", (ctx) => { - var ch = ctx.Channel as ITextChannel; - if(ch == null) - return ""; + //var ch = ctx.Channel as ITextChannel; + //if(ch == null) + // return ""; - var g = ch.Guild as SocketGuild; - if(g == null) - return ""; + //var g = ch.Guild as SocketGuild; + //if(g == null) + // return ""; + //try { + // var usr = g.Users.Skip(new NadekoRandom().Next(0, g.Users.Count)).FirstOrDefault(); + // return usr.Mention; + //} + //catch { + return "[%rnduser% is temp. disabled]"; + //} - var users = g.Users.ToArray(); + //var users = g.Users.ToArray(); - return users[new NadekoRandom().Next(0, users.Length-1)].Mention; + //return users[new NadekoRandom().Next(0, users.Length-1)].Mention; } } //{"%rng%", (ctx) => { return new NadekoRandom().Next(0,10).ToString(); } } }; From ad3dd61d26ca95e3a366192c746ecf261ae591d0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 6 Feb 2017 07:53:27 +0100 Subject: [PATCH 032/256] rule34, e621 back on public bot --- src/NadekoBot/Modules/NSFW/NSFW.cs | 12 +++---- src/NadekoBot/Modules/Utility/Utility.cs | 43 ++++++++++++++++++++---- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 9698379a..fe81fa49 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -147,11 +147,7 @@ namespace NadekoBot.Modules.NSFW [NadekoCommand, Usage, Description, Aliases] public Task Konachan([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan); - - [NadekoCommand, Usage, Description, Aliases] - public Task Rule34([Remainder] string tag = null) - => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34); - +#endif [NadekoCommand, Usage, Description, Aliases] public async Task E621([Remainder] string tag = null) { @@ -168,7 +164,11 @@ namespace NadekoBot.Modules.NSFW .WithFooter(efb => efb.WithText("e621"))) .ConfigureAwait(false); } -#endif + + [NadekoCommand, Usage, Description, Aliases] + public Task Rule34([Remainder] string tag = null) + => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34); + [NadekoCommand, Usage, Description, Aliases] public async Task Danbooru([Remainder] string tag = null) { diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 01f91b67..035acf74 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -25,6 +25,36 @@ namespace NadekoBot.Modules.Utility { private static ConcurrentDictionary rotatingRoleColors = new ConcurrentDictionary(); + //[NadekoCommand, Usage, Description, Aliases] + //[RequireContext(ContextType.Guild)] + //public async Task Midorina([Remainder] string arg) + //{ + // var channel = (ITextChannel)Context.Channel; + + // var roleNames = arg?.Split(';'); + + // if (roleNames == null || roleNames.Length == 0) + // return; + + // var j = 0; + // var roles = roleNames.Select(x => Context.Guild.Roles.FirstOrDefault(r => String.Compare(r.Name, x) == 0)) + // .Where(x => x != null) + // .Select(x => $"`{++j}.` {x.Name}") + // .Take(10) + // .ToArray(); + + // string[] reactions = { "one", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":ten:" }; + + // var msg = await Context.Channel.SendConfirmAsync("Pick a Role", + // string.Join("\n", roles)).ConfigureAwait(false); + + // for (int i = 0; i < roles.Length; i++) + // { + // await msg.AddReactionAsync(reactions[i]).ConfigureAwait(false); + // await Task.Delay(1000).ConfigureAwait(false); + // } + //} + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] @@ -317,10 +347,6 @@ namespace NadekoBot.Modules.Utility var shardId = Context.Guild != null ? NadekoBot.Client.GetShardIdFor(Context.Guild.Id) : 0; - var footer = $"Shard {shardId} | {NadekoBot.Client.Shards.Count} total shards"; -#if !GLOBAL_NADEKO - footer += $" | Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."; -#endif await Context.Channel.EmbedAsync( new EmbedBuilder().WithOkColor() @@ -328,15 +354,18 @@ namespace NadekoBot.Modules.Utility .WithUrl("http://nadekobot.readthedocs.io/en/latest/") .WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg")) .AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Library")).WithValue(stats.Library).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(Format.Bold("Shard")).WithValue($"#{shardId}, {NadekoBot.Client.Shards.Count} total").WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true)) .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)) - .WithFooter(efb => efb.WithText(footer))); +#if !GLOBAL_NADEKO + .WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued.")) +#endif + ); } [NadekoCommand, Usage, Description, Aliases] @@ -393,4 +422,4 @@ namespace NadekoBot.Modules.Utility await JsonConvert.SerializeObject(grouping, Formatting.Indented).ToStream().ConfigureAwait(false), title, title).ConfigureAwait(false); } } -} +} \ No newline at end of file From b73b670f6f5950af645655e1d8282296dc683cac Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 6 Feb 2017 08:35:45 +0100 Subject: [PATCH 033/256] .shardstats improved --- src/NadekoBot/Modules/Utility/Utility.cs | 33 +++++++++++++++--------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 035acf74..7f002e1f 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -312,23 +312,32 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] public async Task ShardStats(int page = 1) { - page -= 1; - if (page < 0) + if (page < 1) return; - var shards = NadekoBot.Client.Shards.Skip(page * 25).Take(25); + var status = string.Join(", ", NadekoBot.Client.Shards.GroupBy(x => x.ConnectionState) + .Select(x => $"{x.Count()} shards {x.Key}") + .ToArray()); - var info = string.Join("\n", - shards.Select(x => $"Shard **#{x.ShardId.ToString()}** is in {Format.Bold(x.ConnectionState.ToString())} state with {Format.Bold(x.Guilds.Count.ToString())} servers")); + var allShardStrings = NadekoBot.Client.Shards + .Select(x => $"Shard **#{x.ShardId.ToString()}** is in {Format.Bold(x.ConnectionState.ToString())} state with {Format.Bold(x.Guilds.Count.ToString())} servers") + .ToArray(); - if (string.IsNullOrWhiteSpace(info)) - info = "No shard stats on this page."; - await Context.Channel.EmbedAsync(new EmbedBuilder() - .WithOkColor() - .WithTitle("Shard Stats - page " + (page + 1)) - .WithDescription(info)) - .ConfigureAwait(false); + + await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => { + + var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25)); + + if (string.IsNullOrWhiteSpace(str)) + str = "No shards on this page."; + + return new EmbedBuilder() + .WithAuthor(a => a.WithName("Shard Stats")) + .WithTitle(status) + .WithOkColor() + .WithDescription(str); + }, allShardStrings.Length / 25); } [NadekoCommand, Usage, Description, Aliases] From ba8e906c970a00e9b9f102be407cf77c300a928f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 6 Feb 2017 09:35:49 +0100 Subject: [PATCH 034/256] gave up on command, commented out, will continue in the future maybe --- .../Gambling/Commands/CurrencyEvents.cs | 2 +- src/NadekoBot/Modules/Utility/Utility.cs | 62 ++++++++++++++++--- 2 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index bea309af..cfc1d789 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -125,7 +125,7 @@ namespace NadekoBot.Modules.Gambling { var msg = await Context.Channel.SendConfirmAsync("Flower reaction event started!", "Add 🌸 reaction to this message to get 100" + NadekoBot.BotConfig.CurrencySign, - footer: "This event is active for 24 hours.") + footer: "This event is active for up to 24 hours.") .ConfigureAwait(false); try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); } catch diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 7f002e1f..d5826f1b 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -37,22 +37,68 @@ namespace NadekoBot.Modules.Utility // return; // var j = 0; - // var roles = roleNames.Select(x => Context.Guild.Roles.FirstOrDefault(r => String.Compare(r.Name, x) == 0)) + // var roles = roleNames.Select(x => Context.Guild.Roles.FirstOrDefault(r => String.Compare(r.Name, x, StringComparison.OrdinalIgnoreCase) == 0)) // .Where(x => x != null) - // .Select(x => $"`{++j}.` {x.Name}") // .Take(10) // .ToArray(); - // string[] reactions = { "one", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:", ":ten:" }; + // var rnd = new NadekoRandom(); + // var reactions = new[] { "🎬", "🐧", "🌍", "🌺", "🚀", "☀", "🌲", "🍒", "🐾", "🏀" } + // .OrderBy(x => rnd.Next()) + // .ToArray(); + // var roleStrings = roles + // .Select(x => $"{reactions[j++]} -> {x.Name}"); + // var msg = await Context.Channel.SendConfirmAsync("Pick a Role", - // string.Join("\n", roles)).ConfigureAwait(false); + // string.Join("\n", roleStrings)).ConfigureAwait(false); // for (int i = 0; i < roles.Length; i++) // { - // await msg.AddReactionAsync(reactions[i]).ConfigureAwait(false); + // try { await msg.AddReactionAsync(reactions[i]).ConfigureAwait(false); } + // catch (Exception ex) { _log.Warn(ex); } // await Task.Delay(1000).ConfigureAwait(false); // } + + // msg.OnReaction((r) => Task.Run(async () => + // { + // try + // { + // var usr = r.User.GetValueOrDefault() as IGuildUser; + + // if (usr == null) + // return; + + // var index = Array.IndexOf(reactions, r.Emoji.Name); + // if (index == -1) + // return; + + // await usr.RemoveRolesAsync(roles[index]); + // } + // catch (Exception ex) + // { + // _log.Warn(ex); + // } + // }), (r) => Task.Run(async () => + // { + // try + // { + // var usr = r.User.GetValueOrDefault() as IGuildUser; + + // if (usr == null) + // return; + + // var index = Array.IndexOf(reactions, r.Emoji.Name); + // if (index == -1) + // return; + + // await usr.RemoveRolesAsync(roles[index]); + // } + // catch (Exception ex) + // { + // _log.Warn(ex); + // } + // })); //} [NadekoCommand, Usage, Description, Aliases] @@ -147,7 +193,8 @@ namespace NadekoBot.Modules.Utility return; var socketGuild = Context.Guild as SocketGuild; - if (socketGuild == null) { + if (socketGuild == null) + { _log.Warn("Can't cast guild to socket guild."); return; } @@ -325,7 +372,8 @@ namespace NadekoBot.Modules.Utility - await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => { + await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => + { var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25)); From 04ea4790170967c49a58409caa59167212202991 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 6 Feb 2017 12:57:38 +0100 Subject: [PATCH 035/256] flowerreaction event under the hood improvements --- .../Gambling/Commands/CurrencyEvents.cs | 105 ++++++++++++++---- 1 file changed, 83 insertions(+), 22 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index cfc1d789..5647b3e4 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -11,6 +11,8 @@ using System.Text; using System.Threading.Tasks; using Discord.WebSocket; using NadekoBot.Services.Database; +using System.Threading; +using NLog; namespace NadekoBot.Modules.Gambling { @@ -25,7 +27,6 @@ namespace NadekoBot.Modules.Gambling SneakyGameStatus } //flower reaction event - public static readonly ConcurrentHashSet _flowerReactionAwardedUsers = new ConcurrentHashSet(); public static readonly ConcurrentHashSet _sneakyGameAwardedUsers = new ConcurrentHashSet(); @@ -45,7 +46,6 @@ namespace NadekoBot.Modules.Gambling var channel = (ITextChannel)Context.Channel; try { - switch (e) { case CurrencyEvent.FlowerReaction: @@ -121,38 +121,99 @@ namespace NadekoBot.Modules.Gambling return Task.Delay(0); } - public static async Task FlowerReactionEvent(CommandContext Context) + public static Task FlowerReactionEvent(CommandContext Context) => + new FlowerReactionEvent().Start(Context); + } + } + + public abstract class CurrencyEvent + { + public abstract Task Start(CommandContext channel); + } + + public class FlowerReactionEvent : CurrencyEvent + { + public readonly ConcurrentHashSet _flowerReactionAwardedUsers = new ConcurrentHashSet(); + private readonly Logger _log; + + private IUserMessage msg { get; set; } = null; + + private CancellationTokenSource source { get; } + private CancellationToken cancelToken { get; } + + public FlowerReactionEvent() + { + _log = LogManager.GetCurrentClassLogger(); + source = new CancellationTokenSource(); + cancelToken = source.Token; + } + + private async Task End() + { + if(msg != null) + await msg.DeleteAsync().ConfigureAwait(false); + + if(!source.IsCancellationRequested) + source.Cancel(); + + NadekoBot.Client.MessageDeleted -= MessageDeletedEventHandler; + } + + private Task MessageDeletedEventHandler(ulong id, Optional _) { + if (msg?.Id == id) { - var msg = await Context.Channel.SendConfirmAsync("Flower reaction event started!", + _log.Warn("Stopping flower reaction event because message is deleted."); + Task.Run(() => End()); + } + + return Task.CompletedTask; + } + + public override async Task Start(CommandContext context) + { + msg = await context.Channel.SendConfirmAsync("Flower reaction event started!", "Add 🌸 reaction to this message to get 100" + NadekoBot.BotConfig.CurrencySign, footer: "This event is active for up to 24 hours.") .ConfigureAwait(false); + + NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler; + + try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); } + catch + { try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); } catch { - try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); } - catch + try { await msg.DeleteAsync().ConfigureAwait(false); } + catch { return; } + } + } + using (msg.OnReaction(async (r) => + { + try + { + if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) { - try { await msg.DeleteAsync().ConfigureAwait(false); } - catch { return; } + try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { } } } - using (msg.OnReaction(async (r) => - { - try - { - if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) - { - try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { } - } - } - catch { } - })) + catch { } + })) + { + try { - await Task.Delay(TimeSpan.FromHours(24)).ConfigureAwait(false); - try { await msg.DeleteAsync().ConfigureAwait(false); } catch { } - _flowerReactionAwardedUsers.Clear(); + await Task.Delay(TimeSpan.FromHours(24), cancelToken).ConfigureAwait(false); } + catch (OperationCanceledException) + { + + } + if (cancelToken.IsCancellationRequested) + return; + + _log.Warn("Stopping flower reaction event because it expired."); + await End(); + } } } From 4429687bd567a1c0631333d7a7cd6bb4635cbf35 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 6 Feb 2017 13:00:39 +0100 Subject: [PATCH 036/256] public nadeko fix --- src/NadekoBot/Modules/NSFW/NSFW.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index fe81fa49..bd213773 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -253,8 +253,6 @@ namespace NadekoBot.Modules.NSFW await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); } } -#if !GLOBAL_NADEKO - public static Task GetE621ImageLink(string tag) => Task.Run(async () => { @@ -278,6 +276,10 @@ namespace NadekoBot.Modules.NSFW } }); + public static Task GetRule34ImageLink(string tag) => + Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34); + +#if !GLOBAL_NADEKO public static Task GetYandereImageLink(string tag) => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere); @@ -286,9 +288,6 @@ namespace NadekoBot.Modules.NSFW public static Task GetGelbooruImageLink(string tag) => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru); - - public static Task GetRule34ImageLink(string tag) => - Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34); #endif } } \ No newline at end of file From 91efe4db48817a553ed4a902d7bf1ffa5287cce3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 6 Feb 2017 20:09:47 +0100 Subject: [PATCH 037/256] .greetmsg, .greetdmmsg, .acr and .byemsg now accept json which will cause the output to be a discord embed. You can create a proper json using http://nadekobot.xyz/embedbuilder/ --- src/NadekoBot/DataStructures/CREmbed.cs | 91 +++++++++++ .../Commands/ServerGreetCommands.cs | 151 ++++++++++++------ .../CustomReactions/CustomReactions.cs | 34 +++- 3 files changed, 229 insertions(+), 47 deletions(-) create mode 100644 src/NadekoBot/DataStructures/CREmbed.cs diff --git a/src/NadekoBot/DataStructures/CREmbed.cs b/src/NadekoBot/DataStructures/CREmbed.cs new file mode 100644 index 00000000..88e81630 --- /dev/null +++ b/src/NadekoBot/DataStructures/CREmbed.cs @@ -0,0 +1,91 @@ +using Discord; +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.DataStructures +{ + public class CREmbed + { + private static readonly Logger _log; + public string PlainText { get; set; } + public string Title { get; set; } + public string Description { get; set; } + public CREmbedFooter Footer { get; set; } + public string Thumbnail { get; set; } + public string Image { get; set; } + public CREmbedField[] Fields { get; set; } + public uint Color { get; set; } = 7458112; + + static CREmbed() + { + _log = LogManager.GetCurrentClassLogger(); + } + + public bool IsValid => + !string.IsNullOrWhiteSpace(Title) || + !string.IsNullOrWhiteSpace(Description) || + !string.IsNullOrWhiteSpace(Thumbnail) || + !string.IsNullOrWhiteSpace(Image) || + (Footer != null && (!string.IsNullOrWhiteSpace(Footer.Text) || !string.IsNullOrWhiteSpace(Footer.IconUrl))) || + (Fields != null && Fields.Length > 0); + + public EmbedBuilder ToEmbed() + { + var embed = new EmbedBuilder() + .WithTitle(Title) + .WithDescription(Description) + .WithColor(new Discord.Color(Color)); + if (Footer != null) + embed.WithFooter(efb => efb.WithIconUrl(Footer.IconUrl).WithText(Footer.Text)); + embed.WithThumbnailUrl(Thumbnail) + .WithImageUrl(Image); + + if (Fields != null) + foreach (var f in Fields) + { + embed.AddField(efb => efb.WithName(f.Name).WithValue(f.Value).WithIsInline(f.Inline)); + } + + return embed; + } + + public static bool TryParse(string input, out CREmbed embed) + { + embed = null; + if (string.IsNullOrWhiteSpace(input)) + return false; + + try + { + var crembed = JsonConvert.DeserializeObject(input); + + if (!crembed.IsValid) + return false; + + embed = crembed; + return true; + } + catch (Exception ex) + { + return false; + } + } + } + + public class CREmbedField + { + public string Name { get; set; } + public string Value { get; set; } + public bool Inline { get; set; } + } + + public class CREmbedFooter { + public string Text { get; set; } + public string IconUrl { get; set; } + } +} diff --git a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs index 9183b814..6c1be19e 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs @@ -2,6 +2,7 @@ using Discord.Commands; using Discord.WebSocket; using NadekoBot.Attributes; +using NadekoBot.DataStructures; using NadekoBot.Extensions; using NadekoBot.Services; using NadekoBot.Services.Database; @@ -83,77 +84,137 @@ namespace NadekoBot.Modules.Administration return settings; } - //todo optimize ASAP - private static async Task UserLeft(IGuildUser user) + private static Task UserLeft(IGuildUser user) { - try + var _ = Task.Run(async () => { - var conf = GetOrAddSettingsForGuild(user.GuildId); - - if (!conf.SendChannelByeMessage) return; - var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId); - - if (channel == null) //maybe warn the server owner that the channel is missing - return; - - var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); - if (string.IsNullOrWhiteSpace(msg)) - return; try { - var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); - if (conf.AutoDeleteByeMessagesTimer > 0) + var conf = GetOrAddSettingsForGuild(user.GuildId); + + if (!conf.SendChannelByeMessage) return; + var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId); + + if (channel == null) //maybe warn the server owner that the channel is missing + return; + CREmbed embedData; + if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData)) { - toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); + embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + try + { + var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); + if (conf.AutoDeleteByeMessagesTimer > 0) + { + toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); + } + } + catch (Exception ex) { _log.Warn(ex); } + } + else + { + var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + if (string.IsNullOrWhiteSpace(msg)) + return; + try + { + var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); + if (conf.AutoDeleteByeMessagesTimer > 0) + { + toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); + } + } + catch (Exception ex) { _log.Warn(ex); } } } - catch (Exception ex) { _log.Warn(ex); } - } - catch { } + catch { } + }); + return Task.CompletedTask; } - private static async Task UserJoined(IGuildUser user) + private static Task UserJoined(IGuildUser user) { - try + var _ = Task.Run(async () => { - var conf = GetOrAddSettingsForGuild(user.GuildId); - - if (conf.SendChannelGreetMessage) + try { - var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId); - if (channel != null) //maybe warn the server owner that the channel is missing + var conf = GetOrAddSettingsForGuild(user.GuildId); + + if (conf.SendChannelGreetMessage) { - var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); - if (!string.IsNullOrWhiteSpace(msg)) + var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId); + if (channel != null) //maybe warn the server owner that the channel is missing { - try + + CREmbed embedData; + if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData)) { - var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); - if (conf.AutoDeleteGreetMessagesTimer > 0) + embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + try { - toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); + var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); + if (conf.AutoDeleteGreetMessagesTimer > 0) + { + toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); + } + } + catch (Exception ex) { _log.Warn(ex); } + } + else + { + var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + if (!string.IsNullOrWhiteSpace(msg)) + { + try + { + var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); + if (conf.AutoDeleteGreetMessagesTimer > 0) + { + toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); + } + } + catch (Exception ex) { _log.Warn(ex); } } } - catch (Exception ex) { _log.Warn(ex); } } } - } - if (conf.SendDmGreetMessage) - { - var channel = await user.CreateDMChannelAsync(); - - if (channel != null) + if (conf.SendDmGreetMessage) { - var msg = conf.DmGreetMessageText.Replace("%user%", user.Username).Replace("%server%", user.Guild.Name); - if (!string.IsNullOrWhiteSpace(msg)) + var channel = await user.CreateDMChannelAsync(); + + if (channel != null) { - await channel.SendConfirmAsync(msg).ConfigureAwait(false); + CREmbed embedData; + if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData)) + { + embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + try + { + await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); + } + catch (Exception ex) { _log.Warn(ex); } + } + else + { + var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); + if (!string.IsNullOrWhiteSpace(msg)) + { + await channel.SendConfirmAsync(msg).ConfigureAwait(false); + } + } } } } - } - catch { } + catch { } + }); + return Task.CompletedTask; } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index 1f1cca1d..8f76f0a8 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -11,6 +11,8 @@ using NLog; using System.Diagnostics; using Discord.WebSocket; using System; +using Newtonsoft.Json; +using NadekoBot.DataStructures; namespace NadekoBot.Modules.CustomReactions { @@ -69,7 +71,22 @@ namespace NadekoBot.Modules.CustomReactions if (reaction != null) { if (reaction.Response != "-") - try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { } + { + CREmbed crembed; + if (CREmbed.TryParse(reaction.Response, out crembed)) + { + try { await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "").ConfigureAwait(false); } + catch (Exception ex) + { + _log.Warn("Sending CREmbed failed"); + _log.Warn(ex); + } + } + else + { + try { await channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { } + } + } ReactionStats.AddOrUpdate(reaction.Trigger, 1, (k, old) => ++old); return true; @@ -91,7 +108,20 @@ namespace NadekoBot.Modules.CustomReactions if (greaction != null) { - try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { } + CREmbed crembed; + if (CREmbed.TryParse(greaction.Response, out crembed)) + { + try { await channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "").ConfigureAwait(false); } + catch (Exception ex) + { + _log.Warn("Sending CREmbed failed"); + _log.Warn(ex); + } + } + else + { + try { await channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { } + } ReactionStats.AddOrUpdate(greaction.Trigger, 1, (k, old) => ++old); return true; } From e36ef3f5096d4c0308f97ef3ef8cef1db40b32f2 Mon Sep 17 00:00:00 2001 From: samvaio Date: Wed, 8 Feb 2017 20:01:22 +0530 Subject: [PATCH 038/256] Updated with addition of CentOS --- docs/guides/Linux Guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/guides/Linux Guide.md b/docs/guides/Linux Guide.md index 1ba7e0c0..1997407b 100644 --- a/docs/guides/Linux Guide.md +++ b/docs/guides/Linux Guide.md @@ -51,12 +51,12 @@ You should see these following options after using the above command: 2. Download Stable Build 3. Run Nadeko (Normally) 4. Run Nadeko with Auto Restart (Run Nadeko normally before using this.) -5. Auto-Install Prerequisites (for Ubuntu and Debian) +5. Auto-Install Prerequisites (for Ubuntu, Debian and CentOS) 6. Set up credentials.json (if you have downloaded the bot already) 7. To exit ``` #####Part II (Optional) -**If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed, Press `5` and `enter` key, then `y` when you see the following: +**If** you are running NadekoBot for the first time on your system and never had any *prerequisites* installed and have Ubuntu, Debian or CentOS, Press `5` and `enter` key, then `y` when you see the following: ``` Welcome to NadekoBot Auto Prerequisites Installer. Would you like to continue? From a8124cf1f240831fd77365e5c70f1803bd6ea910 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 8 Feb 2017 21:44:32 +0100 Subject: [PATCH 039/256] started work on ttt --- .../Modules/Games/Commands/TicTacToe.cs | 127 ++++++++++++++++++ src/NadekoBot/Modules/Games/Games.cs | 1 - .../Resources/CommandStrings.Designer.cs | 8 +- src/NadekoBot/Resources/CommandStrings.resx | 8 +- 4 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 src/NadekoBot/Modules/Games/Commands/TicTacToe.cs diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs new file mode 100644 index 00000000..54afcab9 --- /dev/null +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -0,0 +1,127 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NLog; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Games +{ + public partial class Games + { + [Group] + public class TicTacToeCommands : ModuleBase + { + //channelId/game + private static readonly ConcurrentDictionary _openGames = new ConcurrentDictionary(); + private readonly Logger _log; + + public TicTacToeCommands() + { + _log = LogManager.GetCurrentClassLogger(); + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task Ttt(IGuildUser secondUser) + { + var channel = (ITextChannel)Context.Channel; + + + TicTacToe game; + if (_openGames.TryRemove(channel.Id, out game)) // joining open game + { + if (!game.Join((IGuildUser)Context.User)) + { + await Context.Channel.SendErrorAsync("You can't play against yourself. Game stopped.").ConfigureAwait(false); + return; + } + var _ = Task.Run(() => game.Start()); + _log.Warn($"User {Context.User} joined a TicTacToe game."); + return; + } + game = new TicTacToe(channel, (IGuildUser)Context.User); + if (_openGames.TryAdd(Context.Channel.Id, game)) + { + _log.Warn($"User {Context.User} created a TicTacToe game."); + await Context.Channel.SendConfirmAsync("Tic Tac Toe game created. Waiting for another user.").ConfigureAwait(false); + } + } + } + + public class TicTacToe + { + + private readonly ITextChannel _channel; + private readonly Logger _log; + private readonly IGuildUser[] _users; + private readonly int?[,] _state; + + public TicTacToe(ITextChannel channel, IGuildUser firstUser) + { + _channel = channel; + _users = new IGuildUser[2] { firstUser, null }; + _state = new int?[3, 3] { + { null, null, null }, + { null, 1, 1 }, + { 0, null, 0 }, + }; + + _log = LogManager.GetCurrentClassLogger(); + } + + public string GetState() + { + var sb = new StringBuilder(); + for (int i = 0; i < _state.GetLength(0); i++) + { + for (int j = 0; j < _state.GetLength(1); j++) + { + sb.Append(GetIcon(_state[i, j])); + if (j < _state.GetLength(1) - 1) + sb.Append("┃"); + } + if (i < _state.GetLength(0) - 1) + sb.AppendLine("\n──────────"); + } + + return sb.ToString(); + } + + public EmbedBuilder GetEmbed() => + new EmbedBuilder() + .WithOkColor() + .WithDescription(GetState()) + .WithAuthor(eab => eab.WithName("Tic Tac Toe")) + .WithTitle($"{_users[0]} vs {_users[1]}"); + + private static string GetIcon(int? val) + { + switch (val) + { + case 0: + return "❌"; + case 1: + return "⭕"; + default: + return "⬛"; + } + } + + public Task Start() + { + return Task.CompletedTask; + } + + public void Join(IGuildUser user) + { + + } + } + } +} diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 6faf288c..b4107acd 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -15,7 +15,6 @@ namespace NadekoBot.Modules.Games { private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray(); - [NadekoCommand, Usage, Description, Aliases] public async Task Choose([Remainder] string list = null) { diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 6c880516..fef3709d 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -906,7 +906,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10.. + /// Looks up a localized string similar to Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10.. /// public static string betroll_desc { get { @@ -1041,7 +1041,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message.. + /// Looks up a localized string similar to Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded.. /// public static string byemsg_desc { get { @@ -3066,7 +3066,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message.. + /// Looks up a localized string similar to Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded.. /// public static string greetdmmsg_desc { get { @@ -3093,7 +3093,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message.. + /// Looks up a localized string similar to Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded.. /// public static string greetmsg_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 0f03e989..fb6d8b15 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -184,7 +184,7 @@ greetmsg - Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. + Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. `{0}greetmsg Welcome, %user%.` @@ -202,7 +202,7 @@ byemsg - Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. + Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. `{0}byemsg %user% has left.` @@ -1273,7 +1273,7 @@ betroll br - Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x3 and 100 x10. + Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10. `{0}br 5` @@ -2311,7 +2311,7 @@ `{0}greetdmmsg Welcome to the server, %user%`. - Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. + Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. Check how much currency a person has. (Defaults to yourself) From 959f1726c9490bbbcfa48b565f399b0bddccbda3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Feb 2017 03:09:06 +0100 Subject: [PATCH 040/256] >tictactoe finished, sanity said it seems good to her. Unfortunately it isn't centered and can't be. --- .../Modules/Games/Commands/TicTacToe.cs | 254 ++++++++++++++++-- .../Resources/CommandStrings.Designer.cs | 27 ++ src/NadekoBot/Resources/CommandStrings.resx | 9 + 3 files changed, 263 insertions(+), 27 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index 54afcab9..ef20bfb0 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -8,17 +8,19 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Games { public partial class Games { + //todo timeout [Group] public class TicTacToeCommands : ModuleBase { //channelId/game - private static readonly ConcurrentDictionary _openGames = new ConcurrentDictionary(); + private static readonly Dictionary _games = new Dictionary(); private readonly Logger _log; public TicTacToeCommands() @@ -26,41 +28,69 @@ namespace NadekoBot.Modules.Games _log = LogManager.GetCurrentClassLogger(); } + private readonly SemaphoreSlim sem = new SemaphoreSlim(1, 1); + private readonly object tttLockObj = new object(); + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task Ttt(IGuildUser secondUser) + public async Task TicTacToe() { var channel = (ITextChannel)Context.Channel; - - TicTacToe game; - if (_openGames.TryRemove(channel.Id, out game)) // joining open game + await sem.WaitAsync(1000); + try { - if (!game.Join((IGuildUser)Context.User)) + TicTacToe game; + if (_games.TryGetValue(channel.Id, out game)) { - await Context.Channel.SendErrorAsync("You can't play against yourself. Game stopped.").ConfigureAwait(false); + var _ = Task.Run(async () => + { + await game.Start((IGuildUser)Context.User); + }); return; } - var _ = Task.Run(() => game.Start()); - _log.Warn($"User {Context.User} joined a TicTacToe game."); - return; + game = new TicTacToe(channel, (IGuildUser)Context.User); + _games.Add(channel.Id, game); + await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Created a TicTacToe game.").ConfigureAwait(false); + + game.OnEnded += (g) => + { + _games.Remove(channel.Id); + }; } - game = new TicTacToe(channel, (IGuildUser)Context.User); - if (_openGames.TryAdd(Context.Channel.Id, game)) + finally { - _log.Warn($"User {Context.User} created a TicTacToe game."); - await Context.Channel.SendConfirmAsync("Tic Tac Toe game created. Waiting for another user.").ConfigureAwait(false); + sem.Release(); } } } public class TicTacToe { + enum Phase + { + Starting, + Started, + Ended + } private readonly ITextChannel _channel; private readonly Logger _log; private readonly IGuildUser[] _users; private readonly int?[,] _state; + private Phase _phase; + private readonly Func _playMove; + int curUserIndex = 0; + private readonly SemaphoreSlim moveLock; + + private IGuildUser _winner = null; + + private readonly string[] numbers = { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" }; + + public Action OnEnded; + + private IUserMessage previousMessage = null; + private Timer timeoutTimer; public TicTacToe(ITextChannel channel, IGuildUser firstUser) { @@ -68,11 +98,44 @@ namespace NadekoBot.Modules.Games _users = new IGuildUser[2] { firstUser, null }; _state = new int?[3, 3] { { null, null, null }, - { null, 1, 1 }, - { 0, null, 0 }, + { null, null, null }, + { null, null, null }, }; _log = LogManager.GetCurrentClassLogger(); + _log.Warn($"User {firstUser} created a TicTacToe game."); + _phase = Phase.Starting; + moveLock = new SemaphoreSlim(1, 1); + + timeoutTimer = new Timer(async (_) => + { + await moveLock.WaitAsync(); + try + { + if (_phase == Phase.Ended) + return; + + _phase = Phase.Ended; + if (_users[1] != null) + { + _winner = _users[curUserIndex ^= 1]; + var del = previousMessage?.DeleteAsync(); + try + { + await _channel.EmbedAsync(GetEmbed("Time Expired!")).ConfigureAwait(false); + await del.ConfigureAwait(false); + } + catch { } + } + + OnEnded?.Invoke(this); + } + catch { } + finally + { + moveLock.Release(); + } + }, null, 15000, Timeout.Infinite); } public string GetState() @@ -82,7 +145,7 @@ namespace NadekoBot.Modules.Games { for (int j = 0; j < _state.GetLength(1); j++) { - sb.Append(GetIcon(_state[i, j])); + sb.Append(_state[i, j] == null ? numbers[i * 3 + j] : GetIcon(_state[i, j])); if (j < _state.GetLength(1) - 1) sb.Append("┃"); } @@ -93,12 +156,28 @@ namespace NadekoBot.Modules.Games return sb.ToString(); } - public EmbedBuilder GetEmbed() => - new EmbedBuilder() + public EmbedBuilder GetEmbed(string title = null) + { + var embed = new EmbedBuilder() .WithOkColor() - .WithDescription(GetState()) - .WithAuthor(eab => eab.WithName("Tic Tac Toe")) - .WithTitle($"{_users[0]} vs {_users[1]}"); + .WithDescription(Environment.NewLine + GetState()) + .WithAuthor(eab => eab.WithName($"{_users[0]} vs {_users[1]}")); + + if (!string.IsNullOrWhiteSpace(title)) + embed.WithTitle(title); + + if (_winner == null) + { + if (_phase == Phase.Ended) + embed.WithFooter(efb => efb.WithText($"No moves left!")); + else + embed.WithFooter(efb => efb.WithText($"{_users[curUserIndex]}'s move")); + } + else + embed.WithFooter(efb => efb.WithText($"{_winner} Won!")); + + return embed; + } private static string GetIcon(int? val) { @@ -108,20 +187,141 @@ namespace NadekoBot.Modules.Games return "❌"; case 1: return "⭕"; + case 2: + return "❎"; + case 3: + return "🅾"; default: return "⬛"; } } - public Task Start() + public async Task Start(IGuildUser user) { - return Task.CompletedTask; + if (_phase == Phase.Started || _phase == Phase.Ended) + { + await _channel.SendErrorAsync(user.Mention + " TicTacToe Game is already running in this channel.").ConfigureAwait(false); + return; + } + else if (_users[0] == user) + { + await _channel.SendErrorAsync(user.Mention + " You can't play against yourself.").ConfigureAwait(false); + return; + } + + _users[1] = user; + _log.Warn($"User {user} joined a TicTacToe game."); + + _phase = Phase.Started; + + NadekoBot.Client.MessageReceived += Client_MessageReceived; + + previousMessage = await _channel.EmbedAsync(GetEmbed("Game Started")).ConfigureAwait(false); } - public void Join(IGuildUser user) + private bool IsDraw() { - + for (int i = 0; i < 3; i++) + { + for (int j = 0; j < 3; j++) + { + if (_state[i, j] == null) + return false; + } + } + return true; + } + + private Task Client_MessageReceived(Discord.WebSocket.SocketMessage msg) + { + var _ = Task.Run(async () => + { + await moveLock.WaitAsync().ConfigureAwait(false); + try + { + var curUser = _users[curUserIndex]; + if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id) + return; + + int index; + if (int.TryParse(msg.Content, out index) && + --index >= 0 && + index <= 9 && + _state[index / 3, index % 3] == null) + { + _state[index / 3, index % 3] = curUserIndex; + + // i'm lazy + if (_state[index / 3, 0] == _state[index / 3, 1] && _state[index / 3, 1] == _state[index / 3, 2]) + { + _state[index / 3, 0] = curUserIndex + 2; + _state[index / 3, 1] = curUserIndex + 2; + _state[index / 3, 2] = curUserIndex + 2; + + _phase = Phase.Ended; + } + else if (_state[0, index % 3] == _state[1, index % 3] && _state[1, index % 3] == _state[2, index % 3]) + { + _state[0, index % 3] = curUserIndex + 2; + _state[1, index % 3] = curUserIndex + 2; + _state[2, index % 3] = curUserIndex + 2; + + _phase = Phase.Ended; + } + else if (curUserIndex == _state[0, 0] && _state[0, 0] == _state[1, 1] && _state[1, 1] == _state[2, 2]) + { + _state[0, 0] = curUserIndex + 2; + _state[1, 1] = curUserIndex + 2; + _state[2, 2] = curUserIndex + 2; + + _phase = Phase.Ended; + } + else if (curUserIndex == _state[0, 2] && _state[0, 2] == _state[1, 1] && _state[1, 1] == _state[2, 0]) + { + _state[0, 2] = curUserIndex + 2; + _state[1, 1] = curUserIndex + 2; + _state[2, 0] = curUserIndex + 2; + + _phase = Phase.Ended; + } + string reason = ""; + + if (_phase == Phase.Ended) // if user won, stop receiving moves + { + reason = "Matched three!"; + _winner = _users[curUserIndex]; + NadekoBot.Client.MessageReceived -= Client_MessageReceived; + OnEnded?.Invoke(this); + } + else if (IsDraw()) + { + reason = "A draw!"; + _phase = Phase.Ended; + NadekoBot.Client.MessageReceived -= Client_MessageReceived; + OnEnded?.Invoke(this); + } + + var sendstate = Task.Run(async () => + { + var del1 = msg.DeleteAsync(); + var del2 = previousMessage?.DeleteAsync(); + try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { } + try { await del1; } catch { } + try { await del2; } catch { } + }); + curUserIndex ^= 1; + + timeoutTimer.Change(15000, Timeout.Infinite); + } + } + finally + { + moveLock.Release(); + } + }); + + return Task.CompletedTask; } } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index fef3709d..04745557 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -7727,6 +7727,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to tictactoe ttt. + /// + public static string tictactoe_cmd { + get { + return ResourceManager.GetString("tictactoe_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move.. + /// + public static string tictactoe_desc { + get { + return ResourceManager.GetString("tictactoe_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to >ttt. + /// + public static string tictactoe_usage { + get { + return ResourceManager.GetString("tictactoe_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to tl. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index fb6d8b15..fae1afda 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3078,4 +3078,13 @@ `{0}shardid 117523346618318850` + + tictactoe ttt + + + Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. + + + >ttt + \ No newline at end of file From c6351ea958f5f493c8d148b737083ae179efb9da Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Feb 2017 17:09:11 +0100 Subject: [PATCH 041/256] Updated invite links --- docs/Frequently Asked Questions.md | 2 +- docs/index.md | 2 +- src/NadekoBot/Services/Database/Models/BotConfig.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Frequently Asked Questions.md b/docs/Frequently Asked Questions.md index 32d7a6e9..09eaafd7 100644 --- a/docs/Frequently Asked Questions.md +++ b/docs/Frequently Asked Questions.md @@ -19,7 +19,7 @@ ###Question 5: I have an issue/bug/suggestion, where do I put it so it gets noticed? ----------- -**Answer:** First, check [issues](https://github.com/Kwoth/NadekoBot/issues "GitHub NadekoBot Issues"), then check the `#suggestions` channel in the Nadeko [help server](https://discord.gg/0ehQwTK2RBjAxzEY). +**Answer:** First, check [issues](https://github.com/Kwoth/NadekoBot/issues "GitHub NadekoBot Issues"), then check the `#suggestions` channel in the Nadeko [help server](https://discord.gg/nadekobot). If your problem or suggestion is not there, feel free to request/notify us about it either in the Issues section of GitHub for issues or in the `#suggestions` channel on the Nadeko help server for suggestions. diff --git a/docs/index.md b/docs/index.md index c2dbfa0a..50909579 100644 --- a/docs/index.md +++ b/docs/index.md @@ -33,7 +33,7 @@ If you want to contribute, be sure to PR on the **[dev][dev]** branch. - [Donate](Donate.md) [img]: https://cdn.discordapp.com/attachments/202743183774318593/210580315381563392/discord.png -[NadekoBot Server]: https://discord.gg/0ehQwTK2RBjAxzEY +[NadekoBot Server]: https://discord.gg/nadekobot [GitHub]: https://github.com/Kwoth/NadekoBot [Issues]: https://github.com/Kwoth/NadekoBot/issues [dev]: https://github.com/Kwoth/NadekoBot/tree/dev diff --git a/src/NadekoBot/Services/Database/Models/BotConfig.cs b/src/NadekoBot/Services/Database/Models/BotConfig.cs index d96d4cc8..6b35e5f2 100644 --- a/src/NadekoBot/Services/Database/Models/BotConfig.cs +++ b/src/NadekoBot/Services/Database/Models/BotConfig.cs @@ -54,7 +54,7 @@ For a specific command help, use `{1}h CommandName` (for example {1}h !!q) -Nadeko Support Server: https://discord.gg/0ehQwTK2RBjAxzEY"; +Nadeko Support Server: https://discord.gg/nadekobot"; public int MigrationVersion { get; set; } From 971308d0a29484f3a17712daf66941bcd9301509 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 9 Feb 2017 17:10:39 +0100 Subject: [PATCH 042/256] new discord invite link to readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c0df0727..7a7cd1f4 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) -[![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/0ehQwTK2RBjAxzEY) +[![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/nadekobot) [![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest) # NadekoBot [![nadeko1](https://cdn.discordapp.com/attachments/155726317222887425/252095170676391936/A1.jpg)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) From d032a7c46483f5987fb57ab83df54906cd903910 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 9 Feb 2017 17:11:39 +0100 Subject: [PATCH 043/256] Missed another invite --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a7cd1f4..bb66aa4f 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,6 @@ `Follow me on twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides.` -[![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) [![discord](https://cdn.discordapp.com/attachments/155726317222887425/252192415673221122/discord_banner.JPG)](https://discord.gg/0ehQwTK2RBjAxzEY) [![Wiki](https://cdn.discordapp.com/attachments/155726317222887425/252192472849973250/read_the_docs_banner.JPG)](http://nadekobot.readthedocs.io/en/latest/) +[![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) [![discord](https://cdn.discordapp.com/attachments/155726317222887425/252192415673221122/discord_banner.JPG)](https://discord.gg/nadekobot) [![Wiki](https://cdn.discordapp.com/attachments/155726317222887425/252192472849973250/read_the_docs_banner.JPG)](http://nadekobot.readthedocs.io/en/latest/) From 27d4f79be053d42b261cca01c50c1b9f5be99a79 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Feb 2017 20:27:52 +0100 Subject: [PATCH 044/256] Fixed ~mal command for mal supporters --- src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index 2aeb3c5b..c1d2eef9 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -96,7 +96,7 @@ namespace NadekoBot.Modules.Searches // return $"[{elem.InnerHtml}]({elem.Href})"; // })); - var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li") + var info = document.QuerySelectorAll("ul.user-status:nth-child(3) > li.clearfix") .Select(x => Tuple.Create(x.Children[0].InnerHtml, x.Children[1].InnerHtml)) .ToList(); From a88f0569350ef7394238360b5c21b50f421981ba Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Feb 2017 20:56:27 +0100 Subject: [PATCH 045/256] Fixed spaces in hangman/trivia hints --- .../Modules/Games/Commands/Hangman/HangmanGame.cs | 9 ++++----- .../Modules/Games/Commands/Trivia/TriviaQuestion.cs | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs index 3d5f7ec6..a025eb0d 100644 --- a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs @@ -63,14 +63,13 @@ namespace NadekoBot.Modules.Games.Commands.Hangman public uint MaxErrors { get; } = 6; public uint MessagesSinceLastPost { get; private set; } = 0; public string ScrambledWord => "`" + String.Concat(Term.Word.Select(c => - { - if (!(char.IsLetter(c) || char.IsDigit(c))) + { + if (c == ' ') + return " \u2000"; + if (!(char.IsLetter(c) || char.IsDigit(c))) return $" {c}"; c = char.ToUpperInvariant(c); - - if (c == ' ') - return " "; return Guesses.Contains(c) ? $" {c}" : " _"; })) + "`"; diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestion.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestion.cs index 83ac895a..d9c908a8 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestion.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaQuestion.cs @@ -98,7 +98,7 @@ namespace NadekoBot.Modules.Games.Trivia if (letters[i] != ' ') letters[i] = '_'; } - return string.Join(" \x200B", new string(letters).Replace(" ", " \x200B").AsEnumerable()); + return string.Join(" ", new string(letters).Replace(" ", " \u2000").AsEnumerable()); } } } From 51e45d17a0ed0d0cf4c52608cf19fd3d1ca12612 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Feb 2017 21:08:02 +0100 Subject: [PATCH 046/256] fixed message updated and message title in logs --- src/NadekoBot/Modules/Administration/Commands/LogCommand.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index db339d4f..4219ff98 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -653,7 +653,7 @@ namespace NadekoBot.Modules.Administration return; var embed = new EmbedBuilder() .WithOkColor() - .WithTitle($"🗑 Message Deleted in {((ITextChannel)msg.Channel).Mention}") + .WithTitle($"🗑 Message Deleted in #{((ITextChannel)msg.Channel).Name}") .WithDescription($"{msg.Author}") .AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) @@ -697,7 +697,7 @@ namespace NadekoBot.Modules.Administration var embed = new EmbedBuilder() .WithOkColor() - .WithTitle($"📝 Message Updated in {((ITextChannel)after.Channel).Mention}") + .WithTitle($"📝 Message Updated in #{((ITextChannel)after.Channel).Name}") .WithDescription(after.Author.ToString()) .AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) From 9c00d36f40c98f3c81238cdcb61163187e8d5f03 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 9 Feb 2017 21:11:33 +0100 Subject: [PATCH 047/256] .repinv should now reset the repeater --- src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs index b6efe2fc..1167cab7 100644 --- a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs @@ -141,6 +141,7 @@ namespace NadekoBot.Modules.Utility } var repeater = repList[index].Repeater; + repList[index].Reset(); await Context.Channel.SendMessageAsync("🔄 " + repeater.Message).ConfigureAwait(false); } From 84bec3e19c59a7a065ae23d852e08c3bf6bcc5f3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 10 Feb 2017 11:16:30 +0100 Subject: [PATCH 048/256] Fixed warnings --- src/NadekoBot/DataStructures/CREmbed.cs | 2 +- src/NadekoBot/Modules/Games/Commands/TicTacToe.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/NadekoBot/DataStructures/CREmbed.cs b/src/NadekoBot/DataStructures/CREmbed.cs index 88e81630..cb67503b 100644 --- a/src/NadekoBot/DataStructures/CREmbed.cs +++ b/src/NadekoBot/DataStructures/CREmbed.cs @@ -70,7 +70,7 @@ namespace NadekoBot.DataStructures embed = crembed; return true; } - catch (Exception ex) + catch { return false; } diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index ef20bfb0..1a254e28 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -79,7 +79,6 @@ namespace NadekoBot.Modules.Games private readonly IGuildUser[] _users; private readonly int?[,] _state; private Phase _phase; - private readonly Func _playMove; int curUserIndex = 0; private readonly SemaphoreSlim moveLock; From 494cf9447ee5e60f8018eb1da447a29a40b801b7 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 10 Feb 2017 13:03:15 +0100 Subject: [PATCH 049/256] Currency emoji issue fixed? --- src/NadekoBot/Services/Impl/ImagesService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index 02bcb1cd..77ee9f26 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -82,6 +82,7 @@ namespace NadekoBot.Services.Impl .ToImmutableArray(); SlotEmojis = Directory.GetFiles(slotEmojisPath) + .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) .Select(x => File.ReadAllBytes(x).ToImmutableArray()) .ToImmutableArray(); From 145109cd72298987c907619a957e71c80934079a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 10 Feb 2017 14:46:48 +0100 Subject: [PATCH 050/256] small change to ffmpeg params --- src/NadekoBot/Modules/Music/Classes/SongBuffer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs index 9fd23294..738d07ec 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongBuffer.cs @@ -56,7 +56,7 @@ namespace NadekoBot.Modules.Music.Classes p = Process.Start(new ProcessStartInfo { FileName = "ffmpeg", - Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -ac 2 pipe:1 -loglevel quiet", + Arguments = $"-ss {SkipTo} -i {SongInfo.Uri} -f s16le -ar 48000 -vn -ac 2 pipe:1 -loglevel quiet", UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = false, From 567c6f87bf25177b67546250dbfdc7fcd5a397e8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 10 Feb 2017 15:26:06 +0100 Subject: [PATCH 051/256] SoundcloudClientId is no longer mandatory in order to play soundcloud songs. (thanks to samvaio <3) --- src/NadekoBot/Services/Impl/BotCredentials.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Impl/BotCredentials.cs b/src/NadekoBot/Services/Impl/BotCredentials.cs index fdd0a1fa..47906e8c 100644 --- a/src/NadekoBot/Services/Impl/BotCredentials.cs +++ b/src/NadekoBot/Services/Impl/BotCredentials.cs @@ -25,7 +25,17 @@ namespace NadekoBot.Services.Impl public string LoLApiKey { get; } public string OsuApiKey { get; } - public string SoundCloudClientId { get; } + private string _soundcloudClientId; + public string SoundCloudClientId { + get { + return string.IsNullOrWhiteSpace(_soundcloudClientId) + ? "d0bd7768e3a1a2d15430f0dccb871117" + : _soundcloudClientId; + } + set { + _soundcloudClientId = value; + } + } public DBConfig Db { get; } public int TotalShards { get; } From f91bb2897984b664f8da56b1c6786ecd416fa84b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 11 Feb 2017 10:17:59 +0100 Subject: [PATCH 052/256] kwothy plz fix --- src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 1c8671f8..1cf43a30 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -167,7 +167,7 @@ namespace NadekoBot.Modules.Games var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false); if (!removed) { - await Context.Channel.SendErrorAsync($"You don't have any {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false); + await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false); return; } From 46e74f64f0cc5c628e6fc22de16615d619dacc26 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 11 Feb 2017 15:21:09 +0100 Subject: [PATCH 053/256] small waifu fix --- src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index f04ebda8..1d1f414f 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Gambling result = WaifuClaimResult.Success; } } - else if (isAffinity && amount >= w.Price * 0.88f) + else if (isAffinity && amount > w.Price * 0.88f) { if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User.Id, "Claimed Waifu", amount, uow).ConfigureAwait(false)) { From 4d2d27d48ff0fb664ddfbc946591c94d417c5345 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 11 Feb 2017 15:21:53 +0100 Subject: [PATCH 054/256] small uinfo fix --- src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index b2594d3a..8131bf16 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -94,9 +94,9 @@ namespace NadekoBot.Modules.Utility embed.AddField(fb => fb.WithName("**Nickname**").WithValue(user.Nickname).WithIsInline(true)); } embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) + .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) + .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) .WithColor(NadekoBot.OkColor); if (user.AvatarId != null) From 12eb216a4bd1079affb0cad1360ebb07d8352dfd Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 11 Feb 2017 16:26:04 +0100 Subject: [PATCH 055/256] all nsfw commands except ~hentaibomb and ~autohentai will work on public bot again --- src/NadekoBot/Modules/NSFW/NSFW.cs | 130 ++++++++++++++--------------- 1 file changed, 64 insertions(+), 66 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index bd213773..131d1501 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.NSFW [NadekoModule("NSFW", "~")] public class NSFW : DiscordModule { -#if !GLOBAL_NADEKO + private static ConcurrentDictionary AutoHentaiTimers { get; } = new ConcurrentDictionary(); private static ConcurrentHashSet _hentaiBombBlacklist { get; } = new ConcurrentHashSet(); @@ -66,79 +66,79 @@ namespace NadekoBot.Modules.NSFW public Task Hentai([Remainder] string tag = null) => InternalHentai(Context.Channel, tag, false); - [NadekoCommand, Usage, Description, Aliases] - [RequireUserPermission(ChannelPermission.ManageMessages)] - public async Task AutoHentai(int interval = 0, string tags = null) - { - Timer t; + //[NadekoCommand, Usage, Description, Aliases] + //[RequireUserPermission(ChannelPermission.ManageMessages)] + //public async Task AutoHentai(int interval = 0, string tags = null) + //{ + // Timer t; - if (interval == 0) - { - if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) - { - t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer - await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false); - } - return; - } + // if (interval == 0) + // { + // if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) + // { + // t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer + // await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false); + // } + // return; + // } - if (interval < 20) - return; + // if (interval < 20) + // return; - var tagsArr = tags?.Split('|'); + // var tagsArr = tags?.Split('|'); - t = new Timer(async (state) => - { - try - { - if (tagsArr == null || tagsArr.Length == 0) - await InternalHentai(Context.Channel, null, true).ConfigureAwait(false); - else - await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false); - } - catch { } - }, null, interval * 1000, interval * 1000); + // t = new Timer(async (state) => + // { + // try + // { + // if (tagsArr == null || tagsArr.Length == 0) + // await InternalHentai(Context.Channel, null, true).ConfigureAwait(false); + // else + // await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false); + // } + // catch { } + // }, null, interval * 1000, interval * 1000); - AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => - { - old.Change(Timeout.Infinite, Timeout.Infinite); - return t; - }); + // AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => + // { + // old.Change(Timeout.Infinite, Timeout.Infinite); + // return t; + // }); - await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}") - .ConfigureAwait(false); - } + // await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}") + // .ConfigureAwait(false); + //} - [NadekoCommand, Usage, Description, Aliases] - public async Task HentaiBomb([Remainder] string tag = null) - { - if (!_hentaiBombBlacklist.Add(Context.User.Id)) - return; - try - { - tag = tag?.Trim() ?? ""; - tag = "rating%3Aexplicit+" + tag; + //[NadekoCommand, Usage, Description, Aliases] + //public async Task HentaiBomb([Remainder] string tag = null) + //{ + // if (!_hentaiBombBlacklist.Add(Context.User.Id)) + // return; + // try + // { + // tag = tag?.Trim() ?? ""; + // tag = "rating%3Aexplicit+" + tag; - var links = await Task.WhenAll(GetGelbooruImageLink(tag), - GetDanbooruImageLink(tag), - GetKonachanImageLink(tag), - GetYandereImageLink(tag)).ConfigureAwait(false); + // var links = await Task.WhenAll(GetGelbooruImageLink(tag), + // GetDanbooruImageLink(tag), + // GetKonachanImageLink(tag), + // GetYandereImageLink(tag)).ConfigureAwait(false); - var linksEnum = links?.Where(l => l != null); - if (links == null || !linksEnum.Any()) - { - await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false); - return; - } + // var linksEnum = links?.Where(l => l != null); + // if (links == null || !linksEnum.Any()) + // { + // await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false); + // return; + // } - await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false); - } - finally { - await Task.Delay(5000).ConfigureAwait(false); - _hentaiBombBlacklist.TryRemove(Context.User.Id); - } - } + // await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false); + // } + // finally { + // await Task.Delay(5000).ConfigureAwait(false); + // _hentaiBombBlacklist.TryRemove(Context.User.Id); + // } + //} [NadekoCommand, Usage, Description, Aliases] public Task Yandere([Remainder] string tag = null) @@ -147,7 +147,7 @@ namespace NadekoBot.Modules.NSFW [NadekoCommand, Usage, Description, Aliases] public Task Konachan([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan); -#endif + [NadekoCommand, Usage, Description, Aliases] public async Task E621([Remainder] string tag = null) { @@ -279,7 +279,6 @@ namespace NadekoBot.Modules.NSFW public static Task GetRule34ImageLink(string tag) => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Rule34); -#if !GLOBAL_NADEKO public static Task GetYandereImageLink(string tag) => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Yandere); @@ -288,6 +287,5 @@ namespace NadekoBot.Modules.NSFW public static Task GetGelbooruImageLink(string tag) => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru); -#endif } } \ No newline at end of file From c32546d633e55b3f524f9c1bcf65aa4fd1373f6a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 11 Feb 2017 22:29:25 +0100 Subject: [PATCH 056/256] timezone parsing done, just needs actual db field and usage --- .../Modules/Administration/Administration.cs | 40 ++++++++++++++ .../Resources/CommandStrings.Designer.cs | 54 +++++++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 18 +++++++ 3 files changed, 112 insertions(+) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index cf92fb4b..b86f80e9 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -552,5 +552,45 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false); } + + //[NadekoCommand, Usage, Description, Aliases] + //[RequireContext(ContextType.Guild)] + //public async Task Timezones(int page = 1) + //{ + // page -= 1; + + // if (page < 0 || page > 20) + // return; + + // var timezones = TimeZoneInfo.GetSystemTimeZones(); + // var timezonesPerPage = 20; + + // await Context.Channel.SendPaginatedConfirmAsync(page + 1, (curPage) => new EmbedBuilder() + // .WithOkColor() + // .WithTitle("Available Timezones") + // .WithDescription(string.Join("\n", timezones.Skip((curPage - 1) * timezonesPerPage).Take(timezonesPerPage).Select(x => $"`{x.Id,-25}` UTC{x.BaseUtcOffset:hhmm}"))), + // timezones.Count / timezonesPerPage); + //} + + //[NadekoCommand, Usage, Description, Aliases] + //[RequireContext(ContextType.Guild)] + //public async Task Timezone([Remainder] string id) + //{ + // TimeZoneInfo tz; + // try + // { + // tz = TimeZoneInfo.FindSystemTimeZoneById(id); + // if (tz != null) + // await Context.Channel.SendConfirmAsync(tz.ToString()).ConfigureAwait(false); + // } + // catch (Exception ex) + // { + // tz = null; + // _log.Warn(ex); + // } + + // if (tz == null) + // await Context.Channel.SendErrorAsync("Timezone not found. You should specify one of the timezones listed in the 'timezones' command.").ConfigureAwait(false); + //} } } \ No newline at end of file diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 04745557..11fa3692 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -7754,6 +7754,60 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to timezone. + /// + public static string timezone_cmd { + get { + return ResourceManager.GetString("timezone_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets this guilds timezone. This affects bot's time output in this server (logs, etc..). + /// + public static string timezone_desc { + get { + return ResourceManager.GetString("timezone_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}timezone`. + /// + public static string timezone_usage { + get { + return ResourceManager.GetString("timezone_usage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to timezones. + /// + public static string timezones_cmd { + get { + return ResourceManager.GetString("timezones_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of all timezones available on the system to be used with `{0}timezone`.. + /// + public static string timezones_desc { + get { + return ResourceManager.GetString("timezones_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}timezones`. + /// + public static string timezones_usage { + get { + return ResourceManager.GetString("timezones_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to tl. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index fae1afda..cca4cf5c 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3087,4 +3087,22 @@ >ttt + + timezones + + + List of all timezones available on the system to be used with `{0}timezone`. + + + `{0}timezones` + + + timezone + + + Sets this guilds timezone. This affects bot's time output in this server (logs, etc..) + + + `{0}timezone` + \ No newline at end of file From 3e3c5e75c58acf643df33705369918f58f762234 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 12 Feb 2017 00:27:52 +0100 Subject: [PATCH 057/256] ttt fix, thx aurora --- .../Modules/Games/Commands/TicTacToe.cs | 61 ++++++++++--------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index 1a254e28..4879793b 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -105,36 +105,6 @@ namespace NadekoBot.Modules.Games _log.Warn($"User {firstUser} created a TicTacToe game."); _phase = Phase.Starting; moveLock = new SemaphoreSlim(1, 1); - - timeoutTimer = new Timer(async (_) => - { - await moveLock.WaitAsync(); - try - { - if (_phase == Phase.Ended) - return; - - _phase = Phase.Ended; - if (_users[1] != null) - { - _winner = _users[curUserIndex ^= 1]; - var del = previousMessage?.DeleteAsync(); - try - { - await _channel.EmbedAsync(GetEmbed("Time Expired!")).ConfigureAwait(false); - await del.ConfigureAwait(false); - } - catch { } - } - - OnEnded?.Invoke(this); - } - catch { } - finally - { - moveLock.Release(); - } - }, null, 15000, Timeout.Infinite); } public string GetState() @@ -213,8 +183,39 @@ namespace NadekoBot.Modules.Games _phase = Phase.Started; + timeoutTimer = new Timer(async (_) => + { + await moveLock.WaitAsync(); + try + { + if (_phase == Phase.Ended) + return; + + _phase = Phase.Ended; + if (_users[1] != null) + { + _winner = _users[curUserIndex ^= 1]; + var del = previousMessage?.DeleteAsync(); + try + { + await _channel.EmbedAsync(GetEmbed("Time Expired!")).ConfigureAwait(false); + await del.ConfigureAwait(false); + } + catch { } + } + + OnEnded?.Invoke(this); + } + catch { } + finally + { + moveLock.Release(); + } + }, null, 15000, Timeout.Infinite); + NadekoBot.Client.MessageReceived += Client_MessageReceived; + previousMessage = await _channel.EmbedAsync(GetEmbed("Game Started")).ConfigureAwait(false); } From 85a00f61239d0a7d25ac1eee1a889b0621de202b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 12 Feb 2017 00:33:13 +0100 Subject: [PATCH 058/256] accidentaly disabled autohentai and hentaibomb on self hosteds too. --- src/NadekoBot/Modules/NSFW/NSFW.cs | 129 +++++++++++++++-------------- 1 file changed, 65 insertions(+), 64 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 131d1501..735d29f4 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -65,81 +65,82 @@ namespace NadekoBot.Modules.NSFW [NadekoCommand, Usage, Description, Aliases] public Task Hentai([Remainder] string tag = null) => InternalHentai(Context.Channel, tag, false); +#if !GLOBAL_NADEKO + [NadekoCommand, Usage, Description, Aliases] + [RequireUserPermission(ChannelPermission.ManageMessages)] + public async Task AutoHentai(int interval = 0, string tags = null) + { + Timer t; - //[NadekoCommand, Usage, Description, Aliases] - //[RequireUserPermission(ChannelPermission.ManageMessages)] - //public async Task AutoHentai(int interval = 0, string tags = null) - //{ - // Timer t; + if (interval == 0) + { + if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) + { + t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer + await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false); + } + return; + } - // if (interval == 0) - // { - // if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) - // { - // t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer - // await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false); - // } - // return; - // } + if (interval < 20) + return; - // if (interval < 20) - // return; + var tagsArr = tags?.Split('|'); - // var tagsArr = tags?.Split('|'); + t = new Timer(async (state) => + { + try + { + if (tagsArr == null || tagsArr.Length == 0) + await InternalHentai(Context.Channel, null, true).ConfigureAwait(false); + else + await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false); + } + catch { } + }, null, interval * 1000, interval * 1000); - // t = new Timer(async (state) => - // { - // try - // { - // if (tagsArr == null || tagsArr.Length == 0) - // await InternalHentai(Context.Channel, null, true).ConfigureAwait(false); - // else - // await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false); - // } - // catch { } - // }, null, interval * 1000, interval * 1000); + AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => + { + old.Change(Timeout.Infinite, Timeout.Infinite); + return t; + }); - // AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => - // { - // old.Change(Timeout.Infinite, Timeout.Infinite); - // return t; - // }); - - // await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}") - // .ConfigureAwait(false); - //} + await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}") + .ConfigureAwait(false); + } - //[NadekoCommand, Usage, Description, Aliases] - //public async Task HentaiBomb([Remainder] string tag = null) - //{ - // if (!_hentaiBombBlacklist.Add(Context.User.Id)) - // return; - // try - // { - // tag = tag?.Trim() ?? ""; - // tag = "rating%3Aexplicit+" + tag; + [NadekoCommand, Usage, Description, Aliases] + public async Task HentaiBomb([Remainder] string tag = null) + { + if (!_hentaiBombBlacklist.Add(Context.User.Id)) + return; + try + { + tag = tag?.Trim() ?? ""; + tag = "rating%3Aexplicit+" + tag; - // var links = await Task.WhenAll(GetGelbooruImageLink(tag), - // GetDanbooruImageLink(tag), - // GetKonachanImageLink(tag), - // GetYandereImageLink(tag)).ConfigureAwait(false); + var links = await Task.WhenAll(GetGelbooruImageLink(tag), + GetDanbooruImageLink(tag), + GetKonachanImageLink(tag), + GetYandereImageLink(tag)).ConfigureAwait(false); - // var linksEnum = links?.Where(l => l != null); - // if (links == null || !linksEnum.Any()) - // { - // await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false); - // return; - // } - - // await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false); - // } - // finally { - // await Task.Delay(5000).ConfigureAwait(false); - // _hentaiBombBlacklist.TryRemove(Context.User.Id); - // } - //} + var linksEnum = links?.Where(l => l != null); + if (links == null || !linksEnum.Any()) + { + await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false); + return; + } + await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false); + } + finally + { + await Task.Delay(5000).ConfigureAwait(false); + _hentaiBombBlacklist.TryRemove(Context.User.Id); + } + } +#endif [NadekoCommand, Usage, Description, Aliases] public Task Yandere([Remainder] string tag = null) => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere); From fb837235a7424842038f97242b84ecc7c413146d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 10:28:49 +0100 Subject: [PATCH 059/256] a bit faster OwnerOnly checking --- src/NadekoBot/Services/IBotCredentials.cs | 4 +++- src/NadekoBot/Services/Impl/BotCredentials.cs | 6 ++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Services/IBotCredentials.cs b/src/NadekoBot/Services/IBotCredentials.cs index 88bc5986..a03c6db3 100644 --- a/src/NadekoBot/Services/IBotCredentials.cs +++ b/src/NadekoBot/Services/IBotCredentials.cs @@ -1,4 +1,6 @@ using Discord; +using System.Collections.Generic; +using System.Collections.Immutable; namespace NadekoBot.Services { @@ -9,7 +11,7 @@ namespace NadekoBot.Services string Token { get; } string GoogleApiKey { get; } - ulong[] OwnerIds { get; } + ImmutableHashSet OwnerIds { get; } string MashapeKey { get; } string LoLApiKey { get; } diff --git a/src/NadekoBot/Services/Impl/BotCredentials.cs b/src/NadekoBot/Services/Impl/BotCredentials.cs index 47906e8c..56851762 100644 --- a/src/NadekoBot/Services/Impl/BotCredentials.cs +++ b/src/NadekoBot/Services/Impl/BotCredentials.cs @@ -5,6 +5,8 @@ using Discord; using System.Linq; using NLog; using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using System.Collections.Immutable; namespace NadekoBot.Services.Impl { @@ -21,7 +23,7 @@ namespace NadekoBot.Services.Impl public string Token { get; } - public ulong[] OwnerIds { get; } + public ImmutableHashSet OwnerIds { get; } public string LoLApiKey { get; } public string OsuApiKey { get; } @@ -61,7 +63,7 @@ namespace NadekoBot.Services.Impl Token = data[nameof(Token)]; if (string.IsNullOrWhiteSpace(Token)) throw new ArgumentNullException(nameof(Token), "Token is missing from credentials.json or Environment varibles."); - OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToArray(); + OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToImmutableHashSet(); LoLApiKey = data[nameof(LoLApiKey)]; GoogleApiKey = data[nameof(GoogleApiKey)]; MashapeKey = data[nameof(MashapeKey)]; From 34c69243200c90a1b3c2a8aa30a996e280cbb831 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 10:29:34 +0100 Subject: [PATCH 060/256] Localization in the works --- src/NadekoBot/Services/Impl/Localization.cs | 110 +++++++++++++------- 1 file changed, 74 insertions(+), 36 deletions(-) diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index 5ec6e71d..26de5990 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -1,45 +1,83 @@ -namespace NadekoBot.Services +using Discord; +using Discord.Commands; +using NadekoBot.Extensions; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using System; +using NadekoBot.Services.Database; + +namespace NadekoBot.Services { public class Localization { - public string this[string key] => LoadCommandString(key); + public ConcurrentDictionary GuildCultureInfos { get; } - public static string LoadCommandString(string key) + private Localization() { } + public Localization(IDictionary cultureInfoNames) { - string toReturn = Resources.CommandStrings.ResourceManager.GetString(key); - return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn; + GuildCultureInfos = new ConcurrentDictionary(cultureInfoNames.ToDictionary(x => x.Key, x => + { + CultureInfo cultureInfo = null; + try + { + cultureInfo = new CultureInfo(x.Value); + } + catch + { + } + return cultureInfo; + }).Where(x => x.Value != null)); } - //private static string GetCommandString(string key) - //{ - // return key; - //var resx = new List(); - //var fs = new StreamReader(File.OpenRead("./Strings.resx")); - //Console.WriteLine(fs.ReadToEnd()); - //using (var reader = new ResourceReader(fs.BaseStream)) - //{ - // List existing = new List(); - // foreach (DictionaryEntry item in reader) - // { - // existing.Add(item); - // } - // var existingResource = resx.Where(r => r.Key.ToString() == key).FirstOrDefault(); - // if (existingResource.Key == null) - // { - // resx.Add(new DictionaryEntry() { Key = key, Value = key }); - // } - // else - // return existingResource.Value.ToString(); - //} - //using (var writer = new ResourceWriter(new FileStream("./Strings.resx", FileMode.OpenOrCreate))) - //{ - // resx.ForEach(r => - // { - // writer.AddResource(r.Key.ToString(), r.Value.ToString()); - // }); - // writer.Generate(); - //} - //return key; - //} + public void SetGuildCulture(IGuild guild, CultureInfo ci) => + SetGuildCulture(guild.Id, ci); + + public void SetGuildCulture(ulong guildId, CultureInfo ci) + { + if (ci == DefaultCultureInfo) + { + RemoveGuildCulture(guildId); + return; + } + + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(guildId, set => set); + gc.Locale = ci.Name; + uow.Complete(); + } + } + + public void RemoveGuildCulture(IGuild guild) => + RemoveGuildCulture(guild.Id); + + public void RemoveGuildCulture(ulong guildId) { + + CultureInfo throwaway; + if (GuildCultureInfos.TryRemove(guildId, out throwaway)) + { + using (var uow = DbHandler.UnitOfWork()) + { + var gc = uow.GuildConfigs.For(guildId, set => set); + gc.Locale = null; + uow.Complete(); + } + } + } + + public CultureInfo GetCultureInfo(IGuild guild) => + GetCultureInfo(guild.Id); + + public CultureInfo DefaultCultureInfo { get; } = CultureInfo.CurrentCulture; + + public CultureInfo GetCultureInfo(ulong guildId) + { + CultureInfo info = null; + GuildCultureInfos.TryGetValue(guildId, out info); + return info ?? DefaultCultureInfo; + } } } From 3f420f78f21a2059402bde5b98d4ad1bbef4f37b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 10:29:57 +0100 Subject: [PATCH 061/256] More localization, DiscordModule renamed to NadekoModule --- .../Modules/Administration/Administration.cs | 2 +- .../Commands/LocalizationCommands.cs | 67 ++++++++++++++++++ .../Modules/ClashOfClans/ClashOfClans.cs | 2 +- .../CustomReactions/CustomReactions.cs | 2 +- src/NadekoBot/Modules/DiscordModule.cs | 69 ++++++++++++++++--- src/NadekoBot/Modules/Gambling/Gambling.cs | 2 +- src/NadekoBot/Modules/Games/Games.cs | 2 +- src/NadekoBot/Modules/Help/Help.cs | 2 +- src/NadekoBot/Modules/Music/Music.cs | 2 +- src/NadekoBot/Modules/NSFW/NSFW.cs | 2 +- .../Modules/Permissions/Permissions.cs | 2 +- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 4 +- src/NadekoBot/Modules/Searches/Searches.cs | 2 +- src/NadekoBot/Modules/Utility/Utility.cs | 2 +- src/NadekoBot/NadekoBot.cs | 8 ++- .../Services/Database/Models/GuildConfig.cs | 2 + 16 files changed, 147 insertions(+), 25 deletions(-) create mode 100644 src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index b86f80e9..76b96ec4 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -20,7 +20,7 @@ using NLog; namespace NadekoBot.Modules.Administration { [NadekoModule("Administration", ".")] - public partial class Administration : DiscordModule + public partial class Administration : NadekoModule { private static ConcurrentDictionary GuildMuteRoles { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs new file mode 100644 index 00000000..b06d5dce --- /dev/null +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -0,0 +1,67 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Administration.Commands +{ + public partial class Administration + { + [Group] + public class LocalizationCommands : ModuleBase + { + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task SetLocale([Remainder] string name) + { + CultureInfo ci = null; + try + { + if(name.Trim().ToLowerInvariant() == "default") + { + NadekoBot.Localization.RemoveGuildCulture(Context.Guild); + } + ci = new CultureInfo(name); + NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); + + await Context.Channel.SendConfirmAsync($"Your guild's locale is now {ci}.").ConfigureAwait(false); + } + catch(Exception) { + + //_log.warn(ex); + await Context.Channel.SendConfirmAsync($"Failed setting locale. Revisit this command's help.").ConfigureAwait(false); + } + } + + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task SetDefaulLocale(string name) + { + CultureInfo ci = null; + try + { + if (name.Trim().ToLowerInvariant() == "default") + { + NadekoBot.Localization.RemoveGuildCulture(Context.Guild); + } + ci = new CultureInfo(name); + NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); + + await Context.Channel.SendConfirmAsync($"Your guild's locale is now {ci}.").ConfigureAwait(false); + } + catch (Exception) + { + //_log.warn(ex); + await Context.Channel.SendConfirmAsync($"Failed setting locale. Revisit this command's help.").ConfigureAwait(false); + } + } + } + } +} diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 67510d56..83e6d00b 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -17,7 +17,7 @@ using NLog; namespace NadekoBot.Modules.ClashOfClans { [NadekoModule("ClashOfClans", ",")] - public class ClashOfClans : DiscordModule + public class ClashOfClans : NadekoModule { public static ConcurrentDictionary> ClashWars { get; set; } = new ConcurrentDictionary>(); diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index 8f76f0a8..a5129c2b 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -17,7 +17,7 @@ using NadekoBot.DataStructures; namespace NadekoBot.Modules.CustomReactions { [NadekoModule("CustomReactions", ".")] - public class CustomReactions : DiscordModule + public class CustomReactions : NadekoModule { private static CustomReaction[] _globalReactions = new CustomReaction[] { }; public static CustomReaction[] GlobalReactions => _globalReactions; diff --git a/src/NadekoBot/Modules/DiscordModule.cs b/src/NadekoBot/Modules/DiscordModule.cs index 711431d5..cc609a58 100644 --- a/src/NadekoBot/Modules/DiscordModule.cs +++ b/src/NadekoBot/Modules/DiscordModule.cs @@ -1,22 +1,69 @@ -using Discord.Commands; +using Discord; +using Discord.Commands; +using NadekoBot.Extensions; using NLog; +using System; +using System.Collections.Concurrent; +using System.Globalization; +using System.Threading.Tasks; namespace NadekoBot.Modules { - public abstract class DiscordModule : ModuleBase + public abstract class NadekoModule : ModuleBase { - protected Logger _log { get; } - protected string _prefix { get; } + protected readonly Logger _log; + public readonly string _prefix; + public readonly CultureInfo cultureInfo; - public DiscordModule() + public NadekoModule(bool isTopLevelModule = true) { - string prefix; - if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix)) - _prefix = prefix; - else - _prefix = "?missing_prefix?"; - + //if it's top level module + var typeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; + if (!NadekoBot.ModulePrefixes.TryGetValue(typeName, out _prefix)) + _prefix = "?err?"; _log = LogManager.GetCurrentClassLogger(); + + cultureInfo = (Context.Guild == null + ? CultureInfo.CurrentCulture + : NadekoBot.Localization.GetCultureInfo(Context.Guild)); + } + } + + public abstract class NadekoSubmodule : NadekoModule + { + public NadekoSubmodule() : base(false) + { + + } + } + + + public static class ModuleBaseExtensions + { + public static Task ConfirmLocalized(this NadekoModule module, string titleKey, string textKey, string url = null, string footer = null) + { + var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, module.cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); + return module.Context.Channel.SendConfirmAsync(title, text, url, footer); + } + + public static Task ConfirmLocalized(this NadekoModule module, string textKey) + { + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); + return module.Context.Channel.SendConfirmAsync(textKey); + } + + public static Task ErrorLocalized(this NadekoModule module, string titleKey, string textKey, string url = null, string footer = null) + { + var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, module.cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); + return module.Context.Channel.SendErrorAsync(title, text, url, footer); + } + + public static Task ErrorLocalized(this NadekoModule module, string textKey) + { + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); + return module.Context.Channel.SendErrorAsync(textKey); } } } diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index ceaa4300..555f55cb 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -12,7 +12,7 @@ using System.Collections.Generic; namespace NadekoBot.Modules.Gambling { [NadekoModule("Gambling", "$")] - public partial class Gambling : DiscordModule + public partial class Gambling : NadekoModule { public static string CurrencyName { get; set; } public static string CurrencyPluralName { get; set; } diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index b4107acd..e0dbb2c3 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -11,7 +11,7 @@ using NadekoBot.Extensions; namespace NadekoBot.Modules.Games { [NadekoModule("Games", ">")] - public partial class Games : DiscordModule + public partial class Games : NadekoModule { private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray(); diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index 4a561ca6..a15501b0 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -13,7 +13,7 @@ using System.Collections.Generic; namespace NadekoBot.Modules.Help { [NadekoModule("Help", "-")] - public partial class Help : DiscordModule + public partial class Help : NadekoModule { private static string helpString { get; } = NadekoBot.BotConfig.HelpString; public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]); diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index c6746996..bae06919 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Music { [NadekoModule("Music", "!!")] [DontAutoLoad] - public partial class Music : DiscordModule + public partial class Music : NadekoModule { public static ConcurrentDictionary MusicPlayers { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 735d29f4..c2e42d58 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -17,7 +17,7 @@ using System.Collections.Concurrent; namespace NadekoBot.Modules.NSFW { [NadekoModule("NSFW", "~")] - public class NSFW : DiscordModule + public class NSFW : NadekoModule { private static ConcurrentDictionary AutoHentaiTimers { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index 33d8a27e..407ca2a2 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -15,7 +15,7 @@ using NLog; namespace NadekoBot.Modules.Permissions { [NadekoModule("Permissions", ";")] - public partial class Permissions : DiscordModule + public partial class Permissions : NadekoModule { public class PermissionCache { diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 2ce50745..3ece509f 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -16,7 +16,7 @@ using System.Collections.Concurrent; namespace NadekoBot.Modules.Pokemon { [NadekoModule("Pokemon", ">")] - public partial class Pokemon : DiscordModule + public partial class Pokemon : NadekoModule { private static List PokemonTypes = new List(); private static ConcurrentDictionary Stats = new ConcurrentDictionary(); @@ -105,7 +105,7 @@ namespace NadekoBot.Modules.Pokemon if (targetUser == null) { - await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false); + await ReplyLocalized("no user found").ConfigureAwait(false); return; } else if (targetUser == user) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 8abcfee8..d612299c 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -28,7 +28,7 @@ using System.Xml.Linq; namespace NadekoBot.Modules.Searches { [NadekoModule("Searches", "~")] - public partial class Searches : DiscordModule + public partial class Searches : NadekoModule { [NadekoCommand, Usage, Description, Aliases] public async Task Weather([Remainder] string query) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index d5826f1b..1705d934 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -21,7 +21,7 @@ using NadekoBot.Services; namespace NadekoBot.Modules.Utility { [NadekoModule("Utility", ".")] - public partial class Utility : DiscordModule + public partial class Utility : NadekoModule { private static ConcurrentDictionary rotatingRoleColors = new ConcurrentDictionary(); diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 49d0d5c4..65f1e02b 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -16,6 +16,8 @@ using NadekoBot.TypeReaders; using System.Collections.Concurrent; using NadekoBot.Modules.Music; using NadekoBot.Services.Database.Models; +using System.Resources; +using NadekoBot.Resources; namespace NadekoBot { @@ -29,7 +31,10 @@ namespace NadekoBot public static CommandService CommandService { get; private set; } public static CommandHandler CommandHandler { get; private set; } public static DiscordShardedClient Client { get; private set; } - public static BotCredentials Credentials { get; private set; } + public static BotCredentials Credentials { get; } + + public static Localization Localization { get; private set; } + public static ResourceManager ResponsesResourceManager { get; } = new ResourceManager(typeof(ResponseStrings)); public static GoogleApiService Google { get; private set; } public static StatsService Stats { get; private set; } @@ -79,6 +84,7 @@ namespace NadekoBot #endif //initialize Services + Localization = new Localization(NadekoBot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale)); CommandService = new CommandService(new CommandServiceConfig() { CaseSensitiveCommands = false, DefaultRunMode = RunMode.Sync diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 7931e8c9..9b0209e9 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -64,6 +64,8 @@ namespace NadekoBot.Services.Database.Models public AntiRaidSetting AntiRaidSetting { get; set; } public AntiSpamSetting AntiSpamSetting { get; set; } + public string Locale { get; set; } + //public List ProtectionIgnoredChannels { get; set; } = new List(); } From db79177f0cbc8c2bae097cdffb3e150f48284e1a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 10:50:58 +0100 Subject: [PATCH 062/256] Setting locales done? Needs testing --- .../Commands/LocalizationCommands.cs | 8 +-- .../Resources/CommandStrings.Designer.cs | 54 +++++++++++++++++++ src/NadekoBot/Resources/CommandStrings.resx | 18 +++++++ src/NadekoBot/Services/Impl/Localization.cs | 13 ++++- 4 files changed, 87 insertions(+), 6 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index b06d5dce..a7e39fc6 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Administration.Commands [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.Administrator)] - public async Task SetLocale([Remainder] string name) + public async Task SetLocale([Remainder] string name = null) { CultureInfo ci = null; try @@ -42,17 +42,17 @@ namespace NadekoBot.Modules.Administration.Commands [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] - public async Task SetDefaulLocale(string name) + public async Task SetDefaultLocale(string name) { CultureInfo ci = null; try { if (name.Trim().ToLowerInvariant() == "default") { - NadekoBot.Localization.RemoveGuildCulture(Context.Guild); + NadekoBot.Localization.ResetDefaultCulture(); } ci = new CultureInfo(name); - NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); + NadekoBot.Localization.SetDefaultCulture(ci); await Context.Channel.SendConfirmAsync($"Your guild's locale is now {ci}.").ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 11fa3692..3659c523 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6755,6 +6755,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to setdefaultlocale sdl. + /// + public static string setdefaultlocale_cmd { + get { + return ResourceManager.GetString("setdefaultlocale_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets the bot's default locale. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture.. + /// + public static string setdefaultlocale_desc { + get { + return ResourceManager.GetString("setdefaultlocale_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}sdl en-US` or `{0}sdl default`. + /// + public static string setdefaultlocale_usage { + get { + return ResourceManager.GetString("setdefaultlocale_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to setgame. /// @@ -6782,6 +6809,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to setlocale sl. + /// + public static string setlocale_cmd { + get { + return ResourceManager.GetString("setlocale_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets this server's response locale (language). If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name.. + /// + public static string setlocale_desc { + get { + return ResourceManager.GetString("setlocale_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{}sl de-DE ` or `{0}sl default`. + /// + public static string setlocale_usage { + get { + return ResourceManager.GetString("setlocale_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to setmaxplaytime smp. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index cca4cf5c..0c1566d0 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3105,4 +3105,22 @@ `{0}timezone` + + setdefaultlocale sdl + + + Sets the bot's default locale. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. + + + `{0}sdl en-US` or `{0}sdl default` + + + setlocale sl + + + Sets this server's response locale (language). If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. + + + `{}sl de-DE ` or `{0}sl default` + \ No newline at end of file diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index 26de5990..7a29c233 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -14,6 +14,7 @@ namespace NadekoBot.Services public class Localization { public ConcurrentDictionary GuildCultureInfos { get; } + public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture; private Localization() { } public Localization(IDictionary cultureInfoNames) @@ -68,11 +69,19 @@ namespace NadekoBot.Services } } + internal void SetDefaultCulture(CultureInfo ci) + { + DefaultCultureInfo = ci; + } + + public void ResetDefaultCulture() + { + DefaultCultureInfo = CultureInfo.CurrentCulture; + } + public CultureInfo GetCultureInfo(IGuild guild) => GetCultureInfo(guild.Id); - public CultureInfo DefaultCultureInfo { get; } = CultureInfo.CurrentCulture; - public CultureInfo GetCultureInfo(ulong guildId) { CultureInfo info = null; From a1e25f5149161497b5a2f0f5d2286a759fc5a1eb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 11:12:13 +0100 Subject: [PATCH 063/256] Cleanup, now compiles! :O --- .../{DiscordModule.cs => NadekoModule.cs} | 57 +++++++++---------- .../Modules/NadekoModuleExtensions.cs | 15 +++++ src/NadekoBot/Modules/Pokemon/Pokemon.cs | 6 +- .../Resources/ResponseStrings.Designer.cs | 18 ++++++ src/NadekoBot/Resources/ResponseStrings.resx | 6 ++ src/NadekoBot/Services/CommandHandler.cs | 2 +- src/NadekoBot/Services/Impl/Localization.cs | 6 ++ 7 files changed, 76 insertions(+), 34 deletions(-) rename src/NadekoBot/Modules/{DiscordModule.cs => NadekoModule.cs} (59%) create mode 100644 src/NadekoBot/Modules/NadekoModuleExtensions.cs diff --git a/src/NadekoBot/Modules/DiscordModule.cs b/src/NadekoBot/Modules/NadekoModule.cs similarity index 59% rename from src/NadekoBot/Modules/DiscordModule.cs rename to src/NadekoBot/Modules/NadekoModule.cs index cc609a58..b5541b78 100644 --- a/src/NadekoBot/Modules/DiscordModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -27,43 +27,38 @@ namespace NadekoBot.Modules ? CultureInfo.CurrentCulture : NadekoBot.Localization.GetCultureInfo(Context.Guild)); } + + public Task ConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null) + { + var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + return Context.Channel.SendConfirmAsync(title, text, url, footer); + } + + public Task ConfirmLocalized(string textKey) + { + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + return Context.Channel.SendConfirmAsync(textKey); + } + + public Task ErrorLocalized(string titleKey, string textKey, string url = null, string footer = null) + { + var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + return Context.Channel.SendErrorAsync(title, text, url, footer); + } + + public Task ErrorLocalized(string textKey) + { + var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + return Context.Channel.SendErrorAsync(textKey); + } } public abstract class NadekoSubmodule : NadekoModule { public NadekoSubmodule() : base(false) { - - } - } - - - public static class ModuleBaseExtensions - { - public static Task ConfirmLocalized(this NadekoModule module, string titleKey, string textKey, string url = null, string footer = null) - { - var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, module.cultureInfo); - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); - return module.Context.Channel.SendConfirmAsync(title, text, url, footer); - } - - public static Task ConfirmLocalized(this NadekoModule module, string textKey) - { - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); - return module.Context.Channel.SendConfirmAsync(textKey); - } - - public static Task ErrorLocalized(this NadekoModule module, string titleKey, string textKey, string url = null, string footer = null) - { - var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, module.cultureInfo); - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); - return module.Context.Channel.SendErrorAsync(title, text, url, footer); - } - - public static Task ErrorLocalized(this NadekoModule module, string textKey) - { - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, module.cultureInfo); - return module.Context.Channel.SendErrorAsync(textKey); } } } diff --git a/src/NadekoBot/Modules/NadekoModuleExtensions.cs b/src/NadekoBot/Modules/NadekoModuleExtensions.cs new file mode 100644 index 00000000..09625409 --- /dev/null +++ b/src/NadekoBot/Modules/NadekoModuleExtensions.cs @@ -0,0 +1,15 @@ +using Discord; +using NadekoBot.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Modules +{ + public static class NadekoModuleExtensions + { + + } +} diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 3ece509f..19515ad1 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -12,6 +12,8 @@ using System; using Newtonsoft.Json; using System.IO; using System.Collections.Concurrent; +using NadekoBot.Modules; +using NadekoBot.Resources; namespace NadekoBot.Modules.Pokemon { @@ -105,12 +107,12 @@ namespace NadekoBot.Modules.Pokemon if (targetUser == null) { - await ReplyLocalized("no user found").ConfigureAwait(false); + await ErrorLocalized(nameof(ResponseStrings.Culture)).ConfigureAwait(false); return; } else if (targetUser == user) { - await Context.Channel.SendMessageAsync("You can't attack yourself.").ConfigureAwait(false); + await ErrorLocalized("You can't attack yourself.").ConfigureAwait(false); return; } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 72395491..5b93ef8c 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -58,5 +58,23 @@ namespace NadekoBot.Resources { resourceCulture = value; } } + + /// + /// Looks up a localized string similar to You can't attack yourself.. + /// + public static string cant_attack_yourself { + get { + return ResourceManager.GetString("cant_attack_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User not found.. + /// + public static string no_user_found { + get { + return ResourceManager.GetString("no_user_found", resourceCulture); + } + } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 1af7de15..e740045d 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -117,4 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + You can't attack yourself. + + + User not found. + \ No newline at end of file diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index ac0a5b80..ce17de09 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -73,7 +73,7 @@ namespace NadekoBot.Services if (!ownerChannels.Any()) _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file."); else - _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Length} owner message channels."); + _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels."); _client.MessageReceived += MessageReceivedHandler; } diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index 7a29c233..dc64dd3e 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -88,5 +88,11 @@ namespace NadekoBot.Services GuildCultureInfos.TryGetValue(guildId, out info); return info ?? DefaultCultureInfo; } + + public static string LoadCommandString(string key) + { + string toReturn = Resources.CommandStrings.ResourceManager.GetString(key); + return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn; + } } } From eb17f0b5b77f4e18f9ea13825e89b94b34f0cf92 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 13:24:11 +0100 Subject: [PATCH 064/256] pokemon mostly localized, only strings which are constructed are left. --- src/NadekoBot/Modules/NadekoModule.cs | 38 +++- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 67 +++---- .../Resources/ResponseStrings.Designer.cs | 188 +++++++++++++++++- src/NadekoBot/Resources/ResponseStrings.resx | 65 +++++- 4 files changed, 305 insertions(+), 53 deletions(-) diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index b5541b78..5ce45fb4 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -28,30 +28,48 @@ namespace NadekoBot.Modules : NadekoBot.Localization.GetCultureInfo(Context.Guild)); } - public Task ConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null) + //public Task ReplyConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null) + //{ + // var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo); + // var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + // return Context.Channel.SendConfirmAsync(title, text, url, footer); + //} + + //public Task ReplyConfirmLocalized(string textKey) + //{ + // var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + // return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + textKey); + //} + + //public Task ReplyErrorLocalized(string titleKey, string textKey, string url = null, string footer = null) + //{ + // var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo); + // var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + // return Context.Channel.SendErrorAsync(title, text, url, footer); + //} + + public Task ErrorLocalized(string textKey, params object[] replacements) { - var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo); var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); - return Context.Channel.SendConfirmAsync(title, text, url, footer); + return Context.Channel.SendErrorAsync(string.Format(text, replacements)); } - public Task ConfirmLocalized(string textKey) + public Task ReplyErrorLocalized(string textKey, params object[] replacements) { var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); - return Context.Channel.SendConfirmAsync(textKey); + return Context.Channel.SendErrorAsync(Context.User.Mention + " " +string.Format(text, replacements)); } - public Task ErrorLocalized(string titleKey, string textKey, string url = null, string footer = null) + public Task ConfirmLocalized(string textKey, params object[] replacements) { - var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo); var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); - return Context.Channel.SendErrorAsync(title, text, url, footer); + return Context.Channel.SendConfirmAsync(string.Format(text, replacements)); } - public Task ErrorLocalized(string textKey) + public Task ReplyConfirmLocalized(string textKey, params object[] replacements) { var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); - return Context.Channel.SendErrorAsync(textKey); + return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + string.Format(text, replacements)); } } diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 19515ad1..997f3570 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -107,12 +107,12 @@ namespace NadekoBot.Modules.Pokemon if (targetUser == null) { - await ErrorLocalized(nameof(ResponseStrings.Culture)).ConfigureAwait(false); + await ReplyErrorLocalized("user_not_found").ConfigureAwait(false); return; } - else if (targetUser == user) + if (targetUser == user) { - await ErrorLocalized("You can't attack yourself.").ConfigureAwait(false); + await ReplyErrorLocalized("cant_attack_yourself").ConfigureAwait(false); return; } @@ -126,17 +126,17 @@ namespace NadekoBot.Modules.Pokemon //User not able if HP < 0, has made more than 4 attacks if (userStats.Hp < 0) { - await Context.Channel.SendMessageAsync($"{user.Mention} has fainted and was not able to move!").ConfigureAwait(false); + await ReplyErrorLocalized("you_fainted").ConfigureAwait(false); return; } if (userStats.MovesMade >= 5) { - await Context.Channel.SendMessageAsync($"{user.Mention} has used too many moves in a row and was not able to move!").ConfigureAwait(false); + await ReplyErrorLocalized("too_many_moves").ConfigureAwait(false); return; } if (userStats.LastAttacked.Contains(targetUser.Id)) { - await Context.Channel.SendMessageAsync($"{user.Mention} can't attack again without retaliation!").ConfigureAwait(false); + await ReplyErrorLocalized("cant_attack_again").ConfigureAwait(false); return; } //get target stats @@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Pokemon //If target's HP is below 0, no use attacking if (targetStats.Hp <= 0) { - await Context.Channel.SendMessageAsync($"{targetUser.Mention} has already fainted!").ConfigureAwait(false); + await ReplyErrorLocalized("too_many_moves", targetUser).ConfigureAwait(false); return; } @@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Pokemon var enabledMoves = userType.Moves; if (!enabledMoves.Contains(move.ToLowerInvariant())) { - await Context.Channel.SendMessageAsync($"{user.Mention} is not able to use **{move}**. Type {NadekoBot.ModulePrefixes[typeof(Pokemon).Name]}ml to see moves").ConfigureAwait(false); + await ReplyErrorLocalized("invalid_move", move, _prefix).ConfigureAwait(false); return; } @@ -172,7 +172,7 @@ namespace NadekoBot.Modules.Pokemon //Damage type if (damage < 40) { - response += "\nIt's not effective.."; + response += "\nIt's not effective."; } else if (damage > 60) { @@ -235,7 +235,7 @@ namespace NadekoBot.Modules.Pokemon IGuildUser user = (IGuildUser)Context.User; if (targetUser == null) { - await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false); + await ReplyErrorLocalized("user_not_found").ConfigureAwait(false); return; } @@ -244,7 +244,7 @@ namespace NadekoBot.Modules.Pokemon var targetStats = Stats[targetUser.Id]; if (targetStats.Hp == targetStats.MaxHp) { - await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false); + await ReplyErrorLocalized("already_full", targetUser).ConfigureAwait(false); return; } //Payment~ @@ -253,11 +253,11 @@ namespace NadekoBot.Modules.Pokemon var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention; if (amount > 0) { - if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false)) - { - try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { } - return; - } + if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false)) + { + await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + return; + } } //healing @@ -268,20 +268,18 @@ namespace NadekoBot.Modules.Pokemon Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2); if (target == "yourself") { - await Context.Channel.SendMessageAsync($"You revived yourself with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); + await ReplyErrorLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + return; } - else - { - await Context.Channel.SendMessageAsync($"{user.Mention} revived {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); - } - return; + + await ReplyErrorLocalized("revive_other", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); } - await Context.Channel.SendMessageAsync($"{user.Mention} healed {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); + await ReplyErrorLocalized("healed", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); return; } else { - await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false); + await ErrorLocalized("already_full", targetUser); } } @@ -290,15 +288,9 @@ namespace NadekoBot.Modules.Pokemon [RequireContext(ContextType.Guild)] public async Task Type(IGuildUser targetUser = null) { - IGuildUser user = (IGuildUser)Context.User; - - if (targetUser == null) - { - return; - } - + targetUser = targetUser ?? (IGuildUser)Context.User; var pType = GetPokeType(targetUser.Id); - await Context.Channel.SendMessageAsync($"Type of {targetUser.Mention} is **{pType.Name.ToLowerInvariant()}**{pType.Icon}").ConfigureAwait(false); + await ReplyConfirmLocalized("type_of_user", targetUser.Mention, pType.Name.ToLowerInvariant() + pType.Icon).ConfigureAwait(false); } @@ -320,7 +312,7 @@ namespace NadekoBot.Modules.Pokemon } if (targetType == GetPokeType(user.Id)) { - await Context.Channel.SendMessageAsync($"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Icon}").ConfigureAwait(false); + await ReplyErrorLocalized("already_that_type", targetType.Name.ToLowerInvariant() + targetType.Icon).ConfigureAwait(false); return; } @@ -328,9 +320,9 @@ namespace NadekoBot.Modules.Pokemon var amount = 1; if (amount > 0) { - if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user.Mention} change type to {typeTargeted}", amount, true).ConfigureAwait(false)) + if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false)) { - try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { } + await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); return; } } @@ -363,9 +355,10 @@ namespace NadekoBot.Modules.Pokemon } //Now for the response - await Context.Channel.SendMessageAsync($"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); + await ReplyConfirmLocalized("settype_success", + typeTargeted + targetType.Icon, + NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); } - } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 5b93ef8c..aab0ef1f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -59,21 +59,201 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} has already fainted.. + /// + public static string pokemon_already_fainted { + get { + return ResourceManager.GetString("pokemon_already_fainted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {1} already has full HP.. + /// + public static string pokemon_already_full { + get { + return ResourceManager.GetString("pokemon_already_full", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your type is already {0}. + /// + public static string pokemon_already_that_type { + get { + return ResourceManager.GetString("pokemon_already_that_type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to used **{0}**{1} on {2}{3} for **{4}** damage.. + /// + public static string pokemon_attack { + get { + return ResourceManager.GetString("pokemon_attack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't attack again without retaliation!. + /// + public static string pokemon_cant_attack_again { + get { + return ResourceManager.GetString("pokemon_cant_attack_again", resourceCulture); + } + } + /// /// Looks up a localized string similar to You can't attack yourself.. /// - public static string cant_attack_yourself { + public static string pokemon_cant_attack_yourself { get { - return ResourceManager.GetString("cant_attack_yourself", resourceCulture); + return ResourceManager.GetString("pokemon_cant_attack_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has fainted!. + /// + public static string pokemon_fainted { + get { + return ResourceManager.GetString("pokemon_fainted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has {1} HP remaining.. + /// + public static string pokemon_hp_remaining { + get { + return ResourceManager.GetString("pokemon_hp_remaining", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are not able to use **{0}**. Type {1}ml to see a list of moves you can use.. + /// + public static string pokemon_invalid_move { + get { + return ResourceManager.GetString("pokemon_invalid_move", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's not effective.. + /// + public static string pokemon_its_not_effective { + get { + return ResourceManager.GetString("pokemon_its_not_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's somewhat effective.. + /// + public static string pokemon_its_somewhat_effective { + get { + return ResourceManager.GetString("pokemon_its_somewhat_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's super effective!. + /// + public static string pokemon_its_super_effective { + get { + return ResourceManager.GetString("pokemon_its_super_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Moves for {0} type. + /// + public static string pokemon_moves { + get { + return ResourceManager.GetString("pokemon_moves", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough {0}. + /// + public static string pokemon_no_currency { + get { + return ResourceManager.GetString("pokemon_no_currency", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to revived {0} with one {1}. + /// + public static string pokemon_revive_other { + get { + return ResourceManager.GetString("pokemon_revive_other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You revived yourself with one {0}. + /// + public static string pokemon_revive_yourself { + get { + return ResourceManager.GetString("pokemon_revive_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your type has been changed to {0} for a {1}. + /// + public static string pokemon_settype_success { + get { + return ResourceManager.GetString("pokemon_settype_success", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You used too many moves in a row, so you can't move!. + /// + public static string pokemon_too_many_moves { + get { + return ResourceManager.GetString("pokemon_too_many_moves", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type of {0} is **{1}**. + /// + public static string pokemon_type_of_user { + get { + return ResourceManager.GetString("pokemon_type_of_user", resourceCulture); } } /// /// Looks up a localized string similar to User not found.. /// - public static string no_user_found { + public static string pokemon_user_not_found { get { - return ResourceManager.GetString("no_user_found", resourceCulture); + return ResourceManager.GetString("pokemon_user_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You fainted, so you are not able to move!. + /// + public static string pokemon_you_fainted { + get { + return ResourceManager.GetString("pokemon_you_fainted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to healed {0} with one {1}. + /// + public static string pokmeon_healed { + get { + return ResourceManager.GetString("pokmeon_healed", resourceCulture); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index e740045d..69503270 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -117,10 +117,71 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + {0} has already fainted. + + + {1} already has full HP. + + + Your type is already {0} + + + used **{0}**{1} on {2}{3} for **{4}** damage. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + You can't attack again without retaliation! + + You can't attack yourself. - + + {0} has fainted! + + + {0} has {1} HP remaining. + + + You are not able to use **{0}**. Type {1}ml to see a list of moves you can use. + + + It's not effective. + + + It's somewhat effective. + + + It's super effective! + + + Moves for {0} type + + + You don't have enough {0} + + + revived {0} with one {1} + + + You revived yourself with one {0} + + + Your type has been changed to {0} for a {1} + + + You used too many moves in a row, so you can't move! + + + Type of {0} is **{1}** + + User not found. + + You fainted, so you are not able to move! + + + healed {0} with one {1} + \ No newline at end of file From 5571f74c5227340b68c33c5c75d82d0b10721dd4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 13:44:21 +0100 Subject: [PATCH 065/256] Fixes, everything localized? --- src/NadekoBot/Modules/NadekoModule.cs | 26 +++++++-- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 32 +++++------ .../Resources/ResponseStrings.Designer.cs | 56 +++++++++---------- src/NadekoBot/Resources/ResponseStrings.resx | 20 +++---- 4 files changed, 73 insertions(+), 61 deletions(-) diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 5ce45fb4..9c574cde 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -14,12 +14,16 @@ namespace NadekoBot.Modules protected readonly Logger _log; public readonly string _prefix; public readonly CultureInfo cultureInfo; + public readonly string ModuleTypeName; + public readonly string LowerModuleTypeName; public NadekoModule(bool isTopLevelModule = true) { //if it's top level module - var typeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; - if (!NadekoBot.ModulePrefixes.TryGetValue(typeName, out _prefix)) + ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; + LowerModuleTypeName = ModuleTypeName.ToLowerInvariant(); + + if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out _prefix)) _prefix = "?err?"; _log = LogManager.GetCurrentClassLogger(); @@ -48,27 +52,37 @@ namespace NadekoBot.Modules // return Context.Channel.SendErrorAsync(title, text, url, footer); //} + protected string GetText(string key) + { + return NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, cultureInfo); + } + + protected string GetText(string key, params object[] replacements) + { + return string.Format(GetText(key), replacements); + } + public Task ErrorLocalized(string textKey, params object[] replacements) { - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + var text = GetText(textKey); return Context.Channel.SendErrorAsync(string.Format(text, replacements)); } public Task ReplyErrorLocalized(string textKey, params object[] replacements) { - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + var text = GetText(textKey); return Context.Channel.SendErrorAsync(Context.User.Mention + " " +string.Format(text, replacements)); } public Task ConfirmLocalized(string textKey, params object[] replacements) { - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + var text = GetText(textKey); return Context.Channel.SendConfirmAsync(string.Format(text, replacements)); } public Task ReplyConfirmLocalized(string textKey, params object[] replacements) { - var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo); + var text = GetText(textKey); return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + string.Format(text, replacements)); } } diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 997f3570..dab998ff 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -166,32 +166,32 @@ namespace NadekoBot.Modules.Pokemon int damage = GetDamage(userType, targetType); //apply damage to target targetStats.Hp -= damage; - - var response = $"{user.Mention} used **{move}**{userType.Icon} on {targetUser.Mention}{targetType.Icon} for **{damage}** damage"; + + var response = GetText("attack", move, userType.Icon, targetUser.Mention, targetType.Icon, damage); //Damage type if (damage < 40) { - response += "\nIt's not effective."; + response += "\n" + GetText("not_effective"); } else if (damage > 60) { - response += "\nIt's super effective!"; + response += "\n" + GetText("super_effective"); } else { - response += "\nIt's somewhat effective"; + response += "\n" + GetText("somewhat_effective"); } //check fainted if (targetStats.Hp <= 0) { - response += $"\n**{targetUser.Mention}** has fainted!"; + response += $"\n" + GetText("fainted", targetUser); } else { - response += $"\n**{targetUser.Mention}** has {targetStats.Hp} HP remaining"; + response += $"\n" + GetText("hp_remaining", targetUser, targetStats.Hp); } //update other stats @@ -208,7 +208,7 @@ namespace NadekoBot.Modules.Pokemon Stats[user.Id] = userStats; Stats[targetUser.Id] = targetStats; - await Context.Channel.SendMessageAsync(response).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false); } @@ -220,12 +220,10 @@ namespace NadekoBot.Modules.Pokemon var userType = GetPokeType(user.Id); var movesList = userType.Moves; - var str = $"**Moves for `{userType.Name}` type.**"; - foreach (string m in movesList) - { - str += $"\n{userType.Icon}{m}"; - } - await Context.Channel.SendMessageAsync(str).ConfigureAwait(false); + var embed = new EmbedBuilder().WithOkColor() + .WithTitle(GetText("moves", userType)) + .WithDescription(string.Join("\n", movesList.Select(m => userType.Icon + " " + m))); + await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -268,13 +266,13 @@ namespace NadekoBot.Modules.Pokemon Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2); if (target == "yourself") { - await ReplyErrorLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); return; } - await ReplyErrorLocalized("revive_other", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + await ReplyConfirmLocalized("revive_other", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); } - await ReplyErrorLocalized("healed", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + await ReplyConfirmLocalized("healed", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); return; } else diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index aab0ef1f..1e0c5ff4 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -141,34 +141,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to It's not effective.. - /// - public static string pokemon_its_not_effective { - get { - return ResourceManager.GetString("pokemon_its_not_effective", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It's somewhat effective.. - /// - public static string pokemon_its_somewhat_effective { - get { - return ResourceManager.GetString("pokemon_its_somewhat_effective", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It's super effective!. - /// - public static string pokemon_its_super_effective { - get { - return ResourceManager.GetString("pokemon_its_super_effective", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Moves for {0} type. + /// Looks up a localized string similar to Movelist for "{0}" type. /// public static string pokemon_moves { get { @@ -185,6 +158,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to It's not effective.. + /// + public static string pokemon_not_effective { + get { + return ResourceManager.GetString("pokemon_not_effective", resourceCulture); + } + } + /// /// Looks up a localized string similar to revived {0} with one {1}. /// @@ -212,6 +194,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to It's somewhat effective.. + /// + public static string pokemon_somewhat_effective { + get { + return ResourceManager.GetString("pokemon_somewhat_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's super effective!. + /// + public static string pokemon_super_effective { + get { + return ResourceManager.GetString("pokemon_super_effective", resourceCulture); + } + } + /// /// Looks up a localized string similar to You used too many moves in a row, so you can't move!. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 69503270..1a349d0f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -145,17 +145,11 @@ You are not able to use **{0}**. Type {1}ml to see a list of moves you can use. - - It's not effective. - - - It's somewhat effective. - - - It's super effective! - - Moves for {0} type + Movelist for "{0}" type + + + It's not effective. You don't have enough {0} @@ -169,6 +163,12 @@ Your type has been changed to {0} for a {1} + + It's somewhat effective. + + + It's super effective! + You used too many moves in a row, so you can't move! From fe4f9aad1e9e683a70a2857e1ad3d4e750cc0f02 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 13:56:06 +0100 Subject: [PATCH 066/256] woops, forgot migrations, added timezone and locale to guildconfig --- ...5444_guild-timezone-and-locale.Designer.cs | 1093 +++++++++++++++++ ...0170213125444_guild-timezone-and-locale.cs | 34 + .../NadekoSqliteContextModelSnapshot.cs | 4 + .../Services/Database/Models/GuildConfig.cs | 3 +- 4 files changed, 1133 insertions(+), 1 deletion(-) create mode 100644 src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs diff --git a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs b/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs new file mode 100644 index 00000000..670a7e40 --- /dev/null +++ b/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs @@ -0,0 +1,1093 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170213125444_guild-timezone-and-locale")] + partial class guildtimezoneandlocale + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.Property("UserThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiRaidSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AntiSpamSettingId"); + + b.Property("ChannelId"); + + b.HasKey("Id"); + + b.HasIndex("AntiSpamSettingId"); + + b.ToTable("AntiSpamIgnore"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("GuildConfigId"); + + b.Property("MessageThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiSpamSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ItemId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("BlacklistItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BetflipMultiplier"); + + b.Property("Betroll100Multiplier"); + + b.Property("Betroll67Multiplier"); + + b.Property("Betroll91Multiplier"); + + b.Property("BufferSize"); + + b.Property("CurrencyDropAmount"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("ErrorColor"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("MigrationVersion"); + + b.Property("MinimumBetAmount"); + + b.Property("OkColor"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.Property("TriviaCurrencyReward"); + + b.HasKey("Id"); + + b.ToTable("BotConfig"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BaseDestroyed"); + + b.Property("CallUser"); + + b.Property("ClashWarId"); + + b.Property("SequenceNumber"); + + b.Property("Stars"); + + b.Property("TimeAdded"); + + b.HasKey("Id"); + + b.HasIndex("ClashWarId"); + + b.ToTable("ClashCallers"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("EnemyClan"); + + b.Property("GuildId"); + + b.Property("Size"); + + b.Property("StartedAt"); + + b.Property("WarState"); + + b.HasKey("Id"); + + b.ToTable("ClashOfClans"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommandName"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("CommandName"); + + b.Property("Price"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.HasIndex("Price") + .IsUnique(); + + b.ToTable("CommandPrice"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("IsRegex"); + + b.Property("OwnerOnly"); + + b.Property("Response"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.ToTable("CustomReactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AvatarId"); + + b.Property("Discriminator"); + + b.Property("UserId"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasAlternateKey("UserId"); + + b.ToTable("DiscordUser"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("Name"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Donators"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildConfigId1"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.HasIndex("GuildConfigId1"); + + b.ToTable("FilterChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("Word"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FilteredWord"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GCChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoAssignRoleId"); + + b.Property("AutoDeleteByeMessages"); + + b.Property("AutoDeleteByeMessagesTimer"); + + b.Property("AutoDeleteGreetMessages"); + + b.Property("AutoDeleteGreetMessagesTimer"); + + b.Property("AutoDeleteSelfAssignedRoleMessages"); + + b.Property("ByeMessageChannelId"); + + b.Property("ChannelByeMessageText"); + + b.Property("ChannelGreetMessageText"); + + b.Property("CleverbotEnabled"); + + b.Property("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("Locale"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("TimeZoneId"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.HasIndex("LogSettingId"); + + b.HasIndex("RootPermissionId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GuildRepeater"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelCreatedId"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelDestroyedId"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("ChannelUpdatedId"); + + b.Property("IsLogging"); + + b.Property("LogOtherId"); + + b.Property("LogUserPresence"); + + b.Property("LogUserPresenceId"); + + b.Property("LogVoicePresence"); + + b.Property("LogVoicePresenceId"); + + b.Property("LogVoicePresenceTTSId"); + + b.Property("MessageDeleted"); + + b.Property("MessageDeletedId"); + + b.Property("MessageUpdated"); + + b.Property("MessageUpdatedId"); + + b.Property("UserBanned"); + + b.Property("UserBannedId"); + + b.Property("UserJoined"); + + b.Property("UserJoinedId"); + + b.Property("UserLeft"); + + b.Property("UserLeftId"); + + b.Property("UserMutedId"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUnbannedId"); + + b.Property("UserUpdated"); + + b.Property("UserUpdatedId"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("ModuleName"); + + b.Property("Prefix"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("ModulePrefixes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("AuthorId"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("MutedUserId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("NextId"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("NextId") + .IsUnique(); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Status"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("PlayingStatus"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("MusicPlaylistId"); + + b.Property("Provider"); + + b.Property("ProviderType"); + + b.Property("Query"); + + b.Property("Title"); + + b.Property("Uri"); + + b.HasKey("Id"); + + b.HasIndex("MusicPlaylistId"); + + b.ToTable("PlaylistSong"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("AuthorName") + .IsRequired(); + + b.Property("GuildId"); + + b.Property("Keyword") + .IsRequired(); + + b.Property("Text") + .IsRequired(); + + b.HasKey("Id"); + + b.ToTable("Quotes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("Icon"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("RaceAnimals"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("IsPrivate"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("UserId"); + + b.Property("When"); + + b.HasKey("Id"); + + b.ToTable("Reminders"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("GuildId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildId", "RoleId") + .IsUnique(); + + b.ToTable("SelfAssignableRoles"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AffinityId"); + + b.Property("ClaimerId"); + + b.Property("Price"); + + b.Property("WaifuId"); + + b.HasKey("Id"); + + b.HasIndex("AffinityId"); + + b.HasIndex("ClaimerId"); + + b.HasIndex("WaifuId") + .IsUnique(); + + b.ToTable("WaifuInfo"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("NewId"); + + b.Property("OldId"); + + b.Property("UpdateType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("NewId"); + + b.HasIndex("OldId"); + + b.HasIndex("UserId"); + + b.ToTable("WaifuUpdates"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiRaidSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiRaidSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.HasOne("NadekoBot.Services.Database.Models.AntiSpamSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("AntiSpamSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiSpamSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiSpamSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("Blacklist") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar") + .WithMany("Bases") + .HasForeignKey("ClashWarId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandCooldowns") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandPrice", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("CommandPrices") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("EightBallResponses") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterInvitesChannelIds") + .HasForeignKey("GuildConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterWordsChannelIds") + .HasForeignKey("GuildConfigId1"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilteredWords") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GenerateCurrencyChannelIds") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + + b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission") + .WithMany() + .HasForeignKey("RootPermissionId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GuildRepeaters") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("ModulePrefixes") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("MutedUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") + .WithOne("Previous") + .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RotatingStatusMessages") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist") + .WithMany("Songs") + .HasForeignKey("MusicPlaylistId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RaceAnimals") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => + { + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Affinity") + .WithMany() + .HasForeignKey("AffinityId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Claimer") + .WithMany() + .HasForeignKey("ClaimerId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Waifu") + .WithOne() + .HasForeignKey("NadekoBot.Services.Database.Models.WaifuInfo", "WaifuId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => + { + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "New") + .WithMany() + .HasForeignKey("NewId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Old") + .WithMany() + .HasForeignKey("OldId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs b/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs new file mode 100644 index 00000000..c49b1fff --- /dev/null +++ b/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class guildtimezoneandlocale : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Locale", + table: "GuildConfigs", + nullable: true, + defaultValue: null); + + migrationBuilder.AddColumn( + name: "TimeZoneId", + table: "GuildConfigs", + defaultValue: null); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Locale", + table: "GuildConfigs"); + + migrationBuilder.DropColumn( + name: "TimeZoneId", + table: "GuildConfigs"); + } + } +} diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index dd9bb84d..36fc1624 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -469,6 +469,8 @@ namespace NadekoBot.Migrations b.Property("GuildId"); + b.Property("Locale"); + b.Property("LogSettingId"); b.Property("MuteRoleName"); @@ -483,6 +485,8 @@ namespace NadekoBot.Migrations b.Property("SendDmGreetMessage"); + b.Property("TimeZoneId"); + b.Property("VerbosePermissions"); b.Property("VoicePlusTextEnabled"); diff --git a/src/NadekoBot/Services/Database/Models/GuildConfig.cs b/src/NadekoBot/Services/Database/Models/GuildConfig.cs index 9b0209e9..9bf945cd 100644 --- a/src/NadekoBot/Services/Database/Models/GuildConfig.cs +++ b/src/NadekoBot/Services/Database/Models/GuildConfig.cs @@ -64,7 +64,8 @@ namespace NadekoBot.Services.Database.Models public AntiRaidSetting AntiRaidSetting { get; set; } public AntiSpamSetting AntiSpamSetting { get; set; } - public string Locale { get; set; } + public string Locale { get; set; } = null; + public string TimeZoneId { get; set; } = null; //public List ProtectionIgnoredChannels { get; set; } = new List(); } From dadb7f0166f45d899ee6840b0ee83270c21e2487 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 13:58:08 +0100 Subject: [PATCH 067/256] fixed migration --- .../Migrations/20170213125444_guild-timezone-and-locale.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs b/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs index c49b1fff..6aab0a04 100644 --- a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs +++ b/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs @@ -17,6 +17,7 @@ namespace NadekoBot.Migrations migrationBuilder.AddColumn( name: "TimeZoneId", table: "GuildConfigs", + nullable: true, defaultValue: null); } From 1da9c0ae5a662943eeccf7830c0da947862a3189 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 14:23:29 +0100 Subject: [PATCH 068/256] fixes --- .../Administration/Commands/LocalizationCommands.cs | 2 +- src/NadekoBot/Modules/NadekoModule.cs | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index a7e39fc6..7f555b89 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace NadekoBot.Modules.Administration.Commands +namespace NadekoBot.Modules.Administration { public partial class Administration { diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 9c574cde..d0aa8433 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -12,8 +12,8 @@ namespace NadekoBot.Modules public abstract class NadekoModule : ModuleBase { protected readonly Logger _log; + protected CultureInfo _cultureInfo { get; private set; } public readonly string _prefix; - public readonly CultureInfo cultureInfo; public readonly string ModuleTypeName; public readonly string LowerModuleTypeName; @@ -26,8 +26,11 @@ namespace NadekoBot.Modules if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out _prefix)) _prefix = "?err?"; _log = LogManager.GetCurrentClassLogger(); + } - cultureInfo = (Context.Guild == null + protected override void BeforeExecute() + { + _cultureInfo = (Context.Guild == null ? CultureInfo.CurrentCulture : NadekoBot.Localization.GetCultureInfo(Context.Guild)); } @@ -54,7 +57,7 @@ namespace NadekoBot.Modules protected string GetText(string key) { - return NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, cultureInfo); + return NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, _cultureInfo); } protected string GetText(string key, params object[] replacements) From 995b05e1f7acaa8b3a8afa0ccf0cc09a4530a909 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 15:03:39 +0100 Subject: [PATCH 069/256] a lot of fixes and improvements to pokemon localization --- src/NadekoBot/Modules/NadekoModule.cs | 4 +-- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 32 +++++++++---------- src/NadekoBot/Modules/Pokemon/PokemonType.cs | 3 ++ .../Resources/ResponseStrings.Designer.cs | 28 ++++++++-------- src/NadekoBot/Resources/ResponseStrings.resx | 14 ++++---- 5 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index d0aa8433..89e685f3 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -74,7 +74,7 @@ namespace NadekoBot.Modules public Task ReplyErrorLocalized(string textKey, params object[] replacements) { var text = GetText(textKey); - return Context.Channel.SendErrorAsync(Context.User.Mention + " " +string.Format(text, replacements)); + return Context.Channel.SendErrorAsync(Context.User.Mention + " " + string.Format(text, replacements)); } public Task ConfirmLocalized(string textKey, params object[] replacements) @@ -96,4 +96,4 @@ namespace NadekoBot.Modules { } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index dab998ff..a95a6d5f 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -77,9 +77,7 @@ namespace NadekoBot.Modules.Pokemon return PokemonTypes[remainder]; } - - - + private PokemonType StringToPokemonType(string v) { var str = v?.ToUpperInvariant(); @@ -93,8 +91,7 @@ namespace NadekoBot.Modules.Pokemon } return null; } - - + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Attack(string move, IGuildUser targetUser = null) @@ -156,7 +153,7 @@ namespace NadekoBot.Modules.Pokemon var enabledMoves = userType.Moves; if (!enabledMoves.Contains(move.ToLowerInvariant())) { - await ReplyErrorLocalized("invalid_move", move, _prefix).ConfigureAwait(false); + await ReplyErrorLocalized("invalid_move", Format.Bold(move), _prefix).ConfigureAwait(false); return; } @@ -167,7 +164,7 @@ namespace NadekoBot.Modules.Pokemon //apply damage to target targetStats.Hp -= damage; - var response = GetText("attack", move, userType.Icon, targetUser.Mention, targetType.Icon, damage); + var response = GetText("attack", Format.Bold(move), userType.Icon, Format.Bold(targetUser.ToString()), targetType.Icon, Format.Bold(damage.ToString())); //Damage type if (damage < 40) @@ -187,11 +184,11 @@ namespace NadekoBot.Modules.Pokemon if (targetStats.Hp <= 0) { - response += $"\n" + GetText("fainted", targetUser); + response += $"\n" + GetText("fainted", Format.Bold(targetUser.ToString())); } else { - response += $"\n" + GetText("hp_remaining", targetUser, targetStats.Hp); + response += $"\n" + GetText("hp_remaining", Format.Bold(targetUser.ToString()), targetStats.Hp); } //update other stats @@ -232,7 +229,8 @@ namespace NadekoBot.Modules.Pokemon { IGuildUser user = (IGuildUser)Context.User; - if (targetUser == null) { + if (targetUser == null) + { await ReplyErrorLocalized("user_not_found").ConfigureAwait(false); return; } @@ -242,7 +240,7 @@ namespace NadekoBot.Modules.Pokemon var targetStats = Stats[targetUser.Id]; if (targetStats.Hp == targetStats.MaxHp) { - await ReplyErrorLocalized("already_full", targetUser).ConfigureAwait(false); + await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false); return; } //Payment~ @@ -270,14 +268,14 @@ namespace NadekoBot.Modules.Pokemon return; } - await ReplyConfirmLocalized("revive_other", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); } - await ReplyConfirmLocalized("healed", targetUser, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); + await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); return; } else { - await ErrorLocalized("already_full", targetUser); + await ErrorLocalized("already_full", Format.Bold(targetUser.ToString())); } } @@ -288,7 +286,7 @@ namespace NadekoBot.Modules.Pokemon { targetUser = targetUser ?? (IGuildUser)Context.User; var pType = GetPokeType(targetUser.Id); - await ReplyConfirmLocalized("type_of_user", targetUser.Mention, pType.Name.ToLowerInvariant() + pType.Icon).ConfigureAwait(false); + await ReplyConfirmLocalized("type_of_user", Format.Bold(targetUser.ToString()), pType).ConfigureAwait(false); } @@ -310,7 +308,7 @@ namespace NadekoBot.Modules.Pokemon } if (targetType == GetPokeType(user.Id)) { - await ReplyErrorLocalized("already_that_type", targetType.Name.ToLowerInvariant() + targetType.Icon).ConfigureAwait(false); + await ReplyErrorLocalized("already_that_type", targetType).ConfigureAwait(false); return; } @@ -354,7 +352,7 @@ namespace NadekoBot.Modules.Pokemon //Now for the response await ReplyConfirmLocalized("settype_success", - typeTargeted + targetType.Icon, + targetType, NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Pokemon/PokemonType.cs b/src/NadekoBot/Modules/Pokemon/PokemonType.cs index b73dec6b..20d47df0 100644 --- a/src/NadekoBot/Modules/Pokemon/PokemonType.cs +++ b/src/NadekoBot/Modules/Pokemon/PokemonType.cs @@ -15,6 +15,9 @@ namespace NadekoBot.Modules.Pokemon public List Multipliers { get; set; } public string Icon { get; set; } public string[] Moves { get; set; } + + public override string ToString() => + Icon + "**" + Name.ToLowerInvariant() + "**" + Icon; } public class PokemonMultiplier { diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 1e0c5ff4..337d3b62 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -69,7 +69,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to {1} already has full HP.. + /// Looks up a localized string similar to {0} already has full HP.. /// public static string pokemon_already_full { get { @@ -87,7 +87,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to used **{0}**{1} on {2}{3} for **{4}** damage.. + /// Looks up a localized string similar to used {0}{1} on {2}{3} for {4} damage.. /// public static string pokemon_attack { get { @@ -122,6 +122,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to healed {0} with one {1}. + /// + public static string pokemon_healed { + get { + return ResourceManager.GetString("pokemon_healed", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has {1} HP remaining.. /// @@ -132,7 +141,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to You are not able to use **{0}**. Type {1}ml to see a list of moves you can use.. + /// Looks up a localized string similar to You can't use {0}. Type `{1}ml` to see a list of moves you can use.. /// public static string pokemon_invalid_move { get { @@ -141,7 +150,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Movelist for "{0}" type. + /// Looks up a localized string similar to Movelist for {0} type. /// public static string pokemon_moves { get { @@ -222,7 +231,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Type of {0} is **{1}**. + /// Looks up a localized string similar to Type of {0} is {1}. /// public static string pokemon_type_of_user { get { @@ -247,14 +256,5 @@ namespace NadekoBot.Resources { return ResourceManager.GetString("pokemon_you_fainted", resourceCulture); } } - - /// - /// Looks up a localized string similar to healed {0} with one {1}. - /// - public static string pokmeon_healed { - get { - return ResourceManager.GetString("pokmeon_healed", resourceCulture); - } - } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 1a349d0f..00fc702f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -121,13 +121,13 @@ {0} has already fainted. - {1} already has full HP. + {0} already has full HP. Your type is already {0} - used **{0}**{1} on {2}{3} for **{4}** damage. + used {0}{1} on {2}{3} for {4} damage. Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. @@ -139,6 +139,9 @@ {0} has fainted! + + healed {0} with one {1} + {0} has {1} HP remaining. @@ -146,7 +149,7 @@ You are not able to use **{0}**. Type {1}ml to see a list of moves you can use. - Movelist for "{0}" type + Movelist for {0} type It's not effective. @@ -173,7 +176,7 @@ You used too many moves in a row, so you can't move! - Type of {0} is **{1}** + Type of {0} is {1} User not found. @@ -181,7 +184,4 @@ You fainted, so you are not able to move! - - healed {0} with one {1} - \ No newline at end of file From 8d496563dcda2b65fe78eba6f07587f3891f8133 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 15:04:27 +0100 Subject: [PATCH 070/256] woops, unsaved change --- src/NadekoBot/Resources/ResponseStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 00fc702f..63ef2430 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -146,7 +146,7 @@ {0} has {1} HP remaining. - You are not able to use **{0}**. Type {1}ml to see a list of moves you can use. + You can't use {0}. Type `{1}ml` to see a list of moves you can use. Movelist for {0} type From 34e0399c7e0d28bb59b5d1ce413790b04c13cae4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 15:29:02 +0100 Subject: [PATCH 071/256] sneaky little bugs :3 --- .../Commands/LocalizationCommands.cs | 22 +- src/NadekoBot/Modules/Help/Help.cs | 2 +- src/NadekoBot/Modules/NadekoModule.cs | 4 +- .../Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- .../ResponseStrings-sr-SP.Designer.cs | 260 ++++++++++++++++++ .../Resources/ResponseStrings-sr-SP.resx | 187 +++++++++++++ src/NadekoBot/Services/Impl/Localization.cs | 2 +- 8 files changed, 469 insertions(+), 12 deletions(-) create mode 100644 src/NadekoBot/Resources/ResponseStrings-sr-SP.Designer.cs create mode 100644 src/NadekoBot/Resources/ResponseStrings-sr-SP.resx diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 7f555b89..2db38bc7 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -24,14 +24,18 @@ namespace NadekoBot.Modules.Administration CultureInfo ci = null; try { - if(name.Trim().ToLowerInvariant() == "default") + if (name.Trim().ToLowerInvariant() == "default") { NadekoBot.Localization.RemoveGuildCulture(Context.Guild); + ci = NadekoBot.Localization.DefaultCultureInfo; + } + else + { + ci = new CultureInfo(name); + NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); } - ci = new CultureInfo(name); - NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); - await Context.Channel.SendConfirmAsync($"Your guild's locale is now {ci}.").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Your guild's locale is now {Format.Bold(ci.ToString())} - {Format.Bold(ci.NativeName)}.").ConfigureAwait(false); } catch(Exception) { @@ -50,11 +54,15 @@ namespace NadekoBot.Modules.Administration if (name.Trim().ToLowerInvariant() == "default") { NadekoBot.Localization.ResetDefaultCulture(); + ci = NadekoBot.Localization.DefaultCultureInfo; + } + else + { + ci = new CultureInfo(name); + NadekoBot.Localization.SetDefaultCulture(ci); } - ci = new CultureInfo(name); - NadekoBot.Localization.SetDefaultCulture(ci); - await Context.Channel.SendConfirmAsync($"Your guild's locale is now {ci}.").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Bot's default locale is now {Format.Bold(ci.ToString())} - {Format.Bold(ci.NativeName)}.").ConfigureAwait(false); } catch (Exception) { diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index a15501b0..c447db40 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -82,7 +82,7 @@ namespace NadekoBot.Modules.Help var alias = com.Aliases.Skip(1).FirstOrDefault(); if (alias != null) str += $" **/ `{alias}`**"; - var embed = new EmbedBuilder() + var embed = new EmbedBuilder() .AddField(fb => fb.WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Aliases.First())} { GetCommandRequirements(com)}").WithIsInline(true)) .AddField(fb => fb.WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Aliases.First())}").WithIsInline(false)) .WithColor(NadekoBot.OkColor); diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 89e685f3..24a21954 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -31,8 +31,10 @@ namespace NadekoBot.Modules protected override void BeforeExecute() { _cultureInfo = (Context.Guild == null - ? CultureInfo.CurrentCulture + ? NadekoBot.Localization.DefaultCultureInfo : NadekoBot.Localization.GetCultureInfo(Context.Guild)); + + _log.Warn("Culture info is {0}", _cultureInfo); } //public Task ReplyConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 3659c523..3c6d7404 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6828,7 +6828,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to `{}sl de-DE ` or `{0}sl default`. + /// Looks up a localized string similar to `{0}sl de-DE ` or `{0}sl default`. /// public static string setlocale_usage { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 0c1566d0..577a1b72 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3121,6 +3121,6 @@ Sets this server's response locale (language). If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. - `{}sl de-DE ` or `{0}sl default` + `{0}sl de-DE ` or `{0}sl default` \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings-sr-SP.Designer.cs b/src/NadekoBot/Resources/ResponseStrings-sr-SP.Designer.cs new file mode 100644 index 00000000..4d43d571 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings-sr-SP.Designer.cs @@ -0,0 +1,260 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace NadekoBot.Resources { + using System; + using System.Reflection; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + public class ResponseStrings_sr_SP { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + internal ResponseStrings_sr_SP() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NadekoBot.Resources.ResponseStrings-sr-SP", typeof(ResponseStrings_sr_SP).GetTypeInfo().Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to {0} has already fainted.. + /// + public static string pokemon_already_fainted { + get { + return ResourceManager.GetString("pokemon_already_fainted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} already has full HP.. + /// + public static string pokemon_already_full { + get { + return ResourceManager.GetString("pokemon_already_full", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your type is already {0}. + /// + public static string pokemon_already_that_type { + get { + return ResourceManager.GetString("pokemon_already_that_type", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to used {0}{1} on {2}{3} for {4} damage.. + /// + public static string pokemon_attack { + get { + return ResourceManager.GetString("pokemon_attack", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't attack again without retaliation!. + /// + public static string pokemon_cant_attack_again { + get { + return ResourceManager.GetString("pokemon_cant_attack_again", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't attack yourself.. + /// + public static string pokemon_cant_attack_yourself { + get { + return ResourceManager.GetString("pokemon_cant_attack_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has fainted!. + /// + public static string pokemon_fainted { + get { + return ResourceManager.GetString("pokemon_fainted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to healed {0} with one {1}. + /// + public static string pokemon_healed { + get { + return ResourceManager.GetString("pokemon_healed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has {1} HP remaining.. + /// + public static string pokemon_hp_remaining { + get { + return ResourceManager.GetString("pokemon_hp_remaining", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't use {0}. Type `{1}ml` to see a list of moves you can use.. + /// + public static string pokemon_invalid_move { + get { + return ResourceManager.GetString("pokemon_invalid_move", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Movelist for {0} type. + /// + public static string pokemon_moves { + get { + return ResourceManager.GetString("pokemon_moves", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough {0}. + /// + public static string pokemon_no_currency { + get { + return ResourceManager.GetString("pokemon_no_currency", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's not effective.. + /// + public static string pokemon_not_effective { + get { + return ResourceManager.GetString("pokemon_not_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to revived {0} with one {1}. + /// + public static string pokemon_revive_other { + get { + return ResourceManager.GetString("pokemon_revive_other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You revived yourself with one {0}. + /// + public static string pokemon_revive_yourself { + get { + return ResourceManager.GetString("pokemon_revive_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your type has been changed to {0} for a {1}. + /// + public static string pokemon_settype_success { + get { + return ResourceManager.GetString("pokemon_settype_success", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's somewhat effective.. + /// + public static string pokemon_somewhat_effective { + get { + return ResourceManager.GetString("pokemon_somewhat_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's super effective!. + /// + public static string pokemon_super_effective { + get { + return ResourceManager.GetString("pokemon_super_effective", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You used too many moves in a row, so you can't move!. + /// + public static string pokemon_too_many_moves { + get { + return ResourceManager.GetString("pokemon_too_many_moves", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type of {0} is {1}. + /// + public static string pokemon_type_of_user { + get { + return ResourceManager.GetString("pokemon_type_of_user", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User not found.. + /// + public static string pokemon_user_not_found { + get { + return ResourceManager.GetString("pokemon_user_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You fainted, so you are not able to move!. + /// + public static string pokemon_you_fainted { + get { + return ResourceManager.GetString("pokemon_you_fainted", resourceCulture); + } + } + } +} diff --git a/src/NadekoBot/Resources/ResponseStrings-sr-SP.resx b/src/NadekoBot/Resources/ResponseStrings-sr-SP.resx new file mode 100644 index 00000000..63ef2430 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings-sr-SP.resx @@ -0,0 +1,187 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0} has already fainted. + + + {0} already has full HP. + + + Your type is already {0} + + + used {0}{1} on {2}{3} for {4} damage. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + You can't attack again without retaliation! + + + You can't attack yourself. + + + {0} has fainted! + + + healed {0} with one {1} + + + {0} has {1} HP remaining. + + + You can't use {0}. Type `{1}ml` to see a list of moves you can use. + + + Movelist for {0} type + + + It's not effective. + + + You don't have enough {0} + + + revived {0} with one {1} + + + You revived yourself with one {0} + + + Your type has been changed to {0} for a {1} + + + It's somewhat effective. + + + It's super effective! + + + You used too many moves in a row, so you can't move! + + + Type of {0} is {1} + + + User not found. + + + You fainted, so you are not able to move! + + \ No newline at end of file diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index dc64dd3e..73891d4a 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -69,7 +69,7 @@ namespace NadekoBot.Services } } - internal void SetDefaultCulture(CultureInfo ci) + public void SetDefaultCulture(CultureInfo ci) { DefaultCultureInfo = ci; } From 63dc5f0c46b90d9930aac5c163cb8cb1417f1d87 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 16:42:41 +0100 Subject: [PATCH 072/256] Pokemon completely translated to serbian cyrillic, it's so funny xD :rofl: --- .../Commands/LocalizationCommands.cs | 21 +++++++-- .../Resources/CommandStrings.Designer.cs | 28 ++++++------ src/NadekoBot/Resources/CommandStrings.resx | 16 +++---- ...=> ResponseStrings.sr-cyrl-rs.Designer.cs} | 0 ...P.resx => ResponseStrings.sr-cyrl-rs.resx} | 44 +++++++++---------- 5 files changed, 62 insertions(+), 47 deletions(-) rename src/NadekoBot/Resources/{ResponseStrings-sr-SP.Designer.cs => ResponseStrings.sr-cyrl-rs.Designer.cs} (100%) rename src/NadekoBot/Resources/{ResponseStrings-sr-SP.resx => ResponseStrings.sr-cyrl-rs.resx} (82%) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 2db38bc7..7247c29d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -4,6 +4,7 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Globalization; using System.Linq; using System.Text; @@ -16,10 +17,16 @@ namespace NadekoBot.Modules.Administration [Group] public class LocalizationCommands : ModuleBase { + private ImmutableDictionary SupportedLocales { get; } = new Dictionary() + { + {"en-US", "English, United States" }, + {"sr-cyrl-rs", "Serbian, Cyrillic" } + }.ToImmutableDictionary(); + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.Administrator)] - public async Task SetLocale([Remainder] string name = null) + public async Task SetLanguage([Remainder] string name = null) { CultureInfo ci = null; try @@ -35,7 +42,7 @@ namespace NadekoBot.Modules.Administration NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); } - await Context.Channel.SendConfirmAsync($"Your guild's locale is now {Format.Bold(ci.ToString())} - {Format.Bold(ci.NativeName)}.").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"Your server's locale is now {Format.Bold(ci.ToString())} - {Format.Bold(ci.NativeName)}.").ConfigureAwait(false); } catch(Exception) { @@ -46,7 +53,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] - public async Task SetDefaultLocale(string name) + public async Task SetDefaultLanguage(string name) { CultureInfo ci = null; try @@ -70,6 +77,14 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendConfirmAsync($"Failed setting locale. Revisit this command's help.").ConfigureAwait(false); } } + + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task ListLanguages(string name) + { + await Context.Channel.SendConfirmAsync("List Of Languages", + string.Join("\n", SupportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}"))); + } } } } diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 3c6d7404..11519c80 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -6758,27 +6758,27 @@ namespace NadekoBot.Resources { /// /// Looks up a localized string similar to setdefaultlocale sdl. /// - public static string setdefaultlocale_cmd { + public static string setdefaultlanguage_cmd { get { - return ResourceManager.GetString("setdefaultlocale_cmd", resourceCulture); + return ResourceManager.GetString("setdefaultlanguage_cmd", resourceCulture); } } /// - /// Looks up a localized string similar to Sets the bot's default locale. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture.. + /// Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture.. /// - public static string setdefaultlocale_desc { + public static string setdefaultlanguage_desc { get { - return ResourceManager.GetString("setdefaultlocale_desc", resourceCulture); + return ResourceManager.GetString("setdefaultlanguage_desc", resourceCulture); } } /// /// Looks up a localized string similar to `{0}sdl en-US` or `{0}sdl default`. /// - public static string setdefaultlocale_usage { + public static string setdefaultlanguage_usage { get { - return ResourceManager.GetString("setdefaultlocale_usage", resourceCulture); + return ResourceManager.GetString("setdefaultlanguage_usage", resourceCulture); } } @@ -6812,27 +6812,27 @@ namespace NadekoBot.Resources { /// /// Looks up a localized string similar to setlocale sl. /// - public static string setlocale_cmd { + public static string setlanguage_cmd { get { - return ResourceManager.GetString("setlocale_cmd", resourceCulture); + return ResourceManager.GetString("setlanguage_cmd", resourceCulture); } } /// - /// Looks up a localized string similar to Sets this server's response locale (language). If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name.. + /// Looks up a localized string similar to Sets this server's response language) If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name.. /// - public static string setlocale_desc { + public static string setlanguage_desc { get { - return ResourceManager.GetString("setlocale_desc", resourceCulture); + return ResourceManager.GetString("setlanguage_desc", resourceCulture); } } /// /// Looks up a localized string similar to `{0}sl de-DE ` or `{0}sl default`. /// - public static string setlocale_usage { + public static string setlanguage_usage { get { - return ResourceManager.GetString("setlocale_usage", resourceCulture); + return ResourceManager.GetString("setlanguage_usage", resourceCulture); } } diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 577a1b72..23fb8072 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3105,22 +3105,22 @@ `{0}timezone` - + setdefaultlocale sdl - - Sets the bot's default locale. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. + + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. - + `{0}sdl en-US` or `{0}sdl default` - + setlocale sl - - Sets this server's response locale (language). If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. + + Sets this server's response language) If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. - + `{0}sl de-DE ` or `{0}sl default` \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings-sr-SP.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.Designer.cs similarity index 100% rename from src/NadekoBot/Resources/ResponseStrings-sr-SP.Designer.cs rename to src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.Designer.cs diff --git a/src/NadekoBot/Resources/ResponseStrings-sr-SP.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx similarity index 82% rename from src/NadekoBot/Resources/ResponseStrings-sr-SP.resx rename to src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index 63ef2430..218a7645 100644 --- a/src/NadekoBot/Resources/ResponseStrings-sr-SP.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -118,70 +118,70 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - {0} has already fainted. + {0} је већ онесвешћен. - {0} already has full HP. + {0} је већ потпуно здрав. - Your type is already {0} + Твој тип већ јесте {0} - used {0}{1} on {2}{3} for {4} damage. + је искористио {0}{1} на {2}{3} и нанео {4} штете. Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. - You can't attack again without retaliation! + Не можеш напасти пре узвратног ударца. - You can't attack yourself. + Не можеш напасти себе. - {0} has fainted! + {0} се онесвестио. - healed {0} with one {1} + је излечио {0} са једним {1} - {0} has {1} HP remaining. + {0} има {1} преосталог здравља. - You can't use {0}. Type `{1}ml` to see a list of moves you can use. + Не можеш искористити {0}. Укуцај `{1}ml` да би видео листу удараца које можеш користити. - Movelist for {0} type + Листа покрета за {0} тип - It's not effective. + Није ефективно. - You don't have enough {0} + Немаш довољно {0} - revived {0} with one {1} + је оживео {0} са једним {1} - You revived yourself with one {0} + Оживео си себе са једним {0} - Your type has been changed to {0} for a {1} + Твој тип је промењен на {0} за један {1} - It's somewhat effective. + Има неког ефекта! - It's super effective! + Супер ефективно! - You used too many moves in a row, so you can't move! + Користио си превише напада узастопно, не можеш да се крећеш! - Type of {0} is {1} + {0}-ов тип је {1} - User not found. + Корисник није нађен. - You fainted, so you are not able to move! + Онесвешћен си, не можеш да се крећеш. \ No newline at end of file From 66f046a77c0f14238f485353244d3296c7e84bef Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 16:57:10 +0100 Subject: [PATCH 073/256] .langli, .langset, .langsetd added which: list all languages for which translation exists atm, set server's language, set default bot language (this is used if not server language is set, default) respectively --- .../Commands/LocalizationCommands.cs | 137 +++++++++++++++++- .../Resources/CommandStrings.Designer.cs | 135 ++++++++++------- src/NadekoBot/Resources/CommandStrings.resx | 31 ++-- 3 files changed, 235 insertions(+), 68 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 7247c29d..55b03213 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -26,7 +26,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.Administrator)] - public async Task SetLanguage([Remainder] string name = null) + public async Task LanguageSet([Remainder] string name = null) { CultureInfo ci = null; try @@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] - public async Task SetDefaultLanguage(string name) + public async Task LanguageSetDefault(string name) { CultureInfo ci = null; try @@ -80,7 +80,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] - public async Task ListLanguages(string name) + public async Task LanguagesList(string name) { await Context.Channel.SendConfirmAsync("List Of Languages", string.Join("\n", SupportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}"))); @@ -88,3 +88,134 @@ namespace NadekoBot.Modules.Administration } } } +/* list of language codes for reference. + * taken from https://github.com/dotnet/coreclr/blob/ee5862c6a257e60e263537d975ab6c513179d47f/src/mscorlib/src/System/Globalization/CultureData.cs#L192 + { "029", "en-029" }, + { "AE", "ar-AE" }, + { "AF", "prs-AF" }, + { "AL", "sq-AL" }, + { "AM", "hy-AM" }, + { "AR", "es-AR" }, + { "AT", "de-AT" }, + { "AU", "en-AU" }, + { "AZ", "az-Cyrl-AZ" }, + { "BA", "bs-Latn-BA" }, + { "BD", "bn-BD" }, + { "BE", "nl-BE" }, + { "BG", "bg-BG" }, + { "BH", "ar-BH" }, + { "BN", "ms-BN" }, + { "BO", "es-BO" }, + { "BR", "pt-BR" }, + { "BY", "be-BY" }, + { "BZ", "en-BZ" }, + { "CA", "en-CA" }, + { "CH", "it-CH" }, + { "CL", "es-CL" }, + { "CN", "zh-CN" }, + { "CO", "es-CO" }, + { "CR", "es-CR" }, + { "CS", "sr-Cyrl-CS" }, + { "CZ", "cs-CZ" }, + { "DE", "de-DE" }, + { "DK", "da-DK" }, + { "DO", "es-DO" }, + { "DZ", "ar-DZ" }, + { "EC", "es-EC" }, + { "EE", "et-EE" }, + { "EG", "ar-EG" }, + { "ES", "es-ES" }, + { "ET", "am-ET" }, + { "FI", "fi-FI" }, + { "FO", "fo-FO" }, + { "FR", "fr-FR" }, + { "GB", "en-GB" }, + { "GE", "ka-GE" }, + { "GL", "kl-GL" }, + { "GR", "el-GR" }, + { "GT", "es-GT" }, + { "HK", "zh-HK" }, + { "HN", "es-HN" }, + { "HR", "hr-HR" }, + { "HU", "hu-HU" }, + { "ID", "id-ID" }, + { "IE", "en-IE" }, + { "IL", "he-IL" }, + { "IN", "hi-IN" }, + { "IQ", "ar-IQ" }, + { "IR", "fa-IR" }, + { "IS", "is-IS" }, + { "IT", "it-IT" }, + { "IV", "" }, + { "JM", "en-JM" }, + { "JO", "ar-JO" }, + { "JP", "ja-JP" }, + { "KE", "sw-KE" }, + { "KG", "ky-KG" }, + { "KH", "km-KH" }, + { "KR", "ko-KR" }, + { "KW", "ar-KW" }, + { "KZ", "kk-KZ" }, + { "LA", "lo-LA" }, + { "LB", "ar-LB" }, + { "LI", "de-LI" }, + { "LK", "si-LK" }, + { "LT", "lt-LT" }, + { "LU", "lb-LU" }, + { "LV", "lv-LV" }, + { "LY", "ar-LY" }, + { "MA", "ar-MA" }, + { "MC", "fr-MC" }, + { "ME", "sr-Latn-ME" }, + { "MK", "mk-MK" }, + { "MN", "mn-MN" }, + { "MO", "zh-MO" }, + { "MT", "mt-MT" }, + { "MV", "dv-MV" }, + { "MX", "es-MX" }, + { "MY", "ms-MY" }, + { "NG", "ig-NG" }, + { "NI", "es-NI" }, + { "NL", "nl-NL" }, + { "NO", "nn-NO" }, + { "NP", "ne-NP" }, + { "NZ", "en-NZ" }, + { "OM", "ar-OM" }, + { "PA", "es-PA" }, + { "PE", "es-PE" }, + { "PH", "en-PH" }, + { "PK", "ur-PK" }, + { "PL", "pl-PL" }, + { "PR", "es-PR" }, + { "PT", "pt-PT" }, + { "PY", "es-PY" }, + { "QA", "ar-QA" }, + { "RO", "ro-RO" }, + { "RS", "sr-Latn-RS" }, + { "RU", "ru-RU" }, + { "RW", "rw-RW" }, + { "SA", "ar-SA" }, + { "SE", "sv-SE" }, + { "SG", "zh-SG" }, + { "SI", "sl-SI" }, + { "SK", "sk-SK" }, + { "SN", "wo-SN" }, + { "SV", "es-SV" }, + { "SY", "ar-SY" }, + { "TH", "th-TH" }, + { "TJ", "tg-Cyrl-TJ" }, + { "TM", "tk-TM" }, + { "TN", "ar-TN" }, + { "TR", "tr-TR" }, + { "TT", "en-TT" }, + { "TW", "zh-TW" }, + { "UA", "uk-UA" }, + { "US", "en-US" }, + { "UY", "es-UY" }, + { "UZ", "uz-Cyrl-UZ" }, + { "VE", "es-VE" }, + { "VN", "vi-VN" }, + { "YE", "ar-YE" }, + { "ZA", "af-ZA" }, + { "ZW", "en-ZW" } + */ diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 11519c80..5526e556 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -3677,6 +3677,87 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to languageset langset. + /// + public static string languageset_cmd { + get { + return ResourceManager.GetString("languageset_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name.. + /// + public static string languageset_desc { + get { + return ResourceManager.GetString("languageset_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}langset de-DE ` or `{0}langset default`. + /// + public static string languageset_usage { + get { + return ResourceManager.GetString("languageset_usage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to langsetdefault langsetd. + /// + public static string languagesetdefault_cmd { + get { + return ResourceManager.GetString("languagesetdefault_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture.. + /// + public static string languagesetdefault_desc { + get { + return ResourceManager.GetString("languagesetdefault_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}langsetd en-US` or `{0}langsetd default`. + /// + public static string languagesetdefault_usage { + get { + return ResourceManager.GetString("languagesetdefault_usage", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to languageslist langli. + /// + public static string languageslist_cmd { + get { + return ResourceManager.GetString("languageslist_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of languages for which translation (or part of it) exist atm.. + /// + public static string languageslist_desc { + get { + return ResourceManager.GetString("languageslist_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}langli`. + /// + public static string languageslist_usage { + get { + return ResourceManager.GetString("languageslist_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to lcsc. /// @@ -6755,33 +6836,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to setdefaultlocale sdl. - /// - public static string setdefaultlanguage_cmd { - get { - return ResourceManager.GetString("setdefaultlanguage_cmd", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture.. - /// - public static string setdefaultlanguage_desc { - get { - return ResourceManager.GetString("setdefaultlanguage_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `{0}sdl en-US` or `{0}sdl default`. - /// - public static string setdefaultlanguage_usage { - get { - return ResourceManager.GetString("setdefaultlanguage_usage", resourceCulture); - } - } - /// /// Looks up a localized string similar to setgame. /// @@ -6809,33 +6863,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to setlocale sl. - /// - public static string setlanguage_cmd { - get { - return ResourceManager.GetString("setlanguage_cmd", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Sets this server's response language) If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name.. - /// - public static string setlanguage_desc { - get { - return ResourceManager.GetString("setlanguage_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `{0}sl de-DE ` or `{0}sl default`. - /// - public static string setlanguage_usage { - get { - return ResourceManager.GetString("setlanguage_usage", resourceCulture); - } - } - /// /// Looks up a localized string similar to setmaxplaytime smp. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 23fb8072..fab8d243 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3105,22 +3105,31 @@ `{0}timezone` - - setdefaultlocale sdl + + langsetdefault langsetd - + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. - - `{0}sdl en-US` or `{0}sdl default` + + `{0}langsetd en-US` or `{0}langsetd default` - - setlocale sl + + languageset langset - - Sets this server's response language) If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. + + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. - - `{0}sl de-DE ` or `{0}sl default` + + `{0}langset de-DE ` or `{0}langset default` + + + languageslist langli + + + List of languages for which translation (or part of it) exist atm. + + + `{0}langli` \ No newline at end of file From de4b1fdbf5a211505c9b62e36891d50df6691504 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 17:16:31 +0100 Subject: [PATCH 074/256] In case response key is missing, use the string from ResponseStrings.resx --- src/NadekoBot/Modules/NadekoModule.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 24a21954..9311dac5 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -57,9 +57,20 @@ namespace NadekoBot.Modules // return Context.Channel.SendErrorAsync(title, text, url, footer); //} + /// + /// Used as failsafe in case response key doesn't exist in the selected or default language. + /// + private readonly CultureInfo usCultureInfo = new CultureInfo("en-US"); protected string GetText(string key) { - return NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, _cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, _cultureInfo); + + if (string.IsNullOrWhiteSpace(text)) + { + _log.Warn(LowerModuleTypeName + "_" + key + " key is missing from " + _cultureInfo + " response strings. PLEASE REPORT THIS."); + return NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, usCultureInfo); + } + return text; } protected string GetText(string key, params object[] replacements) From 08c96385ea97f3f81285417df8711b39488a6cfc Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 17:45:29 +0100 Subject: [PATCH 075/256] redid migration in order to add botconfig.locale, added some improvements --- ...350_guild-timezone-and-locale.Designer.cs} | 4 ++- ...170213164350_guild-timezone-and-locale.cs} | 10 +++++++ .../NadekoSqliteContextModelSnapshot.cs | 2 ++ src/NadekoBot/NadekoBot.cs | 2 +- .../Services/Database/Models/BotConfig.cs | 1 + src/NadekoBot/Services/Impl/Localization.cs | 26 ++++++++++++++++--- 6 files changed, 39 insertions(+), 6 deletions(-) rename src/NadekoBot/Migrations/{20170213125444_guild-timezone-and-locale.Designer.cs => 20170213164350_guild-timezone-and-locale.Designer.cs} (99%) rename src/NadekoBot/Migrations/{20170213125444_guild-timezone-and-locale.cs => 20170213164350_guild-timezone-and-locale.cs} (77%) diff --git a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs b/src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs similarity index 99% rename from src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs rename to src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs index 670a7e40..1dc832d9 100644 --- a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.Designer.cs +++ b/src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.Designer.cs @@ -10,7 +10,7 @@ using NadekoBot.Modules.Music.Classes; namespace NadekoBot.Migrations { [DbContext(typeof(NadekoContext))] - [Migration("20170213125444_guild-timezone-and-locale")] + [Migration("20170213164350_guild-timezone-and-locale")] partial class guildtimezoneandlocale { protected override void BuildTargetModel(ModelBuilder modelBuilder) @@ -129,6 +129,8 @@ namespace NadekoBot.Migrations b.Property("HelpString"); + b.Property("Locale"); + b.Property("MigrationVersion"); b.Property("MinimumBetAmount"); diff --git a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs b/src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.cs similarity index 77% rename from src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs rename to src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.cs index 6aab0a04..757d9150 100644 --- a/src/NadekoBot/Migrations/20170213125444_guild-timezone-and-locale.cs +++ b/src/NadekoBot/Migrations/20170213164350_guild-timezone-and-locale.cs @@ -19,6 +19,12 @@ namespace NadekoBot.Migrations table: "GuildConfigs", nullable: true, defaultValue: null); + + migrationBuilder.AddColumn( + name: "Locale", + table: "BotConfig", + nullable: true, + defaultValue: null); } protected override void Down(MigrationBuilder migrationBuilder) @@ -30,6 +36,10 @@ namespace NadekoBot.Migrations migrationBuilder.DropColumn( name: "TimeZoneId", table: "GuildConfigs"); + + migrationBuilder.DropColumn( + name: "Locale", + table: "BotConfig"); } } } diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index 36fc1624..5a03594c 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -128,6 +128,8 @@ namespace NadekoBot.Migrations b.Property("HelpString"); + b.Property("Locale"); + b.Property("MigrationVersion"); b.Property("MinimumBetAmount"); diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 65f1e02b..5df52a6e 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -84,7 +84,7 @@ namespace NadekoBot #endif //initialize Services - Localization = new Localization(NadekoBot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale)); + Localization = new Localization(NadekoBot.BotConfig.Locale, NadekoBot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale)); CommandService = new CommandService(new CommandServiceConfig() { CaseSensitiveCommands = false, DefaultRunMode = RunMode.Sync diff --git a/src/NadekoBot/Services/Database/Models/BotConfig.cs b/src/NadekoBot/Services/Database/Models/BotConfig.cs index 6b35e5f2..8ff90e8a 100644 --- a/src/NadekoBot/Services/Database/Models/BotConfig.cs +++ b/src/NadekoBot/Services/Database/Models/BotConfig.cs @@ -60,6 +60,7 @@ Nadeko Support Server: https://discord.gg/nadekobot"; public string OkColor { get; set; } = "71cd40"; public string ErrorColor { get; set; } = "ee281f"; + public string Locale { get; set; } = null; } public class PlayingStatus :DbEntity diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index 73891d4a..df703381 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -8,17 +8,35 @@ using System.Linq; using System.Threading.Tasks; using System; using NadekoBot.Services.Database; +using NLog; namespace NadekoBot.Services { public class Localization { + private readonly Logger _log; + public ConcurrentDictionary GuildCultureInfos { get; } public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture; private Localization() { } - public Localization(IDictionary cultureInfoNames) + public Localization(string defaultCulture, IDictionary cultureInfoNames) { + _log = LogManager.GetCurrentClassLogger(); + if (string.IsNullOrWhiteSpace(defaultCulture)) + DefaultCultureInfo = new CultureInfo("en-US"); + else + { + try + { + DefaultCultureInfo = new CultureInfo(defaultCulture); + } + catch + { + _log.Warn("Unable to load default bot's locale/language. Using en-US."); + DefaultCultureInfo = new CultureInfo("en-US"); + } + } GuildCultureInfos = new ConcurrentDictionary(cultureInfoNames.ToDictionary(x => x.Key, x => { CultureInfo cultureInfo = null; @@ -26,9 +44,7 @@ namespace NadekoBot.Services { cultureInfo = new CultureInfo(x.Value); } - catch - { - } + catch { } return cultureInfo; }).Where(x => x.Value != null)); } @@ -50,6 +66,8 @@ namespace NadekoBot.Services gc.Locale = ci.Name; uow.Complete(); } + + GuildCultureInfos.AddOrUpdate(guildId, ci, (id, old) => ci); } public void RemoveGuildCulture(IGuild guild) => From 8ed7a4c5d87c19b979b4c275217d1b93b46f85bb Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 17:56:06 +0100 Subject: [PATCH 076/256] .langset and .langsetd can now be used with no arguments to show the currently set language --- .../Commands/LocalizationCommands.cs | 15 ++++++++++++++- .../Resources/CommandStrings.Designer.cs | 4 ++-- src/NadekoBot/Resources/CommandStrings.resx | 4 ++-- src/NadekoBot/Services/Impl/Localization.cs | 12 ++++++++---- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 55b03213..daea20a0 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -28,6 +28,13 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.Administrator)] public async Task LanguageSet([Remainder] string name = null) { + if (string.IsNullOrWhiteSpace(name)) + { + var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild); + await Context.Channel.SendConfirmAsync("This server's language is set to " + cul + " - " + cul.NativeName).ConfigureAwait(false); + return; + } + CultureInfo ci = null; try { @@ -53,8 +60,14 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] - public async Task LanguageSetDefault(string name) + public async Task LanguageSetDefault([Remainder]string name = null) { + if (string.IsNullOrWhiteSpace(name)) + { + var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild); + await Context.Channel.SendConfirmAsync("Bot's language is set to " + cul + " - " + cul.NativeName).ConfigureAwait(false); + return; + } CultureInfo ci = null; try { diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 5526e556..487e5ec8 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -3687,7 +3687,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name.. + /// Looks up a localized string similar to Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language.. /// public static string languageset_desc { get { @@ -3714,7 +3714,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture.. + /// Looks up a localized string similar to Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language.. /// public static string languagesetdefault_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index fab8d243..4ba617cd 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3109,7 +3109,7 @@ langsetdefault langsetd - Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. `{0}langsetd en-US` or `{0}langsetd default` @@ -3118,7 +3118,7 @@ languageset langset - Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. `{0}langset de-DE ` or `{0}langset default` diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index df703381..e4906712 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -89,13 +89,17 @@ namespace NadekoBot.Services public void SetDefaultCulture(CultureInfo ci) { + using (var uow = DbHandler.UnitOfWork()) + { + var bc = uow.BotConfig.GetOrCreate(); + bc.Locale = ci.Name; + uow.Complete(); + } DefaultCultureInfo = ci; } - public void ResetDefaultCulture() - { - DefaultCultureInfo = CultureInfo.CurrentCulture; - } + public void ResetDefaultCulture() => + SetDefaultCulture(CultureInfo.CurrentCulture); public CultureInfo GetCultureInfo(IGuild guild) => GetCultureInfo(guild.Id); From b3a4fec1411395ba15f5de1fe8adc4942fa55b40 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 18:08:14 +0100 Subject: [PATCH 077/256] .langset and .langsetd no longer need perms to run without args (to check current language) --- .../Commands/LocalizationCommands.cs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index daea20a0..684eebf9 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -25,16 +25,17 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - [RequireUserPermission(GuildPermission.Administrator)] - public async Task LanguageSet([Remainder] string name = null) + public async Task LanguageSet() { - if (string.IsNullOrWhiteSpace(name)) - { - var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild); - await Context.Channel.SendConfirmAsync("This server's language is set to " + cul + " - " + cul.NativeName).ConfigureAwait(false); - return; - } + var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild); + await Context.Channel.SendConfirmAsync("This server's language is set to " + Format.Bold(cul.ToString()) + " - " + Format.Bold(cul.NativeName)).ConfigureAwait(false); + } + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.Administrator)] + public async Task LanguageSet(string name) + { CultureInfo ci = null; try { @@ -59,15 +60,17 @@ namespace NadekoBot.Modules.Administration } [NadekoCommand, Usage, Description, Aliases] - [OwnerOnly] - public async Task LanguageSetDefault([Remainder]string name = null) + public async Task LanguageSetDefault() + { + var cul = NadekoBot.Localization.DefaultCultureInfo; + await Context.Channel.SendConfirmAsync("Bot's language is set to " + cul + " - " + cul.NativeName).ConfigureAwait(false); + return; + } + + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task LanguageSetDefault(string name) { - if (string.IsNullOrWhiteSpace(name)) - { - var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild); - await Context.Channel.SendConfirmAsync("Bot's language is set to " + cul + " - " + cul.NativeName).ConfigureAwait(false); - return; - } CultureInfo ci = null; try { From 838e014f7335287055ce4c103fae6f98181fbea1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 18:11:03 +0100 Subject: [PATCH 078/256] cleverbot fixed --- src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs b/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs index d5df8dfe..e183c249 100644 --- a/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs +++ b/src/NadekoBot/Services/CleverBotApi/ChatterBotFactory.cs @@ -30,9 +30,9 @@ namespace Services.CleverBotApi public static ChatterBot Create(ChatterBotType type, object arg) { #if GLOBAL_NADEKO - var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=nadekobot"; + var url = "http://www.cleverbot.com/webservicemin?uc=777&botapi=nadekobot"; #else - var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=chatterbotapi"; + var url = "http://www.cleverbot.com/webservicemin?uc=777&botapi=chatterbotapi"; #endif switch (type) From 53b6a77fc25f249c8d6c648824006567ee46991b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 18:26:58 +0100 Subject: [PATCH 079/256] pick no longer has cooldown on public bot --- .../Games/Commands/PlantAndPickCommands.cs | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 1cf43a30..234da7c2 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -129,32 +129,18 @@ namespace NadekoBot.Modules.Games if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages) return; -#if GLOBAL_NADEKO - if (!usersRecentlyPicked.Add(Context.User.Id)) + + List msgs; + + try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { } + if (!plantedFlowers.TryRemove(channel.Id, out msgs)) return; -#endif - try - { - List msgs; + await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); - try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { } - if (!plantedFlowers.TryRemove(channel.Id, out msgs)) - return; - - await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); - - await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); - var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false); - msg.DeleteAfter(10); - } - finally - { -#if GLOBAL_NADEKO - await Task.Delay(60000); - usersRecentlyPicked.TryRemove(Context.User.Id); -#endif - } + await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); + var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false); + msg.DeleteAfter(10); } [NadekoCommand, Usage, Description, Aliases] From 023df131217b609c0a3e70d7426eebb76ba3054f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 21:29:06 +0100 Subject: [PATCH 080/256] Clash of clans completely localizable, fixes, improvements :ok: :100: --- .../Modules/ClashOfClans/ClashOfClans.cs | 51 +++-- .../Modules/ClashOfClans/Extensions.cs | 33 ++- .../Games/Commands/PlantAndPickCommands.cs | 11 - src/NadekoBot/Modules/NadekoModule.cs | 33 +-- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 2 +- .../Resources/ResponseStrings.Designer.cs | 207 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 69 ++++++ .../Resources/ResponseStrings.sr-cyrl-rs.resx | 69 ++++++ src/NadekoBot/Services/Impl/Localization.cs | 8 +- 9 files changed, 419 insertions(+), 64 deletions(-) diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 83e6d00b..1ed31a6e 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -73,7 +73,11 @@ namespace NadekoBot.Modules.ClashOfClans try { SaveWar(war); - await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); + await war.Channel.SendErrorAsync(GetTextStatic("claim_expired", + NadekoBot.Localization.GetCultureInfo(war.Channel.GuildId), + typeof(ClashOfClans).Name.ToLowerInvariant(), + Format.Bold(Bases[i].CallUser), + war.ShortPrint())); } catch { } } @@ -92,7 +96,7 @@ namespace NadekoBot.Modules.ClashOfClans if (size < 10 || size > 50 || size % 5 != 0) { - await Context.Channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false); + await ReplyErrorLocalized("invalid_size").ConfigureAwait(false); return; } List wars; @@ -107,7 +111,7 @@ namespace NadekoBot.Modules.ClashOfClans var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id); wars.Add(cw); - await Context.Channel.SendConfirmAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); + await ReplyErrorLocalized("war_created", cw.ShortPrint()).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -120,18 +124,18 @@ namespace NadekoBot.Modules.ClashOfClans var warsInfo = GetWarInfo(Context.Guild, num); if (warsInfo == null) { - await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); + await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); return; } var war = warsInfo.Item1[warsInfo.Item2]; try { war.Start(); - await Context.Channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false); + await ReplyConfirmLocalized("war_started", war.ShortPrint()).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false); + await ReplyErrorLocalized("war_already_started", war.ShortPrint()).ConfigureAwait(false); } SaveWar(war); } @@ -149,22 +153,20 @@ namespace NadekoBot.Modules.ClashOfClans ClashWars.TryGetValue(Context.Guild.Id, out wars); if (wars == null || wars.Count == 0) { - await Context.Channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false); + await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false); return; } var sb = new StringBuilder(); - sb.AppendLine("🔰 **LIST OF ACTIVE WARS**"); sb.AppendLine("**-------------------------**"); for (var i = 0; i < wars.Count; i++) { - sb.AppendLine($"**#{i + 1}.** `Enemy:` **{wars[i].EnemyClan}**"); - sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**"); + sb.AppendLine($"**#{i + 1}.** `{GetText("enemy")}:` **{wars[i].EnemyClan}**"); + sb.AppendLine($"\t\t`{GetText("size")}:` **{wars[i].Size} v {wars[i].Size}**"); sb.AppendLine("**-------------------------**"); } - await Context.Channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("list_active_wars"), sb.ToString()).ConfigureAwait(false); return; - } var num = 0; int.TryParse(number, out num); @@ -172,10 +174,11 @@ namespace NadekoBot.Modules.ClashOfClans var warsInfo = GetWarInfo(Context.Guild, num); if (warsInfo == null) { - await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); + await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); return; } - await Context.Channel.SendConfirmAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false); + var war = warsInfo.Item1[warsInfo.Item2]; + await Context.Channel.SendConfirmAsync(war.Localize("info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), war.ToPrettyString()).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -185,7 +188,7 @@ namespace NadekoBot.Modules.ClashOfClans var warsInfo = GetWarInfo(Context.Guild, number); if (warsInfo == null || warsInfo.Item1.Count == 0) { - await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); + await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); return; } var usr = @@ -197,11 +200,11 @@ namespace NadekoBot.Modules.ClashOfClans var war = warsInfo.Item1[warsInfo.Item2]; war.Call(usr, baseNumber - 1); SaveWar(war); - await Context.Channel.SendConfirmAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false); + await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, war.ShortPrint()).ConfigureAwait(false); } catch (Exception ex) { - await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false); + await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); } } @@ -233,13 +236,13 @@ namespace NadekoBot.Modules.ClashOfClans var warsInfo = GetWarInfo(Context.Guild, number); if (warsInfo == null) { - await Context.Channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false); + await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); return; } var war = warsInfo.Item1[warsInfo.Item2]; war.End(); SaveWar(war); - await Context.Channel.SendConfirmAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false); + await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false); var size = warsInfo.Item1[warsInfo.Item2].Size; warsInfo.Item1.RemoveAt(warsInfo.Item2); @@ -252,7 +255,7 @@ namespace NadekoBot.Modules.ClashOfClans var warsInfo = GetWarInfo(Context.Guild, number); if (warsInfo == null || warsInfo.Item1.Count == 0) { - await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); + await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); return; } var usr = @@ -264,11 +267,11 @@ namespace NadekoBot.Modules.ClashOfClans var war = warsInfo.Item1[warsInfo.Item2]; var baseNumber = war.Uncall(usr); SaveWar(war); - await Context.Channel.SendConfirmAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false); + await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, war.ShortPrint()).ConfigureAwait(false); } catch (Exception ex) { - await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false); + await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false); } } @@ -277,7 +280,7 @@ namespace NadekoBot.Modules.ClashOfClans var warInfo = GetWarInfo(Context.Guild, number); if (warInfo == null || warInfo.Item1.Count == 0) { - await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); + await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false); return; } var war = warInfo.Item1[warInfo.Item2]; @@ -292,7 +295,7 @@ namespace NadekoBot.Modules.ClashOfClans { war.FinishClaim(baseNumber, stars); } - await Context.Channel.SendConfirmAsync($"❗🔰{Context.User.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); + await ReplyConfirmLocalized("base_destroyed", baseNumber +1, war.ShortPrint()).ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/NadekoBot/Modules/ClashOfClans/Extensions.cs b/src/NadekoBot/Modules/ClashOfClans/Extensions.cs index de12bcab..032d3bcd 100644 --- a/src/NadekoBot/Modules/ClashOfClans/Extensions.cs +++ b/src/NadekoBot/Modules/ClashOfClans/Extensions.cs @@ -27,13 +27,13 @@ namespace NadekoBot.Modules.ClashOfClans public static void Call(this ClashWar cw, string u, int baseNumber) { if (baseNumber < 0 || baseNumber >= cw.Bases.Count) - throw new ArgumentException("Invalid base number"); + throw new ArgumentException(cw.Localize("invalid_base_number")); if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3) - throw new ArgumentException("That base is already destroyed."); + throw new ArgumentException(cw.Localize("base_already_claimed")); for (var i = 0; i < cw.Bases.Count; i++) { if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u) - throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one."); + throw new ArgumentException(cw.Localize("claimed_other", u, i + 1)); } var cc = cw.Bases[baseNumber]; @@ -45,7 +45,7 @@ namespace NadekoBot.Modules.ClashOfClans public static void Start(this ClashWar cw) { if (cw.WarState == StateOfWar.Started) - throw new InvalidOperationException("War already started"); + throw new InvalidOperationException("war_already_started"); //if (Started) // throw new InvalidOperationException(); //Started = true; @@ -66,7 +66,7 @@ namespace NadekoBot.Modules.ClashOfClans cw.Bases[i].CallUser = null; return i; } - throw new InvalidOperationException("You are not participating in that war."); + throw new InvalidOperationException(cw.Localize("not_partic")); } public static string ShortPrint(this ClashWar cw) => @@ -75,8 +75,7 @@ namespace NadekoBot.Modules.ClashOfClans public static string ToPrettyString(this ClashWar cw) { var sb = new StringBuilder(); - - sb.AppendLine($"🔰**WAR AGAINST `{cw.EnemyClan}` ({cw.Size} v {cw.Size}) INFO:**"); + if (cw.WarState == StateOfWar.Created) sb.AppendLine("`not started`"); var twoHours = new TimeSpan(2, 0, 0); @@ -84,7 +83,7 @@ namespace NadekoBot.Modules.ClashOfClans { if (cw.Bases[i].CallUser == null) { - sb.AppendLine($"`{i + 1}.` ❌*unclaimed*"); + sb.AppendLine($"`{i + 1}.` ❌*{cw.Localize("not_claimed")}*"); } else { @@ -120,7 +119,7 @@ namespace NadekoBot.Modules.ClashOfClans cw.Bases[i].Stars = stars; return i; } - throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base."); + throw new InvalidOperationException(cw.Localize("not_partic_or_destroyed", user)); } public static void FinishClaim(this ClashWar cw, int index, int stars = 3) @@ -128,10 +127,22 @@ namespace NadekoBot.Modules.ClashOfClans if (index < 0 || index > cw.Bases.Count) throw new ArgumentOutOfRangeException(nameof(index)); var toFinish = cw.Bases[index]; - if (toFinish.BaseDestroyed != false) throw new InvalidOperationException("That base is already destroyed."); - if (toFinish.CallUser == null) throw new InvalidOperationException("That base is unclaimed."); + if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(cw.Localize("base_already_destroyed")); + if (toFinish.CallUser == null) throw new InvalidOperationException(cw.Localize("base_already_unclaimed")); toFinish.BaseDestroyed = true; toFinish.Stars = stars; } + + public static string Localize(this ClashWar cw, string key) + { + return NadekoModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId), + typeof(ClashOfClans).Name.ToLowerInvariant()); + } + + public static string Localize(this ClashWar cw, string key, params object[] replacements) + { + return string.Format(cw.Localize(key), replacements); + } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 234da7c2..f664a7f2 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -222,17 +222,6 @@ namespace NadekoBot.Modules.Games return images[rng.Next(0, images.Length)]; } - - int GetRandomNumber() - { - using (var rg = RandomNumberGenerator.Create()) - { - byte[] rno = new byte[4]; - rg.GetBytes(rno); - int randomvalue = BitConverter.ToInt32(rno, 0); - return randomvalue; - } - } } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 9311dac5..7ef213db 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Modules { protected readonly Logger _log; protected CultureInfo _cultureInfo { get; private set; } - public readonly string _prefix; + public readonly string Prefix; public readonly string ModuleTypeName; public readonly string LowerModuleTypeName; @@ -23,16 +23,14 @@ namespace NadekoBot.Modules ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; LowerModuleTypeName = ModuleTypeName.ToLowerInvariant(); - if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out _prefix)) - _prefix = "?err?"; + if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out Prefix)) + Prefix = "?err?"; _log = LogManager.GetCurrentClassLogger(); } protected override void BeforeExecute() { - _cultureInfo = (Context.Guild == null - ? NadekoBot.Localization.DefaultCultureInfo - : NadekoBot.Localization.GetCultureInfo(Context.Guild)); + _cultureInfo = NadekoBot.Localization.GetCultureInfo(Context.Guild?.Id); _log.Warn("Culture info is {0}", _cultureInfo); } @@ -60,24 +58,31 @@ namespace NadekoBot.Modules /// /// Used as failsafe in case response key doesn't exist in the selected or default language. /// - private readonly CultureInfo usCultureInfo = new CultureInfo("en-US"); - protected string GetText(string key) + private static readonly CultureInfo usCultureInfo = new CultureInfo("en-US"); + + public static string GetTextStatic(string key, CultureInfo _cultureInfo, string lowerModuleTypeName) { - var text = NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, _cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _cultureInfo); if (string.IsNullOrWhiteSpace(text)) { - _log.Warn(LowerModuleTypeName + "_" + key + " key is missing from " + _cultureInfo + " response strings. PLEASE REPORT THIS."); - return NadekoBot.ResponsesResourceManager.GetString(LowerModuleTypeName + "_" + key, usCultureInfo); + LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + _cultureInfo + " response strings. PLEASE REPORT THIS."); + return NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} found!"; } - return text; + return text ?? $"Error: key {lowerModuleTypeName + "_" + key} not found."; } - protected string GetText(string key, params object[] replacements) + public static string GetTextStatic(string key, CultureInfo _cultureInfo, string lowerModuleTypeName, params object[] replacements) { - return string.Format(GetText(key), replacements); + return string.Format(GetTextStatic(key, _cultureInfo, lowerModuleTypeName), replacements); } + protected string GetText(string key) => + GetTextStatic(key, _cultureInfo, LowerModuleTypeName); + + protected string GetText(string key, params object[] replacements) => + GetText(key, _cultureInfo, LowerModuleTypeName, replacements); + public Task ErrorLocalized(string textKey, params object[] replacements) { var text = GetText(textKey); diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index a95a6d5f..e2679147 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -153,7 +153,7 @@ namespace NadekoBot.Modules.Pokemon var enabledMoves = userType.Moves; if (!enabledMoves.Contains(move.ToLowerInvariant())) { - await ReplyErrorLocalized("invalid_move", Format.Bold(move), _prefix).ConfigureAwait(false); + await ReplyErrorLocalized("invalid_move", Format.Bold(move), Prefix).ConfigureAwait(false); return; } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 337d3b62..3b58fbab 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -59,6 +59,213 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to That base is already claimed or destroyed.. + /// + public static string clashofclans_base_already_claimed { + get { + return ResourceManager.GetString("clashofclans_base_already_claimed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That base is already destroyed.. + /// + public static string clashofclans_base_already_destroyed { + get { + return ResourceManager.GetString("clashofclans_base_already_destroyed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That base is not claimed.. + /// + public static string clashofclans_base_already_unclaimed { + get { + return ResourceManager.GetString("clashofclans_base_already_unclaimed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to **DESTROYED** base #{0} in a war against {1}. + /// + public static string clashofclans_base_destroyed { + get { + return ResourceManager.GetString("clashofclans_base_destroyed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has **UNCLAIMED** base #{1} in a war against {2}. + /// + public static string clashofclans_base_unclaimed { + get { + return ResourceManager.GetString("clashofclans_base_unclaimed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Claim from @{0} for a war against {1} has expired.. + /// + public static string clashofclans_claim_expired { + get { + return ResourceManager.GetString("clashofclans_claim_expired", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} claimed a base #{1} in a war against {2}. + /// + public static string clashofclans_claimed_base { + get { + return ResourceManager.GetString("clashofclans_claimed_base", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to @{0} You already claimed base #{1}. You can't claim a new one.. + /// + public static string clashofclans_claimed_other { + get { + return ResourceManager.GetString("clashofclans_claimed_other", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enemy. + /// + public static string clashofclans_enemy { + get { + return ResourceManager.GetString("clashofclans_enemy", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Info about war against {0}. + /// + public static string clashofclans_info_about_war { + get { + return ResourceManager.GetString("clashofclans_info_about_war", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid base number.. + /// + public static string clashofclans_invalid_base_number { + get { + return ResourceManager.GetString("clashofclans_invalid_base_number", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not a Valid war size.. + /// + public static string clashofclans_invalid_size { + get { + return ResourceManager.GetString("clashofclans_invalid_size", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List Of Active Wars. + /// + public static string clashofclans_list_active_wars { + get { + return ResourceManager.GetString("clashofclans_list_active_wars", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No active wars.. + /// + public static string clashofclans_no_active_wars { + get { + return ResourceManager.GetString("clashofclans_no_active_wars", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to not claimed. + /// + public static string clashofclans_not_claimed { + get { + return ResourceManager.GetString("clashofclans_not_claimed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are not participating in that war.. + /// + public static string clashofclans_not_partic { + get { + return ResourceManager.GetString("clashofclans_not_partic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to @{0} You are either not participating in that war, or that base is already destroyed.. + /// + public static string clashofclans_not_partic_or_destroyed { + get { + return ResourceManager.GetString("clashofclans_not_partic_or_destroyed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Size. + /// + public static string clashofclans_size { + get { + return ResourceManager.GetString("clashofclans_size", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to War against {0} has already started.. + /// + public static string clashofclans_war_already_started { + get { + return ResourceManager.GetString("clashofclans_war_already_started", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to War against {0} created.. + /// + public static string clashofclans_war_created { + get { + return ResourceManager.GetString("clashofclans_war_created", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to War against {0} ended.. + /// + public static string clashofclans_war_ended { + get { + return ResourceManager.GetString("clashofclans_war_ended", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That war does not exist.. + /// + public static string clashofclans_war_not_exist { + get { + return ResourceManager.GetString("clashofclans_war_not_exist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to War against {0} started!. + /// + public static string clashofclans_war_started { + get { + return ResourceManager.GetString("clashofclans_war_started", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has already fainted.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 63ef2430..155460f7 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -117,6 +117,75 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + That base is already claimed or destroyed. + + + That base is already destroyed. + + + That base is not claimed. + + + **DESTROYED** base #{0} in a war against {1} + + + {0} has **UNCLAIMED** base #{1} in a war against {2} + + + {0} claimed a base #{1} in a war against {2} + + + @{0} You already claimed base #{1}. You can't claim a new one. + + + Claim from @{0} for a war against {1} has expired. + + + Enemy + + + Info about war against {0} + + + Invalid base number. + + + Not a Valid war size. + + + List Of Active Wars + + + not claimed + + + You are not participating in that war. + + + @{0} You are either not participating in that war, or you have already destroyed a base. + + + No active wars. + + + Size + + + War against {0} is already started. + + + War against {0} created. + + + War against {0} ended. + + + That war does not exist. + + + War against {0} started! + {0} has already fainted. diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index 218a7645..43a96f10 100644 --- a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -184,4 +184,73 @@ Онесвешћен си, не можеш да се крећеш. + + Та база је већ под захетвом или је уништена. + + + Та база је већ под захтевом. + + + Та база није под захтевом. + + + {0} је **ПОНИШТИО ЗАХТЕВ** за базу #{1} у рату против {2} + + + {0} захтева базу #{1} у рату против {2} + + + @{0} Ти већ захтеваш базу #{1}. Не можеш захтевати још једну. + + + Захтев од @{0} за рат против {1} је истекао. + + + Противник + + + Подаци о рату против {0} + + + Број базе није валидан. + + + Величина рата није валидна. + + + Листа Ратова У Току + + + нема захтева + + + Ти не учествујеш у том рату. + + + @{0} Ти или не учествујеш у рату или је та база већ уништена. + + + Нема ратова у току. + + + Величина + + + Рат против {0} је већ почео. + + + Рат против {0} је направљен. + + + Рат против {0} је завршен. + + + Тај рат не постоји. + + + Рат против {0} је започет! + + + је **УНИШТИО** базу #{0} у рату против {1} + \ No newline at end of file diff --git a/src/NadekoBot/Services/Impl/Localization.cs b/src/NadekoBot/Services/Impl/Localization.cs index e4906712..e9d792ca 100644 --- a/src/NadekoBot/Services/Impl/Localization.cs +++ b/src/NadekoBot/Services/Impl/Localization.cs @@ -102,12 +102,14 @@ namespace NadekoBot.Services SetDefaultCulture(CultureInfo.CurrentCulture); public CultureInfo GetCultureInfo(IGuild guild) => - GetCultureInfo(guild.Id); + GetCultureInfo(guild?.Id); - public CultureInfo GetCultureInfo(ulong guildId) + public CultureInfo GetCultureInfo(ulong? guildId) { + if (guildId == null) + return DefaultCultureInfo; CultureInfo info = null; - GuildCultureInfos.TryGetValue(guildId, out info); + GuildCultureInfos.TryGetValue(guildId.Value, out info); return info ?? DefaultCultureInfo; } From ff193e4549b768ba6a7d2bf3114ec35aa8bac3e0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 22:11:36 +0100 Subject: [PATCH 081/256] custom reactions fully localizable, found several bugs too. --- .../CustomReactions/CustomReactions.cs | 132 ++++++++++-------- .../Resources/ResponseStrings.Designer.cs | 117 ++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 43 +++++- .../Database/Models/CustomReaction.cs | 1 - 4 files changed, 233 insertions(+), 60 deletions(-) diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index a5129c2b..295d7e37 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -139,7 +139,7 @@ namespace NadekoBot.Modules.CustomReactions if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) { - try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { } + await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); return; } @@ -166,7 +166,7 @@ namespace NadekoBot.Modules.CustomReactions else { var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id, - Array.Empty(), + new CustomReaction[] { cr }, (k, old) => { Array.Resize(ref old, old.Length + 1); @@ -176,10 +176,10 @@ namespace NadekoBot.Modules.CustomReactions } await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithTitle("New Custom Reaction") + .WithTitle(GetText("new_cust_react")) .WithDescription($"#{cr.Id}") - .AddField(efb => efb.WithName("Trigger").WithValue(key)) - .AddField(efb => efb.WithName("Response").WithValue(message)) + .AddField(efb => efb.WithName(GetText("trigger")).WithValue(key)) + .AddField(efb => efb.WithName(GetText("response")).WithValue(message)) ).ConfigureAwait(false); } @@ -196,19 +196,20 @@ namespace NadekoBot.Modules.CustomReactions customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty()).Where(cr => cr != null).ToArray(); if (customReactions == null || !customReactions.Any()) - await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); - else { - var lastPage = customReactions.Length / 20; - await Context.Channel.SendPaginatedConfirmAsync(page, curPage => - new EmbedBuilder().WithOkColor() - .WithTitle("Custom reactions") - .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) - .Skip((curPage - 1) * 20) - .Take(20) - .Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))), lastPage) - .ConfigureAwait(false); + await ReplyErrorLocalized("no_found").ConfigureAwait(false); + return; } + + var lastPage = customReactions.Length / 20; + await Context.Channel.SendPaginatedConfirmAsync(page, curPage => + new EmbedBuilder().WithOkColor() + .WithTitle(GetText("name")) + .WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger) + .Skip((curPage - 1) * 20) + .Take(20) + .Select(cr => $"`#{cr.Id}` `{GetText("trigger")}:` {cr.Trigger}"))), lastPage) + .ConfigureAwait(false); } public enum All @@ -227,20 +228,22 @@ namespace NadekoBot.Modules.CustomReactions customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); if (customReactions == null || !customReactions.Any()) - await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); - else { - var txtStream = await customReactions.GroupBy(cr => cr.Trigger) - .OrderBy(cr => cr.Key) - .Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() }) - .ToJson() - .ToStream() - .ConfigureAwait(false); - if (Context.Guild == null) // its a private one, just send back - await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false); - else - await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false); + await ReplyErrorLocalized("no_found").ConfigureAwait(false); + return; } + + var txtStream = await customReactions.GroupBy(cr => cr.Trigger) + .OrderBy(cr => cr.Key) + .Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() }) + .ToJson() + .ToStream() + .ConfigureAwait(false); + + if (Context.Guild == null) // its a private one, just send back + await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false); + else + await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -255,7 +258,9 @@ namespace NadekoBot.Modules.CustomReactions customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); if (customReactions == null || !customReactions.Any()) - await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); + { + await ReplyErrorLocalized("no_found").ConfigureAwait(false); + } else { var ordered = customReactions @@ -266,7 +271,7 @@ namespace NadekoBot.Modules.CustomReactions var lastPage = ordered.Count / 20; await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => new EmbedBuilder().WithOkColor() - .WithTitle($"Custom Reactions (grouped)") + .WithTitle(GetText("name")) .WithDescription(string.Join("\r\n", ordered .Skip((curPage - 1) * 20) .Take(20) @@ -287,13 +292,16 @@ namespace NadekoBot.Modules.CustomReactions var found = customReactions.FirstOrDefault(cr => cr?.Id == id); if (found == null) - await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false); + { + await ReplyErrorLocalized("no_found_id").ConfigureAwait(false); + return; + } else { await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithDescription($"#{id}") - .AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger)) - .AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```")) + .AddField(efb => efb.WithName(GetText("trigger")).WithValue(found.Trigger)) + .AddField(efb => efb.WithName(GetText("response")).WithValue(found.Response + "\n```css\n" + found.Response + "```")) ).ConfigureAwait(false); } } @@ -303,7 +311,7 @@ namespace NadekoBot.Modules.CustomReactions { if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) { - try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { } + await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false); return; } @@ -313,32 +321,42 @@ namespace NadekoBot.Modules.CustomReactions { toDelete = uow.CustomReactions.Get(id); if (toDelete == null) //not found - return; - - if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null) + success = false; + else { - uow.CustomReactions.Remove(toDelete); - //todo i can dramatically improve performance of this, if Ids are ordered. - _globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray(); - success = true; - } - else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) - { - uow.CustomReactions.Remove(toDelete); - GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) => + if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null) { - return old.Where(cr => cr?.Id != toDelete.Id).ToArray(); - }); - success = true; + uow.CustomReactions.Remove(toDelete); + //todo i can dramatically improve performance of this, if Ids are ordered. + _globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray(); + success = true; + } + else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId) + { + uow.CustomReactions.Remove(toDelete); + GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) => + { + return old.Where(cr => cr?.Id != toDelete.Id).ToArray(); + }); + success = true; + } + if (success) + await uow.CompleteAsync().ConfigureAwait(false); } - if (success) - await uow.CompleteAsync().ConfigureAwait(false); } if (success) - await Context.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false); + { + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle(GetText("deleted")) + .WithDescription("#" + toDelete.Id) + .AddField(efb => efb.WithName(GetText("trigger")).WithValue(toDelete.Trigger)) + .AddField(efb => efb.WithName(GetText("response")).WithValue(toDelete.Response))); + } else - await Context.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false); + { + await ReplyErrorLocalized("no_found_id").ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -348,18 +366,18 @@ namespace NadekoBot.Modules.CustomReactions if (string.IsNullOrWhiteSpace(trigger)) { ClearStats(); - await Context.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false); + await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false); } else { uint throwaway; if (ReactionStats.TryRemove(trigger, out throwaway)) { - await Context.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false); + await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false); } else { - await Context.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false); + await ReplyErrorLocalized("stats_not_found").ConfigureAwait(false); } } } @@ -376,7 +394,7 @@ namespace NadekoBot.Modules.CustomReactions await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => ordered.Skip((curPage - 1) * 9) .Take(9) - .Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction Stats"), + .Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")), (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage) .ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 3b58fbab..76d825ad 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -266,6 +266,123 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to All custom reaction stats cleared.. + /// + public static string customreactions_all_stats_cleared { + get { + return ResourceManager.GetString("customreactions_all_stats_cleared", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom Reaction deleted. + /// + public static string customreactions_deleted { + get { + return ResourceManager.GetString("customreactions_deleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions.. + /// + public static string customreactions_insuff_perms { + get { + return ResourceManager.GetString("customreactions_insuff_perms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of all custom reactions. + /// + public static string customreactions_list_all { + get { + return ResourceManager.GetString("customreactions_list_all", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom Reactions. + /// + public static string customreactions_name { + get { + return ResourceManager.GetString("customreactions_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Custom Reaction. + /// + public static string customreactions_new_cust_react { + get { + return ResourceManager.GetString("customreactions_new_cust_react", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No custom reactions found.. + /// + public static string customreactions_no_found { + get { + return ResourceManager.GetString("customreactions_no_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No custom reaction found with that id.. + /// + public static string customreactions_no_found_id { + get { + return ResourceManager.GetString("customreactions_no_found_id", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Response. + /// + public static string customreactions_response { + get { + return ResourceManager.GetString("customreactions_response", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom Reaction Stats. + /// + public static string customreactions_stats { + get { + return ResourceManager.GetString("customreactions_stats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stats cleared for {0} custom reaction.. + /// + public static string customreactions_stats_cleared { + get { + return ResourceManager.GetString("customreactions_stats_cleared", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No stats for that trigger found, no action taken.. + /// + public static string customreactions_stats_not_found { + get { + return ResourceManager.GetString("customreactions_stats_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trigger. + /// + public static string customreactions_trigger { + get { + return ResourceManager.GetString("customreactions_trigger", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has already fainted.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 155460f7..cd6857ab 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -163,7 +163,7 @@ You are not participating in that war. - @{0} You are either not participating in that war, or you have already destroyed a base. + @{0} You are either not participating in that war, or that base is already destroyed. No active wars. @@ -172,7 +172,7 @@ Size - War against {0} is already started. + War against {0} has already started. War against {0} created. @@ -186,6 +186,45 @@ War against {0} started! + + All custom reaction stats cleared. + + + Custom Reaction deleted + + + Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions. + + + List of all custom reactions + + + Custom Reactions + + + New Custom Reaction + + + No custom reactions found. + + + No custom reaction found with that id. + + + Response + + + Custom Reaction Stats + + + Stats cleared for {0} custom reaction. + + + No stats for that trigger found, no action taken. + + + Trigger + {0} has already fainted. diff --git a/src/NadekoBot/Services/Database/Models/CustomReaction.cs b/src/NadekoBot/Services/Database/Models/CustomReaction.cs index 9ae3ab3b..394e5084 100644 --- a/src/NadekoBot/Services/Database/Models/CustomReaction.cs +++ b/src/NadekoBot/Services/Database/Models/CustomReaction.cs @@ -12,7 +12,6 @@ namespace NadekoBot.Services.Database.Models public string Trigger { get; set; } public bool IsRegex { get; set; } public bool OwnerOnly { get; set; } - public override string ToString() => $"`#{Id}` `Trigger:` {Trigger}\n `Response:` {Response}"; } public class ReactionResponse : DbEntity From 813a9b7839fd0211038e0eb902df5564a1913b4f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 22:21:18 +0100 Subject: [PATCH 082/256] custom reaction module translated --- .../Resources/ResponseStrings.Designer.cs | 2 +- src/NadekoBot/Resources/ResponseStrings.resx | 2 +- .../Resources/ResponseStrings.sr-cyrl-rs.resx | 39 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 76d825ad..342b3967 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -285,7 +285,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions.. + /// Looks up a localized string similar to Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for server custom reactions.. /// public static string customreactions_insuff_perms { get { diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index cd6857ab..a6728d5f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -193,7 +193,7 @@ Custom Reaction deleted - Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions. + Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for server custom reactions. List of all custom reactions diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index 43a96f10..e3d4d417 100644 --- a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -253,4 +253,43 @@ је **УНИШТИО** базу #{0} у рату против {1} + + Сва статистика Реакција по Избору је обрисана. + + + Реакција по Избору обрисана + + + Немате дозволу. Потребно је поседовати бота за глобалне Реакције по Избору, а Администратор за серверске Реакције по Избору. + + + Листа свих реакција по избору + + + Реакције по Избору + + + Нова Реакција по Избору + + + Реакција по избору није нађена. + + + Није нађена реакција са тим идентификатором. + + + Одговор + + + Статистика Реакција по Избору + + + Статистика је обрисана за {0} реакцију по избору. + + + Није нађена статистика за тај окидач. Нема промене. + + + Окидач + \ No newline at end of file From e6af53d6473bd269ea8f3010ea0249252c5f02e9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 13 Feb 2017 23:21:25 +0100 Subject: [PATCH 083/256] nsfw translated. whole 5 strings out of which 3 are the same. :) --- src/NadekoBot/Modules/NSFW/NSFW.cs | 53 ++++++++++--------- .../Resources/ResponseStrings.Designer.cs | 37 +++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 13 +++++ .../Resources/ResponseStrings.sr-cyrl-rs.resx | 13 +++++ 4 files changed, 90 insertions(+), 26 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index c2e42d58..5538e8d6 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -20,8 +20,8 @@ namespace NadekoBot.Modules.NSFW public class NSFW : NadekoModule { - private static ConcurrentDictionary AutoHentaiTimers { get; } = new ConcurrentDictionary(); - private static ConcurrentHashSet _hentaiBombBlacklist { get; } = new ConcurrentHashSet(); + private static readonly ConcurrentDictionary AutoHentaiTimers = new ConcurrentDictionary(); + private static readonly ConcurrentHashSet HentaiBombBlacklist = new ConcurrentHashSet(); private async Task InternalHentai(IMessageChannel channel, string tag, bool noError) { @@ -30,7 +30,7 @@ namespace NadekoBot.Modules.NSFW tag = "rating%3Aexplicit+" + tag; var rng = new NadekoRandom(); - Task provider = Task.FromResult(""); + var provider = Task.FromResult(""); switch (rng.Next(0, 4)) { case 0: @@ -45,20 +45,18 @@ namespace NadekoBot.Modules.NSFW case 3: provider = GetYandereImageLink(tag); break; - default: - break; } var link = await provider.ConfigureAwait(false); if (string.IsNullOrWhiteSpace(link)) { if (!noError) - await channel.SendErrorAsync("No results found.").ConfigureAwait(false); + await ReplyErrorLocalized("not_found").ConfigureAwait(false); return; } await channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithImageUrl(link) - .WithDescription("Tag: " + tag)) + .WithDescription($"{GetText("tag")}: " + tag)) .ConfigureAwait(false); } @@ -74,11 +72,10 @@ namespace NadekoBot.Modules.NSFW if (interval == 0) { - if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) - { - t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer - await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false); - } + if (!AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return; + + t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer + await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false); return; } @@ -96,7 +93,10 @@ namespace NadekoBot.Modules.NSFW else await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } }, null, interval * 1000, interval * 1000); AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => @@ -105,15 +105,16 @@ namespace NadekoBot.Modules.NSFW return t; }); - await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}") - .ConfigureAwait(false); + await ReplyConfirmLocalized("autohentai_started", + interval, + string.Join(", ", tagsArr)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] public async Task HentaiBomb([Remainder] string tag = null) { - if (!_hentaiBombBlacklist.Add(Context.User.Id)) + if (!HentaiBombBlacklist.Add(Context.User.Id)) return; try { @@ -125,19 +126,19 @@ namespace NadekoBot.Modules.NSFW GetKonachanImageLink(tag), GetYandereImageLink(tag)).ConfigureAwait(false); - var linksEnum = links?.Where(l => l != null); + var linksEnum = links?.Where(l => l != null).ToArray(); if (links == null || !linksEnum.Any()) { - await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false); + await ReplyErrorLocalized("not_found").ConfigureAwait(false); return; } - await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false); + await Context.Channel.SendMessageAsync(string.Join("\n\n", linksEnum)).ConfigureAwait(false); } finally { await Task.Delay(5000).ConfigureAwait(false); - _hentaiBombBlacklist.TryRemove(Context.User.Id); + HentaiBombBlacklist.TryRemove(Context.User.Id); } } #endif @@ -157,7 +158,7 @@ namespace NadekoBot.Modules.NSFW var url = await GetE621ImageLink(tag).ConfigureAwait(false); if (url == null) - await Context.Channel.SendErrorAsync(Context.User.Mention + " No results."); + await ReplyErrorLocalized("not_found").ConfigureAwait(false); else await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithDescription(Context.User.Mention + " " + tag) @@ -178,7 +179,7 @@ namespace NadekoBot.Modules.NSFW var url = await GetDanbooruImageLink(tag).ConfigureAwait(false); if (url == null) - await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.").ConfigureAwait(false); + await ReplyErrorLocalized("not_found").ConfigureAwait(false); else await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithDescription(Context.User.Mention + " " + tag) @@ -227,9 +228,9 @@ namespace NadekoBot.Modules.NSFW JToken obj; using (var http = new HttpClient()) { - obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10330) }").ConfigureAwait(false))[0]; + obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{new NadekoRandom().Next(0, 10330)}").ConfigureAwait(false))[0]; } - await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); + await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{obj["preview"]}").ConfigureAwait(false); } catch (Exception ex) { @@ -245,9 +246,9 @@ namespace NadekoBot.Modules.NSFW JToken obj; using (var http = new HttpClient()) { - obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4335) }").ConfigureAwait(false))[0]; + obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{new NadekoRandom().Next(0, 4335)}").ConfigureAwait(false))[0]; } - await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); + await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{obj["preview"]}").ConfigureAwait(false); } catch (Exception ex) { diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 342b3967..23b6abda 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -383,6 +383,43 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Autohentai started. Reposting every {0}s with one of the following tags: + ///{1}. + /// + public static string nsfw_autohentai_started { + get { + return ResourceManager.GetString("nsfw_autohentai_started", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Autohentai stopped.. + /// + public static string nsfw_autohentai_stopped { + get { + return ResourceManager.GetString("nsfw_autohentai_stopped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No results found.. + /// + public static string nsfw_not_found { + get { + return ResourceManager.GetString("nsfw_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tag. + /// + public static string nsfw_tag { + get { + return ResourceManager.GetString("nsfw_tag", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has already fainted.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index a6728d5f..80ada81e 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -225,6 +225,12 @@ Trigger + + Autohentai stopped. + + + No results found. + {0} has already fainted. @@ -292,4 +298,11 @@ You fainted, so you are not able to move! + + Autohentai started. Reposting every {0}s with one of the following tags: +{1} + + + Tag + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index e3d4d417..5b59766c 100644 --- a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -292,4 +292,17 @@ Окидач + + Аутохентаи започет. Постоваћу сваких {0} сек. користећи један од следећих тагова: +{1} + + + АутоХентаи заустављен. + + + Нема резултата. + + + Таг + \ No newline at end of file From e68608770f7944d550b103d96a745917258ae110 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 00:19:59 +0100 Subject: [PATCH 084/256] help fully localized, hgit might not work properly though, needs testing --- src/NadekoBot/Modules/Help/Help.cs | 86 +++++---- src/NadekoBot/Modules/NadekoModule.cs | 6 +- .../Resources/ResponseStrings.Designer.cs | 168 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 60 +++++++ 4 files changed, 271 insertions(+), 49 deletions(-) diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index c447db40..c22a9789 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -13,19 +13,26 @@ using System.Collections.Generic; namespace NadekoBot.Modules.Help { [NadekoModule("Help", "-")] - public partial class Help : NadekoModule + public class Help : NadekoModule { private static string helpString { get; } = NadekoBot.BotConfig.HelpString; public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]); public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString; + public const string PatreonUrl = "https://patreon.com/nadekobot"; + public const string PaypalUrl = "https://paypal.me/Kwoth"; + [NadekoCommand, Usage, Description, Aliases] public async Task Modules() { - - var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" ℹ️ Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`")) - .WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule()).Select(m => m.Key.Name).OrderBy(s => s))); + var embed = new EmbedBuilder().WithOkColor() + .WithFooter(efb => efb.WithText("ℹ️" + GetText("modules_footer", Prefix))) + .WithTitle(GetText("list_of_modules")) + .WithDescription(string.Join("\n", + NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule()) + .Select(m => "• " + m.Key.Name) + .OrderBy(s => s))); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -45,18 +52,13 @@ namespace NadekoBot.Modules.Help var cmdsArray = cmds as CommandInfo[] ?? cmds.ToArray(); if (!cmdsArray.Any()) { - await channel.SendErrorAsync("That module does not exist.").ConfigureAwait(false); + await ReplyErrorLocalized("module_not_found").ConfigureAwait(false); return; } - if (module != "customreactions" && module != "conversations") - { - await channel.SendTableAsync("📃 **List Of Commands:**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false); - } - else - { - await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Aliases.First()}"))); - } - await channel.SendConfirmAsync($"ℹ️ **Type** `\"{NadekoBot.ModulePrefixes[typeof(Help).Name]}h CommandName\"` **to see the help for that specified command.** ***e.g.*** `-h >8ball`").ConfigureAwait(false); + + await channel.SendTableAsync($"📃 **{GetText("list_of_commands")}**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false); + + await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -75,32 +77,33 @@ namespace NadekoBot.Modules.Help if (com == null) { - await channel.SendErrorAsync("I can't find that command. Please check the **command** and **command prefix** before trying again."); + await ReplyErrorLocalized("command_not_found").ConfigureAwait(false); return; } - var str = $"**`{com.Aliases.First()}`**"; + var str = string.Format("**`{0}`**", com.Aliases.First()); var alias = com.Aliases.Skip(1).FirstOrDefault(); if (alias != null) - str += $" **/ `{alias}`**"; + str += string.Format(" **/ `{0}`**", alias); var embed = new EmbedBuilder() - .AddField(fb => fb.WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Aliases.First())} { GetCommandRequirements(com)}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Aliases.First())}").WithIsInline(false)) + .AddField(fb => fb.WithName(str).WithValue($"{string.Format(com.Summary, com.Module.Aliases.First())} {GetCommandRequirements(com)}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("usage")).WithValue(string.Format(com.Remarks, com.Module.Aliases.First())).WithIsInline(false)) .WithColor(NadekoBot.OkColor); await channel.EmbedAsync(embed).ConfigureAwait(false); } private string GetCommandRequirements(CommandInfo cmd) => - String.Join(" ", cmd.Preconditions + string.Join(" ", cmd.Preconditions .Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute) .Select(ca => { if (ca is OwnerOnlyAttribute) - return "**Bot Owner only.**"; + return Format.Bold(GetText("bot_owner_only")); var cau = (RequireUserPermissionAttribute)ca; if (cau.GuildPermission != null) - return $"**Requires {cau.GuildPermission} server permission.**".Replace("Guild", "Server"); - else - return $"**Requires {cau.ChannelPermission} channel permission.**".Replace("Guild", "Server"); + return Format.Bold(GetText("server_permission", cau.GuildPermission)) + .Replace("Guild", "Server"); + return Format.Bold(GetText("channel_permission", cau.ChannelPermission)) + .Replace("Guild", "Server"); })); [NadekoCommand, Usage, Description, Aliases] @@ -109,14 +112,14 @@ namespace NadekoBot.Modules.Help public async Task Hgit() { var helpstr = new StringBuilder(); - helpstr.AppendLine("You can support the project on patreon: or paypal: \n"); - helpstr.AppendLine("##Table Of Contents"); + helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n"); + helpstr.AppendLine("##"+ GetText("table_of_contents")); helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help") .Select(m => m.GetTopLevelModule().Name) .Distinct() .OrderBy(m => m) .Prepend("Help") - .Select(m => $"- [{m}](#{m.ToLowerInvariant()})"))); + .Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant())))); helpstr.AppendLine(); string lastModule = null; foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First())) @@ -127,44 +130,35 @@ namespace NadekoBot.Modules.Help if (lastModule != null) { helpstr.AppendLine(); - helpstr.AppendLine("###### [Back to TOC](#table-of-contents)"); + helpstr.AppendLine($"###### [{GetText("back_to_toc")}](#{GetText("table_of_contents").ToLowerInvariant().Replace(' ', '-')})"); } helpstr.AppendLine(); helpstr.AppendLine("### " + module.Name + " "); - helpstr.AppendLine("Command and aliases | Description | Usage"); + helpstr.AppendLine($"{GetText("cmd_and_alias")} | {GetText("desc")} | {GetText("usage")}"); helpstr.AppendLine("----------------|--------------|-------"); lastModule = module.Name; } - helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} | {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.GetPrefix())}"); + helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" + + $" {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} |" + + $" {string.Format(com.Remarks, com.Module.GetPrefix())}"); } helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName"); File.WriteAllText("../../docs/Commands List.md", helpstr.ToString()); - await Context.Channel.SendConfirmAsync("Commandlist Regenerated").ConfigureAwait(false); + await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] public async Task Guide() { - var channel = (ITextChannel)Context.Channel; - - await channel.SendConfirmAsync( -@"**LIST OF COMMANDS**: -**Hosting Guides and docs can be found here**: ").ConfigureAwait(false); + await ConfirmLocalized("guide", + "http://nadekobot.readthedocs.io/en/latest/Commands%20List/", + "http://nadekobot.readthedocs.io/en/latest/").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] public async Task Donate() { - var channel = (ITextChannel)Context.Channel; - - await channel.SendConfirmAsync( -$@"You can support the NadekoBot project on patreon. or -Paypal -Don't forget to leave your discord name or id in the message. - -**Thank you** ♥️").ConfigureAwait(false); + await ReplyConfirmLocalized("donate", PatreonUrl, PaypalUrl).ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 7ef213db..7f75e891 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules public readonly string ModuleTypeName; public readonly string LowerModuleTypeName; - public NadekoModule(bool isTopLevelModule = true) + protected NadekoModule(bool isTopLevelModule = true) { //if it's top level module ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; @@ -81,7 +81,7 @@ namespace NadekoBot.Modules GetTextStatic(key, _cultureInfo, LowerModuleTypeName); protected string GetText(string key, params object[] replacements) => - GetText(key, _cultureInfo, LowerModuleTypeName, replacements); + GetTextStatic(key, _cultureInfo, LowerModuleTypeName, replacements); public Task ErrorLocalized(string textKey, params object[] replacements) { @@ -110,7 +110,7 @@ namespace NadekoBot.Modules public abstract class NadekoSubmodule : NadekoModule { - public NadekoSubmodule() : base(false) + protected NadekoSubmodule() : base(false) { } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 23b6abda..975e2331 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -383,6 +383,174 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Back to ToC. + /// + public static string help_back_to_toc { + get { + return ResourceManager.GetString("help_back_to_toc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bot Owner Only. + /// + public static string help_bot_owner_only { + get { + return ResourceManager.GetString("help_bot_owner_only", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requires {0} channel permission.. + /// + public static string help_channel_permission { + get { + return ResourceManager.GetString("help_channel_permission", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Command and aliases. + /// + public static string help_cmd_and_alias { + get { + return ResourceManager.GetString("help_cmd_and_alias", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can support the project on patreon: <{0}> or paypal: <{1}>. + /// + public static string help_cmdlist_donate { + get { + return ResourceManager.GetString("help_cmdlist_donate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I can't find that command. Please verify that the command exists before trying again.. + /// + public static string help_command_not_found { + get { + return ResourceManager.GetString("help_command_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Commandlist Regenerated.. + /// + public static string help_commandlist_regen { + get { + return ResourceManager.GetString("help_commandlist_regen", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type `{0}h CommandName` to see the help for that specified command. e.g. `{0}h >8ball`. + /// + public static string help_commands_instr { + get { + return ResourceManager.GetString("help_commands_instr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Description. + /// + public static string help_desc { + get { + return ResourceManager.GetString("help_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can support the NadekoBot project on + ///Patreon <{0}> or + ///Paypal <{1}> + ///Don't forget to leave your discord name or id in the message. + /// + ///**Thank you** ♥️. + /// + public static string help_donate { + get { + return ResourceManager.GetString("help_donate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to **List of Commands**: <{0}> + ///**Hosting Guides and docs can be found here**: <{1}>. + /// + public static string help_guide { + get { + return ResourceManager.GetString("help_guide", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List Of Commands. + /// + public static string help_list_of_commands { + get { + return ResourceManager.GetString("help_list_of_commands", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List Of Modules. + /// + public static string help_list_of_modules { + get { + return ResourceManager.GetString("help_list_of_modules", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That module does not exist.. + /// + public static string help_module_not_found { + get { + return ResourceManager.GetString("help_module_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type `{0}cmds ModuleName` to get a list of commands in that module. eg `{0}cmds games`. + /// + public static string help_modules_footer { + get { + return ResourceManager.GetString("help_modules_footer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Requires {0} server permission.. + /// + public static string help_server_permission { + get { + return ResourceManager.GetString("help_server_permission", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Table Of Contents. + /// + public static string help_table_of_contents { + get { + return ResourceManager.GetString("help_table_of_contents", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Usage. + /// + public static string help_usage { + get { + return ResourceManager.GetString("help_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to Autohentai started. Reposting every {0}s with one of the following tags: ///{1}. diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 80ada81e..5285f3b6 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -298,6 +298,66 @@ You fainted, so you are not able to move! + + Back to ToC + + + Bot Owner Only + + + Requires {0} channel permission. + + + You can support the project on patreon: <{0}> or paypal: <{1}> + + + Command and aliases + + + Commandlist Regenerated. + + + Type `{0}h CommandName` to see the help for that specified command. e.g. `{0}h >8ball` + + + I can't find that command. Please verify that the command exists before trying again. + + + Description + + + You can support the NadekoBot project on +Patreon <{0}> or +Paypal <{1}> +Don't forget to leave your discord name or id in the message. + +**Thank you** ♥️ + + + **List of Commands**: <{0}> +**Hosting Guides and docs can be found here**: <{1}> + + + List Of Commands + + + List Of Modules + + + Type `{0}cmds ModuleName` to get a list of commands in that module. eg `{0}cmds games` + + + That module does not exist. + + + Requires {0} server permission. + + + Table Of Contents + + + Usage + Autohentai started. Reposting every {0}s with one of the following tags: {1} From d49af3389e242f11aa52dd8f4c6d409e61664829 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 00:27:45 +0100 Subject: [PATCH 085/256] translated help xd --- .../Resources/ResponseStrings.sr-cyrl-rs.resx | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index 5b59766c..86b7aa87 100644 --- a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -292,6 +292,66 @@ Окидач + + Назад на Садржај + + + Само Власник Бота + + + Захтева {0} дозволу на каналу. + + + Можете подржати пројекат на Пејтриону: <{0}> или Пејпалу: <{1}> + + + Команде и псеудоними + + + Листа команди је обновљена. + + + Укуцај `{0}h ИмеКоманде` да видиш помоћ за ту команду. нпр. `{0}h >8ball` + + + Не могу да нађем ту команду. Проверите да ли команда постоји, па покушајте поново. + + + Опис + + + Можете подржати НадекоБот пројекат на +Пејтриону <{0}> или +Пејпалу <{1}> +Не заборавите да оставите своје корисничко име или ИД. + +**Хвала** ♥️ + + + **List of Commands**: <{0}> +**Hosting Guides and docs can be found here**: <{1}> + + + Листа Команди + + + Листа Модула + + + Укуцај `{0}cmds ИмеМодула` да видиш листу свих команди у том модулу. нпр `{0}cmds games` + + + Тај модул не постоји. + + + Захтева {0} дозволу на серверу. + + + Садржај + + + Упутство + Аутохентаи започет. Постоваћу сваких {0} сек. користећи један од следећих тагова: {1} From 91a0ee31af034ed2ac74ed26c3d21fe4c2b0ad96 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:30:16 -0500 Subject: [PATCH 086/256] Add quote search by keyword and text command Quote search by keyword and text --- .../Modules/Utility/Commands/QuoteCommands.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index 8add0707..b136b7aa 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -58,6 +58,27 @@ namespace NadekoBot.Modules.Utility await Context.Channel.SendMessageAsync("📣 " + quote.Text.SanitizeMentions()); } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task SearchQuote(string keyword, [Remainder] string text) + { + if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text)) + return; + + keyword = keyword.ToUpperInvariant(); + + Quote keywordquote; + using (var uow = DbHandler.UnitOfWork()) + { + keywordquote = await uow.Quotes.SearchQuoteKeywordTextAsync(Context.Guild.Id, keyword, text).ConfigureAwait(false); + } + + if (keywordquote == null) + return; + + await Context.Channel.SendMessageAsync("💬 " + keyword + ": " + keywordquote.Text.SanitizeMentions()); + } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -143,4 +164,4 @@ namespace NadekoBot.Modules.Utility } } } -} \ No newline at end of file +} From 283fc2c21663a00345f9e95981b67265789cceea Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:31:20 -0500 Subject: [PATCH 087/256] Update IQuoteRepository.cs --- src/NadekoBot/Services/Database/Repositories/IQuoteRepository.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NadekoBot/Services/Database/Repositories/IQuoteRepository.cs b/src/NadekoBot/Services/Database/Repositories/IQuoteRepository.cs index e3ca6ec1..080783ab 100644 --- a/src/NadekoBot/Services/Database/Repositories/IQuoteRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/IQuoteRepository.cs @@ -8,6 +8,7 @@ namespace NadekoBot.Services.Database.Repositories { IEnumerable GetAllQuotesByKeyword(ulong guildId, string keyword); Task GetRandomQuoteByKeywordAsync(ulong guildId, string keyword); + Task SearchQuoteKeywordTextAsync(ulong guildId, string keyword, string text); IEnumerable GetGroup(ulong guildId, int skip, int take); } } From 921f3a6d6c9cbf76e43d0c77a5cf1ad8869fc561 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:32:16 -0500 Subject: [PATCH 088/256] Update QuoteRepository.cs Add quote search by keyword and text --- .../Services/Database/Repositories/Impl/QuoteRepository.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs index 22c6e5c9..d96a98c9 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs @@ -23,5 +23,11 @@ namespace NadekoBot.Services.Database.Repositories.Impl var rng = new NadekoRandom(); return _set.Where(q => q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rng.Next()).FirstOrDefaultAsync(); } + public Task SearchQuoteKeywordTextAsync(ulong guildId, string keyword, string text) + { + + var rngk = new NadekoRandom(); + return _set.Where(q => q.Text.Contains(text) && q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rngk.Next()).FirstOrDefaultAsync(); + } } } From 7a03c277cf6cd60fae86fc049025b08d20e2a2a9 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:32:39 -0500 Subject: [PATCH 089/256] Add quote search by keyword and text Add quote search by keyword and text --- .../Services/Database/Repositories/Impl/QuoteRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs index d96a98c9..6f9cfc20 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs @@ -28,6 +28,6 @@ namespace NadekoBot.Services.Database.Repositories.Impl var rngk = new NadekoRandom(); return _set.Where(q => q.Text.Contains(text) && q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rngk.Next()).FirstOrDefaultAsync(); - } + } } } From 9d84dfbe90e89be4609268906d2f5c62957284c4 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:38:08 -0500 Subject: [PATCH 090/256] Update CommandStrings.Designer.cs --- .../Resources/CommandStrings.Designer.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 11fa3692..73ea4744 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -2381,6 +2381,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to qsearch ..... + /// + public static string searchquote_cmd { + get { + return ResourceManager.GetString("searchquote_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Searches a quote for a given keyword and any string portion of a quote matching that keyword.. + /// + public static string searchquote_desc { + get { + return ResourceManager.GetString("searchquote_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}qsearch keyword text`. + /// + public static string searchquote_usage { + get { + return ResourceManager.GetString("searchquote_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to delmsgoncmd. /// From 599463e9123fa393eb37157559aa94813786f5d8 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:39:39 -0500 Subject: [PATCH 091/256] Add searchquote command Add searchquote command --- src/NadekoBot/Resources/CommandStrings.resx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index cca4cf5c..97ffe02e 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1143,6 +1143,15 @@ `{0}.. abc` + + qsearch .... + + + Searches a quote for a given keyword and any string portion of a quote matching that keyword. + + + `{0}qsearch keyword text` + deletequote delq @@ -3105,4 +3114,4 @@ `{0}timezone` - \ No newline at end of file + From 90d31fc266f0e459b7c17f504066ecfc0c9f8ad0 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:42:10 -0500 Subject: [PATCH 092/256] Add quote search command by keyword and text Add quote search command by keyword and text --- src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index b136b7aa..87285de0 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -60,7 +60,7 @@ namespace NadekoBot.Modules.Utility } [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] + [RequireContext(ContextType.Guild)] public async Task SearchQuote(string keyword, [Remainder] string text) { if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text)) From 9b773f4ea45d58ee4d33bb14d40ed6920e91d971 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 22:51:57 -0500 Subject: [PATCH 093/256] Update CommandStrings.resx --- src/NadekoBot/Resources/CommandStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 97ffe02e..208ab0cc 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1144,7 +1144,7 @@ `{0}.. abc` - qsearch .... + qsearch ... Searches a quote for a given keyword and any string portion of a quote matching that keyword. From c4e228f2766d17369402f95ac8a6c055cea47d72 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 23:43:35 -0500 Subject: [PATCH 094/256] Update QuoteCommands.cs --- src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index 87285de0..bf26a667 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -59,12 +59,12 @@ namespace NadekoBot.Modules.Utility await Context.Channel.SendMessageAsync("📣 " + quote.Text.SanitizeMentions()); } - [NadekoCommand, Usage, Description, Aliases] + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task SearchQuote(string keyword, [Remainder] string text) - { - if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text)) - return; + { + if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text)) + return; keyword = keyword.ToUpperInvariant(); From f0679854024f23b3460bb2c21ff418430a0f9545 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 23:44:09 -0500 Subject: [PATCH 095/256] Update CommandStrings.Designer.cs --- src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 73ea4744..8788c2e3 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -2382,7 +2382,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to qsearch ..... + /// Looks up a localized string similar to qsearch .... /// public static string searchquote_cmd { get { From 672bb565e8891d29ad1a213860e5f1c8f32a9ba7 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Mon, 13 Feb 2017 23:44:50 -0500 Subject: [PATCH 096/256] Update QuoteRepository.cs --- .../Services/Database/Repositories/Impl/QuoteRepository.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs index 6f9cfc20..898f0efd 100644 --- a/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs +++ b/src/NadekoBot/Services/Database/Repositories/Impl/QuoteRepository.cs @@ -24,9 +24,8 @@ namespace NadekoBot.Services.Database.Repositories.Impl return _set.Where(q => q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rng.Next()).FirstOrDefaultAsync(); } public Task SearchQuoteKeywordTextAsync(ulong guildId, string keyword, string text) - { - - var rngk = new NadekoRandom(); + { + var rngk = new NadekoRandom(); return _set.Where(q => q.Text.Contains(text) && q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rngk.Next()).FirstOrDefaultAsync(); } } From db49096246e17aab7b52a411a18736573dc5ca4c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 14:30:21 +0100 Subject: [PATCH 097/256] quite a bit of cleanup --- .../Commands/DMForwardCommands.cs | 4 +- .../Administration/Commands/LogCommand.cs | 29 ++++++----- .../Administration/Commands/Migration.cs | 20 ++++---- .../Administration/Commands/MuteCommands.cs | 2 - .../Commands/ProtectionCommands.cs | 2 - .../Commands/RatelimitCommand.cs | 24 ++++------ .../Commands/ServerGreetCommands.cs | 19 +++++--- .../Commands/VoicePlusTextCommands.cs | 6 +-- .../Modules/ClashOfClans/ClashOfClans.cs | 5 -- .../CustomReactions/CustomReactions.cs | 2 +- .../Modules/Gambling/Commands/AnimalRacing.cs | 22 ++++----- .../Gambling/Commands/CurrencyEvents.cs | 48 ++++++++++--------- .../Modules/Gambling/Commands/Slots.cs | 2 +- .../Gambling/Commands/WaifuClaimCommands.cs | 2 - src/NadekoBot/Modules/Gambling/Gambling.cs | 1 - .../Games/Commands/CleverBotCommands.cs | 20 +++----- .../Modules/Games/Commands/HangmanCommands.cs | 3 +- .../Games/Commands/PlantAndPickCommands.cs | 7 +-- .../Modules/Games/Commands/TicTacToe.cs | 2 +- src/NadekoBot/Modules/Games/Games.cs | 1 - src/NadekoBot/Modules/Music/Music.cs | 33 +++++++------ .../Permissions/Commands/CmdCdsCommands.cs | 28 +++++------ .../Permissions/Commands/FilterCommands.cs | 20 ++++---- .../Searches/Commands/AnimeSearchCommands.cs | 2 +- .../Searches/Commands/OverwatchCommands.cs | 2 +- .../Searches/Commands/PlaceCommands.cs | 3 +- .../Commands/StreamNotificationCommands.cs | 14 ++++-- src/NadekoBot/Modules/Searches/Searches.cs | 8 ++-- .../Modules/Utility/Commands/InfoCommands.cs | 10 ++-- .../Utility/Commands/MessageRepeater.cs | 2 +- .../Modules/Utility/Commands/Remind.cs | 6 +-- src/NadekoBot/Modules/Utility/Utility.cs | 1 - .../Services/Database/NadekoContext.cs | 4 +- src/NadekoBot/Services/Impl/StatsService.cs | 11 +++-- src/NadekoBot/Services/NadekoRandom.cs | 1 - 35 files changed, 174 insertions(+), 192 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs index fd954a4b..fe4a328b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs @@ -74,8 +74,8 @@ namespace NadekoBot.Modules.Administration var title = $"DM from [{msg.Author}]({msg.Author.Id})"; if (ForwardDMsToAllOwners) { - var msgs = await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) - .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false); + await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) + .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false); } else { diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 4219ff98..c5183692 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -35,19 +35,15 @@ namespace NadekoBot.Modules.Administration private static ConcurrentDictionary> PresenceUpdates { get; } = new ConcurrentDictionary>(); private static Timer timerReference { get; } - private IGoogleApiService _google { get; } static LogCommands() { _client = NadekoBot.Client; _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); - - using (var uow = DbHandler.UnitOfWork()) - { - GuildLogSettings = new ConcurrentDictionary(NadekoBot.AllGuildConfigs - .ToDictionary(g => g.GuildId, g => g.LogSetting)); - } + + GuildLogSettings = new ConcurrentDictionary(NadekoBot.AllGuildConfigs + .ToDictionary(g => g.GuildId, g => g.LogSetting)); timerReference = new Timer(async (state) => { @@ -59,7 +55,11 @@ namespace NadekoBot.Modules.Administration { List messages; if (PresenceUpdates.TryRemove(key, out messages)) - try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } catch { } + try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } + catch + { + // ignored + } })); } catch (Exception ex) @@ -159,7 +159,9 @@ namespace NadekoBot.Modules.Administration //} } catch - { } + { + // ignored + } } private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) @@ -188,7 +190,7 @@ namespace NadekoBot.Modules.Administration var str = ""; if (beforeVch?.Guild == afterVch?.Guild) { - str = $"{usr.Username} moved from {beforeVch.Name} to {afterVch.Name}"; + str = $"{usr.Username} moved from {beforeVch?.Name} to {afterVch?.Name}"; } else if (beforeVch == null) { @@ -201,7 +203,10 @@ namespace NadekoBot.Modules.Administration var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false); toDelete.DeleteAfter(5); } - catch { } + catch + { + // ignored + } } private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType) @@ -216,7 +221,7 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) return; - string mutes = ""; + var mutes = ""; switch (muteType) { case MuteCommands.MuteType.Voice: diff --git a/src/NadekoBot/Modules/Administration/Commands/Migration.cs b/src/NadekoBot/Modules/Administration/Commands/Migration.cs index 3a917698..883e5d4b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/Migration.cs +++ b/src/NadekoBot/Modules/Administration/Commands/Migration.cs @@ -103,11 +103,8 @@ namespace NadekoBot.Modules.Administration var greetChannel = (ulong)(long)reader["GreetChannelId"]; var greetMsg = (string)reader["GreetText"]; var bye = (long)reader["Bye"] == 1; - var byeDM = (long)reader["ByePM"] == 1; var byeChannel = (ulong)(long)reader["ByeChannelId"]; var byeMsg = (string)reader["ByeText"]; - var grdel = false; - var byedel = grdel; var gc = uow.GuildConfigs.For(gid, set => set); if (greetDM) @@ -121,7 +118,6 @@ namespace NadekoBot.Modules.Administration gc.ByeMessageChannelId = byeChannel; gc.ChannelByeMessageText = byeMsg; - gc.AutoDeleteGreetMessagesTimer = gc.AutoDeleteByeMessagesTimer = grdel ? 30 : 0; _log.Info(++i); } } @@ -129,12 +125,12 @@ namespace NadekoBot.Modules.Administration _log.Warn("Greet/bye messages won't be migrated"); } var com2 = db.CreateCommand(); - com.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId"; + com2.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId"; i = 0; try { - var reader2 = com.ExecuteReader(); + var reader2 = com2.ExecuteReader(); while (reader2.Read()) { _log.Info(++i); @@ -203,7 +199,6 @@ namespace NadekoBot.Modules.Administration guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles; guildConfig.GenerateCurrencyChannelIds = new HashSet(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key })); selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray()); - var logSetting = guildConfig.LogSetting; guildConfig.LogSetting.IgnoredChannels = new HashSet(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id })); guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel; @@ -249,7 +244,7 @@ namespace NadekoBot.Modules.Administration private void MigratePermissions0_9(IUnitOfWork uow) { - var PermissionsDict = new ConcurrentDictionary(); + var permissionsDict = new ConcurrentDictionary(); if (!Directory.Exists("data/permissions/")) { _log.Warn("No data from permissions will be migrated."); @@ -263,12 +258,15 @@ namespace NadekoBot.Modules.Administration if (string.IsNullOrWhiteSpace(strippedFileName)) continue; var id = ulong.Parse(strippedFileName); var data = JsonConvert.DeserializeObject(File.ReadAllText(file)); - PermissionsDict.TryAdd(id, data); + permissionsDict.TryAdd(id, data); + } + catch + { + // ignored } - catch { } } var i = 0; - PermissionsDict + permissionsDict .Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) }) .AsParallel() .ForAll(perms => diff --git a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs index f37a26ec..89ef6ec6 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs @@ -36,8 +36,6 @@ namespace NadekoBot.Modules.Administration static MuteCommands() { - var _log = LogManager.GetCurrentClassLogger(); - var configs = NadekoBot.AllGuildConfigs; GuildMuteRoles = new ConcurrentDictionary(configs .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName)) diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs index 7639a4dc..ff671a98 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -402,8 +402,6 @@ namespace NadekoBot.Modules.Administration [RequireContext(ContextType.Guild)] public async Task AntiList() { - var channel = (ITextChannel)Context.Channel; - AntiSpamStats spam; antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam); diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index ccff187d..8ed81849 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -37,26 +37,22 @@ namespace NadekoBot.Modules.Administration public bool CheckUserRatelimit(ulong id) { - RatelimitedUser usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id }); + var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id }); if (usr.MessageCount == MaxMessages) { return true; } - else + usr.MessageCount++; + var _ = Task.Run(async () => { - usr.MessageCount++; - var t = Task.Run(async () => + try { - try - { - await Task.Delay(PerSeconds * 1000, cancelSource.Token); - } - catch (OperationCanceledException) { } - usr.MessageCount--; - }); - return false; - } - + await Task.Delay(PerSeconds * 1000, cancelSource.Token); + } + catch (OperationCanceledException) { } + usr.MessageCount--; + }); + return false; } } diff --git a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs index 6c1be19e..7c58fbfd 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs @@ -18,10 +18,10 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class ServerGreetCommands : ModuleBase + public class ServerGreetCommands : NadekoSubmodule { //make this to a field in the guildconfig table - class GreetSettings + private class GreetSettings { public int AutoDeleteGreetMessagesTimer { get; set; } public int AutoDeleteByeMessagesTimer { get; set; } @@ -129,7 +129,10 @@ namespace NadekoBot.Modules.Administration catch (Exception ex) { _log.Warn(ex); } } } - catch { } + catch + { + // ignored + } }); return Task.CompletedTask; } @@ -212,7 +215,10 @@ namespace NadekoBot.Modules.Administration } } } - catch { } + catch + { + // ignored + } }); return Task.CompletedTask; } @@ -222,7 +228,6 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.ManageGuild)] public async Task GreetDel(int timer = 30) { - var channel = (ITextChannel)Context.Channel; if (timer < 0 || timer > 600) return; @@ -375,7 +380,7 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendConfirmAsync("🆗 New DM greet message **set**.").ConfigureAwait(false); if (!sendGreetEnabled) - await Context.Channel.SendConfirmAsync($"ℹ️ Enable DM greet messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}greetdm`").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"ℹ️ Enable DM greet messsages by typing `{Prefix}greetdm`").ConfigureAwait(false); } public static bool SetGreetDmMessage(ulong guildId, ref string message) @@ -450,7 +455,7 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendConfirmAsync("🆗 New bye message **set**.").ConfigureAwait(false); if (!sendByeEnabled) - await Context.Channel.SendConfirmAsync($"ℹ️ Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync($"ℹ️ Enable bye messsages by typing `{Prefix}bye`").ConfigureAwait(false); } public static bool SetByeMessage(ulong guildId, ref string message) diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs index 392b4e88..dba5bb3a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -30,10 +30,8 @@ namespace NadekoBot.Modules.Administration { var _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); - using (var uow = DbHandler.UnitOfWork()) - { - voicePlusTextCache = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId)); - } + + voicePlusTextCache = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId)); NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler; sw.Stop(); diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 1ed31a6e..37f07efa 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -23,12 +23,8 @@ namespace NadekoBot.Modules.ClashOfClans private static Timer checkWarTimer { get; } - private static new readonly Logger _log; - static ClashOfClans() { - _log = LogManager.GetCurrentClassLogger(); - var sw = Stopwatch.StartNew(); using (var uow = DbHandler.UnitOfWork()) { ClashWars = new ConcurrentDictionary>( @@ -244,7 +240,6 @@ namespace NadekoBot.Modules.ClashOfClans SaveWar(war); await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false); - var size = warsInfo.Item1[warsInfo.Item2].Size; warsInfo.Item1.RemoveAt(warsInfo.Item2); } diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index 295d7e37..ebeb4922 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -165,7 +165,7 @@ namespace NadekoBot.Modules.CustomReactions } else { - var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id, + GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { cr }, (k, old) => { diff --git a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index f2b6404c..78de87e6 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { [Group] - public class AnimalRacing : ModuleBase + public class AnimalRacing : NadekoSubmodule { public static ConcurrentDictionary AnimalRaces { get; } = new ConcurrentDictionary(); @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Gambling [RequireContext(ContextType.Guild)] public async Task Race() { - var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel); + var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix); if (ar.Fail) await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false); @@ -59,13 +59,16 @@ namespace NadekoBot.Modules.Gambling public List participants = new List(); private ulong serverId; private int messagesSinceGameStarted = 0; + private readonly string _prefix; + private Logger _log { get; } public ITextChannel raceChannel { get; set; } public bool Started { get; private set; } = false; - public AnimalRace(ulong serverId, ITextChannel ch) + public AnimalRace(ulong serverId, ITextChannel ch, string prefix) { + this._prefix = prefix; this._log = LogManager.GetCurrentClassLogger(); this.serverId = serverId; this.raceChannel = ch; @@ -74,11 +77,8 @@ namespace NadekoBot.Modules.Gambling Fail = true; return; } - - using (var uow = DbHandler.UnitOfWork()) - { - animals = new ConcurrentQueue(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle()); - } + + animals = new ConcurrentQueue(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle()); var cancelSource = new CancellationTokenSource(); @@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Gambling try { await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.", - footer: $"Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race."); + footer: $"Type {_prefix}jr to join the race."); } catch (Exception ex) { @@ -280,9 +280,7 @@ namespace NadekoBot.Modules.Gambling public override bool Equals(object obj) { var p = obj as Participant; - return p == null ? - false : - p.User == User; + return p != null && p.User == User; } public override string ToString() diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index 5647b3e4..02c8803e 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -43,22 +43,15 @@ namespace NadekoBot.Modules.Gambling [OwnerOnly] public async Task StartEvent(CurrencyEvent e, int arg = -1) { - var channel = (ITextChannel)Context.Channel; - try + switch (e) { - switch (e) - { - case CurrencyEvent.FlowerReaction: - await FlowerReactionEvent(Context).ConfigureAwait(false); - break; - case CurrencyEvent.SneakyGameStatus: - await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false); - break; - default: - break; - } + case CurrencyEvent.FlowerReaction: + await FlowerReactionEvent(Context).ConfigureAwait(false); + break; + case CurrencyEvent.SneakyGameStatus: + await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false); + break; } - catch { } } public static async Task SneakyGameStatusEvent(CommandContext Context, int? arg) @@ -77,8 +70,7 @@ namespace NadekoBot.Modules.Gambling { _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; } - - var game = NadekoBot.Client.Game?.Name; + await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName) .ConfigureAwait(false); try @@ -88,7 +80,10 @@ namespace NadekoBot.Modules.Gambling $"Lasts {num} seconds. Don't tell anyone. Shhh.") .ConfigureAwait(false); } - catch { } + catch + { + // ignored + } NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler; @@ -114,15 +109,18 @@ namespace NadekoBot.Modules.Gambling .ConfigureAwait(false); try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } }); } return Task.Delay(0); } - public static Task FlowerReactionEvent(CommandContext Context) => - new FlowerReactionEvent().Start(Context); + public static Task FlowerReactionEvent(CommandContext context) => + new FlowerReactionEvent().Start(context); } } @@ -163,7 +161,7 @@ namespace NadekoBot.Modules.Gambling if (msg?.Id == id) { _log.Warn("Stopping flower reaction event because message is deleted."); - Task.Run(() => End()); + var __ = Task.Run(End); } return Task.CompletedTask; @@ -194,10 +192,14 @@ namespace NadekoBot.Modules.Gambling { if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) { - try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { } + await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false) + .ConfigureAwait(false); } } - catch { } + catch + { + // ignored + } })) { try diff --git a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs index 989d3789..bd977e9b 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs @@ -260,7 +260,7 @@ namespace NadekoBot.Modules.Gambling } finally { - var t = Task.Run(async () => + var _ = Task.Run(async () => { await Task.Delay(2000); runningUsers.Remove(Context.User.Id); diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 1d1f414f..1426bebd 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -197,8 +197,6 @@ namespace NadekoBot.Modules.Gambling [RequireContext(ContextType.Guild)] public async Task Divorce([Remainder]IUser target) { - var channel = (ITextChannel)Context.Channel; - if (target.Id == Context.User.Id) return; diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 555f55cb..6bcf5654 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -103,7 +103,6 @@ namespace NadekoBot.Modules.Gambling [Priority(0)] public async Task Award(int amount, [Remainder] IRole role) { - var channel = (ITextChannel)Context.Channel; var users = (await Context.Guild.GetUsersAsync()) .Where(u => u.GetRoles().Contains(role)) .ToList(); diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index b6544256..0541a4ae 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -21,12 +21,6 @@ namespace NadekoBot.Modules.Games { private static Logger _log { get; } - class CleverAnswer - { - public string Status { get; set; } - public string Response { get; set; } - } - public static ConcurrentDictionary> CleverbotGuilds { get; } = new ConcurrentDictionary>(); static CleverBotCommands() @@ -34,14 +28,12 @@ namespace NadekoBot.Modules.Games _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); - using (var uow = DbHandler.UnitOfWork()) - { - var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT); - CleverbotGuilds = new ConcurrentDictionary>( - NadekoBot.AllGuildConfigs - .Where(gc => gc.CleverbotEnabled) - .ToDictionary(gc => gc.GuildId, gc => new Lazy(() => bot.CreateSession(), true))); - } + + var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT); + CleverbotGuilds = new ConcurrentDictionary>( + NadekoBot.AllGuildConfigs + .Where(gc => gc.CleverbotEnabled) + .ToDictionary(gc => gc.GuildId, gc => new Lazy(() => bot.CreateSession(), true))); sw.Stop(); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index 4d499301..83637a62 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -23,7 +23,8 @@ namespace NadekoBot.Modules.Games static HangmanCommands() { _log = LogManager.GetCurrentClassLogger(); - typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys); + typesStr = + string.Format("`List of \"{0}hangman\" term types:`\n", NadekoBot.ModulePrefixes[typeof(Games).Name]) + String.Join(", ", HangmanTermPool.data.Keys); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index f664a7f2..43cbf52f 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games /// https://discord.gg/0TYNJfCU4De7YIk8 /// [Group] - public class PlantPickCommands : ModuleBase + public class PlantPickCommands : NadekoSubmodule { private static ConcurrentHashSet generationChannels { get; } = new ConcurrentHashSet(); //channelid/message @@ -103,7 +103,8 @@ namespace NadekoBot.Modules.Games var sent = await channel.SendFileAsync( fileStream, file.Key, - $"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") + string.Format("❗ {0} Pick it up by typing `{1}pick`", firstPart, + NadekoBot.ModulePrefixes[typeof(Games).Name])) .ConfigureAwait(false); msgs[0] = sent; @@ -160,7 +161,7 @@ namespace NadekoBot.Modules.Games var imgData = GetRandomCurrencyImage(); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); - var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; + var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {Prefix}pick"; IUserMessage msg; using (var toSend = imgData.Value.ToStream()) diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index 4879793b..de36fe3b 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -307,7 +307,7 @@ namespace NadekoBot.Modules.Games var del2 = previousMessage?.DeleteAsync(); try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { } try { await del1; } catch { } - try { await del2; } catch { } + try { if (del2 != null) await del2; } catch { } }); curUserIndex ^= 1; diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index e0dbb2c3..b10869b8 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Games { if (string.IsNullOrWhiteSpace(question)) return; - var rng = new NadekoRandom(); await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false)) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index bae06919..fa7012fd 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -415,27 +415,26 @@ namespace NadekoBot.Modules.Music var arg = directory; if (string.IsNullOrWhiteSpace(arg)) return; - try + var dir = new DirectoryInfo(arg); + var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories) + .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); + var gusr = (IGuildUser)Context.User; + foreach (var file in fileEnum) { - var dir = new DirectoryInfo(arg); - var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories) - .Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); - var gusr = (IGuildUser)Context.User; - foreach (var file in fileEnum) + try { - try - { - await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); - } - catch (PlaylistFullException) - { - break; - } - catch { } + await QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); + } + catch (PlaylistFullException) + { + break; + } + catch + { + // ignored } - await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false); } - catch { } + await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs index fbb41acd..80b8067b 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs @@ -101,23 +101,23 @@ namespace NadekoBot.Modules.Permissions { return true; } - else + activeCdsForGuild.Add(new ActiveCooldown() { - activeCdsForGuild.Add(new ActiveCooldown() + UserId = user.Id, + Command = cmd.Aliases.First().ToLowerInvariant(), + }); + var _ = Task.Run(async () => + { + try { - UserId = user.Id, - Command = cmd.Aliases.First().ToLowerInvariant(), - }); - var t = Task.Run(async () => + await Task.Delay(cdRule.Seconds * 1000); + activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id); + } + catch { - try - { - await Task.Delay(cdRule.Seconds * 1000); - activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id); - } - catch { } - }); - } + // ignored + } + }); } return false; } diff --git a/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs index 2d0376e1..2576ac78 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs @@ -42,23 +42,19 @@ namespace NadekoBot.Modules.Permissions static FilterCommands() { - using (var uow = DbHandler.UnitOfWork()) - { - var guildConfigs = NadekoBot.AllGuildConfigs; + var guildConfigs = NadekoBot.AllGuildConfigs; - InviteFilteringServers = new ConcurrentHashSet(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId)); - InviteFilteringChannels = new ConcurrentHashSet(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId))); + InviteFilteringServers = new ConcurrentHashSet(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId)); + InviteFilteringChannels = new ConcurrentHashSet(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId))); - var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet(gc.FilteredWords.Select(fw => fw.Word))); + var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet(gc.FilteredWords.Select(fw => fw.Word))); - ServerFilteredWords = new ConcurrentDictionary>(dict); + ServerFilteredWords = new ConcurrentDictionary>(dict); - var serverFiltering = guildConfigs.Where(gc => gc.FilterWords); - WordFilteringServers = new ConcurrentHashSet(serverFiltering.Select(gc => gc.GuildId)); + var serverFiltering = guildConfigs.Where(gc => gc.FilterWords); + WordFilteringServers = new ConcurrentHashSet(serverFiltering.Select(gc => gc.GuildId)); - WordFilteringChannels = new ConcurrentHashSet(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId))); - - } + WordFilteringChannels = new ConcurrentHashSet(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId))); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index c1d2eef9..bf05f0ac 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Searches var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); using (var http = new HttpClient()) { - var res = await http.GetStringAsync("http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false); + var res = await http.GetStringAsync(link + $"?access_token={anilistToken}").ConfigureAwait(false); var smallObj = JArray.Parse(res)[0]; var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs index e70381b2..77e88289 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs @@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Searches var rankimg = $"{model.Competitive.rank_img}"; var rank = $"{model.Competitive.rank}"; - var competitiveplay = $"{model.Games.Competitive.played}"; + //var competitiveplay = $"{model.Games.Competitive.played}"; if (string.IsNullOrWhiteSpace(rank)) { var embed = new EmbedBuilder() diff --git a/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs index af8a1fb3..bd22733b 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs @@ -12,7 +12,8 @@ namespace NadekoBot.Modules.Searches [Group] public class PlaceCommands : ModuleBase { - private static string typesStr { get; } = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Searches).Name]}place\" tags:`\n" + String.Join(", ", Enum.GetNames(typeof(PlaceType))); + private static string typesStr { get; } = + string.Format("`List of \"{0}place\" tags:`\n", NadekoBot.ModulePrefixes[typeof(Searches).Name]) + String.Join(", ", Enum.GetNames(typeof(PlaceType))); public enum PlaceType { diff --git a/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs b/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs index 3fc7a34b..86411634 100644 --- a/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs @@ -103,19 +103,23 @@ namespace NadekoBot.Modules.Searches oldStatus.IsLive != newStatus.IsLive) { var server = NadekoBot.Client.GetGuild(fs.GuildId); - if (server == null) - return; - var channel = server.GetTextChannel(fs.ChannelId); + var channel = server?.GetTextChannel(fs.ChannelId); if (channel == null) return; try { var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } } - catch { } + catch + { + // ignored + } })); FirstPass = false; diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index d612299c..b250e57f 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -499,12 +499,12 @@ namespace NadekoBot.Modules.Searches var data = JsonConvert.DeserializeObject(res); - var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0]; + var sense = data.Results.FirstOrDefault(x => x.Senses?[0].Definition != null)?.Senses[0]; if (sense?.Definition == null) return; - string definition = sense.Definition.ToString(); + var definition = sense.Definition.ToString(); if (!(sense.Definition is string)) definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString(); @@ -536,7 +536,7 @@ namespace NadekoBot.Modules.Searches } await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - string res = ""; + var res = ""; using (var http = new HttpClient()) { http.DefaultRequestHeaders.Clear(); @@ -548,7 +548,7 @@ namespace NadekoBot.Modules.Searches { var items = JObject.Parse(res); var item = items["defs"]["def"]; - var hashtag = item["hashtag"].ToString(); + //var hashtag = item["hashtag"].ToString(); var link = item["uri"].ToString(); var desc = item["text"].ToString(); await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 8131bf16..0eedba24 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Utility var voicechn = (await guild.GetVoiceChannelsAsync()).Count(); var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22); - var sb = new StringBuilder(); var users = await guild.GetUsersAsync().ConfigureAwait(false); var features = string.Join("\n", guild.Features); if (string.IsNullOrWhiteSpace(features)) @@ -45,13 +44,13 @@ namespace NadekoBot.Modules.Utility .AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) + .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true)) .WithImageUrl(guild.IconUrl) .WithColor(NadekoBot.OkColor); - if (guild.Emojis.Count() > 0) + if (guild.Emojis.Any()) { embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); } @@ -71,7 +70,7 @@ namespace NadekoBot.Modules.Utility .WithTitle(ch.Name) .WithDescription(ch.Topic?.SanitizeMentions()) .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) + .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) .AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true)) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); @@ -81,7 +80,6 @@ namespace NadekoBot.Modules.Utility [RequireContext(ContextType.Guild)] public async Task UserInfo(IGuildUser usr = null) { - var channel = (ITextChannel)Context.Channel; var user = usr ?? Context.User as IGuildUser; if (user == null) @@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Utility } embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) + .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) .WithColor(NadekoBot.OkColor); diff --git a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs index 1167cab7..e767bbcf 100644 --- a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs @@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Utility public void Reset() { source.Cancel(); - var t = Task.Run(Run); + var _ = Task.Run(Run); } public void Stop() diff --git a/src/NadekoBot/Modules/Utility/Commands/Remind.cs b/src/NadekoBot/Modules/Utility/Commands/Remind.cs index 94521dd6..10d43352 100644 --- a/src/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/src/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -45,15 +45,15 @@ namespace NadekoBot.Modules.Utility foreach (var r in reminders) { - try { var t = StartReminder(r); } catch (Exception ex) { _log.Warn(ex); } + Task.Run(() => StartReminder(r)); } } private static async Task StartReminder(Reminder r) { var now = DateTime.Now; - var twoMins = new TimeSpan(0, 2, 0); - TimeSpan time = r.When - now; + + var time = r.When - now; if (time.TotalMilliseconds > int.MaxValue) return; diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 1705d934..d627d956 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -468,7 +468,6 @@ namespace NadekoBot.Modules.Utility [OwnerOnly] public async Task SaveChat(int cnt) { - var sb = new StringBuilder(); var msgs = new List(cnt); await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false); diff --git a/src/NadekoBot/Services/Database/NadekoContext.cs b/src/NadekoBot/Services/Database/NadekoContext.cs index 102c6703..4efb6150 100644 --- a/src/NadekoBot/Services/Database/NadekoContext.cs +++ b/src/NadekoBot/Services/Database/NadekoContext.cs @@ -132,7 +132,7 @@ namespace NadekoBot.Services.Database { #region QUOTES - var quoteEntity = modelBuilder.Entity(); + //var quoteEntity = modelBuilder.Entity(); #endregion @@ -166,7 +166,7 @@ namespace NadekoBot.Services.Database #endregion #region BotConfig - var botConfigEntity = modelBuilder.Entity(); + //var botConfigEntity = modelBuilder.Entity(); //botConfigEntity // .HasMany(c => c.ModulePrefixes) // .WithOne(mp => mp.BotConfig) diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index d8995c59..7c6d2201 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -96,18 +96,21 @@ namespace NadekoBot.Services.Impl content.Headers.Clear(); content.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); - var res = await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false); + await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false); } }; } - catch { } + catch + { + // ignored + } }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); } public void Initialize() { - var guilds = this.client.GetGuilds(); - _textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count()); + var guilds = this.client.GetGuilds().ToArray(); + _textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel)); _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; } diff --git a/src/NadekoBot/Services/NadekoRandom.cs b/src/NadekoBot/Services/NadekoRandom.cs index 0d42c065..f1af50c1 100644 --- a/src/NadekoBot/Services/NadekoRandom.cs +++ b/src/NadekoBot/Services/NadekoRandom.cs @@ -41,7 +41,6 @@ namespace NadekoBot.Services return minValue; var bytes = new byte[sizeof(int)]; rng.GetBytes(bytes); - var num = BitConverter.ToInt32(bytes, 0); var sign = Math.Sign(BitConverter.ToInt32(bytes, 0)); return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue; } From b6d93c3105fe47314ed2a3197ed8d4ff8865ee23 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 14:49:21 +0100 Subject: [PATCH 098/256] Bot connecting speed improved by exactly 5 seconds. --- .../Modules/Administration/Commands/ServerGreetCommands.cs | 2 +- .../Modules/Games/Commands/PlantAndPickCommands.cs | 7 +------ src/NadekoBot/NadekoBot.cs | 6 +++++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs index 7c58fbfd..b3f79c83 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs @@ -53,7 +53,7 @@ namespace NadekoBot.Modules.Administration }; } - private static Logger _log { get; } + private new static Logger _log { get; } private static ConcurrentDictionary GuildConfigsCache { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 43cbf52f..ba98f5f1 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -37,13 +37,8 @@ namespace NadekoBot.Modules.Games //channelId/last generation private static ConcurrentDictionary lastGenerations { get; } = new ConcurrentDictionary(); - private static ConcurrentHashSet usersRecentlyPicked { get; } = new ConcurrentHashSet(); - - private static Logger _log { get; } - static PlantPickCommands() { - _log = LogManager.GetCurrentClassLogger(); #if !GLOBAL_NADEKO NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; @@ -116,7 +111,7 @@ namespace NadekoBot.Modules.Games } catch (Exception ex) { - _log.Warn(ex); + LogManager.GetCurrentClassLogger().Warn(ex); } }); return Task.CompletedTask; diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 5df52a6e..21752102 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using NadekoBot.Modules.Permissions; using NadekoBot.TypeReaders; using System.Collections.Concurrent; +using System.Diagnostics; using NadekoBot.Modules.Music; using NadekoBot.Services.Database.Models; using System.Resources; @@ -108,13 +109,16 @@ namespace NadekoBot CommandService.AddTypeReader(new ModuleTypeReader()); CommandService.AddTypeReader(new GuildTypeReader()); + + var sw = Stopwatch.StartNew(); //connect await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false); await Client.ConnectAsync().ConfigureAwait(false); //await Client.DownloadAllUsersAsync().ConfigureAwait(false); Stats.Initialize(); - _log.Info("Connected"); + sw.Stop(); + _log.Info("Connected in " + sw.Elapsed.TotalSeconds.ToString("F2")); //load commands and prefixes From e5d85dd81867bea149b12dcb839b8e038a43fc2a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 15:41:23 +0100 Subject: [PATCH 099/256] All administration submodules now inherit NadekoSubmodule, and woops, forgot to commit discord.net connection change --- Discord.Net | 2 +- .../Commands/AutoAssignRoleCommands.cs | 2 +- .../Commands/DMForwardCommands.cs | 5 +--- .../Commands/LocalizationCommands.cs | 2 +- .../Administration/Commands/LogCommand.cs | 2 +- .../Administration/Commands/Migration.cs | 2 +- .../Administration/Commands/MuteCommands.cs | 7 ++--- .../Commands/PlayingRotateCommands.cs | 2 +- .../Commands/ProtectionCommands.cs | 30 +++++++++---------- .../Commands/RatelimitCommand.cs | 2 +- .../Commands/SelfAssignedRolesCommand.cs | 2 +- .../Administration/Commands/SelfCommands.cs | 2 +- .../Commands/VoicePlusTextCommands.cs | 28 ++++++++--------- 13 files changed, 42 insertions(+), 46 deletions(-) diff --git a/Discord.Net b/Discord.Net index 9ce5c475..d2229228 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit 9ce5c4757efc6cb6bb8959e851abcdcbe03217be +Subproject commit d2229228b92117899d65cd549a1f2853057b255b diff --git a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs index 01c19fa8..3ebddf9d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class AutoAssignRoleCommands : ModuleBase + public class AutoAssignRoleCommands : NadekoSubmodule { private static Logger _log { get; } //guildid/roleid diff --git a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs index fe4a328b..9a9c4129 100644 --- a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs @@ -15,16 +15,13 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class DMForwardCommands : ModuleBase + public class DMForwardCommands : NadekoSubmodule { private static bool ForwardDMs { get; set; } private static bool ForwardDMsToAllOwners { get; set; } - - private static readonly Logger _log; static DMForwardCommands() { - _log = LogManager.GetCurrentClassLogger(); using (var uow = DbHandler.UnitOfWork()) { diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 684eebf9..70fcd6c6 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class LocalizationCommands : ModuleBase + public class LocalizationCommands : NadekoSubmodule { private ImmutableDictionary SupportedLocales { get; } = new Dictionary() { diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index c5183692..8c63191d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class LogCommands : ModuleBase + public class LogCommands : NadekoSubmodule { private const string clockEmojiUrl = "https://cdn.discordapp.com/attachments/155726317222887425/258309524966866945/clock.png"; diff --git a/src/NadekoBot/Modules/Administration/Commands/Migration.cs b/src/NadekoBot/Modules/Administration/Commands/Migration.cs index 883e5d4b..42798128 100644 --- a/src/NadekoBot/Modules/Administration/Commands/Migration.cs +++ b/src/NadekoBot/Modules/Administration/Commands/Migration.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class Migration : ModuleBase + public class Migration : NadekoSubmodule { private const int CURRENT_VERSION = 1; diff --git a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs index 89ef6ec6..362e4960 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs @@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class MuteCommands : ModuleBase + public class MuteCommands : NadekoSubmodule { private static ConcurrentDictionary GuildMuteRoles { get; } = new ConcurrentDictionary(); @@ -58,12 +58,11 @@ namespace NadekoBot.Modules.Administration if (muted == null || !muted.Contains(usr.Id)) return; - else - await MuteUser(usr).ConfigureAwait(false); + await MuteUser(usr).ConfigureAwait(false); } catch (Exception ex) { - _log.Warn(ex); + LogManager.GetCurrentClassLogger().Warn(ex); } } diff --git a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs index 371a6d6b..5b06c944 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs @@ -18,7 +18,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class PlayingRotateCommands : ModuleBase + public class PlayingRotateCommands : NadekoSubmodule { private static Logger _log { get; } public static List RotatingStatusMessages { get; } diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs index ff671a98..ed85c42d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -76,12 +76,12 @@ namespace NadekoBot.Modules.Administration } [Group] - public class ProtectionCommands : ModuleBase + public class ProtectionCommands : NadekoSubmodule { - private static ConcurrentDictionary antiRaidGuilds = + private static readonly ConcurrentDictionary _antiRaidGuilds = new ConcurrentDictionary(); // guildId | (userId|messages) - private static ConcurrentDictionary antiSpamGuilds = + private static readonly ConcurrentDictionary _antiSpamGuilds = new ConcurrentDictionary(); private static Logger _log { get; } @@ -98,11 +98,11 @@ namespace NadekoBot.Modules.Administration if (raid != null) { var raidStats = new AntiRaidStats() { AntiRaidSettings = raid }; - antiRaidGuilds.TryAdd(gc.GuildId, raidStats); + _antiRaidGuilds.TryAdd(gc.GuildId, raidStats); } if (spam != null) - antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam }); + _antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam }); } NadekoBot.Client.MessageReceived += (imsg) => @@ -119,7 +119,7 @@ namespace NadekoBot.Modules.Administration try { AntiSpamStats spamSettings; - if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || + if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore() { ChannelId = channel.Id @@ -151,7 +151,7 @@ namespace NadekoBot.Modules.Administration if (usr.IsBot) return Task.CompletedTask; AntiRaidStats settings; - if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) + if (!_antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) return Task.CompletedTask; if (!settings.RaidUsers.Add(usr)) return Task.CompletedTask; @@ -245,7 +245,7 @@ namespace NadekoBot.Modules.Administration } AntiRaidStats throwaway; - if (antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway)) + if (_antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway)) { using (var uow = DbHandler.UnitOfWork()) { @@ -281,7 +281,7 @@ namespace NadekoBot.Modules.Administration } }; - antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); + _antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); using (var uow = DbHandler.UnitOfWork()) { @@ -304,7 +304,7 @@ namespace NadekoBot.Modules.Administration return; AntiSpamStats throwaway; - if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway)) + if (_antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway)) { using (var uow = DbHandler.UnitOfWork()) { @@ -340,7 +340,7 @@ namespace NadekoBot.Modules.Administration } }; - antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); + _antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); using (var uow = DbHandler.UnitOfWork()) { @@ -376,7 +376,7 @@ namespace NadekoBot.Modules.Administration if (spam.IgnoredChannels.Add(obj)) { AntiSpamStats temp; - if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) + if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) temp.AntiSpamSettings.IgnoredChannels.Add(obj); added = true; } @@ -384,7 +384,7 @@ namespace NadekoBot.Modules.Administration { spam.IgnoredChannels.Remove(obj); AntiSpamStats temp; - if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) + if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) temp.AntiSpamSettings.IgnoredChannels.Remove(obj); added = false; } @@ -403,10 +403,10 @@ namespace NadekoBot.Modules.Administration public async Task AntiList() { AntiSpamStats spam; - antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam); + _antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam); AntiRaidStats raid; - antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid); + _antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid); if (spam == null && raid == null) { diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index 8ed81849..5ece6c1d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class RatelimitCommand : ModuleBase + public class RatelimitCommand : NadekoSubmodule { public static ConcurrentDictionary RatelimitingChannels = new ConcurrentDictionary(); private static Logger _log { get; } diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index 1775e85e..73327f87 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class SelfAssignedRolesCommands : ModuleBase + public class SelfAssignedRolesCommands : NadekoSubmodule { [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index fb316beb..3361eff4 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - class SelfCommands : ModuleBase + public class SelfCommands : NadekoSubmodule { [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs index dba5bb3a..2a4c3152 100644 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -19,19 +19,20 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class VoicePlusTextCommands : ModuleBase + public class VoicePlusTextCommands : NadekoSubmodule { - private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); + private new static readonly Logger _log; - private static ConcurrentHashSet voicePlusTextCache { get; } + private static readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); - private static ConcurrentDictionary guildLockObjects = new ConcurrentDictionary(); + private static readonly ConcurrentHashSet _voicePlusTextCache; + private static readonly ConcurrentDictionary _guildLockObjects = new ConcurrentDictionary(); static VoicePlusTextCommands() { - var _log = LogManager.GetCurrentClassLogger(); + _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); - voicePlusTextCache = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId)); + _voicePlusTextCache = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId)); NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler; sw.Stop(); @@ -51,7 +52,7 @@ namespace NadekoBot.Modules.Administration if (before.VoiceChannel == after.VoiceChannel) return Task.CompletedTask; - if (!voicePlusTextCache.Contains(guild.Id)) + if (!_voicePlusTextCache.Contains(guild.Id)) return Task.CompletedTask; var _ = Task.Run(async () => @@ -71,13 +72,13 @@ namespace NadekoBot.Modules.Administration using (var uow = DbHandler.UnitOfWork()) { uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; - voicePlusTextCache.TryRemove(guild.Id); + _voicePlusTextCache.TryRemove(guild.Id); await uow.CompleteAsync().ConfigureAwait(false); } return; } - var semaphore = guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1)); + var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1)); try { @@ -109,8 +110,7 @@ namespace NadekoBot.Modules.Administration roleToAdd = await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false); ITextChannel textChannel = guild.TextChannels - .Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) - .FirstOrDefault(); + .FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()); if (textChannel == null) { var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); @@ -146,7 +146,7 @@ namespace NadekoBot.Modules.Administration } private static string GetChannelName(string voiceName) => - channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; + _channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; private static string GetRoleName(IVoiceChannel ch) => "nvoice-" + ch.Id; @@ -186,7 +186,7 @@ namespace NadekoBot.Modules.Administration } if (!isEnabled) { - voicePlusTextCache.TryRemove(guild.Id); + _voicePlusTextCache.TryRemove(guild.Id); foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice"))) { try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } @@ -201,7 +201,7 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendConfirmAsync("ℹ️ Successfuly **removed** voice + text feature.").ConfigureAwait(false); return; } - voicePlusTextCache.Add(guild.Id); + _voicePlusTextCache.Add(guild.Id); await Context.Channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false); } From f9c989692616ad1911f8a002b65fd53b2f18d1c2 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 15:53:51 +0100 Subject: [PATCH 100/256] clean MuteCommands.cs --- .../Modules/Administration/Administration.cs | 16 ++++------ .../Administration/Commands/MuteCommands.cs | 29 +++++++++---------- src/NadekoBot/NadekoBot.cs | 5 ++-- src/NadekoBot/NadekoBot.xproj.DotSettings | 2 ++ 4 files changed, 24 insertions(+), 28 deletions(-) create mode 100644 src/NadekoBot/NadekoBot.xproj.DotSettings diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 76b96ec4..21dceacc 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -22,9 +22,6 @@ namespace NadekoBot.Modules.Administration [NadekoModule("Administration", ".")] public partial class Administration : NadekoModule { - - private static ConcurrentDictionary GuildMuteRoles { get; } = new ConcurrentDictionary(); - private static ConcurrentHashSet DeleteMessagesOnCommand { get; } = new ConcurrentHashSet(); private new static Logger _log { get; } @@ -206,7 +203,7 @@ namespace NadekoBot.Modules.Administration return; } var roleName = args[0].ToUpperInvariant(); - var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault(); + var role = Context.Guild.Roles.FirstOrDefault(r => r.Name.ToUpperInvariant() == roleName); if (role == null) { @@ -513,7 +510,7 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendMessageAsync(send).ConfigureAwait(false); } - IGuild nadekoSupportServer; + IGuild _nadekoSupportServer; [NadekoCommand, Usage, Description, Aliases] public async Task Donators() { @@ -525,16 +522,13 @@ namespace NadekoBot.Modules.Administration } await Context.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); - nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); + _nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); - if (nadekoSupportServer == null) - return; - - var patreonRole = nadekoSupportServer.GetRole(236667642088259585); + var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585); if (patreonRole == null) return; - var usrs = (await nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u)); + var usrs = (await _nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u)); await Context.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs index 362e4960..8dac31eb 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs @@ -8,8 +8,6 @@ using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -20,9 +18,8 @@ namespace NadekoBot.Modules.Administration [Group] public class MuteCommands : NadekoSubmodule { - private static ConcurrentDictionary GuildMuteRoles { get; } = new ConcurrentDictionary(); - - private static ConcurrentDictionary> MutedUsers { get; } = new ConcurrentDictionary>(); + private static ConcurrentDictionary guildMuteRoles { get; } + private static ConcurrentDictionary> mutedUsers { get; } public static event Action UserMuted = delegate { }; public static event Action UserUnmuted = delegate { }; @@ -37,11 +34,11 @@ namespace NadekoBot.Modules.Administration static MuteCommands() { var configs = NadekoBot.AllGuildConfigs; - GuildMuteRoles = new ConcurrentDictionary(configs + guildMuteRoles = new ConcurrentDictionary(configs .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName)) .ToDictionary(c => c.GuildId, c => c.MuteRoleName)); - MutedUsers = new ConcurrentDictionary>(configs.ToDictionary( + mutedUsers = new ConcurrentDictionary>(configs.ToDictionary( k => k.GuildId, v => new ConcurrentHashSet(v.MutedUsers.Select(m => m.UserId)) )); @@ -54,7 +51,7 @@ namespace NadekoBot.Modules.Administration try { ConcurrentHashSet muted; - MutedUsers.TryGetValue(usr.Guild.Id, out muted); + mutedUsers.TryGetValue(usr.Guild.Id, out muted); if (muted == null || !muted.Contains(usr.Id)) return; @@ -79,7 +76,7 @@ namespace NadekoBot.Modules.Administration UserId = usr.Id }); ConcurrentHashSet muted; - if (MutedUsers.TryGetValue(usr.Guild.Id, out muted)) + if (mutedUsers.TryGetValue(usr.Guild.Id, out muted)) muted.Add(usr.Id); await uow.CompleteAsync().ConfigureAwait(false); @@ -99,18 +96,18 @@ namespace NadekoBot.Modules.Administration UserId = usr.Id }); ConcurrentHashSet muted; - if (MutedUsers.TryGetValue(usr.Guild.Id, out muted)) + if (mutedUsers.TryGetValue(usr.Guild.Id, out muted)) muted.TryRemove(usr.Id); await uow.CompleteAsync().ConfigureAwait(false); } UserUnmuted(usr, MuteType.All); } - public static async Task GetMuteRole(IGuild guild) + public static async TaskGetMuteRole(IGuild guild) { const string defaultMuteRoleName = "nadeko-mute"; - var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName); + var muteRoleName = guildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName); var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName); if (muteRole == null) @@ -132,7 +129,10 @@ namespace NadekoBot.Modules.Administration await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny)) .ConfigureAwait(false); } - catch { } + catch + { + // ignored + } await Task.Delay(200).ConfigureAwait(false); } } @@ -145,7 +145,6 @@ namespace NadekoBot.Modules.Administration [Priority(1)] public async Task SetMuteRole([Remainder] string name) { - //var channel = (ITextChannel)Context.Channel; name = name.Trim(); if (string.IsNullOrWhiteSpace(name)) return; @@ -154,7 +153,7 @@ namespace NadekoBot.Modules.Administration { var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); config.MuteRoleName = name; - GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name); + guildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name); await uow.CompleteAsync().ConfigureAwait(false); } await Context.Channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false); diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 21752102..40d71602 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using NadekoBot.Modules.Permissions; using NadekoBot.TypeReaders; using System.Collections.Concurrent; +using System.Collections.Immutable; using System.Diagnostics; using NadekoBot.Modules.Music; using NadekoBot.Services.Database.Models; @@ -44,7 +45,7 @@ namespace NadekoBot public static ConcurrentDictionary ModulePrefixes { get; private set; } public static bool Ready { get; private set; } - public static IEnumerable AllGuildConfigs { get; } + public static ImmutableArray AllGuildConfigs { get; } public static BotConfig BotConfig { get; } static NadekoBot() @@ -54,7 +55,7 @@ namespace NadekoBot using (var uow = DbHandler.UnitOfWork()) { - AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(); + AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs().ToImmutableArray(); BotConfig = uow.BotConfig.GetOrCreate(); OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); diff --git a/src/NadekoBot/NadekoBot.xproj.DotSettings b/src/NadekoBot/NadekoBot.xproj.DotSettings new file mode 100644 index 00000000..ae4d5dac --- /dev/null +++ b/src/NadekoBot/NadekoBot.xproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file From 8c27082c14017eff04a6b07274eb42943cb40d58 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 16:36:02 +0100 Subject: [PATCH 101/256] localized mute commands --- .../Administration/Commands/MuteCommands.cs | 28 ++++---- .../Resources/ResponseStrings.Designer.cs | 72 +++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 24 +++++++ 3 files changed, 110 insertions(+), 14 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs index 8dac31eb..5f67b3dd 100644 --- a/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/MuteCommands.cs @@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Administration guildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false); + await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -174,12 +174,12 @@ namespace NadekoBot.Modules.Administration { try { - await MuteUser(user).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"🔇 **{user}** has been **muted** from text and voice chat.").ConfigureAwait(false); + await MuteUser(user).ConfigureAwait(false); + await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + await ReplyErrorLocalized("mute_error").ConfigureAwait(false); } } @@ -192,11 +192,11 @@ namespace NadekoBot.Modules.Administration try { await UnmuteUser(user).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"🔉 **{user}** has been **unmuted** from text and voice chat.").ConfigureAwait(false); + await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + await ReplyErrorLocalized("mute_error").ConfigureAwait(false); } } @@ -209,11 +209,11 @@ namespace NadekoBot.Modules.Administration { await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false); UserMuted(user, MuteType.Chat); - await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false); + await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + await ReplyErrorLocalized("mute_error").ConfigureAwait(false); } } @@ -226,11 +226,11 @@ namespace NadekoBot.Modules.Administration { await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false); UserUnmuted(user, MuteType.Chat); - await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false); + await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + await ReplyErrorLocalized("mute_error").ConfigureAwait(false); } } @@ -243,11 +243,11 @@ namespace NadekoBot.Modules.Administration { await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false); UserMuted(user, MuteType.Voice); - await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false); + await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + await ReplyErrorLocalized("mute_error").ConfigureAwait(false); } } @@ -260,11 +260,11 @@ namespace NadekoBot.Modules.Administration { await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false); UserUnmuted(user, MuteType.Voice); - await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false); + await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + await ReplyErrorLocalized("mute_error").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 975e2331..493016ee 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -59,6 +59,78 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to I don't have the permission necessary for that most likely.. + /// + public static string administration_mute_error { + get { + return ResourceManager.GetString("administration_mute_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New mute role set.. + /// + public static string administration_mute_role_set { + get { + return ResourceManager.GetString("administration_mute_role_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **muted** from chatting.. + /// + public static string administration_user_chat_mute { + get { + return ResourceManager.GetString("administration_user_chat_mute", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **unmuted** from chatting.. + /// + public static string administration_user_chat_unmute { + get { + return ResourceManager.GetString("administration_user_chat_unmute", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **muted** from text and voice chat.. + /// + public static string administration_user_muted { + get { + return ResourceManager.GetString("administration_user_muted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **unmuted** from text and voice chat.. + /// + public static string administration_user_unmuted { + get { + return ResourceManager.GetString("administration_user_unmuted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **voice muted**.. + /// + public static string administration_user_voice_mute { + get { + return ResourceManager.GetString("administration_user_voice_mute", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **voice unmuted**.. + /// + public static string administration_user_voice_unmute { + get { + return ResourceManager.GetString("administration_user_voice_unmute", resourceCulture); + } + } + /// /// Looks up a localized string similar to That base is already claimed or destroyed.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 5285f3b6..480a91f0 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -298,6 +298,30 @@ You fainted, so you are not able to move! + + I don't have the permission necessary for that most likely. + + + New mute role set. + + + {0} has been **muted** from chatting. + + + {0} has been **unmuted** from chatting. + + + {0} has been **muted** from text and voice chat. + + + {0} has been **unmuted** from text and voice chat. + + + {0} has been **voice muted**. + + + {0} has been **voice unmuted**. + Back to ToC From 45fb8b925d6d9d695af1421d11e7330d4294bd41 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 16:55:59 +0100 Subject: [PATCH 102/256] .aar cleaned up, localized --- .../Commands/AutoAssignRoleCommands.cs | 27 ++++++++----------- .../Resources/ResponseStrings.Designer.cs | 18 +++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 6 +++++ 3 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs index 3ebddf9d..98a8844f 100644 --- a/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/AutoAssignRoleCommands.cs @@ -1,13 +1,10 @@ using Discord; using Discord.Commands; using NadekoBot.Attributes; -using NadekoBot.Extensions; using NadekoBot.Services; -using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Concurrent; -using System.Diagnostics; using System.Linq; using System.Threading.Tasks; @@ -18,22 +15,21 @@ namespace NadekoBot.Modules.Administration [Group] public class AutoAssignRoleCommands : NadekoSubmodule { - private static Logger _log { get; } //guildid/roleid - private static ConcurrentDictionary AutoAssignedRoles { get; } + private static ConcurrentDictionary autoAssignedRoles { get; } static AutoAssignRoleCommands() { - _log = LogManager.GetCurrentClassLogger(); + var log = LogManager.GetCurrentClassLogger(); - AutoAssignedRoles = new ConcurrentDictionary(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0) + autoAssignedRoles = new ConcurrentDictionary(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0) .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId)); NadekoBot.Client.UserJoined += async (user) => { try { - ulong roleId = 0; - AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId); + ulong roleId; + autoAssignedRoles.TryGetValue(user.Guild.Id, out roleId); if (roleId == 0) return; @@ -43,7 +39,7 @@ namespace NadekoBot.Modules.Administration if (role != null) await user.AddRolesAsync(role).ConfigureAwait(false); } - catch (Exception ex) { _log.Warn(ex); } + catch (Exception ex) { log.Warn(ex); } }; } @@ -52,20 +48,19 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.ManageRoles)] public async Task AutoAssignRole([Remainder] IRole role = null) { - GuildConfig conf; using (var uow = DbHandler.UnitOfWork()) { - conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); + var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); if (role == null) { conf.AutoAssignRoleId = 0; ulong throwaway; - AutoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway); + autoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway); } else { conf.AutoAssignRoleId = role.Id; - AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id); + autoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id); } await uow.CompleteAsync().ConfigureAwait(false); @@ -73,11 +68,11 @@ namespace NadekoBot.Modules.Administration if (role == null) { - await Context.Channel.SendConfirmAsync("🆗 **Auto assign role** on user join is now **disabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("aar_disabled").ConfigureAwait(false); return; } - await Context.Channel.SendConfirmAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("aar_enabled").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 493016ee..7d27eaf2 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -59,6 +59,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to **Auto assign role** on user join is now **disabled**.. + /// + public static string administration_aar_disabled { + get { + return ResourceManager.GetString("administration_aar_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to **Auto assign role** on user join is now **enabled**.. + /// + public static string administration_aar_enabled { + get { + return ResourceManager.GetString("administration_aar_enabled", resourceCulture); + } + } + /// /// Looks up a localized string similar to I don't have the permission necessary for that most likely.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 480a91f0..7eeb75f1 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -298,6 +298,12 @@ You fainted, so you are not able to move! + + **Auto assign role** on user join is now **disabled**. + + + **Auto assign role** on user join is now **enabled**. + I don't have the permission necessary for that most likely. From a89ca8d1859282577978fcaffbee3dbebba289a4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 17:15:53 +0100 Subject: [PATCH 103/256] dm foward commands cleaned up and localized --- .../Commands/DMForwardCommands.cs | 51 +++++++++++-------- .../Resources/ResponseStrings.Designer.cs | 45 ++++++++++++++++ src/NadekoBot/Services/CommandHandler.cs | 2 +- 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs index 9a9c4129..ef6d1231 100644 --- a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs @@ -4,10 +4,9 @@ using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; -using NLog; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Administration @@ -15,19 +14,21 @@ namespace NadekoBot.Modules.Administration public partial class Administration { [Group] - public class DMForwardCommands : NadekoSubmodule + public class DmForwardCommands : NadekoSubmodule { - private static bool ForwardDMs { get; set; } - private static bool ForwardDMsToAllOwners { get; set; } + private static volatile bool _forwardDMs; + private static volatile bool _forwardDMsToAllOwners; + + private static readonly object _locker = new object(); - static DMForwardCommands() + static DmForwardCommands() { using (var uow = DbHandler.UnitOfWork()) { var config = uow.BotConfig.GetOrCreate(); - ForwardDMs = config.ForwardMessages; - ForwardDMsToAllOwners = config.ForwardToAllOwners; + _forwardDMs = config.ForwardMessages; + _forwardDMsToAllOwners = config.ForwardToAllOwners; } } @@ -38,13 +39,14 @@ namespace NadekoBot.Modules.Administration using (var uow = DbHandler.UnitOfWork()) { var config = uow.BotConfig.GetOrCreate(); - ForwardDMs = config.ForwardMessages = !config.ForwardMessages; + lock(_locker) + _forwardDMs = config.ForwardMessages = !config.ForwardMessages; uow.Complete(); } - if (ForwardDMs) - await Context.Channel.SendConfirmAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false); + if (_forwardDMs) + await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false); + await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -54,22 +56,25 @@ namespace NadekoBot.Modules.Administration using (var uow = DbHandler.UnitOfWork()) { var config = uow.BotConfig.GetOrCreate(); - ForwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners; + lock(_locker) + _forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners; uow.Complete(); } - if (ForwardDMsToAllOwners) - await Context.Channel.SendConfirmAsync("ℹ️ **I will forward DMs to all owners.**").ConfigureAwait(false); + if (_forwardDMsToAllOwners) + await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ **I will forward DMs only to the first owner.**").ConfigureAwait(false); + await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false); } - public static async Task HandleDMForwarding(SocketMessage msg, List ownerChannels) + public static async Task HandleDmForwarding(SocketMessage msg, List ownerChannels) { - if (ForwardDMs && ownerChannels.Any()) + if (_forwardDMs && ownerChannels.Any()) { - var title = $"DM from [{msg.Author}]({msg.Author.Id})"; - if (ForwardDMsToAllOwners) + var title = + GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo, + typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})"; + if (_forwardDMsToAllOwners) { await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false); @@ -78,7 +83,11 @@ namespace NadekoBot.Modules.Administration { var firstOwnerChannel = ownerChannels.First(); if (firstOwnerChannel.Recipient.Id != msg.Author.Id) - try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } catch { } + try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } + catch + { + // ignored + } } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 7d27eaf2..c2109c7d 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -77,6 +77,51 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to DM from. + /// + public static string administration_dm_from { + get { + return ResourceManager.GetString("administration_dm_from", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will forward DMs to all owners.. + /// + public static string administration_fwall_start { + get { + return ResourceManager.GetString("administration_fwall_start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will forward DMs only to the first owner.. + /// + public static string administration_fwall_stop { + get { + return ResourceManager.GetString("administration_fwall_stop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will forward DMs from now on.. + /// + public static string administration_fwdm_start { + get { + return ResourceManager.GetString("administration_fwdm_start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will stop forwarding DMs from now on.. + /// + public static string administration_fwdm_stop { + get { + return ResourceManager.GetString("administration_fwdm_stop", resourceCulture); + } + } + /// /// Looks up a localized string similar to I don't have the permission necessary for that most likely.. /// diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index ce17de09..4dcfd461 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -273,7 +273,7 @@ namespace NadekoBot.Services await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); - await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false); + await DmForwardCommands.HandleDmForwarding(msg, ownerChannels).ConfigureAwait(false); } } } From 64d43fed748d47be09dfe066e64c0c2be8959db6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 17:22:57 +0100 Subject: [PATCH 104/256] moved dmfoward commands to selfcommands --- .../Commands/DMForwardCommands.cs | 96 ------------------- .../Administration/Commands/SelfCommands.cs | 86 ++++++++++++++++- src/NadekoBot/Services/CommandHandler.cs | 2 +- 3 files changed, 83 insertions(+), 101 deletions(-) delete mode 100644 src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs diff --git a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs b/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs deleted file mode 100644 index ef6d1231..00000000 --- a/src/NadekoBot/Modules/Administration/Commands/DMForwardCommands.cs +++ /dev/null @@ -1,96 +0,0 @@ -using Discord; -using Discord.Commands; -using Discord.WebSocket; -using NadekoBot.Attributes; -using NadekoBot.Extensions; -using NadekoBot.Services; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace NadekoBot.Modules.Administration -{ - public partial class Administration - { - [Group] - public class DmForwardCommands : NadekoSubmodule - { - private static volatile bool _forwardDMs; - private static volatile bool _forwardDMsToAllOwners; - - private static readonly object _locker = new object(); - - static DmForwardCommands() - { - - using (var uow = DbHandler.UnitOfWork()) - { - var config = uow.BotConfig.GetOrCreate(); - _forwardDMs = config.ForwardMessages; - _forwardDMsToAllOwners = config.ForwardToAllOwners; - } - } - - [NadekoCommand, Usage, Description, Aliases] - [OwnerOnly] - public async Task ForwardMessages() - { - using (var uow = DbHandler.UnitOfWork()) - { - var config = uow.BotConfig.GetOrCreate(); - lock(_locker) - _forwardDMs = config.ForwardMessages = !config.ForwardMessages; - uow.Complete(); - } - if (_forwardDMs) - await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false); - else - await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false); - } - - [NadekoCommand, Usage, Description, Aliases] - [OwnerOnly] - public async Task ForwardToAll() - { - using (var uow = DbHandler.UnitOfWork()) - { - var config = uow.BotConfig.GetOrCreate(); - lock(_locker) - _forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners; - uow.Complete(); - } - if (_forwardDMsToAllOwners) - await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false); - else - await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false); - - } - - public static async Task HandleDmForwarding(SocketMessage msg, List ownerChannels) - { - if (_forwardDMs && ownerChannels.Any()) - { - var title = - GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo, - typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})"; - if (_forwardDMsToAllOwners) - { - await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) - .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false); - } - else - { - var firstOwnerChannel = ownerChannels.First(); - if (firstOwnerChannel.Recipient.Id != msg.Author.Id) - try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } - catch - { - // ignored - } - } - } - } - } - } -} diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 3361eff4..b67f5d3e 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -3,11 +3,13 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using System; -using System.Diagnostics; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; +using Discord.WebSocket; +using NadekoBot.Services; namespace NadekoBot.Modules.Administration { @@ -16,6 +18,82 @@ namespace NadekoBot.Modules.Administration [Group] public class SelfCommands : NadekoSubmodule { + private static volatile bool _forwardDMs; + private static volatile bool _forwardDMsToAllOwners; + + private static readonly object _locker = new object(); + + static SelfCommands() + { + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.BotConfig.GetOrCreate(); + _forwardDMs = config.ForwardMessages; + _forwardDMsToAllOwners = config.ForwardToAllOwners; + } + } + + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task ForwardMessages() + { + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.BotConfig.GetOrCreate(); + lock (_locker) + _forwardDMs = config.ForwardMessages = !config.ForwardMessages; + uow.Complete(); + } + if (_forwardDMs) + await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false); + else + await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false); + } + + [NadekoCommand, Usage, Description, Aliases] + [OwnerOnly] + public async Task ForwardToAll() + { + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.BotConfig.GetOrCreate(); + lock (_locker) + _forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners; + uow.Complete(); + } + if (_forwardDMsToAllOwners) + await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false); + else + await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false); + + } + + public static async Task HandleDmForwarding(SocketMessage msg, List ownerChannels) + { + if (_forwardDMs && ownerChannels.Any()) + { + var title = + GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo, + typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})"; + if (_forwardDMsToAllOwners) + { + await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) + .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false); + } + else + { + var firstOwnerChannel = ownerChannels.First(); + if (firstOwnerChannel.Recipient.Id != msg.Author.Id) + try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } + catch + { + // ignored + } + } + } + } + + [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] public async Task ConnectShard(int shardid) @@ -148,7 +226,7 @@ namespace NadekoBot.Modules.Administration if (ids.Length != 2) return; var sid = ulong.Parse(ids[0]); - var server = NadekoBot.Client.GetGuilds().Where(s => s.Id == sid).FirstOrDefault(); + var server = NadekoBot.Client.GetGuilds().FirstOrDefault(s => s.Id == sid); if (server == null) return; @@ -156,7 +234,7 @@ namespace NadekoBot.Modules.Administration if (ids[1].ToUpperInvariant().StartsWith("C:")) { var cid = ulong.Parse(ids[1].Substring(2)); - var ch = server.TextChannels.Where(c => c.Id == cid).FirstOrDefault(); + var ch = server.TextChannels.FirstOrDefault(c => c.Id == cid); if (ch == null) { return; @@ -166,7 +244,7 @@ namespace NadekoBot.Modules.Administration else if (ids[1].ToUpperInvariant().StartsWith("U:")) { var uid = ulong.Parse(ids[1].Substring(2)); - var user = server.Users.Where(u => u.Id == uid).FirstOrDefault(); + var user = server.Users.FirstOrDefault(u => u.Id == uid); if (user == null) { return; diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 4dcfd461..a944a21e 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -273,7 +273,7 @@ namespace NadekoBot.Services await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); - await DmForwardCommands.HandleDmForwarding(msg, ownerChannels).ConfigureAwait(false); + await SelfCommands.HandleDmForwarding(msg, ownerChannels).ConfigureAwait(false); } } } From 97d32a46b6726452993756e806c98001acc28eed Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 17:49:15 +0100 Subject: [PATCH 105/256] localization commands localized :OOOO localiception! --- .../Commands/LocalizationCommands.cs | 35 ++++++------ .../Resources/ResponseStrings.Designer.cs | 55 +++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 34 ++++++++++++ 3 files changed, 105 insertions(+), 19 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 70fcd6c6..3b3467f6 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace NadekoBot.Modules.Administration @@ -17,7 +16,7 @@ namespace NadekoBot.Modules.Administration [Group] public class LocalizationCommands : NadekoSubmodule { - private ImmutableDictionary SupportedLocales { get; } = new Dictionary() + private ImmutableDictionary supportedLocales { get; } = new Dictionary() { {"en-US", "English, United States" }, {"sr-cyrl-rs", "Serbian, Cyrillic" } @@ -28,7 +27,8 @@ namespace NadekoBot.Modules.Administration public async Task LanguageSet() { var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild); - await Context.Channel.SendConfirmAsync("This server's language is set to " + Format.Bold(cul.ToString()) + " - " + Format.Bold(cul.NativeName)).ConfigureAwait(false); + await ReplyConfirmLocalized("lang_set_show", Format.Bold(cul.ToString()), Format.Bold(cul.NativeName)) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -36,9 +36,9 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.Administrator)] public async Task LanguageSet(string name) { - CultureInfo ci = null; try { + CultureInfo ci; if (name.Trim().ToLowerInvariant() == "default") { NadekoBot.Localization.RemoveGuildCulture(Context.Guild); @@ -50,12 +50,11 @@ namespace NadekoBot.Modules.Administration NadekoBot.Localization.SetGuildCulture(Context.Guild, ci); } - await Context.Channel.SendConfirmAsync($"Your server's locale is now {Format.Bold(ci.ToString())} - {Format.Bold(ci.NativeName)}.").ConfigureAwait(false); + await ReplyConfirmLocalized("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false); } - catch(Exception) { - - //_log.warn(ex); - await Context.Channel.SendConfirmAsync($"Failed setting locale. Revisit this command's help.").ConfigureAwait(false); + catch(Exception) + { + await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false); } } @@ -63,17 +62,16 @@ namespace NadekoBot.Modules.Administration public async Task LanguageSetDefault() { var cul = NadekoBot.Localization.DefaultCultureInfo; - await Context.Channel.SendConfirmAsync("Bot's language is set to " + cul + " - " + cul.NativeName).ConfigureAwait(false); - return; + await ReplyConfirmLocalized("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] public async Task LanguageSetDefault(string name) { - CultureInfo ci = null; try { + CultureInfo ci; if (name.Trim().ToLowerInvariant() == "default") { NadekoBot.Localization.ResetDefaultCulture(); @@ -84,22 +82,21 @@ namespace NadekoBot.Modules.Administration ci = new CultureInfo(name); NadekoBot.Localization.SetDefaultCulture(ci); } - - await Context.Channel.SendConfirmAsync($"Bot's default locale is now {Format.Bold(ci.ToString())} - {Format.Bold(ci.NativeName)}.").ConfigureAwait(false); + await ReplyConfirmLocalized("lang_set_bot", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false); } catch (Exception) { - //_log.warn(ex); - await Context.Channel.SendConfirmAsync($"Failed setting locale. Revisit this command's help.").ConfigureAwait(false); + await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [OwnerOnly] - public async Task LanguagesList(string name) + public async Task LanguagesList() { - await Context.Channel.SendConfirmAsync("List Of Languages", - string.Join("\n", SupportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}"))); + await ReplyConfirmLocalized("lang_list", + string.Join("\n", supportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}"))) + .ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index c2109c7d..48733a24 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -122,6 +122,61 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to List Of Languages + ///{0}. + /// + public static string administration_lang_list { + get { + return ResourceManager.GetString("administration_lang_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your server's locale is now {0} - {1}. + /// + public static string administration_lang_set { + get { + return ResourceManager.GetString("administration_lang_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bot's default locale is now {0} - {1}. + /// + public static string administration_lang_set_bot { + get { + return ResourceManager.GetString("administration_lang_set_bot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bot's language is set to {0} - {0}. + /// + public static string administration_lang_set_bot_show { + get { + return ResourceManager.GetString("administration_lang_set_bot_show", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed setting locale. Revisit this command's help.. + /// + public static string administration_lang_set_fail { + get { + return ResourceManager.GetString("administration_lang_set_fail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This server's language is set to {0} - {0}. + /// + public static string administration_lang_set_show { + get { + return ResourceManager.GetString("administration_lang_set_show", resourceCulture); + } + } + /// /// Looks up a localized string similar to I don't have the permission necessary for that most likely.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 7eeb75f1..ff566d6c 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -304,6 +304,40 @@ **Auto assign role** on user join is now **enabled**. + + DM from + + + I will forward DMs to all owners. + + + I will forward DMs only to the first owner. + + + I will forward DMs from now on. + + + I will stop forwarding DMs from now on. + + + List Of Languages +{0} + + + Your server's locale is now {0} - {1} + + + Bot's default locale is now {0} - {1} + + + Bot's language is set to {0} - {0} + + + Failed setting locale. Revisit this command's help. + + + This server's language is set to {0} - {0} + I don't have the permission necessary for that most likely. From be8ca3b45dff51d621eaabc8b5afde40087cfeb7 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 19:06:02 +0100 Subject: [PATCH 106/256] logging localized >.< --- .../Administration/Commands/LogCommand.cs | 267 +++++++---- src/NadekoBot/Modules/NadekoModule.cs | 18 +- .../Resources/ResponseStrings.Designer.cs | 450 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 155 ++++++ 4 files changed, 776 insertions(+), 114 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 8c63191d..3ad8c084 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -1,7 +1,6 @@ using Discord; using Discord.Commands; using Discord.WebSocket; -using Microsoft.EntityFrameworkCore; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Modules.Permissions; @@ -23,39 +22,37 @@ namespace NadekoBot.Modules.Administration [Group] public class LogCommands : NadekoSubmodule { - private const string clockEmojiUrl = "https://cdn.discordapp.com/attachments/155726317222887425/258309524966866945/clock.png"; - - private static DiscordShardedClient _client { get; } - private static Logger _log { get; } + private static DiscordShardedClient client { get; } + private new static Logger _log { get; } private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; private static string currentTime => $"{DateTime.Now:HH:mm:ss}"; public static ConcurrentDictionary GuildLogSettings { get; } - private static ConcurrentDictionary> PresenceUpdates { get; } = new ConcurrentDictionary>(); - private static Timer timerReference { get; } + private static ConcurrentDictionary> presenceUpdates { get; } = new ConcurrentDictionary>(); + private static readonly Timer _timerReference; static LogCommands() { - _client = NadekoBot.Client; + client = NadekoBot.Client; _log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); GuildLogSettings = new ConcurrentDictionary(NadekoBot.AllGuildConfigs .ToDictionary(g => g.GuildId, g => g.LogSetting)); - timerReference = new Timer(async (state) => + _timerReference = new Timer(async (state) => { try { - var keys = PresenceUpdates.Keys.ToList(); + var keys = presenceUpdates.Keys.ToList(); await Task.WhenAll(keys.Select(async key => { List messages; - if (PresenceUpdates.TryRemove(key, out messages)) - try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } + if (presenceUpdates.TryRemove(key, out messages)) + try { await key.SendConfirmAsync(key.Guild.GetLogText("presence_updates"), string.Join(Environment.NewLine, messages)); } catch { // ignored @@ -72,23 +69,22 @@ namespace NadekoBot.Modules.Administration _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); //_client.MessageReceived += _client_MessageReceived; - _client.MessageUpdated += _client_MessageUpdated; - _client.MessageDeleted += _client_MessageDeleted; - _client.UserBanned += _client_UserBanned; - _client.UserUnbanned += _client_UserUnbanned; - _client.UserJoined += _client_UserJoined; - _client.UserLeft += _client_UserLeft; - _client.UserPresenceUpdated += _client_UserPresenceUpdated; - _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; - _client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; - _client.GuildMemberUpdated += _client_GuildUserUpdated; + client.MessageUpdated += _client_MessageUpdated; + client.MessageDeleted += _client_MessageDeleted; + client.UserBanned += _client_UserBanned; + client.UserUnbanned += _client_UserUnbanned; + client.UserJoined += _client_UserJoined; + client.UserLeft += _client_UserLeft; + client.UserPresenceUpdated += _client_UserPresenceUpdated; + client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; + client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; + client.GuildMemberUpdated += _client_GuildUserUpdated; #if !GLOBAL_NADEKO - _client.UserUpdated += _client_UserUpdated; + client.UserUpdated += _client_UserUpdated; #endif - - _client.ChannelCreated += _client_ChannelCreated; - _client.ChannelDestroyed += _client_ChannelDestroyed; - _client.ChannelUpdated += _client_ChannelUpdated; + client.ChannelCreated += _client_ChannelCreated; + client.ChannelDestroyed += _client_ChannelDestroyed; + client.ChannelUpdated += _client_ChannelUpdated; MuteCommands.UserMuted += MuteCommands_UserMuted; MuteCommands.UserUnmuted += MuteCommands_UserUnmuted; @@ -119,7 +115,7 @@ namespace NadekoBot.Modules.Administration if (before.Username != after.Username) { - embed.WithTitle("👥 Username Changed") + embed.WithTitle("👥 " + g.GetLogText("username_changed")) .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true)) .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true)) @@ -128,7 +124,7 @@ namespace NadekoBot.Modules.Administration } else if (before.AvatarUrl != after.AvatarUrl) { - embed.WithTitle("👥 Avatar Changed") + embed.WithTitle("👥" + g.GetLogText("avatar_changed")) .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") .WithThumbnailUrl(before.AvatarUrl) @@ -190,15 +186,15 @@ namespace NadekoBot.Modules.Administration var str = ""; if (beforeVch?.Guild == afterVch?.Guild) { - str = $"{usr.Username} moved from {beforeVch?.Name} to {afterVch?.Name}"; + str = logChannel.Guild.GetLogText("moved", usr.Username, beforeVch?.Name, afterVch?.Name); } else if (beforeVch == null) { - str = $"{usr.Username} has joined {afterVch.Name}"; + str = logChannel.Guild.GetLogText("joined", usr.Username, afterVch.Name); } else if (afterVch == null) { - str = $"{usr.Username} has left {beforeVch.Name}"; + str = logChannel.Guild.GetLogText("left", usr.Username, beforeVch.Name); } var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false); toDelete.DeleteAfter(5); @@ -222,27 +218,31 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) return; var mutes = ""; + var mutedLocalized = logChannel.Guild.GetLogText("muted_sn"); switch (muteType) { case MuteCommands.MuteType.Voice: - mutes = "voice chat"; + mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_voice", mutedLocalized); break; case MuteCommands.MuteType.Chat: - mutes = "text chat"; + mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text", mutedLocalized); break; case MuteCommands.MuteType.All: - mutes = "text and voice chat"; + mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", mutedLocalized); break; } - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔇 User Muted from " + mutes)) + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithFooter(fb => fb.WithText(currentTime)) .WithOkColor(); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType) @@ -258,28 +258,32 @@ namespace NadekoBot.Modules.Administration if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) return; - string mutes = ""; + var mutes = ""; + var unmutedLocalized = logChannel.Guild.GetLogText("unmuted_sn"); switch (muteType) { case MuteCommands.MuteType.Voice: - mutes = "voice chat"; + mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_voice", unmutedLocalized); break; case MuteCommands.MuteType.Chat: - mutes = "text chat"; + mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text", unmutedLocalized); break; case MuteCommands.MuteType.All: - mutes = "text and voice chat"; + mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", unmutedLocalized); break; } - var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔊 User Unmuted from " + mutes)) + var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes)) .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithFooter(fb => fb.WithText($"{currentTime}")) .WithOkColor(); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection) @@ -298,28 +302,31 @@ namespace NadekoBot.Modules.Administration return; var punishment = ""; - if (action == PunishmentAction.Mute) + switch (action) { - punishment = "🔇 MUTED"; - } - else if (action == PunishmentAction.Kick) - { - punishment = "☣ SOFT-BANNED (KICKED)"; - } - else if (action == PunishmentAction.Ban) - { - punishment = "⛔️ BANNED"; + case PunishmentAction.Mute: + punishment = "🔇 " + logChannel.Guild.GetLogText("muted_pl").ToUpperInvariant(); + break; + case PunishmentAction.Kick: + punishment = "☣ " + logChannel.Guild.GetLogText("soft_banned_pl").ToUpperInvariant(); + break; + case PunishmentAction.Ban: + punishment = "⛔️ " + logChannel.Guild.GetLogText("banned_pl").ToUpperInvariant(); + break; } var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}")) - .WithTitle($"Users " + punishment) - .WithDescription(String.Join("\n", users.Select(u => u.ToString()))) + .WithTitle(logChannel.Guild.GetLogText("users") + " " + punishment) + .WithDescription(string.Join("\n", users.Select(u => u.ToString()))) .WithFooter(fb => fb.WithText($"{currentTime}")) .WithOkColor(); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) @@ -338,23 +345,23 @@ namespace NadekoBot.Modules.Administration .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); if (before.Nickname != after.Nickname) { - embed.WithAuthor(eab => eab.WithName("👥 Nickname Changed")) + embed.WithAuthor(eab => eab.WithName("👥 " + logChannel.Guild.GetLogText("nick_change"))) - .AddField(efb => efb.WithName("Old Nickname").WithValue($"{before.Nickname}#{before.Discriminator}")) - .AddField(efb => efb.WithName("New Nickname").WithValue($"{after.Nickname}#{after.Discriminator}")); + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_nick")).WithValue($"{before.Nickname}#{before.Discriminator}")) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_nick")).WithValue($"{after.Nickname}#{after.Discriminator}")); } else if (!before.RoleIds.SequenceEqual(after.RoleIds)) { if (before.RoleIds.Count < after.RoleIds.Count) { var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); - embed.WithAuthor(eab => eab.WithName("⚔ User's Role Added")) + embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_add"))) .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); } else if (before.RoleIds.Count > after.RoleIds.Count) { var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); - embed.WithAuthor(eab => eab.WithName("⚔ User's Role Removed")) + embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_rem"))) .WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); } } @@ -362,7 +369,10 @@ namespace NadekoBot.Modules.Administration return; await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) @@ -391,23 +401,26 @@ namespace NadekoBot.Modules.Administration if (before.Name != after.Name) { - embed.WithTitle("ℹ️ Channel Name Changed") + embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_name_change")) .WithDescription($"{after} | {after.Id}") - .AddField(efb => efb.WithName("Old Name").WithValue(before.Name)); + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("ch_old_name")).WithValue(before.Name)); } else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) { - embed.WithTitle("ℹ️ Channel Topic Changed") + embed.WithTitle("ℹ️ " + logChannel.Guild.GetLogText("ch_topic_change")) .WithDescription($"{after} | {after.Id}") - .AddField(efb => efb.WithName("Old Topic").WithValue(beforeTextChannel.Topic)) - .AddField(efb => efb.WithName("New Topic").WithValue(afterTextChannel.Topic)); + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_topic")).WithValue(beforeTextChannel?.Topic ?? "-")) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_topic")).WithValue(afterTextChannel?.Topic ?? "-")); } else return; await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async Task _client_ChannelDestroyed(IChannel ich) @@ -427,14 +440,23 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null) return; - + string title; + if (ch is IVoiceChannel) + { + title = logChannel.Guild.GetLogText("voice_chan_destroyed"); + } + else + title = logChannel.Guild.GetLogText("text_chan_destroyed"); await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed") + .WithTitle("🆕 " + title) .WithDescription($"{ch.Name} | {ch.Id}") .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async Task _client_ChannelCreated(IChannel ich) @@ -453,10 +475,16 @@ namespace NadekoBot.Modules.Administration ITextChannel logChannel; if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null) return; - + string title; + if (ch is IVoiceChannel) + { + title = logChannel.Guild.GetLogText("voice_chan_created"); + } + else + title = logChannel.Guild.GetLogText("text_chan_created"); await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created") + .WithTitle("🆕 " + title) .WithDescription($"{ch.Name} | {ch.Id}") .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } @@ -489,20 +517,29 @@ namespace NadekoBot.Modules.Administration string str = null; if (beforeVch?.Guild == afterVch?.Guild) { - str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel."; + str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vmoved", + "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), + Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? "")); } else if (beforeVch == null) { - str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has joined **{afterVch.Name}** voice channel."; + str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vjoined", + "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), + Format.Bold(afterVch.Name ?? "")); } else if (afterVch == null) { - str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel."; + str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft", + "👤" + Format.Code(prettyCurrentTime), "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), + Format.Bold(beforeVch.Name ?? "")); } if (str != null) - PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + presenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + } + catch + { + // ignored } - catch { } } private static async Task _client_UserPresenceUpdated(Optional optGuild, SocketUser usr, SocketPresence before, SocketPresence after) @@ -525,7 +562,10 @@ namespace NadekoBot.Modules.Administration return; string str = ""; if (before.Status != after.Status) - str = $"🎭`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**."; + str = "🎭" + Format.Code(prettyCurrentTime) + + logChannel.Guild.GetLogText("user_status_change", + "👤" + Format.Bold(usr.Username), + Format.Bold(after.Status.ToString())); //if (before.Game?.Name != after.Game?.Name) //{ @@ -534,9 +574,12 @@ namespace NadekoBot.Modules.Administration // str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**."; //} - PresenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + presenceUpdates.AddOrUpdate(logChannel, new List() { str }, (id, list) => { list.Add(str); return list; }); + } + catch + { + // ignored } - catch { } } private static async Task _client_UserLeft(IGuildUser usr) @@ -554,13 +597,16 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("❌ User Left") + .WithTitle("❌ " + logChannel.Guild.GetLogText("user_left")) .WithThumbnailUrl(usr.AvatarUrl) .WithDescription(usr.ToString()) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async Task _client_UserJoined(IGuildUser usr) @@ -578,7 +624,7 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("✅ User Joined") + .WithTitle("✅ " + logChannel.Guild.GetLogText("user_joined")) .WithThumbnailUrl(usr.AvatarUrl) .WithDescription($"{usr}") .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) @@ -602,7 +648,7 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("♻️ User Unbanned") + .WithTitle("♻️ " + logChannel.Guild.GetLogText("user_unbanned")) .WithThumbnailUrl(usr.AvatarUrl) .WithDescription(usr.ToString()) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) @@ -625,7 +671,7 @@ namespace NadekoBot.Modules.Administration return; await logChannel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithTitle("🚫 User Banned") + .WithTitle("🚫 " + logChannel.Guild.GetLogText("user_banned")) .WithThumbnailUrl(usr.AvatarUrl) .WithDescription(usr.ToString()) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) @@ -658,17 +704,20 @@ namespace NadekoBot.Modules.Administration return; var embed = new EmbedBuilder() .WithOkColor() - .WithTitle($"🗑 Message Deleted in #{((ITextChannel)msg.Channel).Name}") + .WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name)) .WithDescription($"{msg.Author}") - .AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); if (msg.Attachments.Any()) - embed.AddField(efb => efb.WithName("Attachments").WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false)); + embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false)); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } private static async Task _client_MessageUpdated(Optional optmsg, SocketMessage imsg2) @@ -702,16 +751,19 @@ namespace NadekoBot.Modules.Administration var embed = new EmbedBuilder() .WithOkColor() - .WithTitle($"📝 Message Updated in #{((ITextChannel)after.Channel).Name}") + .WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name)) .WithDescription(after.Author.ToString()) - .AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) - .AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } public enum LogType @@ -783,8 +835,6 @@ namespace NadekoBot.Modules.Administration case LogType.UserMuted: id = logSetting.UserMutedId; break; - default: - break; } if (!id.HasValue) @@ -855,8 +905,6 @@ namespace NadekoBot.Modules.Administration case LogType.VoicePresenceTTS: newLogSetting.LogVoicePresenceTTSId = null; break; - default: - break; } GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting); uow.Complete(); @@ -900,9 +948,9 @@ namespace NadekoBot.Modules.Administration await uow.CompleteAsync().ConfigureAwait(false); } if (action.Value) - await channel.SendConfirmAsync("Logging all events in this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("log_all").ConfigureAwait(false); else - await channel.SendConfirmAsync("Logging disabled.").ConfigureAwait(false); + await ReplyConfirmLocalized("log_disabled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -929,9 +977,9 @@ namespace NadekoBot.Modules.Administration } if (removed == 0) - await channel.SendConfirmAsync($"Logging will IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false); + await ReplyConfirmLocalized("log_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false); else - await channel.SendConfirmAsync($"Logging will NOT IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false); + await ReplyConfirmLocalized("log_not_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -940,7 +988,7 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task LogEvents() { - await Context.Channel.SendConfirmAsync("Log events you can subscribe to:", String.Join(", ", Enum.GetNames(typeof(LogType)).Cast())); + await ReplyConfirmLocalized("log_events", string.Join(", ", Enum.GetNames(typeof(LogType)).Cast())).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -1008,10 +1056,19 @@ namespace NadekoBot.Modules.Administration } if (channelId != null) - await channel.SendConfirmAsync($"Logging **{type}** event in this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("log", Format.Bold(type.ToString())).ConfigureAwait(false); else - await channel.SendConfirmAsync($"Stopped logging **{type}** event.").ConfigureAwait(false); + await ReplyConfirmLocalized("log_stop", Format.Bold(type.ToString())).ConfigureAwait(false); } } } + + public static class GuildExtensions + { + public static string GetLogText(this IGuild guild, string key, params object[] replacements) + => NadekoModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(guild), + typeof(Administration).Name.ToLowerInvariant(), + replacements); + } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 7f75e891..9bb524ec 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -12,7 +12,7 @@ namespace NadekoBot.Modules public abstract class NadekoModule : ModuleBase { protected readonly Logger _log; - protected CultureInfo _cultureInfo { get; private set; } + protected CultureInfo _cultureInfo; public readonly string Prefix; public readonly string ModuleTypeName; public readonly string LowerModuleTypeName; @@ -58,23 +58,23 @@ namespace NadekoBot.Modules /// /// Used as failsafe in case response key doesn't exist in the selected or default language. /// - private static readonly CultureInfo usCultureInfo = new CultureInfo("en-US"); + private static readonly CultureInfo _usCultureInfo = new CultureInfo("en-US"); - public static string GetTextStatic(string key, CultureInfo _cultureInfo, string lowerModuleTypeName) + public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName) { - var text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _cultureInfo); + var text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, cultureInfo); if (string.IsNullOrWhiteSpace(text)) { - LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + _cultureInfo + " response strings. PLEASE REPORT THIS."); - return NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} found!"; + LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS."); + return NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} found!"; } - return text ?? $"Error: key {lowerModuleTypeName + "_" + key} not found."; + return text; } - public static string GetTextStatic(string key, CultureInfo _cultureInfo, string lowerModuleTypeName, params object[] replacements) + public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName, params object[] replacements) { - return string.Format(GetTextStatic(key, _cultureInfo, lowerModuleTypeName), replacements); + return string.Format(GetTextStatic(key, cultureInfo, lowerModuleTypeName), replacements); } protected string GetText(string key) => diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 48733a24..66ebbe34 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -77,6 +77,69 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Attachments. + /// + public static string administration_attachments { + get { + return ResourceManager.GetString("administration_attachments", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Avatar Changed. + /// + public static string administration_avatar_changed { + get { + return ResourceManager.GetString("administration_avatar_changed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to banned. + /// + public static string administration_banned_pl { + get { + return ResourceManager.GetString("administration_banned_pl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Channel Name Changed. + /// + public static string administration_ch_name_change { + get { + return ResourceManager.GetString("administration_ch_name_change", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Old Name. + /// + public static string administration_ch_old_name { + get { + return ResourceManager.GetString("administration_ch_old_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Channel Topic Changed. + /// + public static string administration_ch_topic_change { + get { + return ResourceManager.GetString("administration_ch_topic_change", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Content. + /// + public static string administration_content { + get { + return ResourceManager.GetString("administration_content", resourceCulture); + } + } + /// /// Looks up a localized string similar to DM from. /// @@ -122,6 +185,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} has joined {1}. + /// + public static string administration_joined { + get { + return ResourceManager.GetString("administration_joined", resourceCulture); + } + } + /// /// Looks up a localized string similar to List Of Languages ///{0}. @@ -177,6 +249,105 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} has left {1}. + /// + public static string administration_left { + get { + return ResourceManager.GetString("administration_left", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging {0} event in this channel.. + /// + public static string administration_log { + get { + return ResourceManager.GetString("administration_log", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging all events in this channel.. + /// + public static string administration_log_all { + get { + return ResourceManager.GetString("administration_log_all", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging disabled.. + /// + public static string administration_log_disabled { + get { + return ResourceManager.GetString("administration_log_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Log events you can subscribe to:. + /// + public static string administration_log_events { + get { + return ResourceManager.GetString("administration_log_events", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging will ignore {0}. + /// + public static string administration_log_ignore { + get { + return ResourceManager.GetString("administration_log_ignore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Logging will not ignore {0}. + /// + public static string administration_log_not_ignore { + get { + return ResourceManager.GetString("administration_log_not_ignore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stopped logging {0} event.. + /// + public static string administration_log_stop { + get { + return ResourceManager.GetString("administration_log_stop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} moved from {1} to {2}. + /// + public static string administration_moved { + get { + return ResourceManager.GetString("administration_moved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message Deleted in #{0}. + /// + public static string administration_msg_del { + get { + return ResourceManager.GetString("administration_msg_del", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message Updated in #{0}. + /// + public static string administration_msg_update { + get { + return ResourceManager.GetString("administration_msg_update", resourceCulture); + } + } + /// /// Looks up a localized string similar to I don't have the permission necessary for that most likely.. /// @@ -195,6 +366,132 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Muted. + /// + public static string administration_muted_pl { + get { + return ResourceManager.GetString("administration_muted_pl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Muted. + /// + public static string administration_muted_sn { + get { + return ResourceManager.GetString("administration_muted_sn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Message. + /// + public static string administration_new_msg { + get { + return ResourceManager.GetString("administration_new_msg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Nickname. + /// + public static string administration_new_nick { + get { + return ResourceManager.GetString("administration_new_nick", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New Topic. + /// + public static string administration_new_topic { + get { + return ResourceManager.GetString("administration_new_topic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nickname Changed. + /// + public static string administration_nick_change { + get { + return ResourceManager.GetString("administration_nick_change", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Old Message. + /// + public static string administration_old_msg { + get { + return ResourceManager.GetString("administration_old_msg", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Old Nickname. + /// + public static string administration_old_nick { + get { + return ResourceManager.GetString("administration_old_nick", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Old Topic. + /// + public static string administration_old_topic { + get { + return ResourceManager.GetString("administration_old_topic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to soft-banned (kicked). + /// + public static string administration_soft_banned_pl { + get { + return ResourceManager.GetString("administration_soft_banned_pl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text Channel Destroyed . + /// + public static string administration_text_chan_created { + get { + return ResourceManager.GetString("administration_text_chan_created", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text Channel Destroyed . + /// + public static string administration_text_chan_destroyed { + get { + return ResourceManager.GetString("administration_text_chan_destroyed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unmuted. + /// + public static string administration_unmuted_sn { + get { + return ResourceManager.GetString("administration_unmuted_sn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User Banned. + /// + public static string administration_user_banned { + get { + return ResourceManager.GetString("administration_user_banned", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has been **muted** from chatting.. /// @@ -213,6 +510,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to User Joined. + /// + public static string administration_user_joined { + get { + return ResourceManager.GetString("administration_user_joined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User Left. + /// + public static string administration_user_left { + get { + return ResourceManager.GetString("administration_user_left", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has been **muted** from text and voice chat.. /// @@ -222,6 +537,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to User's Role Added. + /// + public static string administration_user_role_add { + get { + return ResourceManager.GetString("administration_user_role_add", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User's Role Removed. + /// + public static string administration_user_role_rem { + get { + return ResourceManager.GetString("administration_user_role_rem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} is now {1}. + /// + public static string administration_user_status_change { + get { + return ResourceManager.GetString("administration_user_status_change", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has been **unmuted** from text and voice chat.. /// @@ -231,6 +573,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} has joined {1} voice channel.. + /// + public static string administration_user_vjoined { + get { + return ResourceManager.GetString("administration_user_vjoined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has left {1} voice channel.. + /// + public static string administration_user_vleft { + get { + return ResourceManager.GetString("administration_user_vleft", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} moved from {1} to {2} voice channel.. + /// + public static string administration_user_vmoved { + get { + return ResourceManager.GetString("administration_user_vmoved", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has been **voice muted**.. /// @@ -249,6 +618,87 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Username Changed. + /// + public static string administration_username_changed { + get { + return ResourceManager.GetString("administration_username_changed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Users. + /// + public static string administration_users { + get { + return ResourceManager.GetString("administration_users", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voice Channel Destroyed. + /// + public static string administration_voice_chan_created { + get { + return ResourceManager.GetString("administration_voice_chan_created", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voice Channel Destroyed. + /// + public static string administration_voice_chan_destroyed { + get { + return ResourceManager.GetString("administration_voice_chan_destroyed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User {0} from text chat. + /// + public static string administration_xmuted_text { + get { + return ResourceManager.GetString("administration_xmuted_text", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User {0} from text and voice chat. + /// + public static string administration_xmuted_text_and_voice { + get { + return ResourceManager.GetString("administration_xmuted_text_and_voice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User {0} from voice chat. + /// + public static string administration_xmuted_voice { + get { + return ResourceManager.GetString("administration_xmuted_voice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User Unbanned. + /// + public static string administraton_user_unbanned { + get { + return ResourceManager.GetString("administraton_user_unbanned", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Presence Updates. + /// + public static string adminsitration_presence_updates { + get { + return ResourceManager.GetString("adminsitration_presence_updates", resourceCulture); + } + } + /// /// Looks up a localized string similar to That base is already claimed or destroyed.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index ff566d6c..1fdaa035 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -304,6 +304,28 @@ **Auto assign role** on user join is now **enabled**. + + Attachments + + + Avatar Changed + + + banned + PLURAL + + + Channel Name Changed + + + Old Name + + + Channel Topic Changed + + + Content + DM from @@ -319,6 +341,9 @@ I will stop forwarding DMs from now on. + + {0} has joined {1} + List Of Languages {0} @@ -338,30 +363,160 @@ This server's language is set to {0} - {0} + + {0} has left {1} + + + Logging {0} event in this channel. + + + Logging all events in this channel. + + + Logging disabled. + + + Log events you can subscribe to: + + + Logging will ignore {0} + + + Logging will not ignore {0} + + + Stopped logging {0} event. + + + {0} moved from {1} to {2} + + + Message Deleted in #{0} + + + Message Updated in #{0} + + + Muted + PLURAL (users have been muted) + + + Muted + singular "User muted." + I don't have the permission necessary for that most likely. New mute role set. + + New Message + + + New Nickname + + + New Topic + + + Nickname Changed + + + Old Message + + + Old Nickname + + + Old Topic + + + soft-banned (kicked) + PLURAL + + + Text Channel Destroyed + + + Text Channel Destroyed + + + Unmuted + singular + + + Username Changed + + + Users + + + User Banned + {0} has been **muted** from chatting. {0} has been **unmuted** from chatting. + + User Joined + + + User Left + {0} has been **muted** from text and voice chat. + + User's Role Added + + + User's Role Removed + + + {0} is now {1} + {0} has been **unmuted** from text and voice chat. + + {0} has joined {1} voice channel. + + + {0} has left {1} voice channel. + + + {0} moved from {1} to {2} voice channel. + {0} has been **voice muted**. {0} has been **voice unmuted**. + + Voice Channel Destroyed + + + Voice Channel Destroyed + + + User {0} from text chat + + + User {0} from text and voice chat + + + User {0} from voice chat + + + User Unbanned + + + Presence Updates + Back to ToC From b24e68c24cb48bad0e7dfb94effdefe93025660e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 19:08:27 +0100 Subject: [PATCH 107/256] migration localized, only 2 responses, won't localize console output --- .../Administration/Commands/Migration.cs | 4 ++-- .../Resources/ResponseStrings.Designer.cs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/Migration.cs b/src/NadekoBot/Modules/Administration/Commands/Migration.cs index 42798128..8ee6f652 100644 --- a/src/NadekoBot/Modules/Administration/Commands/Migration.cs +++ b/src/NadekoBot/Modules/Administration/Commands/Migration.cs @@ -51,12 +51,12 @@ namespace NadekoBot.Modules.Administration break; } } - await Context.Channel.SendMessageAsync("🆙 **Migration done.**").ConfigureAwait(false); + await ReplyConfirmLocalized("migration_done").ConfigureAwait(false); } catch (Exception ex) { _log.Error(ex); - await Context.Channel.SendMessageAsync("⚠️ **Error while migrating, check `logs` for more informations.**").ConfigureAwait(false); + await ReplyErrorLocalized("migration_error").ConfigureAwait(false); } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 66ebbe34..c4cdc664 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -690,6 +690,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Migration done!. + /// + public static string adminsitration_migration_done { + get { + return ResourceManager.GetString("adminsitration_migration_done", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error while migrating, check bot's console for more information.. + /// + public static string adminsitration_migration_error { + get { + return ResourceManager.GetString("adminsitration_migration_error", resourceCulture); + } + } + /// /// Looks up a localized string similar to Presence Updates. /// From b890506de6c46d29bdca333922a5092b98304a63 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 19:54:02 +0100 Subject: [PATCH 108/256] localized playing rotate commands --- .../Commands/PlayingRotateCommands.cs | 76 +++++++++---------- .../Resources/ResponseStrings.Designer.cs | 55 ++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 25 ++++++ 3 files changed, 118 insertions(+), 38 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs index 5b06c944..c398de5f 100644 --- a/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/PlayingRotateCommands.cs @@ -1,5 +1,4 @@ -using Discord; -using Discord.Commands; +using Discord.Commands; using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.Extensions; @@ -8,7 +7,6 @@ using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -20,14 +18,15 @@ namespace NadekoBot.Modules.Administration [Group] public class PlayingRotateCommands : NadekoSubmodule { - private static Logger _log { get; } public static List RotatingStatusMessages { get; } - public static bool RotatingStatuses { get; private set; } = false; - private static Timer _t { get; } + public static volatile bool RotatingStatuses; + private readonly object _locker = new object(); + private new static Logger _log { get; } + private static readonly Timer _t; private class TimerState { - public int Index { get; set; } = 0; + public int Index { get; set; } } static PlayingRotateCommands() @@ -37,8 +36,6 @@ namespace NadekoBot.Modules.Administration RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages; RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses; - - _t = new Timer(async (objState) => { try @@ -46,26 +43,24 @@ namespace NadekoBot.Modules.Administration var state = (TimerState)objState; if (!RotatingStatuses) return; - else - { - if (state.Index >= RotatingStatusMessages.Count) - state.Index = 0; + if (state.Index >= RotatingStatusMessages.Count) + state.Index = 0; - if (!RotatingStatusMessages.Any()) - return; - var status = RotatingStatusMessages[state.Index++].Status; - if (string.IsNullOrWhiteSpace(status)) - return; - PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value())); - var shards = NadekoBot.Client.Shards; - for (int i = 0; i < shards.Count; i++) + if (!RotatingStatusMessages.Any()) + return; + var status = RotatingStatusMessages[state.Index++].Status; + if (string.IsNullOrWhiteSpace(status)) + return; + PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value())); + var shards = NadekoBot.Client.Shards; + for (int i = 0; i < shards.Count; i++) + { + var curShard = shards.ElementAt(i); + ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard))); + try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); } + catch (Exception ex) { - ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(shards.ElementAt(i)))); - try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); } - catch (Exception ex) - { - _log.Warn(ex); - } + _log.Warn(ex); } } } @@ -107,17 +102,20 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task RotatePlaying() { - using (var uow = DbHandler.UnitOfWork()) + lock (_locker) { - var config = uow.BotConfig.GetOrCreate(); + using (var uow = DbHandler.UnitOfWork()) + { + var config = uow.BotConfig.GetOrCreate(); - RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses; - await uow.CompleteAsync(); + RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses; + uow.Complete(); + } } if (RotatingStatuses) - await Context.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false); + await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ **Rotating playing status disabled.**").ConfigureAwait(false); + await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -133,7 +131,7 @@ namespace NadekoBot.Modules.Administration await uow.CompleteAsync(); } - await Context.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false); + await ReplyConfirmLocalized("ropl_added").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -141,11 +139,13 @@ namespace NadekoBot.Modules.Administration public async Task ListPlaying() { if (!RotatingStatusMessages.Any()) - await Context.Channel.SendErrorAsync("❎ **No rotating playing statuses set.**"); + await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false); else { var i = 1; - await Context.Channel.SendConfirmAsync($"ℹ️ {Context.User.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}"))); + await ReplyConfirmLocalized("ropl_list", + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}"))) + .ConfigureAwait(false); } } @@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Administration { index -= 1; - string msg = ""; + string msg; using (var uow = DbHandler.UnitOfWork()) { var config = uow.BotConfig.GetOrCreate(); @@ -168,7 +168,7 @@ namespace NadekoBot.Modules.Administration RotatingStatusMessages.RemoveAt(index); await uow.CompleteAsync(); } - await Context.Channel.SendConfirmAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false); + await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index c4cdc664..042557ed 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -447,6 +447,61 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Removed the playing message: {0}. + /// + public static string administration_reprm { + get { + return ResourceManager.GetString("administration_reprm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Added.. + /// + public static string administration_ropl_added { + get { + return ResourceManager.GetString("administration_ropl_added", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rotating playing status disabled.. + /// + public static string administration_ropl_disabled { + get { + return ResourceManager.GetString("administration_ropl_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rotating playing status enabled.. + /// + public static string administration_ropl_enabled { + get { + return ResourceManager.GetString("administration_ropl_enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Here is a list of rotating statuses: + ///{0}. + /// + public static string administration_ropl_list { + get { + return ResourceManager.GetString("administration_ropl_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No rotating playing statuses set.. + /// + public static string administration_ropl_not_set { + get { + return ResourceManager.GetString("administration_ropl_not_set", resourceCulture); + } + } + /// /// Looks up a localized string similar to soft-banned (kicked). /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 1fdaa035..0f7e0cae 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -431,6 +431,25 @@ Old Topic + + Removed the playing message: {0} + + + Added. + + + Rotating playing status disabled. + + + Rotating playing status enabled. + + + Here is a list of rotating statuses: +{0} + + + No rotating playing statuses set. + soft-banned (kicked) PLURAL @@ -514,6 +533,12 @@ User Unbanned + + Migration done! + + + Error while migrating, check bot's console for more information. + Presence Updates From 9edbd8535a827420f717133963ffdbdf52b153de Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 20:26:48 +0100 Subject: [PATCH 109/256] protection commands localized --- .../Commands/ProtectionCommands.cs | 76 ++++++------- .../Resources/ResponseStrings.Designer.cs | 100 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 34 ++++++ 3 files changed, 172 insertions(+), 38 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs index ed85c42d..0cc02896 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -4,12 +4,10 @@ using Microsoft.EntityFrameworkCore; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; -using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Concurrent; -using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Threading.Tasks; @@ -26,12 +24,8 @@ namespace NadekoBot.Modules.Administration public class AntiRaidStats { public AntiRaidSetting AntiRaidSettings { get; set; } - public int UsersCount { get; set; } = 0; + public int UsersCount { get; set; } public ConcurrentHashSet RaidUsers { get; set; } = new ConcurrentHashSet(); - - public override string ToString() => - $"If **{AntiRaidSettings.UserThreshold}** or more users join within **{AntiRaidSettings.Seconds}** seconds," + - $" I will **{AntiRaidSettings.Action}** them."; } public class AntiSpamStats @@ -39,16 +33,6 @@ namespace NadekoBot.Modules.Administration public AntiSpamSetting AntiSpamSettings { get; set; } public ConcurrentDictionary UserStats { get; set; } = new ConcurrentDictionary(); - - public override string ToString() - { - var ignoredString = string.Join(", ", AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>")); - - if (string.IsNullOrWhiteSpace(ignoredString)) - ignoredString = "none"; - return $"If a user posts **{AntiSpamSettings.MessageThreshold}** same messages in a row, I will **{AntiSpamSettings.Action}** them." - + $"\n\t__IgnoredChannels__: {ignoredString}"; - } } public class UserSpamStats @@ -84,7 +68,7 @@ namespace NadekoBot.Modules.Administration private static readonly ConcurrentDictionary _antiSpamGuilds = new ConcurrentDictionary(); - private static Logger _log { get; } + private new static readonly Logger _log; static ProtectionCommands() { @@ -141,7 +125,10 @@ namespace NadekoBot.Modules.Administration } } } - catch { } + catch + { + // ignored + } }); return Task.CompletedTask; }; @@ -175,7 +162,10 @@ namespace NadekoBot.Modules.Administration --settings.UsersCount; } - catch { } + catch + { + // ignored + } }); return Task.CompletedTask; }; @@ -219,13 +209,27 @@ namespace NadekoBot.Modules.Administration } catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } break; - default: - break; } } await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false); } + private string GetAntiSpamString(AntiSpamStats stats) + { + var ignoredString = string.Join(", ", stats.AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>")); + + if (string.IsNullOrWhiteSpace(ignoredString)) + ignoredString = "none"; + return GetText("spam_stats", + Format.Bold(stats.AntiSpamSettings.MessageThreshold.ToString()), + Format.Bold(stats.AntiSpamSettings.Action.ToString()), + ignoredString); + } + + private string GetAntiRaidString(AntiRaidStats stats) => GetText("raid_stats", + Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()), + Format.Bold(stats.AntiRaidSettings.Seconds.ToString()), + Format.Bold(stats.AntiRaidSettings.Action.ToString())); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -234,13 +238,13 @@ namespace NadekoBot.Modules.Administration { if (userThreshold < 2 || userThreshold > 30) { - await Context.Channel.SendErrorAsync("❗️User threshold must be between **2** and **30**.").ConfigureAwait(false); + await ReplyErrorLocalized("raid_cnt", 2, 30).ConfigureAwait(false); return; } if (seconds < 2 || seconds > 300) { - await Context.Channel.SendErrorAsync("❗️Time must be between **2** and **300** seconds.").ConfigureAwait(false); + await ReplyErrorLocalized("raid_time", 2, 300).ConfigureAwait(false); return; } @@ -254,7 +258,7 @@ namespace NadekoBot.Modules.Administration gc.AntiRaidSetting = null; await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("**Anti-Raid** feature has been **disabled** on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("prot_disable", "Anti-Raid").ConfigureAwait(false); return; } @@ -264,10 +268,8 @@ namespace NadekoBot.Modules.Administration } catch (Exception ex) { - await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + - "or create 'nadeko-mute' role with disabled SendMessages and try again.") - .ConfigureAwait(false); _log.Warn(ex); + await ReplyErrorLocalized("prot_error").ConfigureAwait(false); return; } @@ -291,7 +293,7 @@ namespace NadekoBot.Modules.Administration await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("Anti-Raid Enabled", $"{Context.User.Mention} {stats.ToString()}") + await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Raid"), $"{Context.User.Mention} {GetAntiRaidString(stats)}") .ConfigureAwait(false); } @@ -314,7 +316,7 @@ namespace NadekoBot.Modules.Administration gc.AntiSpamSetting = null; await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("**Anti-Spam** has been **disabled** on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("prot_disable", "Anti-Spam").ConfigureAwait(false); return; } @@ -324,10 +326,8 @@ namespace NadekoBot.Modules.Administration } catch (Exception ex) { - await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" + - "or create 'nadeko-mute' role with disabled SendMessages and try again.") - .ConfigureAwait(false); _log.Warn(ex); + await ReplyErrorLocalized("prot_error").ConfigureAwait(false); return; } @@ -350,7 +350,7 @@ namespace NadekoBot.Modules.Administration await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("Anti-Spam Enabled", $"{Context.User.Mention} {stats.ToString()}").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Spam"), $"{Context.User.Mention} {GetAntiSpamString(stats)}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -392,9 +392,9 @@ namespace NadekoBot.Modules.Administration await uow.CompleteAsync().ConfigureAwait(false); } if (added) - await Context.Channel.SendConfirmAsync("Anti-Spam will ignore this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("spam_ignore", "Anti-Spam").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("Anti-Spam will no longer ignore this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("spam_not_ignore", "Anti-Spam").ConfigureAwait(false); } @@ -410,12 +410,12 @@ namespace NadekoBot.Modules.Administration if (spam == null && raid == null) { - await Context.Channel.SendConfirmAsync("No protections enabled."); + await ReplyConfirmLocalized("prot_none").ConfigureAwait(false); return; } var embed = new EmbedBuilder().WithOkColor() - .WithTitle("Protections Enabled"); + .WithTitle(GetText("prot_active")); if (spam != null) embed.AddField(efb => efb.WithName("Anti-Spam") diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 042557ed..ee7d0001 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -447,6 +447,78 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Active Protections. + /// + public static string administration_prot_active { + get { + return ResourceManager.GetString("administration_prot_active", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been **disabled** on this server.. + /// + public static string administration_prot_disable { + get { + return ResourceManager.GetString("administration_prot_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} Enabled. + /// + public static string administration_prot_enable { + get { + return ResourceManager.GetString("administration_prot_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error. I need ManageRoles permission. + /// + public static string administration_prot_error { + get { + return ResourceManager.GetString("administration_prot_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No protections enabled.. + /// + public static string administration_prot_none { + get { + return ResourceManager.GetString("administration_prot_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User threshold must be between {0} and {1}.. + /// + public static string administration_raid_cnt { + get { + return ResourceManager.GetString("administration_raid_cnt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If {0} or more users join within {1} seconds, I will {2} them.. + /// + public static string administration_raid_stats { + get { + return ResourceManager.GetString("administration_raid_stats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time must be between {0} and {1} seconds.. + /// + public static string administration_raid_time { + get { + return ResourceManager.GetString("administration_raid_time", resourceCulture); + } + } + /// /// Looks up a localized string similar to Removed the playing message: {0}. /// @@ -511,6 +583,34 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} will ignore this channel.. + /// + public static string administration_spam_ignore { + get { + return ResourceManager.GetString("administration_spam_ignore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} will no longer ignore this channel.. + /// + public static string administration_spam_not_ignore { + get { + return ResourceManager.GetString("administration_spam_not_ignore", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to If a user posts {0} same messages in a row, I will {1} them. + /// __IgnoredChannels__: {2}. + /// + public static string administration_spam_stats { + get { + return ResourceManager.GetString("administration_spam_stats", resourceCulture); + } + } + /// /// Looks up a localized string similar to Text Channel Destroyed . /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 0f7e0cae..45a93e60 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -431,6 +431,30 @@ Old Topic + + Active Protections + + + {0} has been **disabled** on this server. + + + {0} Enabled + + + Error. I need ManageRoles permission + + + No protections enabled. + + + User threshold must be between {0} and {1}. + + + If {0} or more users join within {1} seconds, I will {2} them. + + + Time must be between {0} and {1} seconds. + Removed the playing message: {0} @@ -454,6 +478,16 @@ soft-banned (kicked) PLURAL + + {0} will ignore this channel. + + + {0} will no longer ignore this channel. + + + If a user posts {0} same messages in a row, I will {1} them. + __IgnoredChannels__: {2} + Text Channel Destroyed From bfc10ab3e25a5f794fd505d0f7a36a0d6288aaa6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 20:28:43 +0100 Subject: [PATCH 110/256] output fix --- .../Modules/Administration/Commands/ProtectionCommands.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs index 0cc02896..8e2bd691 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ProtectionCommands.cs @@ -419,12 +419,12 @@ namespace NadekoBot.Modules.Administration if (spam != null) embed.AddField(efb => efb.WithName("Anti-Spam") - .WithValue(spam.ToString()) + .WithValue(GetAntiSpamString(spam)) .WithIsInline(true)); if (raid != null) embed.AddField(efb => efb.WithName("Anti-Raid") - .WithValue(raid.ToString()) + .WithValue(GetAntiRaidString(raid)) .WithIsInline(true)); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); From 6ab8f26cd7e17e80a6f30e30dd521fbd2d9abbae Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 20:35:10 +0100 Subject: [PATCH 111/256] slowmode can now be localized --- .../Commands/RatelimitCommand.cs | 15 ++++---- .../Resources/ResponseStrings.Designer.cs | 36 +++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 12 +++++++ 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs index 5ece6c1d..2a4e111f 100644 --- a/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/RatelimitCommand.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Administration public class RatelimitCommand : NadekoSubmodule { public static ConcurrentDictionary RatelimitingChannels = new ConcurrentDictionary(); - private static Logger _log { get; } + private new static readonly Logger _log; public class Ratelimiter { @@ -65,9 +65,7 @@ namespace NadekoBot.Modules.Administration try { var usrMsg = umsg as IUserMessage; - if (usrMsg == null) - return; - var channel = usrMsg.Channel as ITextChannel; + var channel = usrMsg?.Channel as ITextChannel; if (channel == null || usrMsg.IsAuthor()) return; @@ -91,8 +89,7 @@ namespace NadekoBot.Modules.Administration if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway)) { throwaway.cancelSource.Cancel(); - await Context.Channel.SendConfirmAsync("ℹ️ Slow mode disabled.").ConfigureAwait(false); - return; + await ReplyConfirmLocalized("slowmode_disabled").ConfigureAwait(false); } } @@ -105,7 +102,7 @@ namespace NadekoBot.Modules.Administration if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600) { - await Context.Channel.SendErrorAsync("⚠️ Invalid parameters."); + await ReplyErrorLocalized("invalid_params").ConfigureAwait(false); return; } var toAdd = new Ratelimiter() @@ -116,8 +113,8 @@ namespace NadekoBot.Modules.Administration }; if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd)) { - await Context.Channel.SendConfirmAsync("Slow mode initiated", - $"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.") + await Context.Channel.SendConfirmAsync(GetText("slowmode_init"), + GetText("slowmode_desc", Format.Bold(toAdd.MaxMessages.ToString()), Format.Bold(toAdd.PerSeconds.ToString()))) .ConfigureAwait(false); } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index ee7d0001..5ed8eb66 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -185,6 +185,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Invalid parameters.. + /// + public static string administration_invalid_params { + get { + return ResourceManager.GetString("administration_invalid_params", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has joined {1}. /// @@ -574,6 +583,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Users can't send more than {0} messages every {1} seconds.. + /// + public static string administration_slowmode_desc { + get { + return ResourceManager.GetString("administration_slowmode_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Slow mode disabled.. + /// + public static string administration_slowmode_disabled { + get { + return ResourceManager.GetString("administration_slowmode_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Slow mode initiated. + /// + public static string administration_slowmode_init { + get { + return ResourceManager.GetString("administration_slowmode_init", resourceCulture); + } + } + /// /// Looks up a localized string similar to soft-banned (kicked). /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 45a93e60..6bb626c3 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -341,6 +341,9 @@ I will stop forwarding DMs from now on. + + Invalid parameters. + {0} has joined {1} @@ -474,6 +477,15 @@ No rotating playing statuses set. + + Users can't send more than {0} messages every {1} seconds. + + + Slow mode disabled. + + + Slow mode initiated + soft-banned (kicked) PLURAL From fd81b2de26aa05bfcb29f03dc69684c98975a173 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 21:47:46 +0100 Subject: [PATCH 112/256] self assigned role commands can now be localized :3 --- .../Commands/SelfAssignedRolesCommand.cs | 62 +++++---- .../Resources/ResponseStrings.Designer.cs | 126 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 43 ++++++ 3 files changed, 199 insertions(+), 32 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs index 73327f87..7fd35dfc 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfAssignedRolesCommand.cs @@ -44,25 +44,30 @@ namespace NadekoBot.Modules.Administration IEnumerable roles; string msg; + var error = false; using (var uow = DbHandler.UnitOfWork()) { roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id); if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id)) { - await Context.Channel.SendMessageAsync($"💢 Role **{role.Name}** is already in the list.").ConfigureAwait(false); - return; + msg = GetText("role_in_list", Format.Bold(role.Name)); + error = true; } else { - uow.SelfAssignedRoles.Add(new SelfAssignedRole { + uow.SelfAssignedRoles.Add(new SelfAssignedRole + { RoleId = role.Id, GuildId = role.Guild.Id }); await uow.CompleteAsync(); - msg = $"🆗 Role **{role.Name}** added to the list."; + msg = GetText("role_added", Format.Bold(role.Name)); } } - await Context.Channel.SendConfirmAsync(msg.ToString()).ConfigureAwait(false); + if (error) + await Context.Channel.SendErrorAsync(msg).ConfigureAwait(false); + else + await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -70,8 +75,6 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.ManageRoles)] public async Task Rsar([Remainder] IRole role) { - //var channel = (ITextChannel)Context.Channel; - bool success; using (var uow = DbHandler.UnitOfWork()) { @@ -80,18 +83,16 @@ namespace NadekoBot.Modules.Administration } if (!success) { - await Context.Channel.SendErrorAsync("❎ That role is not self-assignable.").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false); return; } - await Context.Channel.SendConfirmAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false); + await ReplyConfirmLocalized("self_assign_rem", Format.Bold(role.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Lsar() { - //var channel = (ITextChannel)Context.Channel; - var toRemove = new ConcurrentHashSet(); var removeMsg = new StringBuilder(); var msg = new StringBuilder(); @@ -116,11 +117,11 @@ namespace NadekoBot.Modules.Administration } foreach (var role in toRemove) { - removeMsg.AppendLine($"`{role.RoleId} not found. Cleaned up.`"); + removeMsg.AppendLine(GetText("role_clean", role.RoleId)); } await uow.CompleteAsync(); } - await Context.Channel.SendConfirmAsync($"ℹ️ There are `{roleCnt}` self assignable roles:", msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("self_assign_list", roleCnt), msg + "\n\n" + removeMsg).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -128,8 +129,6 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.ManageRoles)] public async Task Tesar() { - //var channel = (ITextChannel)Context.Channel; - bool areExclusive; using (var uow = DbHandler.UnitOfWork()) { @@ -138,15 +137,16 @@ namespace NadekoBot.Modules.Administration areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles; await uow.CompleteAsync(); } - string exl = areExclusive ? "**exclusive**." : "**not exclusive**."; - await Context.Channel.SendConfirmAsync("ℹ️ Self assigned roles are now " + exl); + if(areExclusive) + await ReplyConfirmLocalized("self_assign_excl").ConfigureAwait(false); + else + await ReplyConfirmLocalized("self_assign_no_excl").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Iam([Remainder] IRole role) { - //var channel = (ITextChannel)Context.Channel; var guildUser = (IGuildUser)Context.User; GuildConfig conf; @@ -156,25 +156,24 @@ namespace NadekoBot.Modules.Administration conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id); } - SelfAssignedRole roleModel; - if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null) + if (roles.FirstOrDefault(r=>r.RoleId == role.Id) == null) { - await Context.Channel.SendErrorAsync("That role is not self-assignable.").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false); return; } if (guildUser.RoleIds.Contains(role.Id)) { - await Context.Channel.SendErrorAsync($"You already have **{role.Name}** role.").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_already", Format.Bold(role.Name)).ConfigureAwait(false); return; } if (conf.ExclusiveSelfAssignedRoles) { - var sameRoleId = guildUser.RoleIds.Where(r => roles.Select(sar => sar.RoleId).Contains(r)).FirstOrDefault(); + var sameRoleId = guildUser.RoleIds.FirstOrDefault(r => roles.Select(sar => sar.RoleId).Contains(r)); var sameRole = Context.Guild.GetRole(sameRoleId); if (sameRoleId != default(ulong)) { - await Context.Channel.SendErrorAsync($"You already have **{sameRole?.Name}** `exclusive self-assigned` role.").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_already_excl", Format.Bold(sameRole?.Name)).ConfigureAwait(false); return; } } @@ -184,11 +183,11 @@ namespace NadekoBot.Modules.Administration } catch (Exception ex) { - await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false); Console.WriteLine(ex); return; } - var msg = await Context.Channel.SendConfirmAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false); + var msg = await ReplyConfirmLocalized("self_assign_success",Format.Bold(role.Name)).ConfigureAwait(false); if (conf.AutoDeleteSelfAssignedRoleMessages) { @@ -210,15 +209,14 @@ namespace NadekoBot.Modules.Administration autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages; roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id); } - SelfAssignedRole roleModel; - if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null) + if (roles.FirstOrDefault(r => r.RoleId == role.Id) == null) { - await Context.Channel.SendErrorAsync("💢 That role is not self-assignable.").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false); return; } if (!guildUser.RoleIds.Contains(role.Id)) { - await Context.Channel.SendErrorAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_not_have",Format.Bold(role.Name)).ConfigureAwait(false); return; } try @@ -227,10 +225,10 @@ namespace NadekoBot.Modules.Administration } catch (Exception) { - await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't remove roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); + await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false); return; } - var msg = await Context.Channel.SendConfirmAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false); + var msg = await ReplyConfirmLocalized("self_assign_remove", Format.Bold(role.Name)).ConfigureAwait(false); if (autoDeleteSelfAssignedRoleMessages) { diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 5ed8eb66..5afd534e 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -59,6 +59,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You already have {0} role.. + /// + public static string administartion_self_assign_already { + get { + return ResourceManager.GetString("administartion_self_assign_already", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You no longer have {0} role.. + /// + public static string administartion_self_assign_remove { + get { + return ResourceManager.GetString("administartion_self_assign_remove", resourceCulture); + } + } + /// /// Looks up a localized string similar to **Auto assign role** on user join is now **disabled**.. /// @@ -537,6 +555,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Role {0} as been added to the list.. + /// + public static string administration_role_added { + get { + return ResourceManager.GetString("administration_role_added", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} not found.Cleaned up.. + /// + public static string administration_role_clean { + get { + return ResourceManager.GetString("administration_role_clean", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Role {0} is already in the list.. + /// + public static string administration_role_in_list { + get { + return ResourceManager.GetString("administration_role_in_list", resourceCulture); + } + } + /// /// Looks up a localized string similar to Added.. /// @@ -583,6 +628,87 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You already have {0} exclusive self-assigned role.. + /// + public static string administration_self_assign_already_excl { + get { + return ResourceManager.GetString("administration_self_assign_already_excl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Self assigned roles are now exclusive!. + /// + public static string administration_self_assign_excl { + get { + return ResourceManager.GetString("administration_self_assign_excl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to There are {0} self assignable roles. + /// + public static string administration_self_assign_list { + get { + return ResourceManager.GetString("administration_self_assign_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Self assigned roles are now not exclusive!. + /// + public static string administration_self_assign_no_excl { + get { + return ResourceManager.GetString("administration_self_assign_no_excl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That role is not self-assignable.. + /// + public static string administration_self_assign_not { + get { + return ResourceManager.GetString("administration_self_assign_not", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have {0} role.. + /// + public static string administration_self_assign_not_have { + get { + return ResourceManager.GetString("administration_self_assign_not_have", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 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.`. + /// + public static string administration_self_assign_perms { + get { + return ResourceManager.GetString("administration_self_assign_perms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has been removed from the list of self-assignable roles.. + /// + public static string administration_self_assign_rem { + get { + return ResourceManager.GetString("administration_self_assign_rem", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You now have {0} role.. + /// + public static string administration_self_assign_sucess { + get { + return ResourceManager.GetString("administration_self_assign_sucess", resourceCulture); + } + } + /// /// Looks up a localized string similar to Users can't send more than {0} messages every {1} seconds.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 6bb626c3..1d1aaaca 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -298,6 +298,12 @@ You fainted, so you are not able to move! + + You already have {0} role. + + + You no longer have {0} role. + **Auto assign role** on user join is now **disabled**. @@ -461,6 +467,15 @@ Removed the playing message: {0} + + Role {0} is added to the list. + + + {0} not found.Cleaned up. + + + Role {0} is already in the list. + Added. @@ -477,6 +492,34 @@ No rotating playing statuses set. + + You already have {0} exclusive self-assigned role. + + + Self assigned roles are now exclusive! + + + There are {0} self assignable roles: +{1} + + + That role is not self-assignable. + + + You don't have {0} role. + + + Self assigned roles are now not exclusive! + + + 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.` + + + {0} has been removed from the list of self-assignable roles. + + + You now have {0} role. + Users can't send more than {0} messages every {1} seconds. From fdc41caed159e12aae9cdcaa8023b8655daee40e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 22:07:28 +0100 Subject: [PATCH 113/256] self commands done too --- .../Administration/Commands/SelfCommands.cs | 41 ++-- .../Resources/ResponseStrings.Designer.cs | 180 ++++++++++++++++-- src/NadekoBot/Resources/ResponseStrings.resx | 67 ++++++- 3 files changed, 244 insertions(+), 44 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index b67f5d3e..0dfb3d0a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -102,14 +102,14 @@ namespace NadekoBot.Modules.Administration if (shard == null) { - await Context.Channel.SendErrorAsync("No shard by that id found.").ConfigureAwait(false); + await ReplyErrorLocalized("no_shard_id").ConfigureAwait(false); return; } try { - await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnecting.").ConfigureAwait(false); + await ReplyConfirmLocalized("shard_reconnecting", Format.Bold("#" + shardid)).ConfigureAwait(false); await shard.ConnectAsync().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnected.").ConfigureAwait(false); + await ReplyConfirmLocalized("shard_reconnected", Format.Bold("#" + shardid)).ConfigureAwait(false); } catch (Exception ex) { @@ -127,18 +127,18 @@ namespace NadekoBot.Modules.Administration if (server == null) { - await Context.Channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false); + await ReplyErrorLocalized("no_server").ConfigureAwait(false); return; } if (server.OwnerId != NadekoBot.Client.CurrentUser.Id) { await server.LeaveAsync().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false); + await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false); } else { await server.DeleteAsync().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("Deleted server " + server.Name).ConfigureAwait(false); + await ReplyConfirmLocalized("deleted_server",Format.Bold(server.Name)).ConfigureAwait(false); } } @@ -147,7 +147,14 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task Die() { - try { await Context.Channel.SendConfirmAsync("ℹ️ **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try + { + await ReplyConfirmLocalized("shutting_down").ConfigureAwait(false); + } + catch + { + // ignored + } await Task.Delay(2000).ConfigureAwait(false); Environment.Exit(0); } @@ -161,7 +168,7 @@ namespace NadekoBot.Modules.Administration await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"Bot name changed to **{newName}**").ConfigureAwait(false); + await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -170,7 +177,7 @@ namespace NadekoBot.Modules.Administration { await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"Bot status changed to **{status}**").ConfigureAwait(false); + await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -192,7 +199,7 @@ namespace NadekoBot.Modules.Administration } } - await Context.Channel.SendConfirmAsync("🆒 **New avatar set.**").ConfigureAwait(false); + await ReplyConfirmLocalized("set_avatar").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -201,7 +208,7 @@ namespace NadekoBot.Modules.Administration { await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false); + await ReplyConfirmLocalized("set_game").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -212,7 +219,7 @@ namespace NadekoBot.Modules.Administration await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("ℹ️ **New stream set.**").ConfigureAwait(false); + await ReplyConfirmLocalized("set_stream").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -253,8 +260,10 @@ namespace NadekoBot.Modules.Administration } else { - await Context.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false); + await ReplyErrorLocalized("invalid_format").ConfigureAwait(false); + return; } + await ReplyConfirmLocalized("message_sent").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -264,10 +273,10 @@ namespace NadekoBot.Modules.Administration var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray(); if (channels == null) return; - await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message))) + await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message))) .ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false); + await ReplyConfirmLocalized("message_sent").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -275,7 +284,7 @@ namespace NadekoBot.Modules.Administration public async Task ReloadImages() { var time = await NadekoBot.Images.Reload().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"Images loaded after {time.TotalSeconds:F3}s!").ConfigureAwait(false); + await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false); } private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 5afd534e..74cc34e8 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -59,24 +59,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to You already have {0} role.. - /// - public static string administartion_self_assign_already { - get { - return ResourceManager.GetString("administartion_self_assign_already", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to You no longer have {0} role.. - /// - public static string administartion_self_assign_remove { - get { - return ResourceManager.GetString("administartion_self_assign_remove", resourceCulture); - } - } - /// /// Looks up a localized string similar to **Auto assign role** on user join is now **disabled**.. /// @@ -122,6 +104,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Bot name changed to {0}. + /// + public static string administration_bot_name { + get { + return ResourceManager.GetString("administration_bot_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bot status changed to {0}. + /// + public static string administration_bot_status { + get { + return ResourceManager.GetString("administration_bot_status", resourceCulture); + } + } + /// /// Looks up a localized string similar to Channel Name Changed. /// @@ -158,6 +158,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Deleted server {0}. + /// + public static string administration_deleted_server { + get { + return ResourceManager.GetString("administration_deleted_server", resourceCulture); + } + } + /// /// Looks up a localized string similar to DM from. /// @@ -203,6 +212,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Images loaded after {0} seconds!. + /// + public static string administration_images_loaded { + get { + return ResourceManager.GetString("administration_images_loaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid input format.. + /// + public static string administration_invalid_format { + get { + return ResourceManager.GetString("administration_invalid_format", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid parameters.. /// @@ -285,6 +312,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Left server {0}. + /// + public static string administration_left_server { + get { + return ResourceManager.GetString("administration_left_server", resourceCulture); + } + } + /// /// Looks up a localized string similar to Logging {0} event in this channel.. /// @@ -348,6 +384,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Message from {0} `[Bot Owner]`:. + /// + public static string administration_message_from_bo { + get { + return ResourceManager.GetString("administration_message_from_bo", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Message sent.. + /// + public static string administration_message_sent { + get { + return ResourceManager.GetString("administration_message_sent", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} moved from {1} to {2}. /// @@ -447,6 +501,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Can't find that server. + /// + public static string administration_no_server { + get { + return ResourceManager.GetString("administration_no_server", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No shard with that ID found.. + /// + public static string administration_no_shard_id { + get { + return ResourceManager.GetString("administration_no_shard_id", resourceCulture); + } + } + /// /// Looks up a localized string similar to Old Message. /// @@ -628,6 +700,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You already have {0} role.. + /// + public static string administration_self_assign_already { + get { + return ResourceManager.GetString("administration_self_assign_already", resourceCulture); + } + } + /// /// Looks up a localized string similar to You already have {0} exclusive self-assigned role.. /// @@ -700,6 +781,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You no longer have {0} role.. + /// + public static string administration_self_assign_remove { + get { + return ResourceManager.GetString("administration_self_assign_remove", resourceCulture); + } + } + /// /// Looks up a localized string similar to You now have {0} role.. /// @@ -709,6 +799,60 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to New avatar set!. + /// + public static string administration_set_avatar { + get { + return ResourceManager.GetString("administration_set_avatar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New game set!. + /// + public static string administration_set_game { + get { + return ResourceManager.GetString("administration_set_game", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New stream set!. + /// + public static string administration_set_stream { + get { + return ResourceManager.GetString("administration_set_stream", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shard {0} reconnected.. + /// + public static string administration_shard_reconnected { + get { + return ResourceManager.GetString("administration_shard_reconnected", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shard {0} reconnecting.. + /// + public static string administration_shard_reconnecting { + get { + return ResourceManager.GetString("administration_shard_reconnecting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shutting down. + /// + public static string administration_shutting_down { + get { + return ResourceManager.GetString("administration_shutting_down", resourceCulture); + } + } + /// /// Looks up a localized string similar to Users can't send more than {0} messages every {1} seconds.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 1d1aaaca..382b6c14 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -298,12 +298,6 @@ You fainted, so you are not able to move! - - You already have {0} role. - - - You no longer have {0} role. - **Auto assign role** on user join is now **disabled**. @@ -320,6 +314,12 @@ banned PLURAL + + Bot name changed to {0} + + + Bot status changed to {0} + Channel Name Changed @@ -332,6 +332,9 @@ Content + + Deleted server {0} + DM from @@ -347,6 +350,12 @@ I will stop forwarding DMs from now on. + + Images loaded after {0} seconds! + + + Invalid input format. + Invalid parameters. @@ -375,6 +384,9 @@ {0} has left {1} + + Left server {0} + Logging {0} event in this channel. @@ -396,6 +408,12 @@ Stopped logging {0} event. + + Message from {0} `[Bot Owner]`: + + + Message sent. + {0} moved from {1} to {2} @@ -431,6 +449,12 @@ Nickname Changed + + Can't find that server + + + No shard with that ID found. + Old Message @@ -468,7 +492,7 @@ Removed the playing message: {0} - Role {0} is added to the list. + Role {0} as been added to the list. {0} not found.Cleaned up. @@ -492,6 +516,9 @@ No rotating playing statuses set. + + You already have {0} role. + You already have {0} exclusive self-assigned role. @@ -499,8 +526,7 @@ Self assigned roles are now exclusive! - There are {0} self assignable roles: -{1} + There are {0} self assignable roles That role is not self-assignable. @@ -517,9 +543,30 @@ {0} has been removed from the list of self-assignable roles. - + + You no longer have {0} role. + + You now have {0} role. + + New avatar set! + + + New game set! + + + New stream set! + + + Shard {0} reconnected. + + + Shard {0} reconnecting. + + + Shutting down + Users can't send more than {0} messages every {1} seconds. From 431f328bb464bf5a11c45058ac9c9350b1d540e6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 22:33:54 +0100 Subject: [PATCH 114/256] greet commands done and EVEN tested!1!! --- .../Commands/ServerGreetCommands.cs | 64 ++++--- .../Resources/ResponseStrings.Designer.cs | 171 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 57 ++++++ 3 files changed, 259 insertions(+), 33 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs index b3f79c83..b64df312 100644 --- a/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/ServerGreetCommands.cs @@ -1,11 +1,9 @@ using Discord; using Discord.Commands; -using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.DataStructures; using NadekoBot.Extensions; using NadekoBot.Services; -using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; using NLog; using System; @@ -55,7 +53,7 @@ namespace NadekoBot.Modules.Administration private new static Logger _log { get; } - private static ConcurrentDictionary GuildConfigsCache { get; } = new ConcurrentDictionary(); + private static ConcurrentDictionary guildConfigsCache { get; } static ServerGreetCommands() { @@ -63,13 +61,13 @@ namespace NadekoBot.Modules.Administration NadekoBot.Client.UserLeft += UserLeft; _log = LogManager.GetCurrentClassLogger(); - GuildConfigsCache = new ConcurrentDictionary(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, (g) => GreetSettings.Create(g))); + guildConfigsCache = new ConcurrentDictionary(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create)); } private static GreetSettings GetOrAddSettingsForGuild(ulong guildId) { GreetSettings settings; - GuildConfigsCache.TryGetValue(guildId, out settings); + guildConfigsCache.TryGetValue(guildId, out settings); if (settings != null) return settings; @@ -80,7 +78,7 @@ namespace NadekoBot.Modules.Administration settings = GreetSettings.Create(gc); } - GuildConfigsCache.TryAdd(guildId, settings); + guildConfigsCache.TryAdd(guildId, settings); return settings; } @@ -234,9 +232,9 @@ namespace NadekoBot.Modules.Administration await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false); if (timer > 0) - await Context.Channel.SendConfirmAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false); + await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false); } private static async Task SetGreetDel(ulong id, int timer) @@ -250,7 +248,7 @@ namespace NadekoBot.Modules.Administration conf.AutoDeleteGreetMessagesTimer = timer; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd); await uow.CompleteAsync().ConfigureAwait(false); } @@ -264,9 +262,9 @@ namespace NadekoBot.Modules.Administration var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); if (enabled) - await Context.Channel.SendConfirmAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("greet_on").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ Greeting messages **disabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("greet_off").ConfigureAwait(false); } private static async Task SetGreet(ulong guildId, ulong channelId, bool? value = null) @@ -279,7 +277,7 @@ namespace NadekoBot.Modules.Administration conf.GreetMessageChannelId = channelId; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); await uow.CompleteAsync().ConfigureAwait(false); } @@ -298,15 +296,15 @@ namespace NadekoBot.Modules.Administration { channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText; } - await Context.Channel.SendConfirmAsync("Current greet message: ", channelGreetMessageText?.SanitizeMentions()); + await ReplyConfirmLocalized("greetmsg_cur", channelGreetMessageText?.SanitizeMentions()).ConfigureAwait(false); return; } var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text); - await Context.Channel.SendConfirmAsync("🆗 New greet message **set**.").ConfigureAwait(false); + await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false); if (!sendGreetEnabled) - await Context.Channel.SendConfirmAsync("ℹ️ Enable greet messsages by typing `.greet`").ConfigureAwait(false); + await ReplyConfirmLocalized("greetmsg_enable", $"`{Prefix}greet`").ConfigureAwait(false); } public static bool SetGreetMessage(ulong guildId, ref string message) @@ -324,7 +322,7 @@ namespace NadekoBot.Modules.Administration greetMsgEnabled = conf.SendChannelGreetMessage; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); uow.Complete(); } @@ -339,9 +337,9 @@ namespace NadekoBot.Modules.Administration var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false); if (enabled) - await Context.Channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ Greet announcements **disabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("greetdm_off").ConfigureAwait(false); } private static async Task SetGreetDm(ulong guildId, bool? value = null) @@ -353,7 +351,7 @@ namespace NadekoBot.Modules.Administration enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); await uow.CompleteAsync().ConfigureAwait(false); } @@ -372,15 +370,15 @@ namespace NadekoBot.Modules.Administration { config = uow.GuildConfigs.For(Context.Guild.Id); } - await Context.Channel.SendConfirmAsync("ℹ️ Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`"); + await ReplyConfirmLocalized("greetdmmsg_cur", config.DmGreetMessageText?.SanitizeMentions()).ConfigureAwait(false); return; } var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text); - await Context.Channel.SendConfirmAsync("🆗 New DM greet message **set**.").ConfigureAwait(false); + await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false); if (!sendGreetEnabled) - await Context.Channel.SendConfirmAsync($"ℹ️ Enable DM greet messsages by typing `{Prefix}greetdm`").ConfigureAwait(false); + await ReplyConfirmLocalized("greetdmmsg_enable", $"`{Prefix}greetdm`").ConfigureAwait(false); } public static bool SetGreetDmMessage(ulong guildId, ref string message) @@ -398,7 +396,7 @@ namespace NadekoBot.Modules.Administration greetMsgEnabled = conf.SendDmGreetMessage; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); uow.Complete(); } @@ -413,9 +411,9 @@ namespace NadekoBot.Modules.Administration var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); if (enabled) - await Context.Channel.SendConfirmAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("bye_on").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ Bye announcements **disabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("bye_off").ConfigureAwait(false); } private static async Task SetBye(ulong guildId, ulong channelId, bool? value = null) @@ -428,7 +426,7 @@ namespace NadekoBot.Modules.Administration conf.ByeMessageChannelId = channelId; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); await uow.CompleteAsync(); } @@ -447,15 +445,15 @@ namespace NadekoBot.Modules.Administration { byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText; } - await Context.Channel.SendConfirmAsync("ℹ️ Current **bye** message: `" + byeMessageText?.SanitizeMentions() + "`"); + await ReplyConfirmLocalized("byemsg_cur", byeMessageText?.SanitizeMentions()).ConfigureAwait(false); return; } var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text); - await Context.Channel.SendConfirmAsync("🆗 New bye message **set**.").ConfigureAwait(false); + await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false); if (!sendByeEnabled) - await Context.Channel.SendConfirmAsync($"ℹ️ Enable bye messsages by typing `{Prefix}bye`").ConfigureAwait(false); + await ReplyConfirmLocalized("byemsg_enable", $"`{Prefix}bye`").ConfigureAwait(false); } public static bool SetByeMessage(ulong guildId, ref string message) @@ -473,7 +471,7 @@ namespace NadekoBot.Modules.Administration byeMsgEnabled = conf.SendChannelByeMessage; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); uow.Complete(); } @@ -488,9 +486,9 @@ namespace NadekoBot.Modules.Administration await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false); if (timer > 0) - await Context.Channel.SendConfirmAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false); + await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("ℹ️ Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false); + await ReplyConfirmLocalized("byedel_off").ConfigureAwait(false); } private static async Task SetByeDel(ulong guildId, int timer) @@ -504,7 +502,7 @@ namespace NadekoBot.Modules.Administration conf.AutoDeleteByeMessagesTimer = timer; var toAdd = GreetSettings.Create(conf); - GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); + guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); await uow.CompleteAsync().ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 74cc34e8..2d55b084 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -122,6 +122,69 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Bye announcements disabled.. + /// + public static string administration_bye_off { + get { + return ResourceManager.GetString("administration_bye_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bye announcements enabled on this channel.. + /// + public static string administration_bye_on { + get { + return ResourceManager.GetString("administration_bye_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatic deletion of bye messages has been disabled.. + /// + public static string administration_byedel_off { + get { + return ResourceManager.GetString("administration_byedel_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bye messages will be deleted after {0} seconds.. + /// + public static string administration_byedel_on { + get { + return ResourceManager.GetString("administration_byedel_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current bye message: {0}. + /// + public static string administration_byemsg_cur { + get { + return ResourceManager.GetString("administration_byemsg_cur", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable bye messages by typing {0}. + /// + public static string administration_byemsg_enable { + get { + return ResourceManager.GetString("administration_byemsg_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New bye message set.. + /// + public static string administration_byemsg_new { + get { + return ResourceManager.GetString("administration_byemsg_new", resourceCulture); + } + } + /// /// Looks up a localized string similar to Channel Name Changed. /// @@ -212,6 +275,114 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Greet announcements disabled.. + /// + public static string administration_greet_off { + get { + return ResourceManager.GetString("administration_greet_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Greet announcements enabled on this channel.. + /// + public static string administration_greet_on { + get { + return ResourceManager.GetString("administration_greet_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Automatic deletion of greet messages has been disabled.. + /// + public static string administration_greetdel_off { + get { + return ResourceManager.GetString("administration_greetdel_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Greet messages will be deleted after {0} seconds.. + /// + public static string administration_greetdel_on { + get { + return ResourceManager.GetString("administration_greetdel_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DM greet announcements disabled.. + /// + public static string administration_greetdm_off { + get { + return ResourceManager.GetString("administration_greetdm_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to DM greet announcements enabled.. + /// + public static string administration_greetdm_on { + get { + return ResourceManager.GetString("administration_greetdm_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current DM greet message: {0}. + /// + public static string administration_greetdmmsg_cur { + get { + return ResourceManager.GetString("administration_greetdmmsg_cur", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable DM greet messages by typing {0}. + /// + public static string administration_greetdmmsg_enable { + get { + return ResourceManager.GetString("administration_greetdmmsg_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New DM greet message set.. + /// + public static string administration_greetdmmsg_new { + get { + return ResourceManager.GetString("administration_greetdmmsg_new", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current greet message: {0}. + /// + public static string administration_greetmsg_cur { + get { + return ResourceManager.GetString("administration_greetmsg_cur", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable greet messages by typing {0}. + /// + public static string administration_greetmsg_enable { + get { + return ResourceManager.GetString("administration_greetmsg_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New greet message set.. + /// + public static string administration_greetmsg_new { + get { + return ResourceManager.GetString("administration_greetmsg_new", resourceCulture); + } + } + /// /// Looks up a localized string similar to Images loaded after {0} seconds!. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 382b6c14..84b45ae4 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -320,6 +320,27 @@ Bot status changed to {0} + + Automatic deletion of bye messages has been disabled. + + + Bye messages will be deleted after {0} seconds. + + + Current bye message: {0} + + + Enable bye messages by typing {0} + + + New bye message set. + + + Bye announcements disabled. + + + Bye announcements enabled on this channel. + Channel Name Changed @@ -350,6 +371,42 @@ I will stop forwarding DMs from now on. + + Automatic deletion of greet messages has been disabled. + + + Greet messages will be deleted after {0} seconds. + + + Current DM greet message: {0} + + + Enable DM greet messages by typing {0} + + + New DM greet message set. + + + DM greet announcements disabled. + + + DM greet announcements enabled. + + + Current greet message: {0} + + + Enable greet messages by typing {0} + + + New greet message set. + + + Greet announcements disabled. + + + Greet announcements enabled on this channel. + Images loaded after {0} seconds! From 0d0938f613aa25e6f953fc25d8ac30ada6ea8cec Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 14 Feb 2017 22:48:53 +0100 Subject: [PATCH 115/256] .v+t localizable. (only) main file left --- .../Commands/VoicePlusTextCommands.cs | 36 ++++++----- .../Resources/ResponseStrings.Designer.cs | 63 +++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 21 +++++++ 3 files changed, 105 insertions(+), 15 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs index 2a4c3152..7302b09d 100644 --- a/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/VoicePlusTextCommands.cs @@ -65,10 +65,15 @@ namespace NadekoBot.Modules.Administration try { await guild.Owner.SendErrorAsync( - "⚠️ I don't have **manage server** and/or **manage channels** permission," + - $" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); + GetTextStatic("vt_exit", + NadekoBot.Localization.GetCultureInfo(guild), + typeof(Administration).Name.ToLowerInvariant(), + Format.Bold(guild.Name))).ConfigureAwait(false); + } + catch + { + // ignored } - catch { } using (var uow = DbHandler.UnitOfWork()) { uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; @@ -105,9 +110,8 @@ namespace NadekoBot.Modules.Administration if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id) { var roleName = GetRoleName(afterVch); - IRole roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName); - if (roleToAdd == null) - roleToAdd = await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false); + var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ?? + (IRole) await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false); ITextChannel textChannel = guild.TextChannels .FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()); @@ -115,7 +119,7 @@ namespace NadekoBot.Modules.Administration { var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); - try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch { } + try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/} await Task.Delay(50).ConfigureAwait(false); await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions( readMessages: PermValue.Allow, @@ -162,7 +166,7 @@ namespace NadekoBot.Modules.Administration var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false); if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels) { - await Context.Channel.SendErrorAsync("I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`"); + await ReplyErrorLocalized("vt_no_perms").ConfigureAwait(false); return; } @@ -170,10 +174,12 @@ namespace NadekoBot.Modules.Administration { try { - await Context.Channel.SendErrorAsync("⚠️ You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. " + - "`This may cause some issues, and you will have to clean up text channels yourself afterwards.`"); + await ReplyErrorLocalized("vt_no_admin").ConfigureAwait(false); + } + catch + { + // ignored } - catch { } } try { @@ -198,11 +204,11 @@ namespace NadekoBot.Modules.Administration try { await role.DeleteAsync().ConfigureAwait(false); } catch { } await Task.Delay(500).ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("ℹ️ Successfuly **removed** voice + text feature.").ConfigureAwait(false); + await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false); return; } _voicePlusTextCache.Add(guild.Id); - await Context.Channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false); + await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false); } catch (Exception ex) @@ -222,7 +228,7 @@ namespace NadekoBot.Modules.Administration var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false); if (!botUser.GuildPermissions.Administrator) { - await Context.Channel.SendErrorAsync("I need **Administrator permission** to do that.").ConfigureAwait(false); + await ReplyErrorLocalized("need_admin").ConfigureAwait(false); return; } @@ -249,7 +255,7 @@ namespace NadekoBot.Modules.Administration await Task.Delay(500).ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false); + await ReplyConfirmLocalized("cleaned_up").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 2d55b084..bb9992f1 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -212,6 +212,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Cleaned up.. + /// + public static string administration_cleaned_up { + get { + return ResourceManager.GetString("administration_cleaned_up", resourceCulture); + } + } + /// /// Looks up a localized string similar to Content. /// @@ -636,6 +645,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to I need **Administration** permission to do that.. + /// + public static string administration_need_admin { + get { + return ResourceManager.GetString("administration_need_admin", resourceCulture); + } + } + /// /// Looks up a localized string similar to New Message. /// @@ -1286,6 +1304,51 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Disabled voice + text feature.. + /// + public static string administration_vt_disabled { + get { + return ResourceManager.GetString("administration_vt_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled voice + text feature.. + /// + public static string administration_vt_enabled { + get { + return ResourceManager.GetString("administration_vt_enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server.. + /// + public static string administration_vt_exit { + get { + return ResourceManager.GetString("administration_vt_exit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. This may cause some issues, and you will have to clean up text channels yourself afterwards.. + /// + public static string administration_vt_no_admin { + get { + return ResourceManager.GetString("administration_vt_no_admin", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I require atleast **manage roles** and **manage channels** permissions to enable this feature. (preffered Administration permission). + /// + public static string administration_vt_perms { + get { + return ResourceManager.GetString("administration_vt_perms", resourceCulture); + } + } + /// /// Looks up a localized string similar to User {0} from text chat. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 84b45ae4..f044bf3a 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -350,6 +350,9 @@ Channel Topic Changed + + Cleaned up. + Content @@ -494,6 +497,9 @@ New mute role set. + + I need **Administration** permission to do that. + New Message @@ -714,6 +720,21 @@ Voice Channel Destroyed + + Disabled voice + text feature. + + + Enabled voice + text feature. + + + I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server. + + + You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. This may cause some issues, and you will have to clean up text channels yourself afterwards. + + + I require atleast **manage roles** and **manage channels** permissions to enable this feature. (preffered Administration permission) + User {0} from text chat From d0100f0c9f7258214dfc769b5dac3846094a6907 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 00:38:02 +0100 Subject: [PATCH 116/256] Administration module fully translatable!!! Partially tested though, report any missing keys. --- .../Modules/Administration/Administration.cs | 216 ++++++----- .../Administration/Commands/Migration.cs | 2 +- .../Resources/ResponseStrings.Designer.cs | 336 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 114 ++++++ 4 files changed, 552 insertions(+), 116 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 21dceacc..97b872b9 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -1,18 +1,14 @@ using Discord; using Discord.Commands; using NadekoBot.Extensions; -using Newtonsoft.Json; using System; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using NadekoBot.Services; using NadekoBot.Attributes; using Discord.WebSocket; using NadekoBot.Services.Database.Models; -using System.Net.Http; -using System.IO; using static NadekoBot.Modules.Permissions.Permissions; using System.Collections.Concurrent; using NLog; @@ -22,7 +18,7 @@ namespace NadekoBot.Modules.Administration [NadekoModule("Administration", ".")] public partial class Administration : NadekoModule { - private static ConcurrentHashSet DeleteMessagesOnCommand { get; } = new ConcurrentHashSet(); + private static ConcurrentHashSet deleteMessagesOnCommand { get; } private new static Logger _log { get; } @@ -31,7 +27,7 @@ namespace NadekoBot.Modules.Administration _log = LogManager.GetCurrentClassLogger(); NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; - DeleteMessagesOnCommand = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); + deleteMessagesOnCommand = new ConcurrentHashSet(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); } @@ -44,7 +40,7 @@ namespace NadekoBot.Modules.Administration var channel = msg.Channel as SocketTextChannel; if (channel == null) return; - if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune") + if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune") await msg.DeleteAsync().ConfigureAwait(false); } catch (Exception ex) @@ -71,17 +67,17 @@ namespace NadekoBot.Modules.Administration PermRole = config.PermissionRole, Verbose = config.VerbosePermissions, }; - Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id, + Cache.AddOrUpdate(channel.Guild.Id, toAdd, (id, old) => toAdd); await uow.CompleteAsync(); } - - await channel.SendConfirmAsync($"{Context.Message.Author.Mention} 🆗 **Permissions for this server are reset.**"); + await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.Administrator)] + [RequireBotPermission(GuildPermission.ManageMessages)] public async Task Delmsgoncmd() { bool enabled; @@ -94,29 +90,31 @@ namespace NadekoBot.Modules.Administration } if (enabled) { - DeleteMessagesOnCommand.Add(Context.Guild.Id); - await Context.Channel.SendConfirmAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false); + deleteMessagesOnCommand.Add(Context.Guild.Id); + await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false); } else { - DeleteMessagesOnCommand.TryRemove(Context.Guild.Id); - await Context.Channel.SendConfirmAsync("❗**Stopped automatic deletion of successful command invokations.**").ConfigureAwait(false); + deleteMessagesOnCommand.TryRemove(Context.Guild.Id); + await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireBotPermission(GuildPermission.ManageRoles)] public async Task Setrole(IGuildUser usr, [Remainder] IRole role) { try { await usr.AddRolesAsync(role).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"ℹ️ Successfully added role **{role.Name}** to user **{usr.Username}**").ConfigureAwait(false); + await ReplyConfirmLocalized("setrole", Format.Bold(role.Name), Format.Bold(usr.ToString())) + .ConfigureAwait(false); } catch (Exception ex) { - await Context.Channel.SendErrorAsync("⚠️ Failed to add role. **Bot has insufficient permissions.**\n").ConfigureAwait(false); + await ReplyErrorLocalized("setrole_err").ConfigureAwait(false); Console.WriteLine(ex.ToString()); } } @@ -124,82 +122,81 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireBotPermission(GuildPermission.ManageRoles)] public async Task Removerole(IGuildUser usr, [Remainder] IRole role) { try { await usr.RemoveRolesAsync(role).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"ℹ️ Successfully removed role **{role.Name}** from user **{usr.Username}**").ConfigureAwait(false); + await ReplyConfirmLocalized("remrole", Format.Bold(role.Name), Format.Bold(usr.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ Failed to remove role. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false); + await ReplyErrorLocalized("remrole_err").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireBotPermission(GuildPermission.ManageRoles)] public async Task RenameRole(IRole roleToEdit, string newname) { try { if (roleToEdit.Position > (await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position)) { - await Context.Channel.SendErrorAsync("🚫 You can't edit roles higher than your highest role.").ConfigureAwait(false); + await ReplyErrorLocalized("renrole_perms").ConfigureAwait(false); return; } await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("✅ Role renamed.").ConfigureAwait(false); + await ReplyConfirmLocalized("renrole").ConfigureAwait(false); } catch (Exception) { - await Context.Channel.SendErrorAsync("⚠️ Failed to rename role. Probably **insufficient permissions.**").ConfigureAwait(false); + await ReplyErrorLocalized("renrole_err").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireBotPermission(GuildPermission.ManageRoles)] public async Task RemoveAllRoles([Remainder] IGuildUser user) { try { await user.RemoveRolesAsync(user.GetRoles()).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"🗑 Successfully removed **all** roles from user **{user.Username}**").ConfigureAwait(false); + await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("⚠️ Failed to remove roles. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false); + await ReplyErrorLocalized("rar_err").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireBotPermission(GuildPermission.ManageRoles)] public async Task CreateRole([Remainder] string roleName = null) { if (string.IsNullOrWhiteSpace(roleName)) return; - try - { - var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"✅ Successfully created role **{r.Name}**.").ConfigureAwait(false); - } - catch (Exception) - { - await Context.Channel.SendErrorAsync("⚠️ Unspecified error.").ConfigureAwait(false); - } + + var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false); + await ReplyConfirmLocalized("cr", Format.Bold(r.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageRoles)] + [RequireBotPermission(GuildPermission.ManageRoles)] public async Task RoleColor(params string[] args) { - if (args.Count() != 2 && args.Count() != 4) + if (args.Length != 2 && args.Length != 4) { - await Context.Channel.SendErrorAsync("❌ The parameters specified are **invalid.**").ConfigureAwait(false); + await ReplyErrorLocalized("rc_params").ConfigureAwait(false); return; } var roleName = args[0].ToUpperInvariant(); @@ -207,7 +204,7 @@ namespace NadekoBot.Modules.Administration if (role == null) { - await Context.Channel.SendErrorAsync("🚫 That role **does not exist.**").ConfigureAwait(false); + await ReplyErrorLocalized("rc_not_exist").ConfigureAwait(false); return; } try @@ -220,64 +217,54 @@ namespace NadekoBot.Modules.Administration var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16)); await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false); + await ReplyConfirmLocalized("rc", Format.Bold(role.Name)).ConfigureAwait(false); } catch (Exception) { - await Context.Channel.SendErrorAsync("⚠️ Error occured, most likely **invalid parameters** or **insufficient permissions.**").ConfigureAwait(false); + await ReplyErrorLocalized("rc_perms").ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.BanMembers)] + [RequireBotPermission(GuildPermission.BanMembers)] public async Task Ban(IGuildUser user, [Remainder] string msg = null) { - if (string.IsNullOrWhiteSpace(msg)) - { - msg = "❗️No reason provided."; - } if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())) { - await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.").ConfigureAwait(false); + await ReplyErrorLocalized("hierarchy").ConfigureAwait(false); return; } if (!string.IsNullOrWhiteSpace(msg)) { try { - await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{Context.Guild.Name}` server.**\n" + - $"⚖ *Reason:* {msg}").ConfigureAwait(false); + await user.SendErrorAsync(GetText("bandm", Format.Bold(Context.Guild.Name), msg)); await Task.Delay(2000).ConfigureAwait(false); } catch { } } - try - { - await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("⛔️ **Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); - } - catch - { - await Context.Channel.SendErrorAsync("⚠️ **Error.** Most likely I don't have sufficient permissions.").ConfigureAwait(false); - } + await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle("⛔️ " + GetText("banned_user")) + .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true))) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.KickMembers)] [RequireUserPermission(GuildPermission.ManageMessages)] + [RequireBotPermission(GuildPermission.BanMembers)] public async Task Softban(IGuildUser user, [Remainder] string msg = null) { - if (string.IsNullOrWhiteSpace(msg)) - { - msg = "❗️No reason provided."; - } if (Context.User.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()) { - await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); + await ReplyErrorLocalized("hierarchy").ConfigureAwait(false); return; } @@ -285,161 +272,159 @@ namespace NadekoBot.Modules.Administration { try { - await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{Context.Guild.Name}` server.**\n" + - $"⚖ *Reason:* {msg}").ConfigureAwait(false); + await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg)); await Task.Delay(2000).ConfigureAwait(false); } catch { } } - try - { - await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); - try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); } - catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); } - - await Context.Channel.SendConfirmAsync("☣ **Soft-Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); - } - catch - { - await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false); - } + await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); + try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); } + catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); } + + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle("☣ " + GetText("sb_user")) + .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true))) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.KickMembers)] + [RequireBotPermission(GuildPermission.KickMembers)] public async Task Kick(IGuildUser user, [Remainder] string msg = null) { - if (user == null) - { - await Context.Channel.SendErrorAsync("❗️User not found.").ConfigureAwait(false); - return; - } - if (Context.Message.Author.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()) { - await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); + await ReplyErrorLocalized("hierarchy").ConfigureAwait(false); return; } if (!string.IsNullOrWhiteSpace(msg)) { try { - await user.SendErrorAsync($"‼️**You have been KICKED from `{Context.Guild.Name}` server.**\n" + - $"⚖ *Reason:* {msg}").ConfigureAwait(false); + await user.SendErrorAsync(GetText("kickdm", Format.Bold(Context.Guild.Name), msg)); await Task.Delay(2000).ConfigureAwait(false); } catch { } } - try - { - await user.KickAsync().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync("‼️**Kicked** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); - } - catch - { - await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false); - } + + await user.KickAsync().ConfigureAwait(false); + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle(GetText("kicked_user")) + .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true))) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.DeafenMembers)] + [RequireBotPermission(GuildPermission.DeafenMembers)] public async Task Deafen(params IGuildUser[] users) { if (!users.Any()) return; - try + foreach (var u in users) { - foreach (var u in users) + try { await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false); - } - catch - { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + catch + { + // ignored + } } + await ReplyConfirmLocalized("deafen").ConfigureAwait(false); } + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.DeafenMembers)] + [RequireBotPermission(GuildPermission.DeafenMembers)] public async Task UnDeafen(params IGuildUser[] users) { if (!users.Any()) return; - try + + foreach (var u in users) { - foreach (var u in users) + try { await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false); - } - catch - { - await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); + catch + { + // ignored + } } + await ReplyConfirmLocalized("undeafen").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] public async Task DelVoiChanl([Remainder] IVoiceChannel voiceChannel) { await voiceChannel.DeleteAsync().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"🗑 Removed voice channel **{voiceChannel.Name}** successfully.").ConfigureAwait(false); + await ReplyConfirmLocalized("delvoich", Format.Bold(voiceChannel.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] public async Task CreatVoiChanl([Remainder] string channelName) { var ch = await Context.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"✅ Created voice channel **{ch.Name}**. ID: `{ch.Id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("createvoich",Format.Bold(ch.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] public async Task DelTxtChanl([Remainder] ITextChannel toDelete) { await toDelete.DeleteAsync().ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"🗑 Removed text channel **{toDelete.Name}**. ID: `{toDelete.Id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("deltextchan", Format.Bold(toDelete.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] public async Task CreaTxtChanl([Remainder] string channelName) { var txtCh = await Context.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"✅ Added text channel **{txtCh.Name}**. ID: `{txtCh.Id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("createtextchan", Format.Bold(txtCh.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] public async Task SetTopic([Remainder] string topic = null) { var channel = (ITextChannel)Context.Channel; topic = topic ?? ""; await channel.ModifyAsync(c => c.Topic = topic); - await channel.SendConfirmAsync("🆗 **New channel topic set.**").ConfigureAwait(false); + await ReplyConfirmLocalized("set_topic").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(GuildPermission.ManageChannels)] + [RequireBotPermission(GuildPermission.ManageChannels)] public async Task SetChanlName([Remainder] string name) { var channel = (ITextChannel)Context.Channel; await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false); - await channel.SendConfirmAsync("🆗 **New channel name set.**").ConfigureAwait(false); + await ReplyConfirmLocalized("set_channel_name").ConfigureAwait(false); } @@ -459,6 +444,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(ChannelPermission.ManageMessages)] + [RequireBotPermission(GuildPermission.ManageMessages)] public async Task Prune(int count) { if (count < 1) @@ -474,6 +460,7 @@ namespace NadekoBot.Modules.Administration [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] [RequireUserPermission(ChannelPermission.ManageMessages)] + [RequireBotPermission(GuildPermission.ManageMessages)] public async Task Prune(IGuildUser user, int count = 100) { if (count < 1) @@ -492,7 +479,7 @@ namespace NadekoBot.Modules.Administration [RequireUserPermission(GuildPermission.MentionEveryone)] public async Task MentionRole(params IRole[] roles) { - string send = $"❕{Context.User.Mention} has invoked a mention on the following roles ❕"; + string send = "❕" +GetText("menrole",Context.User.Mention); foreach (var role in roles) { send += $"\n**{role.Name}**\n"; @@ -510,7 +497,7 @@ namespace NadekoBot.Modules.Administration await Context.Channel.SendMessageAsync(send).ConfigureAwait(false); } - IGuild _nadekoSupportServer; + private IGuild _nadekoSupportServer; [NadekoCommand, Usage, Description, Aliases] public async Task Donators() { @@ -520,7 +507,7 @@ namespace NadekoBot.Modules.Administration { donatorsOrdered = uow.Donators.GetDonatorsOrdered(); } - await Context.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); _nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); @@ -543,8 +530,7 @@ namespace NadekoBot.Modules.Administration don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount); await uow.CompleteAsync(); } - - await Context.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false); + await ReplyConfirmLocalized("donadd", don.Amount).ConfigureAwait(false); } //[NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Administration/Commands/Migration.cs b/src/NadekoBot/Modules/Administration/Commands/Migration.cs index 8ee6f652..802e5ae9 100644 --- a/src/NadekoBot/Modules/Administration/Commands/Migration.cs +++ b/src/NadekoBot/Modules/Administration/Commands/Migration.cs @@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Administration { private const int CURRENT_VERSION = 1; - private static Logger _log { get; } + private new static readonly Logger _log; static Migration() { diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index bb9992f1..d8db88cd 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -95,6 +95,16 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You have been banned from {0} server. + ///Reason: {1}. + /// + public static string administration_bandm { + get { + return ResourceManager.GetString("administration_bandm", resourceCulture); + } + } + /// /// Looks up a localized string similar to banned. /// @@ -104,6 +114,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to User Banned. + /// + public static string administration_banned_user { + get { + return ResourceManager.GetString("administration_banned_user", resourceCulture); + } + } + /// /// Looks up a localized string similar to Bot name changed to {0}. /// @@ -230,6 +249,42 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Sucessfully created role {0}. + /// + public static string administration_cr { + get { + return ResourceManager.GetString("administration_cr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text channel {0} created.. + /// + public static string administration_createtextchan { + get { + return ResourceManager.GetString("administration_createtextchan", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voice channel {0} created.. + /// + public static string administration_createvoich { + get { + return ResourceManager.GetString("administration_createvoich", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deafen successful.. + /// + public static string administration_deafen { + get { + return ResourceManager.GetString("administration_deafen", resourceCulture); + } + } + /// /// Looks up a localized string similar to Deleted server {0}. /// @@ -239,6 +294,42 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Stopped automatic deletion of successful command invokations.. + /// + public static string administration_delmsg_off { + get { + return ResourceManager.GetString("administration_delmsg_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Now automatically deleting sucessful command invokations.. + /// + public static string administration_delmsg_on { + get { + return ResourceManager.GetString("administration_delmsg_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Text channel {0} deleted.. + /// + public static string administration_deltextchan { + get { + return ResourceManager.GetString("administration_deltextchan", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voice channel {0} deleted.. + /// + public static string administration_delvoich { + get { + return ResourceManager.GetString("administration_delvoich", resourceCulture); + } + } + /// /// Looks up a localized string similar to DM from. /// @@ -248,6 +339,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Sucessfully added a new donator.Total donated amount from this user: {0} 👑. + /// + public static string administration_donadd { + get { + return ResourceManager.GetString("administration_donadd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Thanks to the people listed below for making this project hjappen!. + /// + public static string administration_donators { + get { + return ResourceManager.GetString("administration_donators", resourceCulture); + } + } + /// /// Looks up a localized string similar to I will forward DMs to all owners.. /// @@ -392,6 +501,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You can't use this command on users with a role higher or equal to yours in the role hierarchy.. + /// + public static string administration_hierarchy { + get { + return ResourceManager.GetString("administration_hierarchy", resourceCulture); + } + } + /// /// Looks up a localized string similar to Images loaded after {0} seconds!. /// @@ -428,6 +546,25 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You have been kicked from {0} server. + ///Reason: {1}. + /// + public static string administration_kickdm { + get { + return ResourceManager.GetString("administration_kickdm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User Kicked. + /// + public static string administration_kicked_user { + get { + return ResourceManager.GetString("administration_kicked_user", resourceCulture); + } + } + /// /// Looks up a localized string similar to List Of Languages ///{0}. @@ -564,6 +701,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} has invoked a mention on the following roles. + /// + public static string administration_menrole { + get { + return ResourceManager.GetString("administration_menrole", resourceCulture); + } + } + /// /// Looks up a localized string similar to Message from {0} `[Bot Owner]`:. /// @@ -735,6 +881,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Error. Most likely I don't have sufficient permissions.. + /// + public static string administration_perms { + get { + return ResourceManager.GetString("administration_perms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Permissions for this server are reset.. + /// + public static string administration_perms_reset { + get { + return ResourceManager.GetString("administration_perms_reset", resourceCulture); + } + } + /// /// Looks up a localized string similar to Active Protections. /// @@ -807,6 +971,105 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Successfully removed all roles from user {0}. + /// + public static string administration_rar { + get { + return ResourceManager.GetString("administration_rar", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to remove roles. I have insufficient permissions.. + /// + public static string administration_rar_err { + get { + return ResourceManager.GetString("administration_rar_err", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Color of {0} role has been changed.. + /// + public static string administration_rc { + get { + return ResourceManager.GetString("administration_rc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That role does not exist.. + /// + public static string administration_rc_not_exist { + get { + return ResourceManager.GetString("administration_rc_not_exist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The parameters specified are invalid.. + /// + public static string administration_rc_params { + get { + return ResourceManager.GetString("administration_rc_params", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error occured due to invalid color or insufficient permissions.. + /// + public static string administration_rc_perms { + get { + return ResourceManager.GetString("administration_rc_perms", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Successfully removed role {0} from user {1}. + /// + public static string administration_remrole { + get { + return ResourceManager.GetString("administration_remrole", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to remove role. I have insufficient permissions.. + /// + public static string administration_remrole_err { + get { + return ResourceManager.GetString("administration_remrole_err", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Role renamed.. + /// + public static string administration_renrole { + get { + return ResourceManager.GetString("administration_renrole", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to rename role. I have insufficient permissions.. + /// + public static string administration_renrole_err { + get { + return ResourceManager.GetString("administration_renrole_err", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't edit roles higher than your highest role.. + /// + public static string administration_renrole_perms { + get { + return ResourceManager.GetString("administration_renrole_perms", resourceCulture); + } + } + /// /// Looks up a localized string similar to Removed the playing message: {0}. /// @@ -997,6 +1260,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to New channel name set.. + /// + public static string administration_set_channel_name { + get { + return ResourceManager.GetString("administration_set_channel_name", resourceCulture); + } + } + /// /// Looks up a localized string similar to New game set!. /// @@ -1015,6 +1287,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to New channel topic set.. + /// + public static string administration_set_topic { + get { + return ResourceManager.GetString("administration_set_topic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sucessfully added role {0} to user {1}. + /// + public static string administration_setrole { + get { + return ResourceManager.GetString("administration_setrole", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to add role. I have insufficient permissions.. + /// + public static string administration_setrole_err { + get { + return ResourceManager.GetString("administration_setrole_err", resourceCulture); + } + } + /// /// Looks up a localized string similar to Shard {0} reconnected.. /// @@ -1124,6 +1423,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Undeafen successful.. + /// + public static string administration_undeafen { + get { + return ResourceManager.GetString("administration_undeafen", resourceCulture); + } + } + /// /// Looks up a localized string similar to Unmuted. /// @@ -1268,6 +1576,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Username. + /// + public static string administration_username { + get { + return ResourceManager.GetString("administration_username", resourceCulture); + } + } + /// /// Looks up a localized string similar to Username Changed. /// @@ -1376,6 +1693,16 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to You have been soft-banned from {0} server. + ///Reason: {1}. + /// + public static string administraton_sbdm { + get { + return ResourceManager.GetString("administraton_sbdm", resourceCulture); + } + } + /// /// Looks up a localized string similar to User Unbanned. /// @@ -1412,6 +1739,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to User Soft-Banned. + /// + public static string adminsitration_sb_user { + get { + return ResourceManager.GetString("adminsitration_sb_user", resourceCulture); + } + } + /// /// Looks up a localized string similar to That base is already claimed or destroyed.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index f044bf3a..7fabfa84 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -310,10 +310,17 @@ Avatar Changed + + You have been banned from {0} server. +Reason: {1} + banned PLURAL + + User Banned + Bot name changed to {0} @@ -356,12 +363,42 @@ Content + + Sucessfully created role {0} + + + Text channel {0} created. + + + Voice channel {0} created. + + + Deafen successful. + Deleted server {0} + + Stopped automatic deletion of successful command invokations. + + + Now automatically deleting sucessful command invokations. + + + Text channel {0} deleted. + + + Voice channel {0} deleted. + DM from + + Sucessfully added a new donator.Total donated amount from this user: {0} 👑 + + + Thanks to the people listed below for making this project hjappen! + I will forward DMs to all owners. @@ -410,6 +447,9 @@ Greet announcements enabled on this channel. + + You can't use this command on users with a role higher or equal to yours in the role hierarchy. + Images loaded after {0} seconds! @@ -422,6 +462,13 @@ {0} has joined {1} + + You have been kicked from {0} server. +Reason: {1} + + + User Kicked + List Of Languages {0} @@ -468,6 +515,9 @@ Stopped logging {0} event. + + {0} has invoked a mention on the following roles + Message from {0} `[Bot Owner]`: @@ -527,6 +577,12 @@ Old Topic + + Error. Most likely I don't have sufficient permissions. + + + Permissions for this server are reset. + Active Protections @@ -551,6 +607,39 @@ Time must be between {0} and {1} seconds. + + Successfully removed all roles from user {0} + + + Failed to remove roles. I have insufficient permissions. + + + Color of {0} role has been changed. + + + That role does not exist. + + + The parameters specified are invalid. + + + Error occured due to invalid color or insufficient permissions. + + + Successfully removed role {0} from user {1} + + + Failed to remove role. I have insufficient permissions. + + + Role renamed. + + + Failed to rename role. I have insufficient permissions. + + + You can't edit roles higher than your highest role. + Removed the playing message: {0} @@ -612,15 +701,27 @@ You now have {0} role. + + Sucessfully added role {0} to user {1} + + + Failed to add role. I have insufficient permissions. + New avatar set! + + New channel name set. + New game set! New stream set! + + New channel topic set. + Shard {0} reconnected. @@ -659,10 +760,16 @@ Text Channel Destroyed + + Undeafen successful. + Unmuted singular + + Username + Username Changed @@ -744,6 +851,10 @@ User {0} from voice chat + + You have been soft-banned from {0} server. +Reason: {1} + User Unbanned @@ -756,6 +867,9 @@ Presence Updates + + User Soft-Banned + Back to ToC From 8983c9c37e4590732e2ef091396bcc0ef4c7bdaf Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 00:59:10 +0100 Subject: [PATCH 117/256] small cleanup --- .../Modules/Administration/Administration.cs | 14 ++-- .../Gambling/Commands/WaifuClaimCommands.cs | 2 +- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 67 ++++++++----------- 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 97b872b9..eb07ef1f 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Administration { private static ConcurrentHashSet deleteMessagesOnCommand { get; } - private new static Logger _log { get; } + private new static readonly Logger _log; static Administration() { @@ -209,7 +209,7 @@ namespace NadekoBot.Modules.Administration } try { - var rgb = args.Count() == 4; + var rgb = args.Length == 4; var arg1 = args[1].Replace("#", ""); var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16)); @@ -244,7 +244,10 @@ namespace NadekoBot.Modules.Administration await Task.Delay(2000).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); @@ -275,7 +278,10 @@ namespace NadekoBot.Modules.Administration await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg)); await Task.Delay(2000).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } } await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 1426bebd..8c6cfcd5 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -421,7 +421,7 @@ namespace NadekoBot.Modules.Gambling var embed = new EmbedBuilder() .WithOkColor() - .WithTitle("Waifu " + w.Waifu.ToString() + " - \"the " + claimInfo.Title + "\"") + .WithTitle("Waifu " + w.Waifu + " - \"the " + claimInfo.Title + "\"") .AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true)) .AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true)) diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index e2679147..08d3976f 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -12,27 +12,25 @@ using System; using Newtonsoft.Json; using System.IO; using System.Collections.Concurrent; -using NadekoBot.Modules; -using NadekoBot.Resources; namespace NadekoBot.Modules.Pokemon { [NadekoModule("Pokemon", ">")] - public partial class Pokemon : NadekoModule + public class Pokemon : NadekoModule { - private static List PokemonTypes = new List(); - private static ConcurrentDictionary Stats = new ConcurrentDictionary(); + private static readonly List _pokemonTypes = new List(); + private static readonly ConcurrentDictionary _stats = new ConcurrentDictionary(); public const string PokemonTypesFile = "data/pokemon_types.json"; - private static new Logger _log { get; } + private new static Logger _log { get; } static Pokemon() { _log = LogManager.GetCurrentClassLogger(); if (File.Exists(PokemonTypesFile)) { - PokemonTypes = JsonConvert.DeserializeObject>(File.ReadAllText(PokemonTypesFile)); + _pokemonTypes = JsonConvert.DeserializeObject>(File.ReadAllText(PokemonTypesFile)); } else { @@ -44,21 +42,18 @@ namespace NadekoBot.Modules.Pokemon private int GetDamage(PokemonType usertype, PokemonType targetType) { var rng = new Random(); - int damage = rng.Next(40, 60); - foreach (PokemonMultiplier Multiplier in usertype.Multipliers) + var damage = rng.Next(40, 60); + foreach (var multiplierObj in usertype.Multipliers) { - if (Multiplier.Type == targetType.Name) - { - var multiplier = Multiplier.Multiplication; - damage = (int)(damage * multiplier); - } + if (multiplierObj.Type != targetType.Name) continue; + damage = (int)(damage * multiplierObj.Multiplication); } return damage; } - private PokemonType GetPokeType(ulong id) + private static PokemonType GetPokeType(ulong id) { Dictionary setTypes; @@ -71,18 +66,18 @@ namespace NadekoBot.Modules.Pokemon { return StringToPokemonType(setTypes[id]); } - int count = PokemonTypes.Count; + var count = _pokemonTypes.Count; - int remainder = Math.Abs((int)(id % (ulong)count)); + var remainder = Math.Abs((int)(id % (ulong)count)); - return PokemonTypes[remainder]; + return _pokemonTypes[remainder]; } - private PokemonType StringToPokemonType(string v) + private static PokemonType StringToPokemonType(string v) { var str = v?.ToUpperInvariant(); - var list = PokemonTypes; - foreach (PokemonType p in list) + var list = _pokemonTypes; + foreach (var p in list) { if (str == p.Name) { @@ -116,8 +111,7 @@ namespace NadekoBot.Modules.Pokemon // Checking stats first, then move //Set up the userstats - PokeStats userStats; - userStats = Stats.GetOrAdd(user.Id, new PokeStats()); + var userStats = _stats.GetOrAdd(user.Id, new PokeStats()); //Check if able to move //User not able if HP < 0, has made more than 4 attacks @@ -137,8 +131,7 @@ namespace NadekoBot.Modules.Pokemon return; } //get target stats - PokeStats targetStats; - targetStats = Stats.GetOrAdd(targetUser.Id, new PokeStats()); + var targetStats = _stats.GetOrAdd(targetUser.Id, new PokeStats()); //If target's HP is below 0, no use attacking if (targetStats.Hp <= 0) @@ -184,11 +177,11 @@ namespace NadekoBot.Modules.Pokemon if (targetStats.Hp <= 0) { - response += $"\n" + GetText("fainted", Format.Bold(targetUser.ToString())); + response += "\n" + GetText("fainted", Format.Bold(targetUser.ToString())); } else { - response += $"\n" + GetText("hp_remaining", Format.Bold(targetUser.ToString()), targetStats.Hp); + response += "\n" + GetText("hp_remaining", Format.Bold(targetUser.ToString()), targetStats.Hp); } //update other stats @@ -202,8 +195,8 @@ namespace NadekoBot.Modules.Pokemon //update dictionary //This can stay the same right? - Stats[user.Id] = userStats; - Stats[targetUser.Id] = targetStats; + _stats[user.Id] = userStats; + _stats[targetUser.Id] = targetStats; await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false); } @@ -235,9 +228,9 @@ namespace NadekoBot.Modules.Pokemon return; } - if (Stats.ContainsKey(targetUser.Id)) + if (_stats.ContainsKey(targetUser.Id)) { - var targetStats = Stats[targetUser.Id]; + var targetStats = _stats[targetUser.Id]; if (targetStats.Hp == targetStats.MaxHp) { await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false); @@ -261,7 +254,7 @@ namespace NadekoBot.Modules.Pokemon if (targetStats.Hp < 0) { //Could heal only for half HP? - Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2); + _stats[targetUser.Id].Hp = (targetStats.MaxHp / 2); if (target == "yourself") { await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); @@ -271,7 +264,6 @@ namespace NadekoBot.Modules.Pokemon await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); } await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); - return; } else { @@ -299,7 +291,7 @@ namespace NadekoBot.Modules.Pokemon var targetType = StringToPokemonType(typeTargeted); if (targetType == null) { - await Context.Channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), + await Context.Channel.EmbedAsync(_pokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name) .WithValue(pt.Icon) .WithIsInline(true))) @@ -324,12 +316,11 @@ namespace NadekoBot.Modules.Pokemon } //Actually changing the type here - Dictionary setTypes; using (var uow = DbHandler.UnitOfWork()) { - var pokeUsers = uow.PokeGame.GetAll(); - setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type); + var pokeUsers = uow.PokeGame.GetAll().ToArray(); + var setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type); var pt = new UserPokeTypes { UserId = user.Id, @@ -343,7 +334,7 @@ namespace NadekoBot.Modules.Pokemon else { //update user in db - var pokeUserCmd = pokeUsers.Where(p => p.UserId == user.Id).FirstOrDefault(); + var pokeUserCmd = pokeUsers.FirstOrDefault(p => p.UserId == user.Id); pokeUserCmd.type = targetType.Name; uow.PokeGame.Update(pokeUserCmd); } From 45f69bac585ea7694fed06c2c8f9c002b6d12fd1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 11:03:40 +0100 Subject: [PATCH 118/256] Cleanup, bugfixes, Slots, flip, gambling commands localizable. --- .../Gambling/Commands/CurrencyEvents.cs | 2 +- .../Gambling/Commands/DiceRollCommand.cs | 2 +- .../Modules/Gambling/Commands/DrawCommand.cs | 2 +- .../Gambling/Commands/FlipCoinCommand.cs | 25 +- .../Modules/Gambling/Commands/Slots.cs | 64 ++--- .../Gambling/Commands/WaifuClaimCommands.cs | 2 +- src/NadekoBot/Modules/Gambling/Gambling.cs | 105 ++++---- src/NadekoBot/Modules/NadekoModule.cs | 34 ++- src/NadekoBot/NadekoBot.xproj.DotSettings | 3 +- .../Resources/ResponseStrings.Designer.cs | 234 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 81 ++++++ 11 files changed, 439 insertions(+), 115 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index 02c8803e..4681d91b 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { [Group] - public class CurrencyEvents : ModuleBase + public class CurrencyEvents : NadekoSubmodule { public enum CurrencyEvent { diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 4616aa91..4bda25a4 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { [Group] - public class DriceRollCommands : ModuleBase + public class DriceRollCommands : NadekoSubmodule { private Regex dndRegex { get; } = new Regex(@"^(?\d+)d(?\d+)(?:\+(?\d+))?(?:\-(?\d+))?$", RegexOptions.Compiled); private Regex fudgeRegex { get; } = new Regex(@"^(?\d+)d(?:F|f)$", RegexOptions.Compiled); diff --git a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs index adbe4913..b9ae375b 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { [Group] - public class DrawCommands : ModuleBase + public class DrawCommands : NadekoSubmodule { private static readonly ConcurrentDictionary AllDecks = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 6ccfe786..973bc90e 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -1,12 +1,10 @@ using Discord; using Discord.Commands; -using ImageSharp; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; using System; using System.Collections.Generic; -using System.IO; using System.Threading.Tasks; using Image = ImageSharp.Image; @@ -15,7 +13,7 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { [Group] - public class FlipCoinCommands : ModuleBase + public class FlipCoinCommands : NadekoSubmodule { private readonly IImagesService _images; @@ -24,7 +22,7 @@ namespace NadekoBot.Modules.Gambling public FlipCoinCommands() { //todo DI in the future, can't atm - this._images = NadekoBot.Images; + _images = NadekoBot.Images; } [NadekoCommand, Usage, Description, Aliases] @@ -36,21 +34,21 @@ namespace NadekoBot.Modules.Gambling { using (var heads = _images.Heads.ToStream()) { - await Context.Channel.SendFileAsync(heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads"))) + ".").ConfigureAwait(false); } } else { using (var tails = _images.Tails.ToStream()) { - await Context.Channel.SendFileAsync(tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); + await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails"))) + ".").ConfigureAwait(false); } } return; } if (count > 10 || count < 1) { - await Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`").ConfigureAwait(false); + await ReplyErrorLocalized("flip_invalid", 10).ConfigureAwait(false); return; } var imgs = new Image[count]; @@ -76,14 +74,13 @@ namespace NadekoBot.Modules.Gambling if (amount < NadekoBot.BotConfig.MinimumBetAmount) { - await Context.Channel.SendErrorAsync($"You can't bet less than {NadekoBot.BotConfig.MinimumBetAmount}{CurrencySign}.") - .ConfigureAwait(false); + await ReplyErrorLocalized("min_bet_limit", NadekoBot.BotConfig.MinimumBetAmount + CurrencySign).ConfigureAwait(false); return; } var removed = await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false); if (!removed) { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false); + await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); return; } //heads = true @@ -91,7 +88,7 @@ namespace NadekoBot.Modules.Gambling //todo this seems stinky, no time to look at it right now var isHeads = guessStr == "HEADS" || guessStr == "H"; - bool result = false; + var result = false; IEnumerable imageToSend; if (rng.Next(0, 2) == 1) { @@ -107,12 +104,12 @@ namespace NadekoBot.Modules.Gambling if (isHeads == result) { var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier); - str = $"{Context.User.Mention}`You guessed it!` You won {toWin}{CurrencySign}"; - await CurrencyHandler.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false); + str = Context.User.Mention + " " + GetText("flip_guess", toWin + CurrencySign); + await CurrencyHandler.AddCurrencyAsync(Context.User, GetText("betflip_gamble"), toWin, false).ConfigureAwait(false); } else { - str = $"{Context.User.Mention}`Better luck next time.`"; + str = Context.User.Mention + " " + GetText("better_luck"); } using (var toSend = imageToSend.ToStream()) { diff --git a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs index bd977e9b..c298a9b4 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs @@ -5,7 +5,6 @@ using NadekoBot.Extensions; using NadekoBot.Services; using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; using System.Threading; @@ -16,12 +15,12 @@ namespace NadekoBot.Modules.Gambling public partial class Gambling { [Group] - public class Slots : ModuleBase + public class Slots : NadekoSubmodule { - private static int totalBet = 0; - private static int totalPaidOut = 0; + private static int _totalBet; + private static int _totalPaidOut; - const int alphaCutOut = byte.MaxValue / 3; + private const int _alphaCutOut = byte.MaxValue / 3; //here is a payout chart //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg @@ -31,14 +30,14 @@ namespace NadekoBot.Modules.Gambling public Slots() { - this._images = NadekoBot.Images; + _images = NadekoBot.Images; } public class SlotMachine { public const int MaxValue = 5; - static readonly List> winningCombos = new List>() + static readonly List> _winningCombos = new List>() { //three flowers (arr) => arr.All(a=>a==MaxValue) ? 30 : 0, @@ -53,14 +52,14 @@ namespace NadekoBot.Modules.Gambling public static SlotResult Pull() { var numbers = new int[3]; - for (int i = 0; i < numbers.Length; i++) + for (var i = 0; i < numbers.Length; i++) { numbers[i] = new NadekoRandom().Next(0, MaxValue + 1); } - int multi = 0; - for (int i = 0; i < winningCombos.Count; i++) + var multi = 0; + foreach (var t in _winningCombos) { - multi = winningCombos[i](numbers); + multi = t(numbers); if (multi != 0) break; } @@ -74,8 +73,8 @@ namespace NadekoBot.Modules.Gambling public int Multiplier { get; } public SlotResult(int[] nums, int multi) { - this.Numbers = nums; - this.Multiplier = multi; + Numbers = nums; + Multiplier = multi; } } } @@ -85,8 +84,8 @@ namespace NadekoBot.Modules.Gambling public async Task SlotStats() { //i remembered to not be a moron - var paid = totalPaidOut; - var bet = totalBet; + var paid = _totalPaidOut; + var bet = _totalBet; if (bet <= 0) bet = 1; @@ -130,33 +129,34 @@ namespace NadekoBot.Modules.Gambling footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%"); } - static HashSet runningUsers = new HashSet(); + private static readonly HashSet _runningUsers = new HashSet(); [NadekoCommand, Usage, Description, Aliases] public async Task Slot(int amount = 0) { - if (!runningUsers.Add(Context.User.Id)) + if (!_runningUsers.Add(Context.User.Id)) return; try { if (amount < 1) { - await Context.Channel.SendErrorAsync($"You can't bet less than 1{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); + await ReplyErrorLocalized("min_bet_limit", 1 + CurrencySign).ConfigureAwait(false); return; } if (amount > 999) { - await Context.Channel.SendErrorAsync($"You can't bet more than 999{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); + GetText("slot_maxbet", 999 + CurrencySign); + await ReplyErrorLocalized("max_bet_limit", 999 + CurrencySign).ConfigureAwait(false); return; } if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false)) { - await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencySign}.").ConfigureAwait(false); + await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false); return; } - Interlocked.Add(ref totalBet, amount); + Interlocked.Add(ref _totalBet, amount); using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream()) { var bgImage = new ImageSharp.Image(bgFileStream); @@ -179,7 +179,7 @@ namespace NadekoBot.Modules.Gambling var x = 95 + 142 * i + j; int y = 330 + k; var toSet = toAdd[j, k]; - if (toSet.A < alphaCutOut) + if (toSet.A < _alphaCutOut) continue; bgPixels[x, y] = toAdd[j, k]; } @@ -203,7 +203,7 @@ namespace NadekoBot.Modules.Gambling { for (int j = 0; j < pixels.Height; j++) { - if (pixels[i, j].A < alphaCutOut) + if (pixels[i, j].A < _alphaCutOut) continue; var x = 230 - n * 16 + i; bgPixels[x, 462 + j] = pixels[i, j]; @@ -228,7 +228,7 @@ namespace NadekoBot.Modules.Gambling { for (int j = 0; j < pixels.Height; j++) { - if (pixels[i, j].A < alphaCutOut) + if (pixels[i, j].A < _alphaCutOut) continue; var x = 395 - n * 16 + i; bgPixels[x, 462 + j] = pixels[i, j]; @@ -240,22 +240,22 @@ namespace NadekoBot.Modules.Gambling } while ((printAmount /= 10) != 0); } - var msg = "Better luck next time ^_^"; + var msg = GetText("better_luck"); if (result.Multiplier != 0) { await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false); - Interlocked.Add(ref totalPaidOut, amount * result.Multiplier); + Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier); if (result.Multiplier == 1) - msg = $"A single {NadekoBot.BotConfig.CurrencySign}, x1 - Try again!"; + msg = GetText("slot_single", CurrencySign, 1); else if (result.Multiplier == 4) - msg = $"Good job! Two {NadekoBot.BotConfig.CurrencySign} - bet x4"; + msg = GetText("slot_two", CurrencySign, 4); else if (result.Multiplier == 10) - msg = "Wow! Lucky! Three of a kind! x10"; + msg = GetText("slot_three", 10); else if (result.Multiplier == 30) - msg = "WOAAHHHHHH!!! Congratulations!!! x30"; + msg = GetText("slot_jackpot", 30); } - await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`Bet:`{amount} `Won:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); + await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`{GetText("slot_bet")}:`{amount} `{GetText("slot_won")}:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); } } finally @@ -263,7 +263,7 @@ namespace NadekoBot.Modules.Gambling var _ = Task.Run(async () => { await Task.Delay(2000); - runningUsers.Remove(Context.User.Id); + _runningUsers.Remove(Context.User.Id); }); } } diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 8c6cfcd5..6ff7695d 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Gambling } [Group] - public class WaifuClaimCommands : ModuleBase + public class WaifuClaimCommands : NadekoSubmodule { private static ConcurrentDictionary _divorceCooldowns { get; } = new ConcurrentDictionary(); private static ConcurrentDictionary _affinityCooldowns { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 6bcf5654..6650bee5 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -1,9 +1,9 @@ -using Discord; +using System; +using Discord; using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using System.Linq; -using System.Text; using System.Threading.Tasks; using NadekoBot.Services; using NadekoBot.Services.Database.Models; @@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Gambling var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown); var membersArray = members as IUser[] ?? members.ToArray(); var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; - await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync("🎟 "+ GetText("raffled_user"), $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -50,15 +50,14 @@ namespace NadekoBot.Modules.Gambling public async Task Cash([Remainder] IUser user = null) { user = user ?? Context.User; - - await Context.Channel.SendConfirmAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); + await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [Priority(1)] public async Task Cash(ulong userId) { - await Context.Channel.SendConfirmAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); + await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -70,11 +69,12 @@ namespace NadekoBot.Modules.Gambling var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false); if (!success) { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false); + await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); return; } await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} gifted {amount}{CurrencySign} to {Format.Bold(receiver.ToString())}!").ConfigureAwait(false); + await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString())) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -93,8 +93,7 @@ namespace NadekoBot.Modules.Gambling return; await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false); - - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount}{CurrencySign} to <@{usrId}>!").ConfigureAwait(false); + await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -111,9 +110,10 @@ namespace NadekoBot.Modules.Gambling amount))) .ConfigureAwait(false); - await Context.Channel.SendConfirmAsync($"Awarded `{amount}` {CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.") - .ConfigureAwait(false); - + await ReplyConfirmLocalized("mass_award", + amount + CurrencySign, + Format.Bold(users.Count.ToString()), + Format.Bold(role.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -125,9 +125,9 @@ namespace NadekoBot.Modules.Gambling return; if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false)) - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user}!").ConfigureAwait(false); + await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false); else - await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user} because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false); + await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false); } @@ -139,9 +139,9 @@ namespace NadekoBot.Modules.Gambling return; if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false)) - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false); + await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false); else - await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false); + await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false); } //[NadekoCommand, Usage, Description, Aliases] @@ -205,49 +205,48 @@ namespace NadekoBot.Modules.Gambling if (amount < 1) return; - long userFlowers; - using (var uow = DbHandler.UnitOfWork()) + if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false)) { - userFlowers = uow.Currency.GetOrCreate(Context.User.Id).Amount; - } - - if (userFlowers < amount) - { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}. You only have {userFlowers}{CurrencySign}.").ConfigureAwait(false); + await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false); return; } - await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false); - - var rng = new NadekoRandom().Next(0, 101); - var str = $"{Context.User.Mention} `You rolled {rng}.` "; - if (rng < 67) + var rnd = new NadekoRandom().Next(0, 101); + var str = Context.User.Mention + Format.Code(GetText("roll", rnd)); + if (rnd < 67) { - str += "Better luck next time."; - } - else if (rng < 91) - { - str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll67Multiplier}{CurrencySign} for rolling above 66"; - await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false); - } - else if (rng < 100) - { - str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll91Multiplier}{CurrencySign} for rolling above 90."; - await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false); + str += GetText("better_luck"); } else { - str += $"👑 Congratulations! You won {amount * NadekoBot.BotConfig.Betroll100Multiplier}{CurrencySign} for rolling **100**. 👑"; - await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false); + if (rnd < 91) + { + str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll67Multiplier) + CurrencySign, 66); + await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", + (int) (amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false); + } + else if (rnd < 100) + { + str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll91Multiplier) + CurrencySign, 90); + await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", + (int) (amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false); + } + else + { + str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll100Multiplier) + CurrencySign, 100) + " 👑"; + await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", + (int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false); + } } - + Console.WriteLine("started sending"); await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false); + Console.WriteLine("done sending"); } [NadekoCommand, Usage, Description, Aliases] public async Task Leaderboard() { - var richest = new List(); + List richest; using (var uow = DbHandler.UnitOfWork()) { richest = uow.Currency.GetTopRichest(9).ToList(); @@ -255,22 +254,22 @@ namespace NadekoBot.Modules.Gambling if (!richest.Any()) return; - var embed = new EmbedBuilder() .WithOkColor() - .WithTitle(NadekoBot.BotConfig.CurrencySign + " Leaderboard"); + .WithTitle(NadekoBot.BotConfig.CurrencySign + " " + GetText("leaderboard")); for (var i = 0; i < richest.Count; i++) { var x = richest[i]; var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false); - var usrStr = ""; - if (usr == null) - usrStr = x.UserId.ToString(); - else - usrStr = usr.Username?.TrimTo(20, true); + var usrStr = usr == null + ? x.UserId.ToString() + : usr.Username?.TrimTo(20, true); - embed.AddField(efb => efb.WithName("#" + (i + 1) + " " + usrStr).WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign).WithIsInline(true)); + var j = i; + embed.AddField(efb => efb.WithName("#" + (j + 1) + " " + usrStr) + .WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign) + .WithIsInline(true)); } await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 9bb524ec..4c8d17d3 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -67,14 +67,26 @@ namespace NadekoBot.Modules if (string.IsNullOrWhiteSpace(text)) { LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS."); - return NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} found!"; + text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!"; + if (string.IsNullOrWhiteSpace(text)) + return "I cant tell if you command is executed, because there was an error printing out the response. Key '" + + lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this."; } return text; } - public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName, params object[] replacements) + public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName, + params object[] replacements) { - return string.Format(GetTextStatic(key, cultureInfo, lowerModuleTypeName), replacements); + try + { + return string.Format(GetTextStatic(key, cultureInfo, lowerModuleTypeName), replacements); + } + catch (FormatException) + { + return "I cant tell if you command is executed, because there was an error printing out the response. Key '" + + lowerModuleTypeName + "_" + key + "' " + "is not properly formatted. Please report this."; + } } protected string GetText(string key) => @@ -85,26 +97,26 @@ namespace NadekoBot.Modules public Task ErrorLocalized(string textKey, params object[] replacements) { - var text = GetText(textKey); - return Context.Channel.SendErrorAsync(string.Format(text, replacements)); + var text = GetText(textKey, replacements); + return Context.Channel.SendErrorAsync(text); } public Task ReplyErrorLocalized(string textKey, params object[] replacements) { - var text = GetText(textKey); - return Context.Channel.SendErrorAsync(Context.User.Mention + " " + string.Format(text, replacements)); + var text = GetText(textKey, replacements); + return Context.Channel.SendErrorAsync(Context.User.Mention + " " + text); } public Task ConfirmLocalized(string textKey, params object[] replacements) { - var text = GetText(textKey); - return Context.Channel.SendConfirmAsync(string.Format(text, replacements)); + var text = GetText(textKey, replacements); + return Context.Channel.SendConfirmAsync(text); } public Task ReplyConfirmLocalized(string textKey, params object[] replacements) { - var text = GetText(textKey); - return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + string.Format(text, replacements)); + var text = GetText(textKey, replacements); + return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text); } } diff --git a/src/NadekoBot/NadekoBot.xproj.DotSettings b/src/NadekoBot/NadekoBot.xproj.DotSettings index ae4d5dac..3a1cb1b5 100644 --- a/src/NadekoBot/NadekoBot.xproj.DotSettings +++ b/src/NadekoBot/NadekoBot.xproj.DotSettings @@ -1,2 +1,3 @@  - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index d8db88cd..95a0be9a 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2072,6 +2072,240 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to has awarded {0} to {1}. + /// + public static string gambling_awarded { + get { + return ResourceManager.GetString("gambling_awarded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Betflip Gamble. + /// + public static string gambling_betflip_gamble { + get { + return ResourceManager.GetString("gambling_betflip_gamble", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Better luck next time ^_^. + /// + public static string gambling_better_luck { + get { + return ResourceManager.GetString("gambling_better_luck", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Congratulations! You won {0} for rolling above {1}. + /// + public static string gambling_br_win { + get { + return ResourceManager.GetString("gambling_br_win", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You guessed it! You won {0}. + /// + public static string gambling_flip_guess { + get { + return ResourceManager.GetString("gambling_flip_guess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid number specified. You can flip 1 to {0} coins.. + /// + public static string gambling_flip_invalid { + get { + return ResourceManager.GetString("gambling_flip_invalid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to flipped {0}.. + /// + public static string gambling_flipped { + get { + return ResourceManager.GetString("gambling_flipped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to has gifted {0} to {1}. + /// + public static string gambling_gifted { + get { + return ResourceManager.GetString("gambling_gifted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has {1}. + /// + public static string gambling_has { + get { + return ResourceManager.GetString("gambling_has", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Heads. + /// + public static string gambling_heads { + get { + return ResourceManager.GetString("gambling_heads", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Leaderboard. + /// + public static string gambling_leaderboard { + get { + return ResourceManager.GetString("gambling_leaderboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Awarded {0} to {1} users from {2} role.. + /// + public static string gambling_mass_award { + get { + return ResourceManager.GetString("gambling_mass_award", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't bet more than {0}. + /// + public static string gambling_max_bet_limit { + get { + return ResourceManager.GetString("gambling_max_bet_limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't bet less than {0}. + /// + public static string gambling_min_bet_limit { + get { + return ResourceManager.GetString("gambling_min_bet_limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough {0}. + /// + public static string gambling_not_enough { + get { + return ResourceManager.GetString("gambling_not_enough", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Raffled User. + /// + public static string gambling_raffled_user { + get { + return ResourceManager.GetString("gambling_raffled_user", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You rolled {0}.. + /// + public static string gambling_roll { + get { + return ResourceManager.GetString("gambling_roll", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bet. + /// + public static string gambling_slot_bet { + get { + return ResourceManager.GetString("gambling_slot_bet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to WOAAHHHHHH!!! Congratulations!!! x{0}. + /// + public static string gambling_slot_jackpot { + get { + return ResourceManager.GetString("gambling_slot_jackpot", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A single {0}, x{1}. + /// + public static string gambling_slot_single { + get { + return ResourceManager.GetString("gambling_slot_single", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wow! Lucky! Three of a kind! x{0}. + /// + public static string gambling_slot_three { + get { + return ResourceManager.GetString("gambling_slot_three", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Good job! Two {0} - bet x{1}. + /// + public static string gambling_slot_two { + get { + return ResourceManager.GetString("gambling_slot_two", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Won. + /// + public static string gambling_slot_won { + get { + return ResourceManager.GetString("gambling_slot_won", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Tails. + /// + public static string gambling_tails { + get { + return ResourceManager.GetString("gambling_tails", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to successfully took {0} from {1}. + /// + public static string gambling_take { + get { + return ResourceManager.GetString("gambling_take", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to was unable to take {0} from{1} because the user doesn't have that much {2}!. + /// + public static string gambling_take_fail { + get { + return ResourceManager.GetString("gambling_take_fail", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to ToC. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 7fabfa84..6fee14d7 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -870,6 +870,87 @@ Reason: {1} User Soft-Banned + + has awarded {0} to {1} + + + Betflip Gamble + + + Better luck next time ^_^ + + + Congratulations! You won {0} for rolling above {1} + + + flipped {0}. + User flipped tails. + + + You guessed it! You won {0} + + + Invalid number specified. You can flip 1 to {0} coins. + + + has gifted {0} to {1} + X has gifted 15 flowers to Y + + + {0} has {1} + X has Y flowers + + + Heads + + + Leaderboard + + + Awarded {0} to {1} users from {2} role. + + + You can't bet more than {0} + + + You can't bet less than {0} + + + You don't have enough {0} + + + Raffled User + + + You rolled {0}. + + + Bet + + + WOAAHHHHHH!!! Congratulations!!! x{0} + + + A single {0}, x{1} + + + Wow! Lucky! Three of a kind! x{0} + + + Good job! Two {0} - bet x{1} + + + Won + + + Tails + + + successfully took {0} from {1} + + + was unable to take {0} from{1} because the user doesn't have that much {2}! + Back to ToC From 5ed0ee4ef8a78e5b5b5bc1e86afa4327b7c304e4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 11:17:55 +0100 Subject: [PATCH 119/256] Draw commands done --- .../Modules/Gambling/Commands/DrawCommand.cs | 23 +++++++++++-------- .../Resources/ResponseStrings.Designer.cs | 18 +++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 6 +++++ 3 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs index b9ae375b..7471f7d0 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs @@ -4,8 +4,6 @@ using ImageSharp; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Modules.Gambling.Models; -using NLog; -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; @@ -19,15 +17,15 @@ namespace NadekoBot.Modules.Gambling [Group] public class DrawCommands : NadekoSubmodule { - private static readonly ConcurrentDictionary AllDecks = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary _allDecks = new ConcurrentDictionary(); - private const string cardsPath = "data/images/cards"; + private const string _cardsPath = "data/images/cards"; [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Draw(int num = 1) { - var cards = AllDecks.GetOrAdd(Context.Guild, (s) => new Cards()); + var cards = _allDecks.GetOrAdd(Context.Guild, (s) => new Cards()); var images = new List(); var cardObjects = new List(); if (num > 5) num = 5; @@ -35,12 +33,19 @@ namespace NadekoBot.Modules.Gambling { if (cards.CardPool.Count == 0 && i != 0) { - try { await Context.Channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch { } + try + { + await ReplyErrorLocalized("no_more_cards").ConfigureAwait(false); + } + catch + { + // ignored + } break; } var currentCard = cards.DrawACard(); cardObjects.Add(currentCard); - using (var stream = File.OpenRead(Path.Combine(cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_'))) + using (var stream = File.OpenRead(Path.Combine(_cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_'))) images.Add(new Image(stream)); } MemoryStream bitmapStream = new MemoryStream(); @@ -59,7 +64,7 @@ namespace NadekoBot.Modules.Gambling { //var channel = (ITextChannel)Context.Channel; - AllDecks.AddOrUpdate(Context.Guild, + _allDecks.AddOrUpdate(Context.Guild, (g) => new Cards(), (g, c) => { @@ -67,7 +72,7 @@ namespace NadekoBot.Modules.Gambling return c; }); - await Context.Channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false); + await ReplyConfirmLocalized("deck_reshuffled").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 95a0be9a..7dda445c 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2108,6 +2108,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Deck reshuffled.. + /// + public static string gambling_deck_reshuffled { + get { + return ResourceManager.GetString("gambling_deck_reshuffled", resourceCulture); + } + } + /// /// Looks up a localized string similar to You guessed it! You won {0}. /// @@ -2198,6 +2207,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to No more cards in the deck.. + /// + public static string gambling_no_more_cards { + get { + return ResourceManager.GetString("gambling_no_more_cards", resourceCulture); + } + } + /// /// Looks up a localized string similar to You don't have enough {0}. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 6fee14d7..eb8b565a 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -882,6 +882,9 @@ Reason: {1} Congratulations! You won {0} for rolling above {1} + + Deck reshuffled. + flipped {0}. User flipped tails. @@ -918,6 +921,9 @@ Reason: {1} You don't have enough {0} + + No more cards in the deck. + Raffled User From 1804b81b8a8a55a7842ddb430d08841a2c18f7bf Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 11:41:32 +0100 Subject: [PATCH 120/256] currency events localizable :ok: --- .../Gambling/Commands/CurrencyEvents.cs | 51 ++++++++--------- .../Resources/ResponseStrings.Designer.cs | 55 +++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 19 +++++++ 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index 4681d91b..814fb747 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -5,12 +5,9 @@ using NadekoBot.Extensions; using NadekoBot.Services; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; using Discord.WebSocket; -using NadekoBot.Services.Database; using System.Threading; using NLog; @@ -27,7 +24,7 @@ namespace NadekoBot.Modules.Gambling SneakyGameStatus } //flower reaction event - public static readonly ConcurrentHashSet _sneakyGameAwardedUsers = new ConcurrentHashSet(); + private static readonly ConcurrentHashSet _sneakyGameAwardedUsers = new ConcurrentHashSet(); private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10) @@ -36,7 +33,7 @@ namespace NadekoBot.Modules.Gambling .Select(x => (char)x) .ToArray(); - private static string _secretCode = String.Empty; + private static string _secretCode = string.Empty; [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -54,7 +51,7 @@ namespace NadekoBot.Modules.Gambling } } - public static async Task SneakyGameStatusEvent(CommandContext Context, int? arg) + public async Task SneakyGameStatusEvent(CommandContext context, int? arg) { int num; if (arg == null || arg < 5) @@ -62,11 +59,11 @@ namespace NadekoBot.Modules.Gambling else num = arg.Value; - if (_secretCode != String.Empty) + if (_secretCode != string.Empty) return; var rng = new NadekoRandom(); - for (int i = 0; i < 5; i++) + for (var i = 0; i < 5; i++) { _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; } @@ -75,10 +72,9 @@ namespace NadekoBot.Modules.Gambling .ConfigureAwait(false); try { - await Context.Channel.SendConfirmAsync($"SneakyGameStatus event started", - $"Users must type a secret code to get 100 currency.\n" + - $"Lasts {num} seconds. Don't tell anyone. Shhh.") - .ConfigureAwait(false); + var title = GetText("sneakygamestatus_title"); + var desc = GetText("sneakygamestatus_desc", Format.Bold(100.ToString()) + CurrencySign, Format.Bold(num.ToString())); + await context.Channel.SendConfirmAsync(title, desc).ConfigureAwait(false); } catch { @@ -92,9 +88,9 @@ namespace NadekoBot.Modules.Gambling var cnt = _sneakyGameAwardedUsers.Count; _sneakyGameAwardedUsers.Clear(); - _secretCode = String.Empty; + _secretCode = string.Empty; - await NadekoBot.Client.SetGameAsync($"SneakyGame event ended. {cnt} users received a reward.") + await NadekoBot.Client.SetGameAsync(GetText("sneakygamestatus_end", cnt)) .ConfigureAwait(false); } @@ -119,22 +115,31 @@ namespace NadekoBot.Modules.Gambling return Task.Delay(0); } - public static Task FlowerReactionEvent(CommandContext context) => - new FlowerReactionEvent().Start(context); + public async Task FlowerReactionEvent(CommandContext context) + { + var title = GetText("flowerreaction_title"); + var desc = GetText("flowerreaction_desc", "🌸", Format.Bold(100.ToString()) + CurrencySign); + var footer = GetText("flowerreaction_footer", 24); + var msg = await context.Channel.SendConfirmAsync(title, + desc, footer: footer) + .ConfigureAwait(false); + + await new FlowerReactionEvent().Start(msg, context); + } } } public abstract class CurrencyEvent { - public abstract Task Start(CommandContext channel); + public abstract Task Start(IUserMessage msg, CommandContext channel); } public class FlowerReactionEvent : CurrencyEvent { - public readonly ConcurrentHashSet _flowerReactionAwardedUsers = new ConcurrentHashSet(); + private readonly ConcurrentHashSet _flowerReactionAwardedUsers = new ConcurrentHashSet(); private readonly Logger _log; - private IUserMessage msg { get; set; } = null; + private IUserMessage msg { get; set; } private CancellationTokenSource source { get; } private CancellationToken cancelToken { get; } @@ -167,13 +172,9 @@ namespace NadekoBot.Modules.Gambling return Task.CompletedTask; } - public override async Task Start(CommandContext context) + public override async Task Start(IUserMessage umsg, CommandContext context) { - msg = await context.Channel.SendConfirmAsync("Flower reaction event started!", - "Add 🌸 reaction to this message to get 100" + NadekoBot.BotConfig.CurrencySign, - footer: "This event is active for up to 24 hours.") - .ConfigureAwait(false); - + msg = umsg; NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler; try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 7dda445c..30c80001 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2144,6 +2144,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Add {0} reaction to this message to get {1} . + /// + public static string gambling_flowerreaction_desc { + get { + return ResourceManager.GetString("gambling_flowerreaction_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This event is active for up to {0} hours.. + /// + public static string gambling_flowerreaction_footer { + get { + return ResourceManager.GetString("gambling_flowerreaction_footer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Flower reaction event started!. + /// + public static string gambling_flowerreaction_title { + get { + return ResourceManager.GetString("gambling_flowerreaction_title", resourceCulture); + } + } + /// /// Looks up a localized string similar to has gifted {0} to {1}. /// @@ -2297,6 +2324,34 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Users must type a secret code to get {0}. + ///Lasts {1} seconds. Don't tell anyone. Shhh.. + /// + public static string gambling_sneakygamestatus_desc { + get { + return ResourceManager.GetString("gambling_sneakygamestatus_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SneakyGame event ended. {0} users received the reward.. + /// + public static string gambling_sneakygamestatus_end { + get { + return ResourceManager.GetString("gambling_sneakygamestatus_end", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to SneakyGameStatus event started. + /// + public static string gambling_sneakygamestatus_title { + get { + return ResourceManager.GetString("gambling_sneakygamestatus_title", resourceCulture); + } + } + /// /// Looks up a localized string similar to Tails. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index eb8b565a..c4228d5c 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -895,6 +895,15 @@ Reason: {1} Invalid number specified. You can flip 1 to {0} coins. + + Add {0} reaction to this message to get {1} + + + This event is active for up to {0} hours. + + + Flower reaction event started! + has gifted {0} to {1} X has gifted 15 flowers to Y @@ -948,6 +957,16 @@ Reason: {1} Won + + Users must type a secret code to get {0}. +Lasts {1} seconds. Don't tell anyone. Shhh. + + + SneakyGame event ended. {0} users received the reward. + + + SneakyGameStatus event started + Tails From da79daa92402b6db4229cc0cea1040ec92a4ae42 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 11:46:58 +0100 Subject: [PATCH 121/256] closes #1057 mention role can now mention up to 50 peopl --- src/NadekoBot/Modules/Administration/Administration.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index eb07ef1f..20760373 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -489,7 +489,7 @@ namespace NadekoBot.Modules.Administration foreach (var role in roles) { send += $"\n**{role.Name}**\n"; - send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Distinct().Select(u => u.Mention)); + send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Take(50).Select(u => u.Mention)); } while (send.Length > 2000) From 2edbbed583c79a13fefb888c2a824520aaa228c4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 12:09:10 +0100 Subject: [PATCH 122/256] song not found fix --- src/NadekoBot/project.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/project.json b/src/NadekoBot/project.json index a88058ea..88ebdd52 100644 --- a/src/NadekoBot/project.json +++ b/src/NadekoBot/project.json @@ -18,7 +18,7 @@ }, "dependencies": { "AngleSharp": "0.9.9", - "libvideo": "1.0.0", + "libvideo": "1.0.1", "CoreCLR-NCalc": "2.1.2", "Google.Apis.Urlshortener.v1": "1.19.0.138", "Google.Apis.YouTube.v3": "1.20.0.701", From d2484fa42b481ab788a7fcf1d22b34f1f1f5b478 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 12:17:06 +0100 Subject: [PATCH 123/256] 1.1.8-alpha --- src/NadekoBot/Services/Impl/StatsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 7c6d2201..40823b1b 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -15,7 +15,7 @@ namespace NadekoBot.Services.Impl private DiscordShardedClient client; private DateTime started; - public const string BotVersion = "1.1.6"; + public const string BotVersion = "1.1.8-alpha"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net"; From b5db72fa1a131f06cd8ae603933785b7421b6989 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:18:58 -0500 Subject: [PATCH 124/256] matches latest dev --- src/NadekoBot/Resources/CommandStrings.resx | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 208ab0cc..951a745c 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,4 +3114,32 @@ `{0}timezone` + + langsetdefault langsetd + + + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. + + + `{0}langsetd en-US` or `{0}langsetd default` + + + languageset langset + + + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. + + + `{0}langset de-DE ` or `{0}langset default` + + + languageslist langli + + + List of languages for which translation (or part of it) exist atm. + + + `{0}langli` + + From 0ae08ec556d04b5c9229ee745babe0dbd5ad5d44 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:20:19 -0500 Subject: [PATCH 125/256] Update --- src/NadekoBot/Resources/CommandStrings.resx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 951a745c..27d7b10b 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3142,4 +3142,3 @@ `{0}langli` - From a5f0cf8abfdd8b8d13b2908b0ab7dbfe1154983e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 12:24:12 +0100 Subject: [PATCH 126/256] ~yoda usage fix --- src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 487e5ec8..a95f8db1 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -9042,7 +9042,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to {0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`. + /// Looks up a localized string similar to `{0}yoda my feelings hurt`. /// public static string yodify_usage { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 4ba617cd..664b0713 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2689,7 +2689,7 @@ Translates your normal sentences into Yoda styled sentences! - {0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt` + `{0}yoda my feelings hurt` attack From 641f26242c62aa699d1fa646c726ae54ebbbbc83 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:24:23 -0500 Subject: [PATCH 127/256] revert --- src/NadekoBot/Resources/CommandStrings.resx | 27 --------------------- 1 file changed, 27 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 27d7b10b..208ab0cc 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,31 +3114,4 @@ `{0}timezone` - - langsetdefault langsetd - - - Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. - - - `{0}langsetd en-US` or `{0}langsetd default` - - - languageset langset - - - Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. - - - `{0}langset de-DE ` or `{0}langset default` - - - languageslist langli - - - List of languages for which translation (or part of it) exist atm. - - - `{0}langli` - From 317edc2d9475f1875377840d1c7835b33cb6b53a Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:56:53 -0500 Subject: [PATCH 128/256] Revert "revert" This reverts commit 641f26242c62aa699d1fa646c726ae54ebbbbc83. --- src/NadekoBot/Resources/CommandStrings.resx | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 208ab0cc..27d7b10b 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,4 +3114,31 @@ `{0}timezone` + + langsetdefault langsetd + + + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. + + + `{0}langsetd en-US` or `{0}langsetd default` + + + languageset langset + + + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. + + + `{0}langset de-DE ` or `{0}langset default` + + + languageslist langli + + + List of languages for which translation (or part of it) exist atm. + + + `{0}langli` + From 73ec3e43a1208d85e53403c462c165b828d981b2 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:57:06 -0500 Subject: [PATCH 129/256] Revert "Update" This reverts commit 0ae08ec556d04b5c9229ee745babe0dbd5ad5d44. --- src/NadekoBot/Resources/CommandStrings.resx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 27d7b10b..951a745c 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3142,3 +3142,4 @@ `{0}langli` + From 56a8914423f25f24a58453a0eaa26c42d9778445 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:57:10 -0500 Subject: [PATCH 130/256] Revert "matches latest dev" This reverts commit b5db72fa1a131f06cd8ae603933785b7421b6989. --- src/NadekoBot/Resources/CommandStrings.resx | 28 --------------------- 1 file changed, 28 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 951a745c..208ab0cc 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,32 +3114,4 @@ `{0}timezone` - - langsetdefault langsetd - - - Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. - - - `{0}langsetd en-US` or `{0}langsetd default` - - - languageset langset - - - Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. - - - `{0}langset de-DE ` or `{0}langset default` - - - languageslist langli - - - List of languages for which translation (or part of it) exist atm. - - - `{0}langli` - - From 2a3eaa316a1efc90b1d8c10577385907bcf7115d Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 06:57:16 -0500 Subject: [PATCH 131/256] Revert "revert" This reverts commit 641f26242c62aa699d1fa646c726ae54ebbbbc83. --- src/NadekoBot/Resources/CommandStrings.resx | 27 +++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 208ab0cc..27d7b10b 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,4 +3114,31 @@ `{0}timezone` + + langsetdefault langsetd + + + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. + + + `{0}langsetd en-US` or `{0}langsetd default` + + + languageset langset + + + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. + + + `{0}langset de-DE ` or `{0}langset default` + + + languageslist langli + + + List of languages for which translation (or part of it) exist atm. + + + `{0}langli` + From d704e77124a9539377a5b7b784c603bf6ddb5be3 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 07:04:26 -0500 Subject: [PATCH 132/256] Update CommandStrings.resx --- src/NadekoBot/Resources/CommandStrings.resx | 27 --------------------- 1 file changed, 27 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 27d7b10b..208ab0cc 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,31 +3114,4 @@ `{0}timezone` - - langsetdefault langsetd - - - Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. - - - `{0}langsetd en-US` or `{0}langsetd default` - - - languageset langset - - - Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. - - - `{0}langset de-DE ` or `{0}langset default` - - - languageslist langli - - - List of languages for which translation (or part of it) exist atm. - - - `{0}langli` - From 77f0a3b4ee17caa276b4723f73fdc5c71181f5a6 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Wed, 15 Feb 2017 07:16:23 -0500 Subject: [PATCH 133/256] Update CommandStrings.resx --- src/NadekoBot/Resources/CommandStrings.resx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 6b03349f..69aa41c4 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -3114,9 +3114,6 @@ `{0}timezone` -<<<<<<< HEAD - -======= langsetdefault langsetd @@ -3145,4 +3142,3 @@ `{0}langli` ->>>>>>> Kwoth/dev From 615502fc073f1ce3937e7f5eb4db1228bd08f0f3 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 15 Feb 2017 15:08:00 +0100 Subject: [PATCH 134/256] fixed 5 administration response strings --- src/NadekoBot/Resources/ResponseStrings.resx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index c4228d5c..2f82096e 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -851,23 +851,23 @@ Reason: {1} User {0} from voice chat - + You have been soft-banned from {0} server. Reason: {1} - + User Unbanned - + Migration done! Error while migrating, check bot's console for more information. - + Presence Updates - + User Soft-Banned From fa20d8cc954661e4c24ab4f7de742d25f67b1249 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 10:57:15 +0100 Subject: [PATCH 135/256] fixed self_assign_success key, thanks chf --- .../Resources/ResponseStrings.Designer.cs | 96 +++++++++---------- src/NadekoBot/Resources/ResponseStrings.resx | 2 +- 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 30c80001..78867fa4 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -728,6 +728,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Migration done!. + /// + public static string administration_migration_done { + get { + return ResourceManager.GetString("administration_migration_done", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} moved from {1} to {2}. /// @@ -899,6 +908,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Presence Updates. + /// + public static string administration_presence_updates { + get { + return ResourceManager.GetString("administration_presence_updates", resourceCulture); + } + } + /// /// Looks up a localized string similar to Active Protections. /// @@ -1152,6 +1170,25 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to User Soft-Banned. + /// + public static string administration_sb_user { + get { + return ResourceManager.GetString("administration_sb_user", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have been soft-banned from {0} server. + ///Reason: {1}. + /// + public static string administration_sbdm { + get { + return ResourceManager.GetString("administration_sbdm", resourceCulture); + } + } + /// /// Looks up a localized string similar to You already have {0} role.. /// @@ -1245,9 +1282,9 @@ namespace NadekoBot.Resources { /// /// Looks up a localized string similar to You now have {0} role.. /// - public static string administration_self_assign_sucess { + public static string administration_self_assign_success { get { - return ResourceManager.GetString("administration_self_assign_sucess", resourceCulture); + return ResourceManager.GetString("administration_self_assign_success", resourceCulture); } } @@ -1522,6 +1559,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to User Unbanned. + /// + public static string administration_user_unbanned { + get { + return ResourceManager.GetString("administration_user_unbanned", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has been **unmuted** from text and voice chat.. /// @@ -1693,34 +1739,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to You have been soft-banned from {0} server. - ///Reason: {1}. - /// - public static string administraton_sbdm { - get { - return ResourceManager.GetString("administraton_sbdm", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User Unbanned. - /// - public static string administraton_user_unbanned { - get { - return ResourceManager.GetString("administraton_user_unbanned", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Migration done!. - /// - public static string adminsitration_migration_done { - get { - return ResourceManager.GetString("adminsitration_migration_done", resourceCulture); - } - } - /// /// Looks up a localized string similar to Error while migrating, check bot's console for more information.. /// @@ -1730,24 +1748,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to Presence Updates. - /// - public static string adminsitration_presence_updates { - get { - return ResourceManager.GetString("adminsitration_presence_updates", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to User Soft-Banned. - /// - public static string adminsitration_sb_user { - get { - return ResourceManager.GetString("adminsitration_sb_user", resourceCulture); - } - } - /// /// Looks up a localized string similar to That base is already claimed or destroyed.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 2f82096e..1afc5b8b 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -698,7 +698,7 @@ Reason: {1} You no longer have {0} role. - + You now have {0} role. From d8a04ed78b8f98b19e0a0ebb28ddb35376d20478 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 16:34:14 +0100 Subject: [PATCH 136/256] a few speedtyping fixes, some music experimentation --- .../Games/Commands/SpeedTypingCommands.cs | 20 ++++++++--------- .../Modules/Music/Classes/MusicControls.cs | 22 +++++++++---------- src/NadekoBot/NadekoBot.xproj.DotSettings | 3 ++- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs index 47724820..96866f0e 100644 --- a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs @@ -116,7 +116,7 @@ namespace NadekoBot.Modules.Games if (msg == null) return; - if (this.Channel == null || this.Channel.Id != this.Channel.Id) return; + if (this.Channel == null || this.Channel.Id != msg.Channel.Id) return; var guess = msg.Content; @@ -127,10 +127,10 @@ namespace NadekoBot.Modules.Games var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60; finishedUserIds.Add(msg.Author.Id); await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithTitle((string)$"{msg.Author} finished the race!") + .WithTitle($"{msg.Author} finished the race!") .AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true)) - .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.Seconds.ToString()}sec]*").WithIsInline(true)) - .AddField(efb => efb.WithName((string)"Errors").WithValue((string)distance.ToString()).WithIsInline((bool)true))) + .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.TotalSeconds:F2}sec]*").WithIsInline(true)) + .AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true))) .ConfigureAwait(false); if (finishedUserIds.Count % 4 == 0) { @@ -150,11 +150,11 @@ namespace NadekoBot.Modules.Games { public static List TypingArticles { get; } = new List(); - const string typingArticlesPath = "data/typing_articles.json"; + private const string _typingArticlesPath = "data/typing_articles.json"; static SpeedTypingCommands() { - try { TypingArticles = JsonConvert.DeserializeObject>(File.ReadAllText(typingArticlesPath)); } catch { } + try { TypingArticles = JsonConvert.DeserializeObject>(File.ReadAllText(_typingArticlesPath)); } catch { } } public static ConcurrentDictionary RunningContests = new ConcurrentDictionary(); @@ -207,7 +207,7 @@ namespace NadekoBot.Modules.Games Text = text.SanitizeMentions(), }); - File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); + File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false); } @@ -221,7 +221,7 @@ namespace NadekoBot.Modules.Games if (page < 1) return; - var articles = TypingArticles.Skip((page - 1) * 15).Take(15); + var articles = TypingArticles.Skip((page - 1) * 15).Take(15).ToArray(); if (!articles.Any()) { @@ -229,7 +229,7 @@ namespace NadekoBot.Modules.Games return; } var i = (page - 1) * 15; - await channel.SendConfirmAsync("List of articles for Type Race", String.Join("\n", articles.Select(a => $"`#{++i}` - {a.Text.TrimTo(50)}"))) + await channel.SendConfirmAsync("List of articles for Type Race", string.Join("\n", articles.Select(a => $"`#{++i}` - {a.Text.TrimTo(50)}"))) .ConfigureAwait(false); } @@ -247,7 +247,7 @@ namespace NadekoBot.Modules.Games var removed = TypingArticles[index]; TypingArticles.RemoveAt(index); - File.WriteAllText(typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); + File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); await channel.SendConfirmAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}") .ConfigureAwait(false); diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index e11b680e..a1b0d0a9 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -67,7 +67,7 @@ namespace NadekoBot.Modules.Music.Classes public float Volume { get; private set; } public event Action OnCompleted = delegate { }; - public event Action OnStarted = delegate { }; + public event Action OnStarted = delegate { }; public event Action OnPauseChanged = delegate { }; public IVoiceChannel PlaybackVoiceChannel { get; private set; } @@ -130,13 +130,9 @@ namespace NadekoBot.Modules.Music.Classes { try { - if (audioClient?.ConnectionState != ConnectionState.Connected) - { - if (audioClient != null) - try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { } - audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); - continue; - } + if (audioClient != null) + try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { } + audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); CurrentSong = GetNextSong(); @@ -313,11 +309,15 @@ namespace NadekoBot.Modules.Music.Classes { var curSong = CurrentSong; var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal && - s.TotalTime == TimeSpan.Zero); + s.TotalTime == TimeSpan.Zero) + .ToArray(); if (curSong != null) - toUpdate = toUpdate.Append(curSong); + { + Array.Resize(ref toUpdate, toUpdate.Length + 1); + toUpdate[toUpdate.Length - 1] = curSong; + } var ids = toUpdate.Select(s => s.SongInfo.Query.Substring(s.SongInfo.Query.LastIndexOf("?v=") + 3)) - .Distinct(); + .Distinct(); var durations = await NadekoBot.Google.GetVideoDurationsAsync(ids); diff --git a/src/NadekoBot/NadekoBot.xproj.DotSettings b/src/NadekoBot/NadekoBot.xproj.DotSettings index 3a1cb1b5..65ccbd58 100644 --- a/src/NadekoBot/NadekoBot.xproj.DotSettings +++ b/src/NadekoBot/NadekoBot.xproj.DotSettings @@ -1,3 +1,4 @@  True - True \ No newline at end of file + True + True \ No newline at end of file From e648ff7d48120ddd3e325018b842c0b70c3df6b9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 16:37:15 +0100 Subject: [PATCH 137/256] I didn't save /facepalm --- src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs index 96866f0e..fc5a67a2 100644 --- a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs @@ -124,12 +124,13 @@ namespace NadekoBot.Modules.Games var decision = Judge(distance, guess.Length); if (decision && !finishedUserIds.Contains(msg.Author.Id)) { - var wpm = CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60; + var elapsed = sw.Elapsed; + var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60; finishedUserIds.Add(msg.Author.Id); await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithTitle($"{msg.Author} finished the race!") .AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true)) - .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F2} *[{sw.Elapsed.TotalSeconds:F2}sec]*").WithIsInline(true)) + .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*").WithIsInline(true)) .AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true))) .ConfigureAwait(false); if (finishedUserIds.Count % 4 == 0) From 8eb2b980d1657c593f8408211b4c41ed2c0b9c1d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 17:47:37 +0100 Subject: [PATCH 138/256] Fixed random disconnects and reconnects --- src/NadekoBot/Modules/Music/Classes/MusicControls.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index a1b0d0a9..fe7fe953 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -130,15 +130,15 @@ namespace NadekoBot.Modules.Music.Classes { try { - if (audioClient != null) - try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { } - audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); - CurrentSong = GetNextSong(); if (CurrentSong == null) continue; + if (audioClient != null) + try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { } + audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); + var index = playlist.IndexOf(CurrentSong); if (index != -1) RemoveSongAt(index, true); From 108a9fb1560da923d1b6e97c521618ca4cab23ad Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 18:13:24 +0100 Subject: [PATCH 139/256] Fixes to music. Thanks to samvaio --- .../Modules/Music/Classes/MusicControls.cs | 85 +++++++++++-------- src/NadekoBot/Modules/Music/Music.cs | 11 +-- 2 files changed, 52 insertions(+), 44 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index fe7fe953..92ca19ae 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -7,6 +7,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using NLog; + namespace NadekoBot.Modules.Music.Classes { @@ -46,17 +48,19 @@ namespace NadekoBot.Modules.Music.Classes // this should be written better public TimeSpan TotalPlaytime => - playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ? + _playlist.Any(s => s.TotalTime == TimeSpan.MaxValue) ? TimeSpan.MaxValue : - new TimeSpan(playlist.Sum(s => s.TotalTime.Ticks)); + new TimeSpan(_playlist.Sum(s => s.TotalTime.Ticks)); /// /// Users who recently got their music wish /// private ConcurrentHashSet recentlyPlayedUsers { get; } = new ConcurrentHashSet(); - private readonly List playlist = new List(); - public IReadOnlyCollection Playlist => playlist; + private readonly List _playlist = new List(); + private readonly Logger _log; + + public IReadOnlyCollection Playlist => _playlist; public Song CurrentSong { get; private set; } public CancellationTokenSource SongCancelSource { get; private set; } @@ -73,7 +77,7 @@ namespace NadekoBot.Modules.Music.Classes public IVoiceChannel PlaybackVoiceChannel { get; private set; } public ITextChannel OutputTextChannel { get; set; } - private bool Destroyed { get; set; } = false; + private bool destroyed { get; set; } = false; public bool RepeatSong { get; private set; } = false; public bool RepeatPlaylist { get; private set; } = false; public bool Autoplay { get; set; } = false; @@ -90,6 +94,8 @@ namespace NadekoBot.Modules.Music.Classes if (startingVoiceChannel == null) throw new ArgumentNullException(nameof(startingVoiceChannel)); + _log = LogManager.GetCurrentClassLogger(); + OutputTextChannel = outputChannel; Volume = defaultVolume ?? 1.0f; @@ -101,7 +107,7 @@ namespace NadekoBot.Modules.Music.Classes { try { - while (!Destroyed) + while (!destroyed) { try { @@ -119,14 +125,14 @@ namespace NadekoBot.Modules.Music.Classes } catch (Exception ex) { - Console.WriteLine("Action queue crashed"); - Console.WriteLine(ex); + _log.Warn("Action queue crashed"); + _log.Warn(ex); } }).ConfigureAwait(false); - var t = new Thread(new ThreadStart(async () => + var t = new Thread(async () => { - while (!Destroyed) + while (!destroyed) { try { @@ -139,7 +145,7 @@ namespace NadekoBot.Modules.Music.Classes try { await audioClient.DisconnectAsync().ConfigureAwait(false); } catch { } audioClient = await PlaybackVoiceChannel.ConnectAsync().ConfigureAwait(false); - var index = playlist.IndexOf(CurrentSong); + var index = _playlist.IndexOf(CurrentSong); if (index != -1) RemoveSongAt(index, true); @@ -148,11 +154,14 @@ namespace NadekoBot.Modules.Music.Classes { await CurrentSong.Play(audioClient, cancelToken); } - catch(OperationCanceledException) + catch (OperationCanceledException) + { + } + finally { OnCompleted(this, CurrentSong); } - + if (RepeatPlaylist) AddSong(CurrentSong, CurrentSong.QueuerName); @@ -163,8 +172,8 @@ namespace NadekoBot.Modules.Music.Classes } catch (Exception ex) { - Console.WriteLine("Music thread almost crashed."); - Console.WriteLine(ex); + _log.Warn("Music thread almost crashed."); + _log.Warn(ex); await Task.Delay(3000).ConfigureAwait(false); } finally @@ -179,7 +188,7 @@ namespace NadekoBot.Modules.Music.Classes await Task.Delay(300).ConfigureAwait(false); } } - })); + }); t.Start(); } @@ -199,7 +208,8 @@ namespace NadekoBot.Modules.Music.Classes { RepeatPlaylist = false; RepeatSong = false; - playlist.Clear(); + Autoplay = false; + _playlist.Clear(); if (!SongCancelSource.IsCancellationRequested) SongCancelSource.Cancel(); }); @@ -222,10 +232,10 @@ namespace NadekoBot.Modules.Music.Classes { if (!FairPlay) { - return playlist.FirstOrDefault(); + return _playlist.FirstOrDefault(); } - var song = playlist.FirstOrDefault(c => !recentlyPlayedUsers.Contains(c.QueuerName)) - ?? playlist.FirstOrDefault(); + var song = _playlist.FirstOrDefault(c => !recentlyPlayedUsers.Contains(c.QueuerName)) + ?? _playlist.FirstOrDefault(); if (song == null) return null; @@ -243,9 +253,9 @@ namespace NadekoBot.Modules.Music.Classes { actionQueue.Enqueue(() => { - var oldPlaylist = playlist.ToArray(); - playlist.Clear(); - playlist.AddRange(oldPlaylist.Shuffle()); + var oldPlaylist = _playlist.ToArray(); + _playlist.Clear(); + _playlist.AddRange(oldPlaylist.Shuffle()); }); } @@ -258,7 +268,7 @@ namespace NadekoBot.Modules.Music.Classes { s.MusicPlayer = this; s.QueuerName = username.TrimTo(10); - playlist.Add(s); + _playlist.Add(s); }); } @@ -268,7 +278,7 @@ namespace NadekoBot.Modules.Music.Classes throw new ArgumentNullException(nameof(s)); actionQueue.Enqueue(() => { - playlist.Insert(index, s); + _playlist.Insert(index, s); }); } @@ -278,7 +288,7 @@ namespace NadekoBot.Modules.Music.Classes throw new ArgumentNullException(nameof(s)); actionQueue.Enqueue(() => { - playlist.Remove(s); + _playlist.Remove(s); }); } @@ -286,10 +296,10 @@ namespace NadekoBot.Modules.Music.Classes { actionQueue.Enqueue(() => { - if (index < 0 || index >= playlist.Count) + if (index < 0 || index >= _playlist.Count) return; - var song = playlist.ElementAtOrDefault(index); - if (playlist.Remove(song) && !silent) + var song = _playlist.ElementAtOrDefault(index); + if (_playlist.Remove(song) && !silent) { SongRemoved(song, index); } @@ -301,14 +311,14 @@ namespace NadekoBot.Modules.Music.Classes { actionQueue.Enqueue(() => { - playlist.Clear(); + _playlist.Clear(); }); } public async Task UpdateSongDurationsAsync() { var curSong = CurrentSong; - var toUpdate = playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal && + var toUpdate = _playlist.Where(s => s.SongInfo.ProviderType == MusicType.Normal && s.TotalTime == TimeSpan.Zero) .ToArray(); if (curSong != null) @@ -341,8 +351,9 @@ namespace NadekoBot.Modules.Music.Classes { RepeatPlaylist = false; RepeatSong = false; - Destroyed = true; - playlist.Clear(); + Autoplay = false; + destroyed = true; + _playlist.Clear(); try { await audioClient.DisconnectAsync(); } catch { } if (!SongCancelSource.IsCancellationRequested) @@ -358,17 +369,17 @@ namespace NadekoBot.Modules.Music.Classes // audioClient = await voiceChannel.ConnectAsync().ConfigureAwait(false); //} - public bool ToggleRepeatSong() => this.RepeatSong = !this.RepeatSong; + public bool ToggleRepeatSong() => RepeatSong = !RepeatSong; - public bool ToggleRepeatPlaylist() => this.RepeatPlaylist = !this.RepeatPlaylist; + public bool ToggleRepeatPlaylist() => RepeatPlaylist = !RepeatPlaylist; - public bool ToggleAutoplay() => this.Autoplay = !this.Autoplay; + public bool ToggleAutoplay() => Autoplay = !Autoplay; public void ThrowIfQueueFull() { if (MaxQueueSize == 0) return; - if (playlist.Count >= MaxQueueSize) + if (_playlist.Count >= MaxQueueSize) throw new PlaylistFullException(); } } diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index fa7012fd..15d8e82e 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -838,8 +838,7 @@ namespace NadekoBot.Modules.Music { try { - if (lastFinishedMessage != null) - lastFinishedMessage.DeleteAfter(0); + lastFinishedMessage?.DeleteAfter(0); lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon()) @@ -855,7 +854,7 @@ namespace NadekoBot.Modules.Music textCh, voiceCh, relatedVideos[new NadekoRandom().Next(0, relatedVideos.Count)], - silent, + true, musicType).ConfigureAwait(false); } } @@ -870,8 +869,7 @@ namespace NadekoBot.Modules.Music return; try { - if (playingMessage != null) - playingMessage.DeleteAfter(0); + playingMessage?.DeleteAfter(0); playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon()) @@ -891,8 +889,7 @@ namespace NadekoBot.Modules.Music else msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); - if (msg != null) - msg.DeleteAfter(10); + msg?.DeleteAfter(10); } catch { } }; From ad2dd3a565f715337840943e2f7ceebc914af91c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 19:44:39 +0100 Subject: [PATCH 140/256] reduced the delay on .ban, .softban, and .kick to 1 second, from 2 --- src/NadekoBot/Modules/Administration/Administration.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 20760373..ab618ac5 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -241,7 +241,7 @@ namespace NadekoBot.Modules.Administration try { await user.SendErrorAsync(GetText("bandm", Format.Bold(Context.Guild.Name), msg)); - await Task.Delay(2000).ConfigureAwait(false); + await Task.Delay(1000).ConfigureAwait(false); } catch @@ -276,7 +276,7 @@ namespace NadekoBot.Modules.Administration try { await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg)); - await Task.Delay(2000).ConfigureAwait(false); + await Task.Delay(1000).ConfigureAwait(false); } catch { @@ -311,7 +311,7 @@ namespace NadekoBot.Modules.Administration try { await user.SendErrorAsync(GetText("kickdm", Format.Bold(Context.Guild.Name), msg)); - await Task.Delay(2000).ConfigureAwait(false); + await Task.Delay(1000).ConfigureAwait(false); } catch { } } From 7ce0702c786c2a8acf6d6a2ccb6d1916f13eb5a1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 19:48:51 +0100 Subject: [PATCH 141/256] banning, kicking and softbanning no longer have the delay at all. --- src/NadekoBot/Modules/Administration/Administration.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index ab618ac5..40d7776f 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -241,8 +241,6 @@ namespace NadekoBot.Modules.Administration try { await user.SendErrorAsync(GetText("bandm", Format.Bold(Context.Guild.Name), msg)); - await Task.Delay(1000).ConfigureAwait(false); - } catch { @@ -276,7 +274,6 @@ namespace NadekoBot.Modules.Administration try { await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg)); - await Task.Delay(1000).ConfigureAwait(false); } catch { @@ -311,7 +308,6 @@ namespace NadekoBot.Modules.Administration try { await user.SendErrorAsync(GetText("kickdm", Format.Bold(Context.Guild.Name), msg)); - await Task.Delay(1000).ConfigureAwait(false); } catch { } } From b9673d5918839fdb1e9e2f2b064549e6c378bda9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 20:32:42 +0100 Subject: [PATCH 142/256] no idea what i did, locale won't be pink anymore though, thx aurora --- src/NadekoBot/Modules/Music/Music.cs | 17 ++++++++++++----- src/NadekoBot/Modules/NadekoModule.cs | 2 +- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 15d8e82e..256a1c43 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -840,11 +840,18 @@ namespace NadekoBot.Modules.Music { lastFinishedMessage?.DeleteAfter(0); - lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon()) - .WithDescription(song.PrettyName) - .WithFooter(ef => ef.WithText(song.PrettyInfo))) - .ConfigureAwait(false); + try + { + lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon()) + .WithDescription(song.PrettyName) + .WithFooter(ef => ef.WithText(song.PrettyInfo))) + .ConfigureAwait(false); + } + catch + { + // ignored + } if (mp.Autoplay && mp.Playlist.Count == 0 && song.SongInfo.ProviderType == MusicType.Normal) { diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index 4c8d17d3..d76a483c 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -32,7 +32,7 @@ namespace NadekoBot.Modules { _cultureInfo = NadekoBot.Localization.GetCultureInfo(Context.Guild?.Id); - _log.Warn("Culture info is {0}", _cultureInfo); + _log.Info("Culture info is {0}", _cultureInfo); } //public Task ReplyConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null) From 9f9548b33ba2ce9920200a7bfd1a28873afe6e67 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 16 Feb 2017 21:49:30 +0100 Subject: [PATCH 143/256] stats should work properly now --- src/NadekoBot/Services/IStatsService.cs | 1 - src/NadekoBot/Services/Impl/StatsService.cs | 117 ++++++++++++-------- 2 files changed, 72 insertions(+), 46 deletions(-) diff --git a/src/NadekoBot/Services/IStatsService.cs b/src/NadekoBot/Services/IStatsService.cs index b1cb948f..5634790d 100644 --- a/src/NadekoBot/Services/IStatsService.cs +++ b/src/NadekoBot/Services/IStatsService.cs @@ -5,6 +5,5 @@ namespace NadekoBot.Services public interface IStatsService { Task Print(); - Task Reset(); } } diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index 40823b1b..f7f39985 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -3,6 +3,7 @@ using Discord.WebSocket; using NadekoBot.Extensions; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net.Http; using System.Threading; @@ -12,75 +13,108 @@ namespace NadekoBot.Services.Impl { public class StatsService : IStatsService { - private DiscordShardedClient client; - private DateTime started; + private readonly DiscordShardedClient _client; + private DateTime _started; public const string BotVersion = "1.1.8-alpha"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net"; - public int MessageCounter { get; private set; } = 0; - public int CommandsRan { get; private set; } = 0; public string Heap => - Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(); + Math.Round((double)GC.GetTotalMemory(false) / 1.MiB(), 2).ToString(CultureInfo.InvariantCulture); public double MessagesPerSecond => MessageCounter / GetUptime().TotalSeconds; - private int _textChannels = 0; - public int TextChannels => _textChannels; - private int _voiceChannels = 0; - public int VoiceChannels => _voiceChannels; - Timer carbonitexTimer { get; } + private long _textChannels; + public long TextChannels => Interlocked.Read(ref _textChannels); + private long _voiceChannels; + public long VoiceChannels => Interlocked.Read(ref _voiceChannels); + private long _messageCounter; + public long MessageCounter => Interlocked.Read(ref _messageCounter); + private long _commandsRan; + public long CommandsRan => Interlocked.Read(ref _commandsRan); + + private Timer carbonitexTimer { get; } public StatsService(DiscordShardedClient client, CommandHandler cmdHandler) { - this.client = client; + _client = client; - Reset(); - this.client.MessageReceived += _ => Task.FromResult(MessageCounter++); - cmdHandler.CommandExecuted += (_, e) => Task.FromResult(CommandsRan++); + _started = DateTime.Now; + _client.MessageReceived += _ => Task.FromResult(Interlocked.Increment(ref _messageCounter)); + cmdHandler.CommandExecuted += (_, e) => Task.FromResult(Interlocked.Increment(ref _commandsRan)); - this.client.ChannelCreated += (c) => + _client.ChannelCreated += (c) => { if (c is ITextChannel) - ++_textChannels; + Interlocked.Increment(ref _textChannels); else if (c is IVoiceChannel) - ++_voiceChannels; + Interlocked.Increment(ref _voiceChannels); return Task.CompletedTask; }; - this.client.ChannelDestroyed += (c) => + _client.ChannelDestroyed += (c) => { if (c is ITextChannel) - --_textChannels; + Interlocked.Decrement(ref _textChannels); else if (c is IVoiceChannel) - --_voiceChannels; + Interlocked.Decrement(ref _voiceChannels); return Task.CompletedTask; }; - this.client.JoinedGuild += (g) => + _client.GuildAvailable += (g) => { - var tc = g.Channels.Where(cx => cx is ITextChannel).Count(); - var vc = g.Channels.Count - tc; - _textChannels += tc; - _voiceChannels += vc; - + var _ = Task.Run(() => + { + var tc = g.Channels.Count(cx => cx is ITextChannel); + var vc = g.Channels.Count - tc; + Interlocked.Add(ref _textChannels, tc); + Interlocked.Add(ref _voiceChannels, vc); + }); return Task.CompletedTask; }; - this.client.LeftGuild += (g) => + _client.JoinedGuild += (g) => { - var tc = g.Channels.Where(cx => cx is ITextChannel).Count(); - var vc = g.Channels.Count - tc; - _textChannels -= tc; - _voiceChannels -= vc; + var _ = Task.Run(() => + { + var tc = g.Channels.Count(cx => cx is ITextChannel); + var vc = g.Channels.Count - tc; + Interlocked.Add(ref _textChannels, tc); + Interlocked.Add(ref _voiceChannels, vc); + }); + return Task.CompletedTask; + }; + + _client.GuildUnavailable += (g) => + { + var _ = Task.Run(() => + { + var tc = g.Channels.Count(cx => cx is ITextChannel); + var vc = g.Channels.Count - tc; + Interlocked.Add(ref _textChannels, -tc); + Interlocked.Add(ref _voiceChannels, -vc); + }); return Task.CompletedTask; }; - this.carbonitexTimer = new Timer(async (state) => + _client.LeftGuild += (g) => + { + var _ = Task.Run(() => + { + var tc = g.Channels.Count(cx => cx is ITextChannel); + var vc = g.Channels.Count - tc; + Interlocked.Add(ref _textChannels, -tc); + Interlocked.Add(ref _voiceChannels, -vc); + }); + + return Task.CompletedTask; + }; + + carbonitexTimer = new Timer(async (state) => { if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey)) return; @@ -90,7 +124,7 @@ namespace NadekoBot.Services.Impl { using (var content = new FormUrlEncodedContent( new Dictionary { - { "servercount", this.client.GetGuildCount().ToString() }, + { "servercount", _client.GetGuildCount().ToString() }, { "key", NadekoBot.Credentials.CarbonKey }})) { content.Headers.Clear(); @@ -98,7 +132,7 @@ namespace NadekoBot.Services.Impl await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false); } - }; + } } catch { @@ -109,34 +143,27 @@ namespace NadekoBot.Services.Impl public void Initialize() { - var guilds = this.client.GetGuilds().ToArray(); + var guilds = _client.GetGuilds().ToArray(); _textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel)); _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; } public Task Print() { - var curUser = client.CurrentUser; + var curUser = _client.CurrentUser; return Task.FromResult($@" Author: [{Author}] | Library: [{Library}] Bot Version: [{BotVersion}] Bot ID: {curUser.Id} Owner ID(s): {string.Join(", ", NadekoBot.Credentials.OwnerIds)} Uptime: {GetUptimeString()} -Servers: {client.GetGuildCount()} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels} +Servers: {_client.GetGuildCount()} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels} Commands Ran this session: {CommandsRan} Messages: {MessageCounter} [{MessagesPerSecond:F2}/sec] Heap: [{Heap} MB]"); } - public Task Reset() - { - MessageCounter = 0; - started = DateTime.Now; - return Task.CompletedTask; - } - public TimeSpan GetUptime() => - DateTime.Now - started; + DateTime.Now - _started; public string GetUptimeString(string separator = ", ") { From d104d86df5fdf5135e64ce2f3016acebe059407c Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Thu, 16 Feb 2017 23:55:36 +0100 Subject: [PATCH 144/256] updated front page, thanks to rjt --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bb66aa4f..62289d80 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ ![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) [![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/nadekobot) [![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest) -# NadekoBot -[![nadeko1](https://cdn.discordapp.com/attachments/155726317222887425/252095170676391936/A1.jpg)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) -[![nadeko2](https://cdn.discordapp.com/attachments/155726317222887425/252095207514832896/A2.jpg)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/) +![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png) +[![nadeko1](https://cdn.discordapp.com/attachments/266240393639755778/281920134967328768/part2.png)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) +[![nadeko2](https://cdn.discordapp.com/attachments/266240393639755778/281920161311883264/part3.png)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/) ##For Update, Help and Guidlines `Follow me on twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides.` -[![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) [![discord](https://cdn.discordapp.com/attachments/155726317222887425/252192415673221122/discord_banner.JPG)](https://discord.gg/nadekobot) [![Wiki](https://cdn.discordapp.com/attachments/155726317222887425/252192472849973250/read_the_docs_banner.JPG)](http://nadekobot.readthedocs.io/en/latest/) +[![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) [![discord](https://cdn.discordapp.com/attachments/266240393639755778/281920766490968064/discord.png)](https://discord.gg/nadekobot) [![Wiki](https://cdn.discordapp.com/attachments/266240393639755778/281920793330581506/datcord.png)](http://nadekobot.readthedocs.io/en/latest/) From 28563c1f0bf5b787d3803f36eaf1e249d5da974b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 11:38:06 +0100 Subject: [PATCH 145/256] fixed !!ms on local songs --- src/NadekoBot/Modules/Music/Classes/Song.cs | 4 ++-- src/NadekoBot/Modules/Music/Music.cs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index 04e49a70..31c92d10 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Music.Classes public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime; - public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({songUrl})**"; + public string PrettyName => $"**[{SongInfo.Title.TrimTo(65)}]({SongUrl})**"; public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}"; @@ -104,7 +104,7 @@ namespace NadekoBot.Modules.Music.Classes } } - private string songUrl { + public string SongUrl { get { switch (SongInfo.ProviderType) { diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 256a1c43..231bcc65 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -512,12 +512,13 @@ namespace NadekoBot.Modules.Music [RequireContext(ContextType.Guild)] public async Task MoveSong([Remainder] string fromto) { + if (string.IsNullOrWhiteSpace(fromto)) + return; MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) - { return; - } + fromto = fromto?.Trim(); var fromtoArr = fromto.Split('>'); @@ -541,7 +542,7 @@ namespace NadekoBot.Modules.Music var embed = new EmbedBuilder() .WithTitle($"{s.SongInfo.Title.TrimTo(70)}") - .WithUrl($"{s.SongInfo.Query}") + .WithUrl(s.SongUrl) .WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png")) .AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true)) .AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true)) From cf686a20c3afec14dee48162c19f74476ac02121 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 12:05:05 +0100 Subject: [PATCH 146/256] owner channel bugs fixed, you can now see files sent to the bot --- .../Administration/Commands/SelfCommands.cs | 29 +++++++++++--- src/NadekoBot/Services/CommandHandler.cs | 39 +++++++++++++------ 2 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs index 0dfb3d0a..89d23094 100644 --- a/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/SelfCommands.cs @@ -72,23 +72,42 @@ namespace NadekoBot.Modules.Administration { if (_forwardDMs && ownerChannels.Any()) { - var title = - GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo, - typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})"; + var title = GetTextStatic("dm_from", + NadekoBot.Localization.DefaultCultureInfo, + typeof(Administration).Name.ToLowerInvariant()) + + $" [{msg.Author}]({msg.Author.Id})"; + + var attachamentsTxt = GetTextStatic("attachments", + NadekoBot.Localization.DefaultCultureInfo, + typeof(Administration).Name.ToLowerInvariant()); + + var toSend = msg.Content; + + if (msg.Attachments.Count > 0) + { + toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" + + string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl)); + } + if (_forwardDMsToAllOwners) { await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) - .Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false); + .Select(ch => ch.SendConfirmAsync(title, toSend))).ConfigureAwait(false); } else { var firstOwnerChannel = ownerChannels.First(); if (firstOwnerChannel.Recipient.Id != msg.Author.Id) - try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } + { + try + { + await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false); + } catch { // ignored } + } } } } diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index a944a21e..2b827191 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Services private readonly CommandService _commandService; private readonly Logger _log; - private List ownerChannels { get; set; } + private List ownerChannels { get; set; } = new List(); public event Func CommandExecuted = delegate { return Task.CompletedTask; }; @@ -61,21 +61,36 @@ namespace NadekoBot.Services UsersOnShortCooldown.Clear(); }, null, GlobalCommandsCooldown, GlobalCommandsCooldown); } - public async Task StartHandling() + public Task StartHandling() { - ownerChannels = (await Task.WhenAll(_client.GetGuilds().SelectMany(g => g.Users) - .Where(u => NadekoBot.Credentials.OwnerIds.Contains(u.Id)) - .Distinct(new IGuildUserComparer()) - .Select(async u => { try { return await u.CreateDMChannelAsync(); } catch { return null; } }))) - .Where(ch => ch != null) - .ToList(); + var _ = Task.Run(async () => + { + await Task.Delay(5000).ConfigureAwait(false); + ownerChannels = (await Task.WhenAll(_client.GetGuilds().SelectMany(g => g.Users) + .Where(u => NadekoBot.Credentials.OwnerIds.Contains(u.Id)) + .Distinct(new IGuildUserComparer()) + .Select(async u => + { + try + { + return await u.CreateDMChannelAsync(); + } + catch + { + return null; + } + }))) + .Where(ch => ch != null) + .ToList(); - if (!ownerChannels.Any()) - _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file."); - else - _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels."); + if (!ownerChannels.Any()) + _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file."); + else + _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels."); + }); _client.MessageReceived += MessageReceivedHandler; + return Task.CompletedTask; } private async Task TryRunCleverbot(SocketUserMessage usrMsg, IGuild guild) From b588da43e59e2aa043877c3f5fb039eacdb30892 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 13:11:24 +0100 Subject: [PATCH 147/256] !!ap bug fixed --- .../Modules/Music/Classes/SongHandler.cs | 13 +++++--- src/NadekoBot/Modules/Music/Music.cs | 33 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/SongHandler.cs b/src/NadekoBot/Modules/Music/Classes/SongHandler.cs index f296d2cb..e374279f 100644 --- a/src/NadekoBot/Modules/Music/Classes/SongHandler.cs +++ b/src/NadekoBot/Modules/Music/Classes/SongHandler.cs @@ -6,12 +6,14 @@ using System.Net.Http; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using NLog; using VideoLibrary; namespace NadekoBot.Modules.Music.Classes { public static class SongHandler { + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); public static async Task ResolveSong(string query, MusicType musicType = MusicType.Normal) { if (string.IsNullOrWhiteSpace(query)) @@ -106,7 +108,8 @@ namespace NadekoBot.Modules.Music.Classes } catch (Exception ex) { - Console.WriteLine($"Failed resolving the link.{ex.Message}"); + _log.Warn($"Failed resolving the link.{ex.Message}"); + _log.Warn(ex); return null; } } @@ -137,7 +140,7 @@ namespace NadekoBot.Modules.Music.Classes } catch { - Console.WriteLine($"Failed reading .pls:\n{file}"); + _log.Warn($"Failed reading .pls:\n{file}"); return null; } } @@ -156,7 +159,7 @@ namespace NadekoBot.Modules.Music.Classes } catch { - Console.WriteLine($"Failed reading .m3u:\n{file}"); + _log.Warn($"Failed reading .m3u:\n{file}"); return null; } @@ -172,7 +175,7 @@ namespace NadekoBot.Modules.Music.Classes } catch { - Console.WriteLine($"Failed reading .asx:\n{file}"); + _log.Warn($"Failed reading .asx:\n{file}"); return null; } } @@ -192,7 +195,7 @@ namespace NadekoBot.Modules.Music.Classes } catch { - Console.WriteLine($"Failed reading .xspf:\n{file}"); + _log.Warn($"Failed reading .xspf:\n{file}"); return null; } } diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 231bcc65..4fbc8701 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -14,7 +14,6 @@ using System.Net.Http; using Newtonsoft.Json.Linq; using System.Collections.Generic; using NadekoBot.Services.Database.Models; -using System.Text.RegularExpressions; using System.Threading; namespace NadekoBot.Modules.Music @@ -78,7 +77,10 @@ namespace NadekoBot.Modules.Music } } - catch { } + catch + { + // ignored + } return Task.CompletedTask; } @@ -215,9 +217,8 @@ namespace NadekoBot.Modules.Music .Skip(startAt) .Take(itemsPerPage) .Select(v => $"`{++number}.` {v.PrettyFullName}")); - - if (currentSong != null) - desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc; + + desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc; if (musicPlayer.RepeatSong) desc = "🔂 Repeating Current Song\n\n" + desc; @@ -862,8 +863,7 @@ namespace NadekoBot.Modules.Music textCh, voiceCh, relatedVideos[new NadekoRandom().Next(0, relatedVideos.Count)], - true, - musicType).ConfigureAwait(false); + true).ConfigureAwait(false); } } catch { } @@ -871,7 +871,11 @@ namespace NadekoBot.Modules.Music mp.OnStarted += async (player, song) => { - try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } + try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } + catch + { + // ignored + } var sender = player as MusicPlayer; if (sender == null) return; @@ -915,7 +919,10 @@ namespace NadekoBot.Modules.Music await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch { } + catch + { + // ignored + } }; return mp; }); @@ -946,10 +953,12 @@ namespace NadekoBot.Modules.Music .WithThumbnailUrl(resolvedSong.Thumbnail) .WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider))) .ConfigureAwait(false); - if (queuedMessage != null) - queuedMessage.DeleteAfter(10); + queuedMessage?.DeleteAfter(10); } - catch { } // if queued message sending fails, don't attempt to delete it + catch + { + // ignored + } // if queued message sending fails, don't attempt to delete it } } } From 40989604e94e5f56e091f3cc42ad40916716f710 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 13:57:01 +0100 Subject: [PATCH 148/256] fixed current time on repeating songs --- src/NadekoBot/Modules/Music/Classes/Song.cs | 95 +++++++++------------ 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index 31c92d10..8a69ec6f 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -5,12 +5,9 @@ using System; using System.Diagnostics; using System.Diagnostics.Contracts; using System.IO; -using System.Linq; -using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using VideoLibrary; using System.Net; namespace NadekoBot.Modules.Music.Classes @@ -32,13 +29,13 @@ namespace NadekoBot.Modules.Music.Classes public string QueuerName { get; set; } public TimeSpan TotalTime { get; set; } = TimeSpan.Zero; - public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / frameBytes / (1000 / milliseconds)); + public TimeSpan CurrentTime => TimeSpan.FromSeconds(bytesSent / (float)_frameBytes / (1000 / (float)_milliseconds)); - const int milliseconds = 20; - const int samplesPerFrame = (48000 / 1000) * milliseconds; - const int frameBytes = 3840; //16-bit, 2 channels + private const int _milliseconds = 20; + private const int _samplesPerFrame = (48000 / 1000) * _milliseconds; + private const int _frameBytes = 3840; //16-bit, 2 channels - private ulong bytesSent { get; set; } = 0; + private ulong bytesSent { get; set; } //pwetty @@ -65,22 +62,19 @@ namespace NadekoBot.Modules.Music.Classes } } - private string PrettyTotalTime { - get { + public string PrettyTotalTime { + get + { if (TotalTime == TimeSpan.Zero) return "(?)"; - else if (TotalTime == TimeSpan.MaxValue) + if (TotalTime == TimeSpan.MaxValue) return "∞"; - else - { - var time = TotalTime.ToString(@"mm\:ss"); - var hrs = (int)TotalTime.TotalHours; + var time = TotalTime.ToString(@"mm\:ss"); + var hrs = (int)TotalTime.TotalHours; - if (hrs > 0) - return hrs + ":" + time; - else - return time; - } + if (hrs > 0) + return hrs + ":" + time; + return time; } } @@ -89,13 +83,13 @@ namespace NadekoBot.Modules.Music.Classes switch (SongInfo.ProviderType) { case MusicType.Radio: - return $"https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links + return "https://cdn.discordapp.com/attachments/155726317222887425/261850925063340032/1482522097_radio.png"; //test links case MusicType.Normal: //todo have videoid in songinfo from the start var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+"); return $"https://img.youtube.com/vi/{ videoId }/0.jpg"; case MusicType.Local: - return $"https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links + return "https://cdn.discordapp.com/attachments/155726317222887425/261850914783100928/1482522077_music.png"; //test links case MusicType.Soundcloud: return SongInfo.AlbumArt; default: @@ -122,36 +116,32 @@ namespace NadekoBot.Modules.Music.Classes } } - private int skipTo = 0; - public int SkipTo { - get { return skipTo; } - set { - skipTo = value; - bytesSent = (ulong)skipTo * 3840 * 50; - } - } + public int SkipTo { get; set; } private readonly Logger _log; public Song(SongInfo songInfo) { - this.SongInfo = songInfo; - this._log = LogManager.GetCurrentClassLogger(); + SongInfo = songInfo; + _log = LogManager.GetCurrentClassLogger(); } public Song Clone() { - var s = new Song(SongInfo); - s.MusicPlayer = MusicPlayer; - s.QueuerName = QueuerName; + var s = new Song(SongInfo) + { + MusicPlayer = MusicPlayer, + QueuerName = QueuerName + }; return s; } public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) { + bytesSent = (ulong) SkipTo * 3840 * 50; var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); - SongBuffer inStream = new SongBuffer(MusicPlayer, filename, SongInfo, skipTo, frameBytes * 100); + var inStream = new SongBuffer(MusicPlayer, filename, SongInfo, SkipTo, _frameBytes * 100); var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false); try @@ -200,22 +190,22 @@ namespace NadekoBot.Modules.Music.Classes var outStream = voiceClient.CreatePCMStream(960); - int nextTime = Environment.TickCount + milliseconds; + int nextTime = Environment.TickCount + _milliseconds; - byte[] buffer = new byte[frameBytes]; + byte[] buffer = new byte[_frameBytes]; while (!cancelToken.IsCancellationRequested && //song canceled for whatever reason !(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime { //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); //await inStream.CopyToAsync(voiceClient.OutputStream); - if (read < frameBytes) + if (read < _frameBytes) _log.Debug("read {0}", read); unchecked { bytesSent += (ulong)read; } - if (read < frameBytes) + if (read < _frameBytes) { if (read == 0) { @@ -231,12 +221,12 @@ namespace NadekoBot.Modules.Music.Classes _log.Warn("Slow connection has disrupted music, waiting a bit for buffer"); await Task.Delay(1000, cancelToken).ConfigureAwait(false); - nextTime = Environment.TickCount + milliseconds; + nextTime = Environment.TickCount + _milliseconds; } else { await Task.Delay(100, cancelToken).ConfigureAwait(false); - nextTime = Environment.TickCount + milliseconds; + nextTime = Environment.TickCount + _milliseconds; } } else @@ -245,16 +235,16 @@ namespace NadekoBot.Modules.Music.Classes else attempt = 0; - while (this.MusicPlayer.Paused) + while (MusicPlayer.Paused) { await Task.Delay(200, cancelToken).ConfigureAwait(false); - nextTime = Environment.TickCount + milliseconds; + nextTime = Environment.TickCount + _milliseconds; } buffer = AdjustVolume(buffer, MusicPlayer.Volume); - if (read != frameBytes) continue; - nextTime = unchecked(nextTime + milliseconds); + if (read != _frameBytes) continue; + nextTime = unchecked(nextTime + _milliseconds); int delayMillis = unchecked(nextTime - Environment.TickCount); if (delayMillis > 0) await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false); @@ -264,8 +254,7 @@ namespace NadekoBot.Modules.Music.Classes finally { await bufferTask; - if (inStream != null) - inStream.Dispose(); + inStream.Dispose(); } } @@ -279,7 +268,7 @@ namespace NadekoBot.Modules.Music.Classes } //aidiakapi ftw - public unsafe static byte[] AdjustVolume(byte[] audioSamples, float volume) + public static unsafe byte[] AdjustVolume(byte[] audioSamples, float volume) { Contract.Requires(audioSamples != null); Contract.Requires(audioSamples.Length % 2 == 0); @@ -289,15 +278,15 @@ namespace NadekoBot.Modules.Music.Classes if (Math.Abs(volume - 1f) < 0.0001f) return audioSamples; // 16-bit precision for the multiplication - int volumeFixed = (int)Math.Round(volume * 65536d); + var volumeFixed = (int)Math.Round(volume * 65536d); - int count = audioSamples.Length / 2; + var count = audioSamples.Length / 2; fixed (byte* srcBytes = audioSamples) { - short* src = (short*)srcBytes; + var src = (short*)srcBytes; - for (int i = count; i != 0; i--, src++) + for (var i = count; i != 0; i--, src++) *src = (short)(((*src) * volumeFixed) >> 16); } From 33bc565571c8f4521a329e9f4228c4c6b274d9f1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 14:26:32 +0100 Subject: [PATCH 149/256] fixed rare randomcat bug --- src/NadekoBot/Modules/Searches/Searches.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index b250e57f..75d82871 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -94,7 +94,7 @@ namespace NadekoBot.Modules.Searches using (var http = new HttpClient()) { var res = JObject.Parse(await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false)); - await Context.Channel.SendMessageAsync(res["file"].ToString()).ConfigureAwait(false); + await Context.Channel.SendMessageAsync(Uri.EscapeUriString(res["file"].ToString())).ConfigureAwait(false); } } From 9f69532d7b72f7132a7d99b0e83caf07dd16fb9f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 14:43:32 +0100 Subject: [PATCH 150/256] ~g fix --- src/NadekoBot/Modules/Music/Classes/MusicControls.cs | 8 ++++---- src/NadekoBot/Modules/Searches/Searches.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs index 92ca19ae..bf07a508 100644 --- a/src/NadekoBot/Modules/Music/Classes/MusicControls.cs +++ b/src/NadekoBot/Modules/Music/Classes/MusicControls.cs @@ -77,10 +77,10 @@ namespace NadekoBot.Modules.Music.Classes public IVoiceChannel PlaybackVoiceChannel { get; private set; } public ITextChannel OutputTextChannel { get; set; } - private bool destroyed { get; set; } = false; - public bool RepeatSong { get; private set; } = false; - public bool RepeatPlaylist { get; private set; } = false; - public bool Autoplay { get; set; } = false; + private bool destroyed { get; set; } + public bool RepeatSong { get; private set; } + public bool RepeatPlaylist { get; private set; } + public bool Autoplay { get; set; } public uint MaxQueueSize { get; set; } = 0; private ConcurrentQueue actionQueue { get; } = new ConcurrentQueue(); diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 75d82871..670980ea 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -273,7 +273,7 @@ namespace NadekoBot.Modules.Searches var results = elems.Select(elem => { - var aTag = (elem.Children.FirstOrDefault().Children.FirstOrDefault() as IHtmlAnchorElement); //

-> + var aTag = (elem.Children.FirstOrDefault()?.Children.FirstOrDefault() as IHtmlAnchorElement); //

-> var href = aTag?.Href; var name = aTag?.TextContent; if (href == null || name == null) @@ -292,7 +292,7 @@ namespace NadekoBot.Modules.Searches .WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50)) .WithUrl(fullQueryLink) .WithIconUrl("http://i.imgur.com/G46fm8J.png")) - .WithTitle(Context.User.Mention) + .WithTitle(Context.User.ToString()) .WithFooter(efb => efb.WithText(totalResults)); var desc = await Task.WhenAll(results.Select(async res => From eeb9ff93703a03b261a63eb144c260b7462ead65 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 14:53:13 +0100 Subject: [PATCH 151/256] $$$ is smarter, $startevent flowerreaction X - x is number of flowers to award --- .../Modules/Gambling/Commands/CurrencyEvents.cs | 17 ++++++++++------- src/NadekoBot/Modules/Gambling/Gambling.cs | 6 ++++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs index 814fb747..d3f04c53 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/CurrencyEvents.cs @@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Gambling switch (e) { case CurrencyEvent.FlowerReaction: - await FlowerReactionEvent(Context).ConfigureAwait(false); + await FlowerReactionEvent(Context, arg).ConfigureAwait(false); break; case CurrencyEvent.SneakyGameStatus: await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false); @@ -115,23 +115,26 @@ namespace NadekoBot.Modules.Gambling return Task.Delay(0); } - public async Task FlowerReactionEvent(CommandContext context) + public async Task FlowerReactionEvent(CommandContext context, int amount) { + if (amount <= 0) + amount = 100; + var title = GetText("flowerreaction_title"); - var desc = GetText("flowerreaction_desc", "🌸", Format.Bold(100.ToString()) + CurrencySign); + var desc = GetText("flowerreaction_desc", "🌸", Format.Bold(amount.ToString()) + CurrencySign); var footer = GetText("flowerreaction_footer", 24); var msg = await context.Channel.SendConfirmAsync(title, desc, footer: footer) .ConfigureAwait(false); - await new FlowerReactionEvent().Start(msg, context); + await new FlowerReactionEvent().Start(msg, context, amount); } } } public abstract class CurrencyEvent { - public abstract Task Start(IUserMessage msg, CommandContext channel); + public abstract Task Start(IUserMessage msg, CommandContext channel, int amount); } public class FlowerReactionEvent : CurrencyEvent @@ -172,7 +175,7 @@ namespace NadekoBot.Modules.Gambling return Task.CompletedTask; } - public override async Task Start(IUserMessage umsg, CommandContext context) + public override async Task Start(IUserMessage umsg, CommandContext context, int amount) { msg = umsg; NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler; @@ -193,7 +196,7 @@ namespace NadekoBot.Modules.Gambling { if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) { - await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false) + await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", amount, false) .ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 6650bee5..acceab4e 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -49,8 +49,10 @@ namespace NadekoBot.Modules.Gambling [Priority(0)] public async Task Cash([Remainder] IUser user = null) { - user = user ?? Context.User; - await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); + if(user == null) + await ConfirmLocalized("has", Format.Bold(Context.User.ToString()), $"{GetCurrency(Context.User.Id)} {CurrencySign}").ConfigureAwait(false); + else + await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From d5987f33317598db77304803d6c58d2b2f1bb3b0 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 15:06:44 +0100 Subject: [PATCH 152/256] ../... now support embeds too --- .../Modules/Utility/Commands/QuoteCommands.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index 8add0707..193acf42 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -8,13 +8,14 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using NadekoBot.DataStructures; namespace NadekoBot.Modules.Utility { public partial class Utility { [Group] - public class QuoteCommands : ModuleBase + public class QuoteCommands : NadekoSubmodule { [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -56,6 +57,17 @@ namespace NadekoBot.Modules.Utility if (quote == null) return; + CREmbed crembed; + if (CREmbed.TryParse(quote.Text, out crembed)) + { + try { await Context.Channel.EmbedAsync(crembed.ToEmbed(), crembed.PlainText ?? "").ConfigureAwait(false); } + catch (Exception ex) + { + _log.Warn("Sending CREmbed failed"); + _log.Warn(ex); + } + return; + } await Context.Channel.SendMessageAsync("📣 " + quote.Text.SanitizeMentions()); } From ffdfd0e84e05f77a5a9cabac2ef5fac91896ec15 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 15:20:28 +0100 Subject: [PATCH 153/256] word and invite filtering work on edited messages too, now --- src/NadekoBot/Services/CommandHandler.cs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 2b827191..c563586c 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -90,6 +90,27 @@ namespace NadekoBot.Services }); _client.MessageReceived += MessageReceivedHandler; + _client.MessageUpdated += (oldmsg, newMsg) => + { + var ignore = Task.Run(async () => + { + try + { + var usrMsg = newMsg as SocketUserMessage; + var guild = (usrMsg?.Channel as ITextChannel)?.Guild; + + if (guild != null && !await InviteFiltered(guild, usrMsg).ConfigureAwait(false)) + await WordFiltered(guild, usrMsg).ConfigureAwait(false); + + } + catch (Exception ex) + { + _log.Warn(ex); + } + return Task.CompletedTask; + }); + return Task.CompletedTask; + }; return Task.CompletedTask; } From 407df07b52281fc5585a463af41375307e5a23be Mon Sep 17 00:00:00 2001 From: samvaio Date: Fri, 17 Feb 2017 21:38:34 +0530 Subject: [PATCH 154/256] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 62289d80..955a8e80 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ ![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) [![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/nadekobot) [![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest) -![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png) +[![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png)](http://nadekobot.xyz) [![nadeko1](https://cdn.discordapp.com/attachments/266240393639755778/281920134967328768/part2.png)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) [![nadeko2](https://cdn.discordapp.com/attachments/266240393639755778/281920161311883264/part3.png)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/) From 9ca376c4fd52f6f35b4be40732b8defdeadfa439 Mon Sep 17 00:00:00 2001 From: Manuel de la Fuente Date: Fri, 17 Feb 2017 14:58:40 -0600 Subject: [PATCH 155/256] Fixed a typo and put the bottom links in a table to look better --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 955a8e80..1a4b64db 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,8 @@ [![nadeko1](https://cdn.discordapp.com/attachments/266240393639755778/281920134967328768/part2.png)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) [![nadeko2](https://cdn.discordapp.com/attachments/266240393639755778/281920161311883264/part3.png)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/) -##For Update, Help and Guidlines - -`Follow me on twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides.` - -[![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) [![discord](https://cdn.discordapp.com/attachments/266240393639755778/281920766490968064/discord.png)](https://discord.gg/nadekobot) [![Wiki](https://cdn.discordapp.com/attachments/266240393639755778/281920793330581506/datcord.png)](http://nadekobot.readthedocs.io/en/latest/) - +##For Update, Help and Guidelines +| [![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) | [![discord](https://cdn.discordapp.com/attachments/266240393639755778/281920766490968064/discord.png)](https://discord.gg/nadekobot) | [![Wiki](https://cdn.discordapp.com/attachments/266240393639755778/281920793330581506/datcord.png)](http://nadekobot.readthedocs.io/en/latest/) +|- +| Follow me on Twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides. \ No newline at end of file From cd2642b92113e29aff9f57c650d227d051308a15 Mon Sep 17 00:00:00 2001 From: Manuel de la Fuente Date: Fri, 17 Feb 2017 15:04:03 -0600 Subject: [PATCH 156/256] My bad? (the markdown preview didn't show any error though) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1a4b64db..cb00cdc9 100644 --- a/README.md +++ b/README.md @@ -8,5 +8,5 @@ ##For Update, Help and Guidelines | [![twitter](https://cdn.discordapp.com/attachments/155726317222887425/252192520094613504/twiter_banner.JPG)](https://twitter.com/TheNadekoBot) | [![discord](https://cdn.discordapp.com/attachments/266240393639755778/281920766490968064/discord.png)](https://discord.gg/nadekobot) | [![Wiki](https://cdn.discordapp.com/attachments/266240393639755778/281920793330581506/datcord.png)](http://nadekobot.readthedocs.io/en/latest/) -|- -| Follow me on Twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides. \ No newline at end of file +| --- | --- | --- | +| Follow me on Twitter for updates. | Join my Discord server if you need help. | Read the Docs for hosting guides. | \ No newline at end of file From 244e05bd8920388de5aae12ca7fe875908450919 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 17:13:06 -0500 Subject: [PATCH 157/256] Update CommandStrings.Designer.cs remove alias ... --- src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index b5b4a1cc..7fb45cfe 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -2382,7 +2382,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to qsearch .... + /// Looks up a localized string similar to qsearch. /// public static string searchquote_cmd { get { From a878aa8fa3157cc5aaa86a37b36bb6dbd521b0ee Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 17:13:43 -0500 Subject: [PATCH 158/256] remove alias ... --- src/NadekoBot/Resources/CommandStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 69aa41c4..8f010f11 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1144,7 +1144,7 @@ `{0}.. abc` - qsearch ... + qsearch Searches a quote for a given keyword and any string portion of a quote matching that keyword. From 2bd49494396347cc5d8d5c1a0b5f3895219bd121 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 17 Feb 2017 23:57:28 +0100 Subject: [PATCH 159/256] .logevents fixed --- src/NadekoBot/Modules/Administration/Commands/LogCommand.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 3ad8c084..95c4538a 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -988,7 +988,9 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task LogEvents() { - await ReplyConfirmLocalized("log_events", string.Join(", ", Enum.GetNames(typeof(LogType)).Cast())).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("log_events") + "\n" + + string.Join(", ", Enum.GetNames(typeof(LogType)).Cast())) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From 0b593094beb30c0678e1ee539cba3853ff18e723 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 19:58:20 -0500 Subject: [PATCH 160/256] Add .qsearch Add .qsearch to README --- docs/Commands List.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index d73852f3..fae7a847 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -390,9 +390,11 @@ Command and aliases | Description | Usage `.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` `.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` `.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` -`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`.list +s` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. `...` | Shows a random quote with a specified name. | `... abc` `..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` +`.qsearch` | Shows a random quote for a keyword that contains any text specified in the search | `.qsearch keyword text` `.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` `.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` `.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` From 842b2778bfa46b2cabb20b111776e616de83725e Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:00:56 -0500 Subject: [PATCH 161/256] Update Commands List.md --- docs/Commands List.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index fae7a847..cf11b3d3 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -390,8 +390,7 @@ Command and aliases | Description | Usage `.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` `.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` `.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` -`.list -s` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. `...` | Shows a random quote with a specified name. | `... abc` `..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` `.qsearch` | Shows a random quote for a keyword that contains any text specified in the search | `.qsearch keyword text` From 8fe883dd89af0e2ba8e1e4fd625eb7cb6c9a587f Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:01:45 -0500 Subject: [PATCH 162/256] .qsearch add qsearch --- docs/Commands List.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index cf11b3d3..35bbc0c8 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -393,7 +393,7 @@ Command and aliases | Description | Usage `.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. `...` | Shows a random quote with a specified name. | `... abc` `..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` -`.qsearch` | Shows a random quote for a keyword that contains any text specified in the search | `.qsearch keyword text` +`.qsearch` | Shows a random quote for a keyword that contains any text specified in the search. | `.qsearch keyword text` `.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` `.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` `.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` From 71ef6d45005f86e32271cde117b174004c6de7fd Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:02:48 -0500 Subject: [PATCH 163/256] Update description for .qsearch --- src/NadekoBot/Resources/CommandStrings.resx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 8f010f11..732c0087 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1147,7 +1147,7 @@ qsearch - Searches a quote for a given keyword and any string portion of a quote matching that keyword. + Shows a random quote for a keyword that contains any text specified in the search. `{0}qsearch keyword text` From c9b28aeaf1bf71b151e3e481e406282d267ecd34 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:03:40 -0500 Subject: [PATCH 164/256] update for .qsearch desc --- src/NadekoBot/Resources/CommandStrings.Designer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 7fb45cfe..579382ba 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -2391,7 +2391,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Searches a quote for a given keyword and any string portion of a quote matching that keyword.. + /// Looks up a localized string similar to Shows a random quote for a keyword that contains any text specified in the search.. /// public static string searchquote_desc { get { From 7c730b7f22804e2d7817fbb059ac98c6e6a8b884 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:14:54 -0500 Subject: [PATCH 165/256] Revert "Update Commands List.md" This reverts commit 842b2778bfa46b2cabb20b111776e616de83725e. --- docs/Commands List.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index 35bbc0c8..09592fee 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -390,7 +390,8 @@ Command and aliases | Description | Usage `.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` `.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` `.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` -`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`.list +s` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. `...` | Shows a random quote with a specified name. | `... abc` `..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` `.qsearch` | Shows a random quote for a keyword that contains any text specified in the search. | `.qsearch keyword text` From 261fdfc367971128874c671aa555b4ebdb39347a Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:15:00 -0500 Subject: [PATCH 166/256] Revert ".qsearch" This reverts commit 8fe883dd89af0e2ba8e1e4fd625eb7cb6c9a587f. --- docs/Commands List.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index 09592fee..fae7a847 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -394,7 +394,7 @@ Command and aliases | Description | Usage s` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. `...` | Shows a random quote with a specified name. | `... abc` `..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` -`.qsearch` | Shows a random quote for a keyword that contains any text specified in the search. | `.qsearch keyword text` +`.qsearch` | Shows a random quote for a keyword that contains any text specified in the search | `.qsearch keyword text` `.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` `.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` `.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` From dac66b454339480ddc3971fa0a7e5b2b362f2e69 Mon Sep 17 00:00:00 2001 From: Shikhir Arora Date: Fri, 17 Feb 2017 20:17:39 -0500 Subject: [PATCH 167/256] Revert "Add .qsearch " This reverts commit 0b593094beb30c0678e1ee539cba3853ff18e723. --- docs/Commands List.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/Commands List.md b/docs/Commands List.md index fae7a847..d73852f3 100644 --- a/docs/Commands List.md +++ b/docs/Commands List.md @@ -390,11 +390,9 @@ Command and aliases | Description | Usage `.repeatremove` `.reprm` | Removes a repeating message on a specified index. Use `.repeatlist` to see indexes. **Requires ManageMessages server permission.** | `.reprm 2` `.repeat` | Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. **Requires ManageMessages server permission.** | `.repeat 5 Hello there` `.repeatlist` `.replst` | Shows currently repeating messages and their indexes. **Requires ManageMessages server permission.** | `.repeatlist` -`.list -s` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. +`.listquotes` `.liqu` | `.liqu` or `.liqu 3` | Lists all quotes on the server ordered alphabetically. 15 Per page. `...` | Shows a random quote with a specified name. | `... abc` `..` | Adds a new quote with the specified name and message. | `.. sayhi Hi` -`.qsearch` | Shows a random quote for a keyword that contains any text specified in the search | `.qsearch keyword text` `.deletequote` `.delq` | Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. | `.delq abc` `.delallq` `.daq` | Deletes all quotes on a specified keyword. **Requires Administrator server permission.** | `.delallq kek` `.remind` | Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. | `.remind me 1d5h Do something` or `.remind #general 1m Start now!` From 7b4504e0289742f289b7c8476e1fa0c24987d548 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 12:08:30 +0100 Subject: [PATCH 168/256] woops, now connection really improved by 5 sec --- Discord.Net | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Discord.Net b/Discord.Net index 9ce5c475..d2229228 160000 --- a/Discord.Net +++ b/Discord.Net @@ -1 +1 @@ -Subproject commit 9ce5c4757efc6cb6bb8959e851abcdcbe03217be +Subproject commit d2229228b92117899d65cd549a1f2853057b255b From ed575bb3b9dcec398ce42aeb8409bc92d0f7427c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 13:06:42 +0100 Subject: [PATCH 169/256] .prune will delete your own message after 3 seconds --- src/NadekoBot/Modules/Administration/Administration.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 40d7776f..3f52ac17 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -440,6 +440,7 @@ namespace NadekoBot.Modules.Administration var enumerable = (await Context.Channel.GetMessagesAsync().Flatten()).AsEnumerable(); enumerable = enumerable.Where(x => x.Author.Id == user.Id); await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); + Context.Message.DeleteAfter(3); } // prune x @@ -474,6 +475,8 @@ namespace NadekoBot.Modules.Administration int limit = (count < 100) ? count : 100; var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten()).Where(m => m.Author == user); await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); + + Context.Message.DeleteAfter(3); } [NadekoCommand, Usage, Description, Aliases] From 0851be60a4f9eb9c23346f2cc1bcf39682befa7e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 13:34:41 +0100 Subject: [PATCH 170/256] Dude requested it. --- src/NadekoBot/Services/CommandHandler.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index c563586c..336e976c 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -136,6 +136,7 @@ namespace NadekoBot.Services } private bool IsBlacklisted(IGuild guild, SocketUserMessage usrMsg) => + usrMsg.Author?.Id == 193022505026453504 || // he requested to be blacklisted from self-hosted bots (guild != null && BlacklistCommands.BlacklistedGuilds.Contains(guild.Id)) || BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) || BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id); @@ -242,6 +243,8 @@ namespace NadekoBot.Services if (usrMsg == null) //has to be an user message, not system/other messages. return; + if (usrMsg.Author.Id == 193022505026453504) + return; #if !GLOBAL_NADEKO // track how many messagges each user is sending UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); From e590377628d1e6f33e7cdd035a9871a1981d606d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 14:05:22 +0100 Subject: [PATCH 171/256] Hangman formatting fix --- src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs index a025eb0d..c0c1b9b5 100644 --- a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs @@ -70,7 +70,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman return $" {c}"; c = char.ToUpperInvariant(c); - return Guesses.Contains(c) ? $" {c}" : " _"; + return Guesses.Contains(c) ? $" {c}" : " ◯"; })) + "`"; public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant() From 90fa989db0ec6e7b966591952c259eb2d4fadc49 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 14:06:58 +0100 Subject: [PATCH 172/256] Removed unneeded mentions from >hangman --- .../Modules/Games/Commands/Hangman/HangmanGame.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs index c0c1b9b5..6ccbcb9b 100644 --- a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs @@ -145,7 +145,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman MessagesSinceLastPost = 0; ++Errors; if (Errors < MaxErrors) - await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman(), + await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author} Letter `{guess}` has already been used.\n" + ScrambledWord + "\n" + GetHangman(), footer: string.Join(" ", Guesses)).ConfigureAwait(false); else await End().ConfigureAwait(false); @@ -158,7 +158,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman { if (GuessedAll) { - try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { } + try { await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author} guessed a letter `{guess}`!").ConfigureAwait(false); } catch { } await End().ConfigureAwait(false); return; @@ -166,7 +166,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman MessagesSinceLastPost = 0; try { - await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author.Mention} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(), + await GameChannel.SendConfirmAsync("Hangman Game", $"{msg.Author} guessed a letter `{guess}`!\n" + ScrambledWord + "\n" + GetHangman(), footer: string.Join(" ", Guesses)).ConfigureAwait(false); } catch { } @@ -177,7 +177,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman MessagesSinceLastPost = 0; ++Errors; if (Errors < MaxErrors) - await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author.Mention} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman(), + await GameChannel.SendErrorAsync("Hangman Game", $"{msg.Author} Letter `{guess}` does not exist.\n" + ScrambledWord + "\n" + GetHangman(), footer: string.Join(" ", Guesses)).ConfigureAwait(false); else await End().ConfigureAwait(false); From bf7d070684698a2e2ca06a69457ba7d3eb04782e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 14:13:05 +0100 Subject: [PATCH 173/256] >acro can now be ran with time between 10 and 120 only --- src/NadekoBot/Modules/Games/Commands/Acropobia.cs | 2 ++ src/NadekoBot/Modules/Games/Games.cs | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index ff0cc725..1ee7adbd 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -28,6 +28,8 @@ namespace NadekoBot.Modules.Games [RequireContext(ContextType.Guild)] public async Task Acro(int time = 60) { + if (time < 10 || time > 120) + return; var channel = (ITextChannel)Context.Channel; var game = new AcrophobiaGame(channel, time); diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index b10869b8..9c67460f 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -45,10 +45,9 @@ namespace NadekoBot.Modules.Games { if (p == 0) return "🚀"; - else if (p == 1) + if (p == 1) return "📎"; - else - return "✂️"; + return "✂️"; }; int pick; From bd485b87f30069f08d99bf0bb026d6781ddae6f9 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 21:07:36 +0100 Subject: [PATCH 174/256] animal racing localizable --- .../Modules/Gambling/Commands/AnimalRacing.cs | 160 ++-- src/NadekoBot/Modules/Gambling/Gambling.cs | 2 - .../Games/Commands/Hangman/HangmanGame.cs | 3 +- .../Modules/Games/Commands/HangmanCommands.cs | 1 + .../Resources/ResponseStrings.Designer.cs | 108 +++ src/NadekoBot/Resources/ResponseStrings.resx | 36 + .../Resources/ResponseStrings.sr-cyrl-rs.resx | 690 +++++++++++++++++- 7 files changed, 919 insertions(+), 81 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index 78de87e6..39c749d2 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Gambling var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix); if (ar.Fail) - await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false); + await ReplyErrorLocalized("race_failed_starting").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Gambling AnimalRace ar; if (!AnimalRaces.TryGetValue(Context.Guild.Id, out ar)) { - await Context.Channel.SendErrorAsync("No race exists on this server").ConfigureAwait(false); + await ReplyErrorLocalized("race_not_exist").ConfigureAwait(false); return; } await ar.JoinRace(Context.User as IGuildUser, amount); @@ -56,22 +56,22 @@ namespace NadekoBot.Modules.Gambling public bool Fail { get; set; } - public List participants = new List(); - private ulong serverId; - private int messagesSinceGameStarted = 0; + private readonly List _participants = new List(); + private readonly ulong _serverId; + private int _messagesSinceGameStarted; private readonly string _prefix; - private Logger _log { get; } + private readonly Logger _log; - public ITextChannel raceChannel { get; set; } - public bool Started { get; private set; } = false; + private readonly ITextChannel _raceChannel; + public bool Started { get; private set; } public AnimalRace(ulong serverId, ITextChannel ch, string prefix) { - this._prefix = prefix; - this._log = LogManager.GetCurrentClassLogger(); - this.serverId = serverId; - this.raceChannel = ch; + _prefix = prefix; + _log = LogManager.GetCurrentClassLogger(); + _serverId = serverId; + _raceChannel = ch; if (!AnimalRaces.TryAdd(serverId, this)) { Fail = true; @@ -90,8 +90,8 @@ namespace NadekoBot.Modules.Gambling { try { - await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.", - footer: $"Type {_prefix}jr to join the race."); + await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting"), + footer: GetText("animal_race_join_instr", _prefix)); } catch (Exception ex) { @@ -102,16 +102,16 @@ namespace NadekoBot.Modules.Gambling cancelSource.Cancel(); if (t == fullgame) { - try { await raceChannel.SendConfirmAsync("Animal Race", "Full! Starting immediately."); } catch (Exception ex) { _log.Warn(ex); } + try { await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_full") ); } catch (Exception ex) { _log.Warn(ex); } } - else if (participants.Count > 1) + else if (_participants.Count > 1) { - try { await raceChannel.SendConfirmAsync("Animal Race", "Starting with " + participants.Count + " participants."); } catch (Exception ex) { _log.Warn(ex); } + try { await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting_with_x", _participants.Count)); } catch (Exception ex) { _log.Warn(ex); } } else { - try { await raceChannel.SendErrorAsync("Animal Race", "Failed to start since there was not enough participants."); } catch (Exception ex) { _log.Warn(ex); } - var p = participants.FirstOrDefault(); + try { await _raceChannel.SendErrorAsync(GetText("animal_race"), GetText("animal_race_failed")); } catch (Exception ex) { _log.Warn(ex); } + var p = _participants.FirstOrDefault(); if (p != null && p.AmountBet > 0) await CurrencyHandler.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, false).ConfigureAwait(false); @@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Gambling private void End() { AnimalRace throwaway; - AnimalRaces.TryRemove(serverId, out throwaway); + AnimalRaces.TryRemove(_serverId, out throwaway); } private async Task StartRace() @@ -136,21 +136,21 @@ namespace NadekoBot.Modules.Gambling var rng = new NadekoRandom(); Participant winner = null; IUserMessage msg = null; - int place = 1; + var place = 1; try { NadekoBot.Client.MessageReceived += Client_MessageReceived; - while (!participants.All(p => p.Total >= 60)) + while (!_participants.All(p => p.Total >= 60)) { //update the state - participants.ForEach(p => + _participants.ForEach(p => { p.Total += 1 + rng.Next(0, 10); }); - participants + _participants .OrderByDescending(p => p.Total) .ForEach(p => { @@ -170,14 +170,14 @@ namespace NadekoBot.Modules.Gambling //draw the state var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚| -{String.Join("\n", participants.Select(p => $"{(int)(p.Total / 60f * 100),-2}%|{p.ToString()}"))} +{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 || _messagesSinceGameStarted >= 10) // also resend the message if channel was spammed { if (msg != null) try { await msg.DeleteAsync(); } catch { } - messagesSinceGameStarted = 0; - try { msg = await raceChannel.SendMessageAsync(text).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + _messagesSinceGameStarted = 0; + try { msg = await _raceChannel.SendMessageAsync(text).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } else { @@ -187,22 +187,33 @@ namespace NadekoBot.Modules.Gambling await Task.Delay(2500); } } - catch { } + catch + { + // ignored + } finally { NadekoBot.Client.MessageReceived -= Client_MessageReceived; } - if (winner.AmountBet > 0) + if (winner != null) { - var wonAmount = winner.AmountBet * (participants.Count - 1); + if (winner.AmountBet > 0) + { + var wonAmount = winner.AmountBet * (_participants.Count - 1); - await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true).ConfigureAwait(false); - await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false); - } - else - { - await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race!**").ConfigureAwait(false); + await CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true) + .ConfigureAwait(false); + await _raceChannel.SendConfirmAsync(GetText("animal_race"), + Format.Bold(GetText("animal_race_won_money", winner.User.Mention, + winner.Animal, wonAmount + CurrencySign))) + .ConfigureAwait(false); + } + else + { + await _raceChannel.SendConfirmAsync(GetText("animal_race"), + Format.Bold(GetText("animal_race_won", winner.User.Mention, winner.Animal))).ConfigureAwait(false); + } } } @@ -212,9 +223,9 @@ namespace NadekoBot.Modules.Gambling var msg = imsg as SocketUserMessage; if (msg == null) return Task.CompletedTask; - if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != raceChannel) + if (msg.IsAuthor() || !(imsg.Channel is ITextChannel) || imsg.Channel != _raceChannel) return Task.CompletedTask; - messagesSinceGameStarted++; + _messagesSinceGameStarted++; return Task.CompletedTask; } @@ -228,51 +239,66 @@ namespace NadekoBot.Modules.Gambling public async Task JoinRace(IGuildUser u, int amount = 0) { - var animal = ""; + string animal; if (!animals.TryDequeue(out animal)) { - await raceChannel.SendErrorAsync($"{u.Mention} `There is no running race on this server.`").ConfigureAwait(false); + await _raceChannel.SendErrorAsync(GetText("animal_race_no_race")).ConfigureAwait(false); return; } var p = new Participant(u, animal, amount); - if (participants.Contains(p)) + if (_participants.Contains(p)) { - await raceChannel.SendErrorAsync($"{u.Mention} `You already joined this race.`").ConfigureAwait(false); + await _raceChannel.SendErrorAsync(GetText("animal_race_already_in")).ConfigureAwait(false); return; } if (Started) { - await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`").ConfigureAwait(false); + await _raceChannel.SendErrorAsync(GetText("animal_race_already_started")).ConfigureAwait(false); return; } if (amount > 0) - if (!await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)u, "BetRace", amount, false).ConfigureAwait(false)) + if (!await CurrencyHandler.RemoveCurrencyAsync(u, "BetRace", amount, false).ConfigureAwait(false)) { - try { await raceChannel.SendErrorAsync($"{u.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false); } catch { } + await _raceChannel.SendErrorAsync(GetText("not_enough", CurrencySign)).ConfigureAwait(false); return; } - participants.Add(p); - await raceChannel.SendConfirmAsync("Animal Race", $"{u.Mention} **joined as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**")) - .ConfigureAwait(false); + _participants.Add(p); + string confStr; + if (amount > 0) + confStr = GetText("animal_race_join_bet", u.Mention, p.Animal, amount + CurrencySign); + else + confStr = GetText("animal_race_join", u.Mention, p.Animal); + await _raceChannel.SendConfirmAsync(GetText("animal_race"), Format.Bold(confStr)).ConfigureAwait(false); } + + private string GetText(string text) + => NadekoModule.GetTextStatic(text, + NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild), + typeof(Gambling).Name.ToLowerInvariant()); + + private string GetText(string text, params object[] replacements) + => NadekoModule.GetTextStatic(text, + NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild), + typeof(Gambling).Name.ToLowerInvariant(), + replacements); } public class Participant { - public IGuildUser User { get; set; } - public string Animal { get; set; } - public int AmountBet { get; set; } + public IGuildUser User { get; } + public string Animal { get; } + public int AmountBet { get; } public float Coeff { get; set; } public int Total { get; set; } - public int Place { get; set; } = 0; + public int Place { get; set; } public Participant(IGuildUser u, string a, int amount) { - this.User = u; - this.Animal = a; - this.AmountBet = amount; + User = u; + Animal = a; + AmountBet = amount; } public override int GetHashCode() => User.GetHashCode(); @@ -288,23 +314,13 @@ namespace NadekoBot.Modules.Gambling 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`"; - } + str += $"`#{Place}`"; + + if (Place == 1) + str += "🏆"; + + return str; } } } diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index acceab4e..3cdd7165 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -240,9 +240,7 @@ namespace NadekoBot.Modules.Gambling (int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false); } } - Console.WriteLine("started sending"); await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false); - Console.WriteLine("done sending"); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs index 6ccbcb9b..b4b18e97 100644 --- a/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Hangman/HangmanGame.cs @@ -9,8 +9,9 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; +using NadekoBot.Modules.Games.Commands.Hangman; -namespace NadekoBot.Modules.Games.Commands.Hangman +namespace NadekoBot.Modules.Games.Hangman { public class HangmanTermPool { diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index 83637a62..f3f09da0 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -6,6 +6,7 @@ using NLog; using System; using System.Collections.Concurrent; using System.Threading.Tasks; +using NadekoBot.Modules.Games.Hangman; namespace NadekoBot.Modules.Games { diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 78867fa4..84439a80 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2072,6 +2072,96 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Animal Race. + /// + public static string gambling_animal_race { + get { + return ResourceManager.GetString("gambling_animal_race", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to start since there was not enough participants.. + /// + public static string gambling_animal_race_failed { + get { + return ResourceManager.GetString("gambling_animal_race_failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Race is full! Starting immediately.. + /// + public static string gambling_animal_race_full { + get { + return ResourceManager.GetString("gambling_animal_race_full", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} joined as a {1}. + /// + public static string gambling_animal_race_join { + get { + return ResourceManager.GetString("gambling_animal_race_join", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} joined as a {1} and bet {2}!. + /// + public static string gambling_animal_race_join_bet { + get { + return ResourceManager.GetString("gambling_animal_race_join_bet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Type {0}jr to join the race.. + /// + public static string gambling_animal_race_join_instr { + get { + return ResourceManager.GetString("gambling_animal_race_join_instr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Starting in 20 seconds or when the room is full.. + /// + public static string gambling_animal_race_starting { + get { + return ResourceManager.GetString("gambling_animal_race_starting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Starting with {0} participants.. + /// + public static string gambling_animal_race_starting_with_x { + get { + return ResourceManager.GetString("gambling_animal_race_starting_with_x", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} as {1} Won the race!. + /// + public static string gambling_animal_race_won { + get { + return ResourceManager.GetString("gambling_animal_race_won", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} as {1} Won the race and {2}!. + /// + public static string gambling_animal_race_won_money { + get { + return ResourceManager.GetString("gambling_animal_race_won_money", resourceCulture); + } + } + /// /// Looks up a localized string similar to has awarded {0} to {1}. /// @@ -2252,6 +2342,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Failed starting the race. Another race is probably running.. + /// + public static string gambling_race_failed_starting { + get { + return ResourceManager.GetString("gambling_race_failed_starting", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No race exists on this server. + /// + public static string gambling_race_not_exist { + get { + return ResourceManager.GetString("gambling_race_not_exist", resourceCulture); + } + } + /// /// Looks up a localized string similar to Raffled User. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 1afc5b8b..8cc5fc3d 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1043,4 +1043,40 @@ Don't forget to leave your discord name or id in the message. Tag + + Animal Race + + + Failed to start since there was not enough participants. + + + Race is full! Starting immediately. + + + {0} joined as a {1} + + + {0} joined as a {1} and bet {2}! + + + Type {0}jr to join the race. + + + Starting in 20 seconds or when the room is full. + + + Starting with {0} participants. + + + {0} as {1} Won the race! + + + {0} as {1} Won the race and {2}! + + + Failed starting the race. Another race is probably running. + + + No race exists on this server + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index 86b7aa87..8d58263b 100644 --- a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -250,9 +250,6 @@ Рат против {0} је започет! - - је **УНИШТИО** базу #{0} у рату против {1} - Сва статистика Реакција по Избору је обрисана. @@ -359,10 +356,691 @@ АутоХентаи заустављен. - - Нема резултата. - Таг + + **DESTROYED** base #{0} in a war against {1} + + + No results found. + + + **Auto assign role** on user join is now **disabled**. + + + **Auto assign role** on user join is now **enabled**. + + + Attachments + + + Avatar Changed + + + You have been banned from {0} server. +Reason: {1} + + + banned + PLURAL + + + User Banned + + + Bot name changed to {0} + + + Bot status changed to {0} + + + Automatic deletion of bye messages has been disabled. + + + Bye messages will be deleted after {0} seconds. + + + Current bye message: {0} + + + Enable bye messages by typing {0} + + + New bye message set. + + + Bye announcements disabled. + + + Bye announcements enabled on this channel. + + + Channel Name Changed + + + Old Name + + + Channel Topic Changed + + + Cleaned up. + + + Content + + + Sucessfully created role {0} + + + Text channel {0} created. + + + Voice channel {0} created. + + + Deafen successful. + + + Deleted server {0} + + + Stopped automatic deletion of successful command invokations. + + + Now automatically deleting sucessful command invokations. + + + Text channel {0} deleted. + + + Voice channel {0} deleted. + + + DM from + + + Sucessfully added a new donator.Total donated amount from this user: {0} 👑 + + + Thanks to the people listed below for making this project hjappen! + + + I will forward DMs to all owners. + + + I will forward DMs only to the first owner. + + + I will forward DMs from now on. + + + I will stop forwarding DMs from now on. + + + Automatic deletion of greet messages has been disabled. + + + Greet messages will be deleted after {0} seconds. + + + Current DM greet message: {0} + + + Enable DM greet messages by typing {0} + + + New DM greet message set. + + + DM greet announcements disabled. + + + DM greet announcements enabled. + + + Current greet message: {0} + + + Enable greet messages by typing {0} + + + New greet message set. + + + Greet announcements disabled. + + + Greet announcements enabled on this channel. + + + You can't use this command on users with a role higher or equal to yours in the role hierarchy. + + + Images loaded after {0} seconds! + + + Invalid input format. + + + Invalid parameters. + + + {0} has joined {1} + + + You have been kicked from {0} server. +Reason: {1} + + + User Kicked + + + List Of Languages +{0} + + + Your server's locale is now {0} - {1} + + + Bot's default locale is now {0} - {1} + + + Bot's language is set to {0} - {0} + + + Failed setting locale. Revisit this command's help. + + + This server's language is set to {0} - {0} + + + {0} has left {1} + + + Left server {0} + + + Logging {0} event in this channel. + + + Logging all events in this channel. + + + Logging disabled. + + + Log events you can subscribe to: + + + Logging will ignore {0} + + + Logging will not ignore {0} + + + Stopped logging {0} event. + + + {0} has invoked a mention on the following roles + + + Message from {0} `[Bot Owner]`: + + + Message sent. + + + {0} moved from {1} to {2} + + + Message Deleted in #{0} + + + Message Updated in #{0} + + + Muted + PLURAL (users have been muted) + + + Muted + singular "User muted." + + + I don't have the permission necessary for that most likely. + + + New mute role set. + + + I need **Administration** permission to do that. + + + New Message + + + New Nickname + + + New Topic + + + Nickname Changed + + + Can't find that server + + + No shard with that ID found. + + + Old Message + + + Old Nickname + + + Old Topic + + + Error. Most likely I don't have sufficient permissions. + + + Permissions for this server are reset. + + + Active Protections + + + {0} has been **disabled** on this server. + + + {0} Enabled + + + Error. I need ManageRoles permission + + + No protections enabled. + + + User threshold must be between {0} and {1}. + + + If {0} or more users join within {1} seconds, I will {2} them. + + + Time must be between {0} and {1} seconds. + + + Successfully removed all roles from user {0} + + + Failed to remove roles. I have insufficient permissions. + + + Color of {0} role has been changed. + + + That role does not exist. + + + The parameters specified are invalid. + + + Error occured due to invalid color or insufficient permissions. + + + Successfully removed role {0} from user {1} + + + Failed to remove role. I have insufficient permissions. + + + Role renamed. + + + Failed to rename role. I have insufficient permissions. + + + You can't edit roles higher than your highest role. + + + Removed the playing message: {0} + + + Role {0} as been added to the list. + + + {0} not found.Cleaned up. + + + Role {0} is already in the list. + + + Added. + + + Rotating playing status disabled. + + + Rotating playing status enabled. + + + Here is a list of rotating statuses: +{0} + + + No rotating playing statuses set. + + + You already have {0} role. + + + You already have {0} exclusive self-assigned role. + + + Self assigned roles are now exclusive! + + + There are {0} self assignable roles + + + That role is not self-assignable. + + + You don't have {0} role. + + + Self assigned roles are now not exclusive! + + + 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.` + + + {0} has been removed from the list of self-assignable roles. + + + You no longer have {0} role. + + + You now have {0} role. + + + Sucessfully added role {0} to user {1} + + + Failed to add role. I have insufficient permissions. + + + New avatar set! + + + New channel name set. + + + New game set! + + + New stream set! + + + New channel topic set. + + + Shard {0} reconnected. + + + Shard {0} reconnecting. + + + Shutting down + + + Users can't send more than {0} messages every {1} seconds. + + + Slow mode disabled. + + + Slow mode initiated + + + soft-banned (kicked) + PLURAL + + + {0} will ignore this channel. + + + {0} will no longer ignore this channel. + + + If a user posts {0} same messages in a row, I will {1} them. + __IgnoredChannels__: {2} + + + Text Channel Destroyed + + + Text Channel Destroyed + + + Undeafen successful. + + + Unmuted + singular + + + Username + + + Username Changed + + + Users + + + User Banned + + + {0} has been **muted** from chatting. + + + {0} has been **unmuted** from chatting. + + + User Joined + + + User Left + + + {0} has been **muted** from text and voice chat. + + + User's Role Added + + + User's Role Removed + + + {0} is now {1} + + + {0} has been **unmuted** from text and voice chat. + + + {0} has joined {1} voice channel. + + + {0} has left {1} voice channel. + + + {0} moved from {1} to {2} voice channel. + + + {0} has been **voice muted**. + + + {0} has been **voice unmuted**. + + + Voice Channel Destroyed + + + Voice Channel Destroyed + + + Disabled voice + text feature. + + + Enabled voice + text feature. + + + I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server. + + + You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. This may cause some issues, and you will have to clean up text channels yourself afterwards. + + + I require atleast **manage roles** and **manage channels** permissions to enable this feature. (preffered Administration permission) + + + User {0} from text chat + + + User {0} from text and voice chat + + + User {0} from voice chat + + + You have been soft-banned from {0} server. +Reason: {1} + + + User Unbanned + + + Migration done! + + + Error while migrating, check bot's console for more information. + + + Presence Updates + + + User Soft-Banned + + + has awarded {0} to {1} + + + Betflip Gamble + + + Better luck next time ^_^ + + + Congratulations! You won {0} for rolling above {1} + + + Deck reshuffled. + + + flipped {0}. + User flipped tails. + + + You guessed it! You won {0} + + + Invalid number specified. You can flip 1 to {0} coins. + + + Add {0} reaction to this message to get {1} + + + This event is active for up to {0} hours. + + + Flower reaction event started! + + + has gifted {0} to {1} + X has gifted 15 flowers to Y + + + {0} has {1} + X has Y flowers + + + Heads + + + Leaderboard + + + Awarded {0} to {1} users from {2} role. + + + You can't bet more than {0} + + + You can't bet less than {0} + + + You don't have enough {0} + + + No more cards in the deck. + + + Raffled User + + + You rolled {0}. + + + Bet + + + WOAAHHHHHH!!! Congratulations!!! x{0} + + + A single {0}, x{1} + + + Wow! Lucky! Three of a kind! x{0} + + + Good job! Two {0} - bet x{1} + + + Won + + + Users must type a secret code to get {0}. +Lasts {1} seconds. Don't tell anyone. Shhh. + + + SneakyGame event ended. {0} users received the reward. + + + SneakyGameStatus event started + + + Tails + + + successfully took {0} from {1} + + + was unable to take {0} from{1} because the user doesn't have that much {2}! + \ No newline at end of file From b28732171215427379fd4e517f32259ef58403d8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 18 Feb 2017 21:39:13 +0100 Subject: [PATCH 175/256] dice rolls localizable --- .../Gambling/Commands/DiceRollCommand.cs | 57 ++++++++++--------- .../Resources/ResponseStrings.Designer.cs | 36 ++++++++++++ 2 files changed, 66 insertions(+), 27 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 4bda25a4..a75827e7 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Gambling private Regex dndRegex { get; } = new Regex(@"^(?\d+)d(?\d+)(?:\+(?\d+))?(?:\-(?\d+))?$", RegexOptions.Compiled); private Regex fudgeRegex { get; } = new Regex(@"^(?\d+)d(?:F|f)$", RegexOptions.Compiled); - private readonly char[] fateRolls = new[] { '-', ' ', '+' }; + private readonly char[] _fateRolls = { '-', ' ', '+' }; [NadekoCommand, Usage, Description, Aliases] public async Task Roll() @@ -40,7 +40,9 @@ namespace NadekoBot.Modules.Gambling return ms; }).ConfigureAwait(false); - await Context.Channel.SendFileAsync(imageStream, "dice.png", $"{Context.User.Mention} rolled " + Format.Code(gen.ToString())).ConfigureAwait(false); + await Context.Channel.SendFileAsync(imageStream, + "dice.png", + Context.User.Mention + " " + GetText("dice_rolled", Format.Code(gen.ToString()))).ConfigureAwait(false); } public enum RollOrderType @@ -82,7 +84,7 @@ namespace NadekoBot.Modules.Gambling { if (num < 1 || num > 30) { - await Context.Channel.SendErrorAsync("Invalid number specified. You can roll up to 1-30 dice at a time.").ConfigureAwait(false); + await ReplyErrorLocalized("dice_invalid_number", 1, 30).ConfigureAwait(false); return; } @@ -120,7 +122,12 @@ namespace NadekoBot.Modules.Gambling var ms = new MemoryStream(); bitmap.SaveAsPng(ms); ms.Position = 0; - await Context.Channel.SendFileAsync(ms, "dice.png", $"{Context.User.Mention} rolled {values.Count} {(values.Count == 1 ? "die" : "dice")}. Total: **{values.Sum()}** Average: **{(values.Sum() / (1.0f * values.Count)).ToString("N2")}**").ConfigureAwait(false); + await Context.Channel.SendFileAsync(ms, "dice.png", + Context.User.Mention + + GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) + + " " + GetText("Total: {1} Average: {2}", + Format.Bold(values.Sum().ToString()), + Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false); } private async Task InternallDndRoll(string arg, bool ordered) @@ -138,9 +145,9 @@ namespace NadekoBot.Modules.Gambling for (int i = 0; i < n1; i++) { - rolls.Add(fateRolls[rng.Next(0, fateRolls.Length)]); + rolls.Add(_fateRolls[rng.Next(0, _fateRolls.Length)]); } - var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} fate {(n1 == 1 ? "die" : "dice")}.") + var embed = new EmbedBuilder().WithOkColor().WithDescription(Context.User.Mention + " " + GetText("dice_rolled_num", Format.Bold(n1.ToString()))) .AddField(efb => efb.WithName(Format.Bold("Result")) .WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]"))))); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); @@ -164,7 +171,7 @@ namespace NadekoBot.Modules.Gambling } var sum = arr.Sum(); - var embed = new EmbedBuilder().WithOkColor().WithDescription($"{Context.User.Mention} rolled {n1} {(n1 == 1 ? "die" : "dice")} `1 to {n2}`") + var embed = new EmbedBuilder().WithOkColor().WithDescription(Context.User.Mention + " " +GetText("dice_rolled_num", n1) + $"`1 - {n2}`") .AddField(efb => efb.WithName(Format.Bold("Rolls")) .WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString()))))) .AddField(efb => efb.WithName(Format.Bold("Sum")) @@ -177,30 +184,26 @@ namespace NadekoBot.Modules.Gambling [NadekoCommand, Usage, Description, Aliases] public async Task NRoll([Remainder] string range) { - try + int rolled; + if (range.Contains("-")) { - int rolled; - if (range.Contains("-")) + var arr = range.Split('-') + .Take(2) + .Select(int.Parse) + .ToArray(); + if (arr[0] > arr[1]) { - var arr = range.Split('-') - .Take(2) - .Select(int.Parse) - .ToArray(); - if (arr[0] > arr[1]) - throw new ArgumentException("Second argument must be larger than the first one."); - rolled = new NadekoRandom().Next(arr[0], arr[1] + 1); - } - else - { - rolled = new NadekoRandom().Next(0, int.Parse(range) + 1); + await ReplyErrorLocalized("second_larger_than_first").ConfigureAwait(false); + return; } + rolled = new NadekoRandom().Next(arr[0], arr[1] + 1); + } + else + { + rolled = new NadekoRandom().Next(0, int.Parse(range) + 1); + } - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} rolled **{rolled}**.").ConfigureAwait(false); - } - catch (Exception ex) - { - await Context.Channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false); - } + await ReplyConfirmLocalized("dice_rolled", Format.Bold(rolled.ToString())).ConfigureAwait(false); } private Image GetDice(int num) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 84439a80..08c355b1 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2207,6 +2207,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Invalid number specified. You can roll up to {0}-{1} dice at a time.. + /// + public static string gambling_dice_invalid_number { + get { + return ResourceManager.GetString("gambling_dice_invalid_number", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to rolled {0}. + /// + public static string gambling_dice_rolled { + get { + return ResourceManager.GetString("gambling_dice_rolled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dice rolled: {1}. + /// + public static string gambling_dice_rolled_num { + get { + return ResourceManager.GetString("gambling_dice_rolled_num", resourceCulture); + } + } + /// /// Looks up a localized string similar to You guessed it! You won {0}. /// @@ -2378,6 +2405,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to second_larger_than_first. + /// + public static string gambling_second_larger_than_first { + get { + return ResourceManager.GetString("gambling_second_larger_than_first", resourceCulture); + } + } + /// /// Looks up a localized string similar to Bet. /// From 1517143248d669e7cd8b4e6a08d98ae7bb9f8e43 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 19 Feb 2017 02:11:31 +0100 Subject: [PATCH 176/256] waifu game completely localizable? --- .../Gambling/Commands/WaifuClaimCommands.cs | 129 +++++------ src/NadekoBot/Modules/Searches/Searches.cs | 8 +- .../Resources/ResponseStrings.Designer.cs | 204 +++++++++++++++++- src/NadekoBot/Resources/ResponseStrings.resx | 85 ++++++++ 4 files changed, 359 insertions(+), 67 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 6ff7695d..212fd1d2 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -3,13 +3,11 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; -using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace NadekoBot.Modules.Gambling @@ -49,8 +47,8 @@ namespace NadekoBot.Modules.Gambling [Group] public class WaifuClaimCommands : NadekoSubmodule { - private static ConcurrentDictionary _divorceCooldowns { get; } = new ConcurrentDictionary(); - private static ConcurrentDictionary _affinityCooldowns { get; } = new ConcurrentDictionary(); + private static ConcurrentDictionary divorceCooldowns { get; } = new ConcurrentDictionary(); + private static ConcurrentDictionary affinityCooldowns { get; } = new ConcurrentDictionary(); enum WaifuClaimResult { @@ -65,20 +63,19 @@ namespace NadekoBot.Modules.Gambling { if (amount < 50) { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} No waifu is that cheap. You must pay at least 50{NadekoBot.BotConfig.CurrencySign} to get a waifu, even if their actual value is lower.").ConfigureAwait(false); + await ReplyErrorLocalized("waifu_isnt_cheap", 50 + CurrencySign).ConfigureAwait(false); return; } if (target.Id == Context.User.Id) { - await Context.Channel.SendErrorAsync(Context.User.Mention + " You can't claim yourself.").ConfigureAwait(false); + await ReplyErrorLocalized("waifu_not_yourself").ConfigureAwait(false); return; } - WaifuClaimResult result = WaifuClaimResult.NotEnoughFunds; - int? oldPrice = null; + WaifuClaimResult result; WaifuInfo w; - var isAffinity = false; + bool isAffinity; using (var uow = DbHandler.UnitOfWork()) { w = uow.Waifus.ByWaifuUserId(target.Id); @@ -120,7 +117,6 @@ namespace NadekoBot.Modules.Gambling { var oldClaimer = w.Claimer; w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User); - oldPrice = w.Price; w.Price = amount + (amount / 4); result = WaifuClaimResult.Success; @@ -143,7 +139,6 @@ namespace NadekoBot.Modules.Gambling { var oldClaimer = w.Claimer; w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User); - oldPrice = w.Price; w.Price = amount; result = WaifuClaimResult.Success; @@ -165,22 +160,20 @@ namespace NadekoBot.Modules.Gambling if (result == WaifuClaimResult.InsufficientAmount) { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} You must pay {Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f))} or more to claim that waifu!").ConfigureAwait(false); + await ReplyErrorLocalized("waifu_not_enough", Math.Ceiling(w.Price * (isAffinity ? 0.88f : 1.1f))).ConfigureAwait(false); return; } - else if (result == WaifuClaimResult.NotEnoughFunds) + if (result == WaifuClaimResult.NotEnoughFunds) { - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} you don't have {amount}{NadekoBot.BotConfig.CurrencySign}!") - .ConfigureAwait(false); - } - else - { - var msg = $"{Context.User.Mention} claimed {target.Mention} as their waifu for {amount}{NadekoBot.BotConfig.CurrencySign}!"; - if (w.Affinity?.UserId == Context.User.Id) - msg += $"\n🎉 Their love is fulfilled! 🎉\n**{target}'s** new value is {w.Price}{NadekoBot.BotConfig.CurrencySign}!"; - await Context.Channel.SendConfirmAsync(msg) - .ConfigureAwait(false); + await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false); + return; } + var msg = GetText("waifu_claimed", + Format.Bold(target.ToString()), + amount + CurrencySign); + if (w.Affinity?.UserId == Context.User.Id) + msg += "\n" + GetText("waifu_fulfilled", target, w.Price + CurrencySign); + await Context.Channel.SendConfirmAsync(Context.User.Mention + msg).ConfigureAwait(false); } public enum DivorceResult @@ -192,7 +185,7 @@ namespace NadekoBot.Modules.Gambling } - private static readonly TimeSpan DivorceLimit = TimeSpan.FromHours(6); + private static readonly TimeSpan _divorceLimit = TimeSpan.FromHours(6); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Divorce([Remainder]IUser target) @@ -200,19 +193,19 @@ namespace NadekoBot.Modules.Gambling if (target.Id == Context.User.Id) return; - var result = DivorceResult.NotYourWife; - TimeSpan difference = TimeSpan.Zero; + DivorceResult result; + var difference = TimeSpan.Zero; var amount = 0; WaifuInfo w = null; using (var uow = DbHandler.UnitOfWork()) { w = uow.Waifus.ByWaifuUserId(target.Id); var now = DateTime.UtcNow; - if (w == null || w.Claimer == null || w.Claimer.UserId != Context.User.Id) + if (w?.Claimer == null || w.Claimer.UserId != Context.User.Id) result = DivorceResult.NotYourWife; - else if (_divorceCooldowns.AddOrUpdate(Context.User.Id, + else if (divorceCooldowns.AddOrUpdate(Context.User.Id, now, - (key, old) => ((difference = now.Subtract(old)) > DivorceLimit) ? now : old) != now) + (key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now) { result = DivorceResult.Cooldown; } @@ -249,37 +242,39 @@ namespace NadekoBot.Modules.Gambling if (result == DivorceResult.SucessWithPenalty) { - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} You have divorced a waifu who likes you. You heartless monster.\n{w.Waifu} received {amount}{NadekoBot.BotConfig.CurrencySign} as a compensation.").ConfigureAwait(false); + await ReplyConfirmLocalized("waifu_divorced_like", Format.Bold(w.Waifu.ToString()), amount + CurrencySign).ConfigureAwait(false); } else if (result == DivorceResult.Success) { - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} You have divorced a waifu who doesn't like you. You received {amount}{NadekoBot.BotConfig.CurrencySign} back.").ConfigureAwait(false); + await ReplyConfirmLocalized("waifu_divorced_notlike", amount + CurrencySign).ConfigureAwait(false); } else if (result == DivorceResult.NotYourWife) { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} That waifu is not yours.").ConfigureAwait(false); + await ReplyErrorLocalized("waifu_not_yours").ConfigureAwait(false); } else { - var remaining = DivorceLimit.Subtract(difference); - await Context.Channel.SendErrorAsync($"{Context.User.Mention} You divorced recently. You must wait **{remaining.Hours} hours and {remaining.Minutes} minutes** to divorce again.").ConfigureAwait(false); + var remaining = _divorceLimit.Subtract(difference); + await ReplyErrorLocalized("waifu_recent_divorce", + Format.Bold(((int)remaining.TotalHours).ToString()), + Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false); } } - private static readonly TimeSpan AffinityLimit = TimeSpan.FromMinutes(30); + private static readonly TimeSpan _affinityLimit = TimeSpan.FromMinutes(30); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task WaifuClaimerAffinity([Remainder]IUser u = null) { if (u?.Id == Context.User.Id) { - await Context.Channel.SendErrorAsync($"{Context.User.Mention} you can't set affinity to yourself, you egomaniac.").ConfigureAwait(false); + await ReplyErrorLocalized("waifu_egomaniac").ConfigureAwait(false); return; } DiscordUser oldAff = null; var sucess = false; var cooldown = false; - TimeSpan difference = TimeSpan.Zero; + var difference = TimeSpan.Zero; using (var uow = DbHandler.UnitOfWork()) { var w = uow.Waifus.ByWaifuUserId(Context.User.Id); @@ -287,13 +282,11 @@ namespace NadekoBot.Modules.Gambling var now = DateTime.UtcNow; if (w?.Affinity?.UserId == u?.Id) { - sucess = false; } - else if (_affinityCooldowns.AddOrUpdate(Context.User.Id, + else if (affinityCooldowns.AddOrUpdate(Context.User.Id, now, - (key, old) => ((difference = now.Subtract(old)) > AffinityLimit) ? now : old) != now) + (key, old) => ((difference = now.Subtract(old)) > _affinityLimit) ? now : old) != now) { - sucess = false; cooldown = true; } else if (w == null) @@ -338,19 +331,29 @@ namespace NadekoBot.Modules.Gambling { if (cooldown) { - var remaining = AffinityLimit.Subtract(difference); - await Context.Channel.SendErrorAsync($"{Context.User.Mention} You must wait **{remaining.Hours} hours and {remaining.Minutes} minutes** in order to change your affinity again.").ConfigureAwait(false); + var remaining = _affinityLimit.Subtract(difference); + await ReplyErrorLocalized("waifu_affinity_cooldown", + Format.Bold(((int)remaining.TotalHours).ToString()), + Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false); } else - await Context.Channel.SendErrorAsync($"{Context.User.Mention} your affinity is already set to that waifu or you're trying to remove your affinity while not having one.").ConfigureAwait(false); + { + await ReplyErrorLocalized("waifu_affinity_already").ConfigureAwait(false); + } return; } if (u == null) - await Context.Channel.SendConfirmAsync("Affinity Reset", $"{Context.User.Mention} Your affinity is reset. You no longer have a person you like.").ConfigureAwait(false); + { + await ReplyConfirmLocalized("waifu_affinity_reset").ConfigureAwait(false); + } else if (oldAff == null) - await Context.Channel.SendConfirmAsync("Affinity Set", $"{Context.User.Mention} wants to be {u.Mention}'s waifu. Aww <3").ConfigureAwait(false); + { + await ReplyConfirmLocalized("waifu_affinity_set", Format.Bold(u.ToString())).ConfigureAwait(false); + } else - await Context.Channel.SendConfirmAsync("Affinity Changed", $"{Context.User.Mention} changed their affinity from {oldAff} to {u.Mention}.\n\n*This is morally questionable.*🤔").ConfigureAwait(false); + { + await ReplyConfirmLocalized("waifu_affinity_changed", Format.Bold(oldAff.ToString()), Format.Bold(u.ToString())).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -365,19 +368,20 @@ namespace NadekoBot.Modules.Gambling if (waifus.Count == 0) { - await Context.Channel.SendConfirmAsync("No waifus have been claimed yet.").ConfigureAwait(false); + await ReplyConfirmLocalized("waifu_none").ConfigureAwait(false); return; } - + var embed = new EmbedBuilder() - .WithTitle("Top Waifus") + .WithTitle(GetText("waifus_top_waifus")) .WithOkColor(); - for (int i = 0; i < waifus.Count; i++) + for (var i = 0; i < waifus.Count; i++) { var w = waifus[i]; - embed.AddField(efb => efb.WithName("#" + (i + 1) + " - " + w.Price + NadekoBot.BotConfig.CurrencySign).WithValue(w.ToString()).WithIsInline(false)); + var j = i; + embed.AddField(efb => efb.WithName("#" + (j + 1) + " - " + w.Price + NadekoBot.BotConfig.CurrencySign).WithValue(w.ToString()).WithIsInline(false)); } await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); @@ -419,15 +423,16 @@ namespace NadekoBot.Modules.Gambling var claimInfo = GetClaimTitle(target.Id); var affInfo = GetAffinityTitle(target.Id); + var nobody = GetText("nobody"); var embed = new EmbedBuilder() .WithOkColor() .WithTitle("Waifu " + w.Waifu + " - \"the " + claimInfo.Title + "\"") - .AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true)) - .AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true)) - .AddField(efb => efb.WithName("Changes Of Heart").WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true)) - .AddField(efb => efb.WithName("Divorces").WithValue(divorces.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? "Nobody" : string.Join("\n", claims.Select(x => x.Waifu))).WithIsInline(true)); + .AddField(efb => efb.WithName(GetText("price")).WithValue(w.Price.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("claimed_by")).WithValue(w.Claimer?.ToString() ?? nobody).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.Select(x => x.Waifu))).WithIsInline(true)); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -447,13 +452,13 @@ namespace NadekoBot.Modules.Gambling private static WaifuProfileTitle GetClaimTitle(ulong userId) { - int count = 0; + int count; using (var uow = DbHandler.UnitOfWork()) { count = uow.Waifus.ByClaimerUserId(userId).Count; } - ClaimTitles title = ClaimTitles.Lonely; + ClaimTitles title; if (count == 0) title = ClaimTitles.Lonely; else if (count == 1) @@ -484,13 +489,13 @@ namespace NadekoBot.Modules.Gambling private static WaifuProfileTitle GetAffinityTitle(ulong userId) { - int count = 0; + int count; using (var uow = DbHandler.UnitOfWork()) { count = uow._context.WaifuUpdates.Count(w => w.User.UserId == userId && w.UpdateType == WaifuUpdateType.AffinityChanged); } - AffinityTitles title = AffinityTitles.Pure; + AffinityTitles title; if (count < 1) title = AffinityTitles.Pure; else if (count < 2) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 670980ea..28a00413 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Searches .WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithDescription(res.Link) .WithImageUrl(res.Link) - .WithTitle(Context.User.Mention); + .WithTitle(Context.User.ToString()); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } catch @@ -157,7 +157,7 @@ namespace NadekoBot.Modules.Searches .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) .WithDescription(source) .WithImageUrl(source) - .WithTitle(Context.User.Mention); + .WithTitle(Context.User.ToString()); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } } @@ -179,7 +179,7 @@ namespace NadekoBot.Modules.Searches .WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithDescription(res.Link) .WithImageUrl(res.Link) - .WithTitle(Context.User.Mention); + .WithTitle(Context.User.ToString()); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } catch @@ -210,7 +210,7 @@ namespace NadekoBot.Modules.Searches .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) .WithDescription(source) .WithImageUrl(source) - .WithTitle(Context.User.Mention); + .WithTitle(Context.User.ToString()); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 08c355b1..afe8652d 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2198,6 +2198,24 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Changes Of Heart. + /// + public static string gambling_changes_of_heart { + get { + return ResourceManager.GetString("gambling_changes_of_heart", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Claimed By. + /// + public static string gambling_claimed_by { + get { + return ResourceManager.GetString("gambling_claimed_by", resourceCulture); + } + } + /// /// Looks up a localized string similar to Deck reshuffled.. /// @@ -2234,6 +2252,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Divorces. + /// + public static string gambling_divorces { + get { + return ResourceManager.GetString("gambling_divorces", resourceCulture); + } + } + /// /// Looks up a localized string similar to You guessed it! You won {0}. /// @@ -2324,6 +2351,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Likes. + /// + public static string gambling_likes { + get { + return ResourceManager.GetString("gambling_likes", resourceCulture); + } + } + /// /// Looks up a localized string similar to Awarded {0} to {1} users from {2} role.. /// @@ -2369,6 +2405,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Price. + /// + public static string gambling_price { + get { + return ResourceManager.GetString("gambling_price", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed starting the race. Another race is probably running.. /// @@ -2406,7 +2451,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to second_larger_than_first. + /// Looks up a localized string similar to Second number must be larger than the first one.. /// public static string gambling_second_larger_than_first { get { @@ -2523,6 +2568,163 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to your affinity is already set to that waifu or you're trying to remove your affinity while not having one.. + /// + public static string gambling_waifu_affinity_already { + get { + return ResourceManager.GetString("gambling_waifu_affinity_already", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to changed their affinity from {0} to {1}. + /// + ///*This is morally questionable.*🤔. + /// + public static string gambling_waifu_affinity_changed { + get { + return ResourceManager.GetString("gambling_waifu_affinity_changed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You must wait {0} hours and {1} minutes in order to change your affinity again.. + /// + public static string gambling_waifu_affinity_cooldown { + get { + return ResourceManager.GetString("gambling_waifu_affinity_cooldown", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your affinity is reset. You no longer have a person you like.. + /// + public static string gambling_waifu_affinity_reset { + get { + return ResourceManager.GetString("gambling_waifu_affinity_reset", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to wants to be {0}'s waifu. Aww <3. + /// + public static string gambling_waifu_affinity_set { + get { + return ResourceManager.GetString("gambling_waifu_affinity_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to claimed {0} as their waifu for {1}!. + /// + public static string gambling_waifu_claimed { + get { + return ResourceManager.GetString("gambling_waifu_claimed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have divorced a waifu who likes you. You heartless monster. + ///{0} received {1} as a compensation.. + /// + public static string gambling_waifu_divorced_like { + get { + return ResourceManager.GetString("gambling_waifu_divorced_like", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have divorced a waifu who doesn't like you. You received {0} back.. + /// + public static string gambling_waifu_divorced_not_like { + get { + return ResourceManager.GetString("gambling_waifu_divorced_not_like", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to you can't set affinity to yourself, you egomaniac.. + /// + public static string gambling_waifu_egomaniac { + get { + return ResourceManager.GetString("gambling_waifu_egomaniac", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to 🎉 Their love is fulfilled! 🎉 + ///{0}'s new value is {1}!. + /// + public static string gambling_waifu_fulfilled { + get { + return ResourceManager.GetString("gambling_waifu_fulfilled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No waifu is that cheap. You must pay at least {0} to get a waifu, even if their actual value is lower.. + /// + public static string gambling_waifu_isnt_cheap { + get { + return ResourceManager.GetString("gambling_waifu_isnt_cheap", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You must pay {0} or more to claim that waifu!. + /// + public static string gambling_waifu_not_enough { + get { + return ResourceManager.GetString("gambling_waifu_not_enough", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to That waifu is not yours.. + /// + public static string gambling_waifu_not_yours { + get { + return ResourceManager.GetString("gambling_waifu_not_yours", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't claim yourself.. + /// + public static string gambling_waifu_not_yourself { + get { + return ResourceManager.GetString("gambling_waifu_not_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You divorced recently. You must wait {0} hours and {1} minutes to divorce again.. + /// + public static string gambling_waifu_recent_divorce { + get { + return ResourceManager.GetString("gambling_waifu_recent_divorce", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No waifus have been claimed yet.. + /// + public static string gambling_waifus_none { + get { + return ResourceManager.GetString("gambling_waifus_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Top Waifus. + /// + public static string gambling_waifus_top_waifus { + get { + return ResourceManager.GetString("gambling_waifus_top_waifus", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to ToC. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 8cc5fc3d..4db9ff76 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1073,10 +1073,95 @@ Don't forget to leave your discord name or id in the message. {0} as {1} Won the race and {2}! + + Invalid number specified. You can roll up to {0}-{1} dice at a time. + + + rolled {0} + Someone rolled 35 + + + Dice rolled: {1} + Dice Rolled: 5 + Failed starting the race. Another race is probably running. No race exists on this server + + Second number must be larger than the first one. + + + Changes Of Heart + + + Claimed By + + + Divorces + + + Likes + + + Price + + + No waifus have been claimed yet. + + + Top Waifus + + + your affinity is already set to that waifu or you're trying to remove your affinity while not having one. + + + changed their affinity from {0} to {1}. + +*This is morally questionable.*🤔 + Make sure to get the formatting right, and leave the thinking emoji + + + You must wait {0} hours and {1} minutes in order to change your affinity again. + + + Your affinity is reset. You no longer have a person you like. + + + wants to be {0}'s waifu. Aww <3 + + + claimed {0} as their waifu for {1}! + + + You have divorced a waifu who likes you. You heartless monster. +{0} received {1} as a compensation. + + + You have divorced a waifu who doesn't like you. You received {0} back. + + + you can't set affinity to yourself, you egomaniac. + + + 🎉 Their love is fulfilled! 🎉 +{0}'s new value is {1}! + + + No waifu is that cheap. You must pay at least {0} to get a waifu, even if their actual value is lower. + + + You must pay {0} or more to claim that waifu! + + + That waifu is not yours. + + + You can't claim yourself. + + + You divorced recently. You must wait {0} hours and {1} minutes to divorce again. + \ No newline at end of file From 81675781b42a8d3b9a25f8f456995b1291077881 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 19 Feb 2017 02:12:14 +0100 Subject: [PATCH 177/256] forgot nobody key --- src/NadekoBot/Resources/ResponseStrings.Designer.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index afe8652d..6e3c725a 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2396,6 +2396,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Nobody. + /// + public static string gambling_nobody { + get { + return ResourceManager.GetString("gambling_nobody", resourceCulture); + } + } + /// /// Looks up a localized string similar to You don't have enough {0}. /// From 8b703294f163eac6c6d5c2cf21a4ff0ea3cc6c15 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 19 Feb 2017 15:18:40 +0100 Subject: [PATCH 178/256] acrophobia localizable --- .../Modules/Games/Commands/Acropobia.cs | 162 ++++++++++-------- .../Games/Commands/CleverBotCommands.cs | 2 +- .../Modules/Games/Commands/HangmanCommands.cs | 2 +- .../Modules/Games/Commands/PollCommands.cs | 2 +- .../Games/Commands/SpeedTypingCommands.cs | 2 +- .../Modules/Games/Commands/TicTacToe.cs | 2 +- .../Modules/Games/Commands/TriviaCommands.cs | 2 +- src/NadekoBot/Modules/Games/Games.cs | 31 ++-- .../Resources/ResponseStrings.Designer.cs | 153 +++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 18 ++ 10 files changed, 282 insertions(+), 94 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index 1ee7adbd..088b95d7 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games public partial class Games { [Group] - public class Acropobia : ModuleBase + public class Acropobia : NadekoSubmodule { //channelId, game public static ConcurrentDictionary AcrophobiaGames { get; } = new ConcurrentDictionary(); @@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Games } else { - await channel.SendErrorAsync("Acrophobia game is already running in this channel.").ConfigureAwait(false); + await ReplyErrorLocalized("acro_running").ConfigureAwait(false); } } } @@ -61,44 +61,44 @@ namespace NadekoBot.Modules.Games public class AcrophobiaGame { - private readonly ITextChannel channel; - private readonly int time; - private readonly NadekoRandom rng; - private readonly ImmutableArray startingLetters; - private readonly CancellationTokenSource source; + private readonly ITextChannel _channel; + private readonly int _time; + private readonly NadekoRandom _rng; + private readonly ImmutableArray _startingLetters; + private readonly CancellationTokenSource _source; private AcroPhase phase { get; set; } = AcroPhase.Submitting; - private readonly ConcurrentDictionary submissions = new ConcurrentDictionary(); - public IReadOnlyDictionary Submissions => submissions; + private readonly ConcurrentDictionary _submissions = new ConcurrentDictionary(); + public IReadOnlyDictionary Submissions => _submissions; - private readonly ConcurrentHashSet usersWhoSubmitted = new ConcurrentHashSet(); - private readonly ConcurrentHashSet usersWhoVoted = new ConcurrentHashSet(); + private readonly ConcurrentHashSet _usersWhoSubmitted = new ConcurrentHashSet(); + private readonly ConcurrentHashSet _usersWhoVoted = new ConcurrentHashSet(); - private int spamCount = 0; + private int _spamCount; //text, votes - private readonly ConcurrentDictionary votes = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _votes = new ConcurrentDictionary(); private readonly Logger _log; public AcrophobiaGame(ITextChannel channel, int time) { - this._log = LogManager.GetCurrentClassLogger(); + _log = LogManager.GetCurrentClassLogger(); - this.channel = channel; - this.time = time; - this.source = new CancellationTokenSource(); + _channel = channel; + _time = time; + _source = new CancellationTokenSource(); - this.rng = new NadekoRandom(); - var wordCount = rng.Next(3, 6); + _rng = new NadekoRandom(); + var wordCount = _rng.Next(3, 6); var lettersArr = new char[wordCount]; for (int i = 0; i < wordCount; i++) { - var randChar = (char)rng.Next(65, 91); - lettersArr[i] = randChar == 'X' ? (char)rng.Next(65, 88) : randChar; + var randChar = (char)_rng.Next(65, 91); + lettersArr[i] = randChar == 'X' ? (char)_rng.Next(65, 88) : randChar; } - startingLetters = lettersArr.ToImmutableArray(); + _startingLetters = lettersArr.ToImmutableArray(); } private EmbedBuilder GetEmbed() @@ -106,19 +106,19 @@ namespace NadekoBot.Modules.Games var i = 0; return phase == AcroPhase.Submitting - ? new EmbedBuilder().WithOkColor() - .WithTitle("Acrophobia") - .WithDescription($"Game started. Create a sentence with the following acronym: **{string.Join(".", startingLetters)}.**\n") - .WithFooter(efb => efb.WithText("You have " + this.time + " seconds to make a submission.")) + ? new EmbedBuilder().WithOkColor() + .WithTitle(GetText("acrophobia")) + .WithDescription(GetText("acro_started", Format.Bold(string.Join(".", _startingLetters)))) + .WithFooter(efb => efb.WithText(GetText("acro_started_footer", _time))) - : new EmbedBuilder() - .WithOkColor() - .WithTitle("Acrophobia - Submissions Closed") - .WithDescription($@"Acronym was **{string.Join(".", startingLetters)}.** --- -{this.submissions.Aggregate("", (agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")} ---") - .WithFooter(efb => efb.WithText("Vote by typing a number of the submission")); + : new EmbedBuilder() + .WithOkColor() + .WithTitle(GetText("acrophobia") + " - " + GetText("submissions_closed")) + .WithDescription(GetText("acro_nym_was", Format.Bold(string.Join(".", _startingLetters)) + "\n" + +$@"-- +{_submissions.Aggregate("",(agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")} +--")) + .WithFooter(efb => efb.WithText(GetText("acro_vote"))); } public async Task Run() @@ -127,10 +127,10 @@ namespace NadekoBot.Modules.Games var embed = GetEmbed(); //SUBMISSIONS PHASE - await channel.EmbedAsync(embed).ConfigureAwait(false); + await _channel.EmbedAsync(embed).ConfigureAwait(false); try { - await Task.Delay(time * 1000, source.Token).ConfigureAwait(false); + await Task.Delay(_time * 1000, _source.Token).ConfigureAwait(false); phase = AcroPhase.Idle; } catch (OperationCanceledException) @@ -139,30 +139,32 @@ namespace NadekoBot.Modules.Games } //var i = 0; - if (submissions.Count == 0) + if (_submissions.Count == 0) { - await channel.SendErrorAsync("Acrophobia", "Game ended with no submissions."); + await _channel.SendErrorAsync(GetText("acrophobia"), GetText("acro_ended_no_sub")); return; } - else if (submissions.Count == 1) + if (_submissions.Count == 1) { - await channel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithDescription($"{submissions.First().Value.Mention} is the winner for being the only user who made a submission!") - .WithFooter(efb => efb.WithText(submissions.First().Key.ToLowerInvariant().ToTitleCase()))) - .ConfigureAwait(false); + await _channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithDescription( + GetText("acro_winner_only", + Format.Bold(_submissions.First().Value.ToString()))) + .WithFooter(efb => efb.WithText(_submissions.First().Key.ToLowerInvariant().ToTitleCase()))) + .ConfigureAwait(false); return; } var submissionClosedEmbed = GetEmbed(); - await channel.EmbedAsync(submissionClosedEmbed).ConfigureAwait(false); + await _channel.EmbedAsync(submissionClosedEmbed).ConfigureAwait(false); //VOTING PHASE - this.phase = AcroPhase.Voting; + phase = AcroPhase.Voting; try { //30 secondds for voting - await Task.Delay(30000, source.Token).ConfigureAwait(false); - this.phase = AcroPhase.Idle; + await Task.Delay(30000, _source.Token).ConfigureAwait(false); + phase = AcroPhase.Idle; } catch (OperationCanceledException) { @@ -176,10 +178,10 @@ namespace NadekoBot.Modules.Games try { var msg = arg as SocketUserMessage; - if (msg == null || msg.Author.IsBot || msg.Channel.Id != channel.Id) + if (msg == null || msg.Author.IsBot || msg.Channel.Id != _channel.Id) return; - ++spamCount; + ++_spamCount; var guildUser = (IGuildUser)msg.Author; @@ -187,37 +189,39 @@ namespace NadekoBot.Modules.Games if (phase == AcroPhase.Submitting) { - if (spamCount > 10) + if (_spamCount > 10) { - spamCount = 0; - try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } + _spamCount = 0; + try { await _channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } catch { } } var inputWords = input.Split(' '); //get all words - if (inputWords.Length != startingLetters.Length) // number of words must be the same as the number of the starting letters + if (inputWords.Length != _startingLetters.Length) // number of words must be the same as the number of the starting letters return; - for (int i = 0; i < startingLetters.Length; i++) + for (int i = 0; i < _startingLetters.Length; i++) { - var letter = startingLetters[i]; + var letter = _startingLetters[i]; if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match return; } - if (!usersWhoSubmitted.Add(guildUser.Id)) + if (!_usersWhoSubmitted.Add(guildUser.Id)) return; //try adding it to the list of answers - if (!submissions.TryAdd(input, guildUser)) + if (!_submissions.TryAdd(input, guildUser)) { - usersWhoSubmitted.TryRemove(guildUser.Id); + _usersWhoSubmitted.TryRemove(guildUser.Id); return; } // all good. valid input. answer recorded - await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} submitted their sentence. ({submissions.Count} total)"); + await _channel.SendConfirmAsync(GetText("acrophobia"), + GetText("acro_submit", guildUser.Mention, + _submissions.Count)); try { await msg.DeleteAsync(); @@ -229,10 +233,10 @@ namespace NadekoBot.Modules.Games } else if (phase == AcroPhase.Voting) { - if (spamCount > 10) + if (_spamCount > 10) { - spamCount = 0; - try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } + _spamCount = 0; + try { await _channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } catch { } } @@ -248,17 +252,17 @@ namespace NadekoBot.Modules.Games //} int num; - if (int.TryParse(input, out num) && num > 0 && num <= submissions.Count) + if (int.TryParse(input, out num) && num > 0 && num <= _submissions.Count) { - var kvp = submissions.Skip(num - 1).First(); + var kvp = _submissions.Skip(num - 1).First(); usr = kvp.Value; //can't vote for yourself, can't vote multiple times - if (usr.Id == guildUser.Id || !usersWhoVoted.Add(guildUser.Id)) + if (usr.Id == guildUser.Id || !_usersWhoVoted.Add(guildUser.Id)) return; - votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old); - await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false); + _votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old); + await _channel.SendConfirmAsync(GetText("acrophobia"), + GetText("vote_cast", Format.Bold(guildUser.ToString()))).ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false); - return; } } @@ -271,27 +275,33 @@ namespace NadekoBot.Modules.Games public async Task End() { - if (!votes.Any()) + if (!_votes.Any()) { - await channel.SendErrorAsync("Acrophobia", "No votes cast. Game ended with no winner.").ConfigureAwait(false); + await _channel.SendErrorAsync(GetText("acrophobia"), GetText("no_votes_cast")).ConfigureAwait(false); return; } - var table = votes.OrderByDescending(v => v.Value); + var table = _votes.OrderByDescending(v => v.Value); var winner = table.First(); var embed = new EmbedBuilder().WithOkColor() - .WithTitle("Acrophobia") - .WithDescription($"Winner is {submissions[winner.Key].Mention} with {winner.Value} points.\n") + .WithTitle(GetText("acrophobia")) + .WithDescription(GetText("winner", Format.Bold(_submissions[winner.Key].ToString()), + Format.Bold(winner.Value.ToString()))) .WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase())); - await channel.EmbedAsync(embed).ConfigureAwait(false); + await _channel.EmbedAsync(embed).ConfigureAwait(false); } public void EnsureStopped() { NadekoBot.Client.MessageReceived -= PotentialAcro; - if (!source.IsCancellationRequested) - source.Cancel(); + if (!_source.IsCancellationRequested) + _source.Cancel(); } + + private string GetText(string key, params object[] replacements) + => NadekoModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(_channel.Guild), + typeof(Games).Name.ToLowerInvariant()); } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index 0541a4ae..1f33bc2a 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Games public partial class Games { [Group] - public class CleverBotCommands : ModuleBase + public class CleverBotCommands : NadekoSubmodule { private static Logger _log { get; } diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index f3f09da0..0fb8e2f3 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Games public partial class Games { [Group] - public class HangmanCommands : ModuleBase + public class HangmanCommands : NadekoSubmodule { private static Logger _log { get; } diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index 3088546c..1434ee9e 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Games public partial class Games { [Group] - public class PollCommands : ModuleBase + public class PollCommands : NadekoSubmodule { public static ConcurrentDictionary ActivePolls = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs index fc5a67a2..7dec0992 100644 --- a/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/SpeedTypingCommands.cs @@ -147,7 +147,7 @@ namespace NadekoBot.Modules.Games } [Group] - public class SpeedTypingCommands : ModuleBase + public class SpeedTypingCommands : NadekoSubmodule { public static List TypingArticles { get; } = new List(); diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index de36fe3b..2e115fd2 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Games { //todo timeout [Group] - public class TicTacToeCommands : ModuleBase + public class TicTacToeCommands : NadekoSubmodule { //channelId/game private static readonly Dictionary _games = new Dictionary(); diff --git a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs index 6f43ff83..be7d135c 100644 --- a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs @@ -14,7 +14,7 @@ namespace NadekoBot.Modules.Games public partial class Games { [Group] - public class TriviaCommands : ModuleBase + public class TriviaCommands : NadekoSubmodule { public static ConcurrentDictionary RunningTrivias { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 9c67460f..b6b9130b 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -6,6 +6,7 @@ using NadekoBot.Attributes; using System; using System.Linq; using System.Collections.Generic; +using System.Collections.Immutable; using NadekoBot.Extensions; namespace NadekoBot.Modules.Games @@ -13,7 +14,7 @@ namespace NadekoBot.Modules.Games [NadekoModule("Games", ">")] public partial class Games : NadekoModule { - private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray(); + private static readonly ImmutableArray _8BallResponses = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray(); [NadekoCommand, Usage, Description, Aliases] public async Task Choose([Remainder] string list = null) @@ -34,8 +35,8 @@ namespace NadekoBot.Modules.Games return; await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) - .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false)) - .AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false))); + .AddField(efb => efb.WithName("❓ " + GetText("question") ).WithValue(question).WithIsInline(false)) + .AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false))); } [NadekoCommand, Usage, Description, Aliases] @@ -43,11 +44,15 @@ namespace NadekoBot.Modules.Games { Func GetRPSPick = (p) => { - if (p == 0) - return "🚀"; - if (p == 1) - return "📎"; - return "✂️"; + switch (p) + { + case 0: + return "🚀"; + case 1: + return "📎"; + default: + return "✂️"; + } }; int pick; @@ -71,15 +76,17 @@ namespace NadekoBot.Modules.Games return; } var nadekoPick = new NadekoRandom().Next(0, 3); - var msg = ""; + string msg; if (pick == nadekoPick) - msg = $"It's a draw! Both picked {GetRPSPick(pick)}"; + msg = GetText("rps_draw", GetRPSPick(pick)); else if ((pick == 0 && nadekoPick == 1) || (pick == 1 && nadekoPick == 2) || (pick == 2 && nadekoPick == 0)) - msg = $"{NadekoBot.Client.CurrentUser.Mention} won! {GetRPSPick(nadekoPick)} beats {GetRPSPick(pick)}"; + msg = GetText("rps_win", NadekoBot.Client.CurrentUser.Mention, + GetRPSPick(nadekoPick), GetRPSPick(pick)); else - msg = $"{Context.User.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}"; + msg = GetText("rps_win", Context.User.Mention, GetRPSPick(pick), + GetRPSPick(nadekoPick)); await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 6e3c725a..e2d01a97 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2734,6 +2734,159 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to 8ball. + /// + public static string games_8ball { + get { + return ResourceManager.GetString("games_8ball", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Game ended with no submissions.. + /// + public static string games_acro_ended_no_sub { + get { + return ResourceManager.GetString("games_acro_ended_no_sub", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No votes cast. Game ended with no winner.. + /// + public static string games_acro_no_votes_cast { + get { + return ResourceManager.GetString("games_acro_no_votes_cast", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Acronym was {0}.. + /// + public static string games_acro_nym_was { + get { + return ResourceManager.GetString("games_acro_nym_was", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Acrophobia game is already running in this channel.. + /// + public static string games_acro_running { + get { + return ResourceManager.GetString("games_acro_running", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Game started. Create a sentence with the following acronym: {0}.. + /// + public static string games_acro_started { + get { + return ResourceManager.GetString("games_acro_started", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You have {0} seconds to make a submission.. + /// + public static string games_acro_started_footer { + get { + return ResourceManager.GetString("games_acro_started_footer", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} submitted their sentence. ({1} total). + /// + public static string games_acro_submit { + get { + return ResourceManager.GetString("games_acro_submit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Vote by typing a number of the submission. + /// + public static string games_acro_vote { + get { + return ResourceManager.GetString("games_acro_vote", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} cast their vote!. + /// + public static string games_acro_vote_cast { + get { + return ResourceManager.GetString("games_acro_vote_cast", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Winner is {0} with {1} points.. + /// + public static string games_acro_winner { + get { + return ResourceManager.GetString("games_acro_winner", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} is the winner for being the only user who made a submission!. + /// + public static string games_acro_winner_only { + get { + return ResourceManager.GetString("games_acro_winner_only", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Acrophobia. + /// + public static string games_acrophobia { + get { + return ResourceManager.GetString("games_acrophobia", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Question. + /// + public static string games_question { + get { + return ResourceManager.GetString("games_question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to It's a draw! Both picked {0}. + /// + public static string games_rps_draw { + get { + return ResourceManager.GetString("games_rps_draw", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} won! {1} beats {2}. + /// + public static string games_rps_win { + get { + return ResourceManager.GetString("games_rps_win", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Submissions Closed. + /// + public static string games_submissions_closed { + get { + return ResourceManager.GetString("games_submissions_closed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to ToC. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 4db9ff76..90024cd2 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1105,6 +1105,9 @@ Don't forget to leave your discord name or id in the message. Likes + + Nobody + Price @@ -1164,4 +1167,19 @@ Don't forget to leave your discord name or id in the message. You divorced recently. You must wait {0} hours and {1} minutes to divorce again. + + 8ball + + + Acrophobia game is already running in this channel. + + + Question + + + It's a draw! Both picked {0} + + + {0} won! {1} beats {2} + \ No newline at end of file From 06cafa1296a39385d07ef69deab3c46267e77004 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 20 Feb 2017 23:46:34 +0100 Subject: [PATCH 179/256] more localization, fixes, etc, no idea exactly what, rategirl is commented out until i fix the last bug --- ...dekoModule.cs => NadekoModuleAttribute.cs} | 0 src/NadekoBot/DataStructures/AsyncLazy.cs | 23 +++ .../Modules/Administration/Administration.cs | 2 +- .../Administration/Commands/LogCommand.cs | 2 +- .../Modules/ClashOfClans/ClashOfClans.cs | 2 +- .../Modules/ClashOfClans/Extensions.cs | 2 +- .../CustomReactions/CustomReactions.cs | 2 +- .../Modules/Gambling/Commands/AnimalRacing.cs | 4 +- .../Gambling/Commands/DiceRollCommand.cs | 5 +- .../Modules/Gambling/Commands/DrawCommand.cs | 2 +- .../Gambling/Commands/FlipCoinCommand.cs | 19 +- .../Modules/Gambling/Commands/Slots.cs | 105 ++++------- src/NadekoBot/Modules/Gambling/Gambling.cs | 2 +- .../Modules/Games/Commands/Acropobia.cs | 2 +- src/NadekoBot/Modules/Games/Games.cs | 166 +++++++++++++++++- src/NadekoBot/Modules/Help/Help.cs | 2 +- src/NadekoBot/Modules/Music/Music.cs | 2 +- src/NadekoBot/Modules/NSFW/NSFW.cs | 2 +- src/NadekoBot/Modules/NadekoModule.cs | 6 +- .../Modules/Permissions/Permissions.cs | 2 +- src/NadekoBot/Modules/Pokemon/Pokemon.cs | 2 +- src/NadekoBot/Modules/Searches/Searches.cs | 15 +- src/NadekoBot/Modules/Utility/Utility.cs | 7 +- src/NadekoBot/NadekoBot.cs | 3 + .../Resources/CommandStrings.Designer.cs | 81 ++++++--- src/NadekoBot/Resources/CommandStrings.resx | 13 +- src/NadekoBot/Resources/ResponseStrings.resx | 36 ++++ src/NadekoBot/Services/IImagesService.cs | 2 + src/NadekoBot/Services/Impl/ImagesService.cs | 6 + src/NadekoBot/_Extensions/Extensions.cs | 27 ++- src/NadekoBot/data/images/wifematrix.png | Bin 0 -> 34050 bytes src/NadekoBot/project.json | 7 +- 32 files changed, 393 insertions(+), 158 deletions(-) rename src/NadekoBot/Attributes/{NadekoModule.cs => NadekoModuleAttribute.cs} (100%) create mode 100644 src/NadekoBot/DataStructures/AsyncLazy.cs create mode 100644 src/NadekoBot/data/images/wifematrix.png diff --git a/src/NadekoBot/Attributes/NadekoModule.cs b/src/NadekoBot/Attributes/NadekoModuleAttribute.cs similarity index 100% rename from src/NadekoBot/Attributes/NadekoModule.cs rename to src/NadekoBot/Attributes/NadekoModuleAttribute.cs diff --git a/src/NadekoBot/DataStructures/AsyncLazy.cs b/src/NadekoBot/DataStructures/AsyncLazy.cs new file mode 100644 index 00000000..40800755 --- /dev/null +++ b/src/NadekoBot/DataStructures/AsyncLazy.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.DataStructures +{ + public class AsyncLazy : Lazy> + { + public AsyncLazy(Func valueFactory) : + base(() => Task.Factory.StartNew(valueFactory)) + { } + + public AsyncLazy(Func> taskFactory) : + base(() => Task.Factory.StartNew(taskFactory).Unwrap()) + { } + + public TaskAwaiter GetAwaiter() { return Value.GetAwaiter(); } + } + +} diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 3f52ac17..405485dd 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -16,7 +16,7 @@ using NLog; namespace NadekoBot.Modules.Administration { [NadekoModule("Administration", ".")] - public partial class Administration : NadekoModule + public partial class Administration : NadekoTopLevelModule { private static ConcurrentHashSet deleteMessagesOnCommand { get; } diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 95c4538a..88d512e7 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -1068,7 +1068,7 @@ namespace NadekoBot.Modules.Administration public static class GuildExtensions { public static string GetLogText(this IGuild guild, string key, params object[] replacements) - => NadekoModule.GetTextStatic(key, + => NadekoTopLevelModule.GetTextStatic(key, NadekoBot.Localization.GetCultureInfo(guild), typeof(Administration).Name.ToLowerInvariant(), replacements); diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 37f07efa..043a12b2 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -17,7 +17,7 @@ using NLog; namespace NadekoBot.Modules.ClashOfClans { [NadekoModule("ClashOfClans", ",")] - public class ClashOfClans : NadekoModule + public class ClashOfClans : NadekoTopLevelModule { public static ConcurrentDictionary> ClashWars { get; set; } = new ConcurrentDictionary>(); diff --git a/src/NadekoBot/Modules/ClashOfClans/Extensions.cs b/src/NadekoBot/Modules/ClashOfClans/Extensions.cs index 032d3bcd..5756d3db 100644 --- a/src/NadekoBot/Modules/ClashOfClans/Extensions.cs +++ b/src/NadekoBot/Modules/ClashOfClans/Extensions.cs @@ -135,7 +135,7 @@ namespace NadekoBot.Modules.ClashOfClans public static string Localize(this ClashWar cw, string key) { - return NadekoModule.GetTextStatic(key, + return NadekoTopLevelModule.GetTextStatic(key, NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId), typeof(ClashOfClans).Name.ToLowerInvariant()); } diff --git a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs index ebeb4922..efd3717e 100644 --- a/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs +++ b/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs @@ -17,7 +17,7 @@ using NadekoBot.DataStructures; namespace NadekoBot.Modules.CustomReactions { [NadekoModule("CustomReactions", ".")] - public class CustomReactions : NadekoModule + public class CustomReactions : NadekoTopLevelModule { private static CustomReaction[] _globalReactions = new CustomReaction[] { }; public static CustomReaction[] GlobalReactions => _globalReactions; diff --git a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs index 39c749d2..040e657e 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/AnimalRacing.cs @@ -272,12 +272,12 @@ namespace NadekoBot.Modules.Gambling } private string GetText(string text) - => NadekoModule.GetTextStatic(text, + => NadekoTopLevelModule.GetTextStatic(text, NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild), typeof(Gambling).Name.ToLowerInvariant()); private string GetText(string text, params object[] replacements) - => NadekoModule.GetTextStatic(text, + => NadekoTopLevelModule.GetTextStatic(text, NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild), typeof(Gambling).Name.ToLowerInvariant(), replacements); diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index a75827e7..721b5b4e 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -10,6 +10,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; +using ImageSharp.Formats; using Image = ImageSharp.Image; namespace NadekoBot.Modules.Gambling @@ -35,7 +36,7 @@ namespace NadekoBot.Modules.Gambling var imageStream = await Task.Run(() => { var ms = new MemoryStream(); - new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); + new[] { GetDice(num1), GetDice(num2) }.Merge().Save(ms); ms.Position = 0; return ms; }).ConfigureAwait(false); @@ -120,7 +121,7 @@ namespace NadekoBot.Modules.Gambling var bitmap = dice.Merge(); var ms = new MemoryStream(); - bitmap.SaveAsPng(ms); + bitmap.Save(ms); ms.Position = 0; await Context.Channel.SendFileAsync(ms, "dice.png", Context.User.Mention + diff --git a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs index 7471f7d0..b7a68ece 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DrawCommand.cs @@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Gambling images.Add(new Image(stream)); } MemoryStream bitmapStream = new MemoryStream(); - images.Merge().SaveAsPng(bitmapStream); + images.Merge().Save(bitmapStream); bitmapStream.Position = 0; var toSend = $"{Context.User.Mention}"; if (cardObjects.Count == 5) diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 973bc90e..40b8a735 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -52,17 +52,22 @@ namespace NadekoBot.Modules.Gambling return; } var imgs = new Image[count]; - using (var heads = _images.Heads.ToStream()) - using(var tails = _images.Tails.ToStream()) + for (var i = 0; i < count; i++) { - for (var i = 0; i < count; i++) + using (var heads = _images.Heads.ToStream()) + using (var tails = _images.Tails.ToStream()) { - imgs[i] = rng.Next(0, 10) < 5 ? - new Image(heads) : - new Image(tails); + if (rng.Next(0, 10) < 5) + { + imgs[i] = new Image(heads); + } + else + { + imgs[i] = new Image(tails); + } } - await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } + await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs index c298a9b4..ea20b4b1 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/Slots.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/Slots.cs @@ -1,5 +1,6 @@ using Discord; using Discord.Commands; +using ImageSharp; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; @@ -163,83 +164,43 @@ namespace NadekoBot.Modules.Gambling var result = SlotMachine.Pull(); int[] numbers = result.Numbers; - using (var bgPixels = bgImage.Lock()) + + for (int i = 0; i < 3; i++) { - for (int i = 0; i < 3; i++) + using (var file = _images.SlotEmojis[numbers[i]].ToStream()) + using (var randomImage = new ImageSharp.Image(file)) { - using (var file = _images.SlotEmojis[numbers[i]].ToStream()) - { - var randomImage = new ImageSharp.Image(file); - using (var toAdd = randomImage.Lock()) - { - for (int j = 0; j < toAdd.Width; j++) - { - for (int k = 0; k < toAdd.Height; k++) - { - var x = 95 + 142 * i + j; - int y = 330 + k; - var toSet = toAdd[j, k]; - if (toSet.A < _alphaCutOut) - continue; - bgPixels[x, y] = toAdd[j, k]; - } - } - } - } + bgImage.DrawImage(randomImage, 100, default(Size), new Point(95 + 142 * i, 330)); } - - var won = amount * result.Multiplier; - var printWon = won; - var n = 0; - do - { - var digit = printWon % 10; - using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream()) - { - var img = new ImageSharp.Image(fs); - using (var pixels = img.Lock()) - { - for (int i = 0; i < pixels.Width; i++) - { - for (int j = 0; j < pixels.Height; j++) - { - if (pixels[i, j].A < _alphaCutOut) - continue; - var x = 230 - n * 16 + i; - bgPixels[x, 462 + j] = pixels[i, j]; - } - } - } - } - n++; - } while ((printWon /= 10) != 0); - - var printAmount = amount; - n = 0; - do - { - var digit = printAmount % 10; - using (var fs = _images.SlotNumbers[digit].ToStream()) - { - var img = new ImageSharp.Image(fs); - using (var pixels = img.Lock()) - { - for (int i = 0; i < pixels.Width; i++) - { - for (int j = 0; j < pixels.Height; j++) - { - if (pixels[i, j].A < _alphaCutOut) - continue; - var x = 395 - n * 16 + i; - bgPixels[x, 462 + j] = pixels[i, j]; - } - } - } - } - n++; - } while ((printAmount /= 10) != 0); } + var won = amount * result.Multiplier; + var printWon = won; + var n = 0; + do + { + var digit = printWon % 10; + using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream()) + using (var img = new ImageSharp.Image(fs)) + { + bgImage.DrawImage(img, 100, default(Size), new Point(230 - n * 16, 462)); + } + n++; + } while ((printWon /= 10) != 0); + + var printAmount = amount; + n = 0; + do + { + var digit = printAmount % 10; + using (var fs = _images.SlotNumbers[digit].ToStream()) + using (var img = new ImageSharp.Image(fs)) + { + bgImage.DrawImage(img, 100, default(Size), new Point(395 - n * 16, 462)); + } + n++; + } while ((printAmount /= 10) != 0); + var msg = GetText("better_luck"); if (result.Multiplier != 0) { diff --git a/src/NadekoBot/Modules/Gambling/Gambling.cs b/src/NadekoBot/Modules/Gambling/Gambling.cs index 3cdd7165..0db8e198 100644 --- a/src/NadekoBot/Modules/Gambling/Gambling.cs +++ b/src/NadekoBot/Modules/Gambling/Gambling.cs @@ -12,7 +12,7 @@ using System.Collections.Generic; namespace NadekoBot.Modules.Gambling { [NadekoModule("Gambling", "$")] - public partial class Gambling : NadekoModule + public partial class Gambling : NadekoTopLevelModule { public static string CurrencyName { get; set; } public static string CurrencyPluralName { get; set; } diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index 088b95d7..aa11f1c6 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -299,7 +299,7 @@ $@"-- } private string GetText(string key, params object[] replacements) - => NadekoModule.GetTextStatic(key, + => NadekoTopLevelModule.GetTextStatic(key, NadekoBot.Localization.GetCultureInfo(_channel.Guild), typeof(Games).Name.ToLowerInvariant()); } diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index b6b9130b..88a663df 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -4,18 +4,33 @@ using NadekoBot.Services; using System.Threading.Tasks; using NadekoBot.Attributes; using System; +using System.Collections.Concurrent; using System.Linq; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; +using System.Threading; using NadekoBot.Extensions; +using System.Net.Http; +using ImageSharp; +using NadekoBot.DataStructures; +using NLog; +using ImageSharp.Drawing.Pens; +using SixLabors.Shapes; namespace NadekoBot.Modules.Games { [NadekoModule("Games", ">")] - public partial class Games : NadekoModule + public partial class Games : NadekoTopLevelModule { private static readonly ImmutableArray _8BallResponses = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray(); + private static readonly Timer _t = new Timer((_) => + { + _girlRatings.Clear(); + + }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1)); + [NadekoCommand, Usage, Description, Aliases] public async Task Choose([Remainder] string list = null) { @@ -91,6 +106,155 @@ namespace NadekoBot.Modules.Games await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false); } + private static readonly ConcurrentDictionary _girlRatings = new ConcurrentDictionary(); + + public class GirlRating + { + private static Logger _log = LogManager.GetCurrentClassLogger(); + + public double Crazy { get; } + public double Hot { get; } + public int Roll { get; } + public string Advice { get; } + public AsyncLazy Url { get; } + + public GirlRating(double crazy, double hot, int roll, string advice) + { + Crazy = crazy; + Hot = hot; + Roll = roll; + Advice = advice; // convenient to have it here, even though atm there are only few different ones. + + Url = new AsyncLazy(async () => + { + try + { + using (var ms = new MemoryStream(NadekoBot.Images.WifeMatrix.ToArray(), false)) + using (var img = new ImageSharp.Image(ms)) + { + var clr = new ImageSharp.Color(0x0000ff); + const int minx = 35; + const int miny = 385; + const int length = 345; + + var pointx = (int)(minx + length * (Hot / 10)); + var pointy = (int)(miny - length * ((Crazy - 4) / 6)); + + var p = new Pen(ImageSharp.Color.Red, 5); + + img.Draw(p, new SixLabors.Shapes.Ellipse(200, 200, 5, 5)); + + string url; + using (var http = new HttpClient()) + using (var imgStream = new MemoryStream()) + { + img.Save(imgStream); + var byteContent = new ByteArrayContent(imgStream.ToArray()); + http.AddFakeHeaders(); + + var reponse = await http.PutAsync("https://transfer.sh/img.png", byteContent); + url = await reponse.Content.ReadAsStringAsync(); + } + return url; + } + } + catch (Exception ex) + { + _log.Warn(ex); + return null; + } + }); + } + } + + //[NadekoCommand, Usage, Description, Aliases] + //[RequireContext(ContextType.Guild)] + //public async Task RateGirl(IGuildUser usr) + //{ + // var gr = _girlRatings.GetOrAdd(usr.Id, GetGirl); + // var img = await gr.Url; + // await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + // .WithTitle("Girl Rating For " + usr) + // .AddField(efb => efb.WithName("Hot").WithValue(gr.Hot.ToString("F2")).WithIsInline(true)) + // .AddField(efb => efb.WithName("Crazy").WithValue(gr.Crazy.ToString("F2")).WithIsInline(true)) + // .AddField(efb => efb.WithName("Advice").WithValue(gr.Advice).WithIsInline(false)) + // .WithImageUrl(img)).ConfigureAwait(false); + //} + + private double NextDouble(double x, double y) + { + var rng = new Random(); + return rng.NextDouble() * (y - x) + x; + } + + private GirlRating GetGirl(ulong uid) + { + var rng = new NadekoRandom(); + + var roll = rng.Next(1, 1001); + + double hot; + double crazy; + string advice; + if (roll < 500) + { + hot = NextDouble(0, 5); + crazy = NextDouble(4, 10); + advice = + "This is your NO-GO ZONE. We do not hang around, and date, and marry women who are atleast, in our mind, a 5. " + + "So, this is your no-go zone. You don't go here. You just rule this out. Life is better this way, that's the way it is."; + } + else if (roll < 750) + { + hot = NextDouble(5, 8); + crazy = NextDouble(4, .6 * hot + 4); + advice = "Above a 5, and to about an 8, and below the crazy line - this is your FUN ZONE. You can " + + "hang around here, and meet these girls and spend time with them. Keep in mind, while you're " + + "in the fun zone, you want to move OUT of the fun zone to a more permanent location. " + + "These girls are most of the time not crazy."; + } + else if (roll < 900) + { + hot = NextDouble(5, 10); + crazy = NextDouble(.61 * hot + 4, 10); + advice = "Above the crazy line - it's the DANGER ZONE. This is redheads, strippers, anyone named Tiffany, " + + "hairdressers... This is where your car gets keyed, you get bunny in the pot, your tires get slashed, " + + "and you wind up in jail."; + } + else if (roll < 951) + { + hot = NextDouble(8, 10); + crazy = NextDouble(4, 10); + advice = "Below the crazy line, above an 8 hot, but still about 7 crazy. This is your DATE ZONE. " + + "You can stay in the date zone indefinitely. These are the girls you introduce to your friends and your family. " + + "They're good looking, and they're reasonably not crazy most of the time. You can stay here indefinitely."; + } + else if (roll < 990) + { + hot = NextDouble(8, 10); + crazy = NextDouble(5, 7); + advice = "Above an 8 hot, and between about 7 and a 5 crazy - this is WIFE ZONE. You you meet this girl, you should consider long-term " + + "relationship. Rare."; + } + else if (roll < 999) + { + hot = NextDouble(8, 10); + crazy = NextDouble(2, 3.99d); + advice = "You've met a girl she's above 8 hot, and not crazy at all (below 4)... totally cool?" + + " You should be careful. That's a dude. It's a tranny."; + } + else + { + hot = NextDouble(8, 10); + crazy = NextDouble(4, 5); + advice = "Below 5 crazy, and above 8 hot, this is the UNICORN ZONE, these things don't exist." + + "If you find a unicorn, please capture it safely, keep it alive, we'd like to study it, " + + "and maybe look at how to replicate that."; + } + + return new GirlRating(crazy, hot, roll, advice); + } + [NadekoCommand, Usage, Description, Aliases] public async Task Linux(string guhnoo, string loonix) { diff --git a/src/NadekoBot/Modules/Help/Help.cs b/src/NadekoBot/Modules/Help/Help.cs index c22a9789..d6ceb1bd 100644 --- a/src/NadekoBot/Modules/Help/Help.cs +++ b/src/NadekoBot/Modules/Help/Help.cs @@ -13,7 +13,7 @@ using System.Collections.Generic; namespace NadekoBot.Modules.Help { [NadekoModule("Help", "-")] - public class Help : NadekoModule + public class Help : NadekoTopLevelModule { private static string helpString { get; } = NadekoBot.BotConfig.HelpString; public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]); diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 4fbc8701..a7b46269 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Music { [NadekoModule("Music", "!!")] [DontAutoLoad] - public partial class Music : NadekoModule + public partial class Music : NadekoTopLevelModule { public static ConcurrentDictionary MusicPlayers { get; } = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 5538e8d6..53a3f3bb 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -17,7 +17,7 @@ using System.Collections.Concurrent; namespace NadekoBot.Modules.NSFW { [NadekoModule("NSFW", "~")] - public class NSFW : NadekoModule + public class NSFW : NadekoTopLevelModule { private static readonly ConcurrentDictionary AutoHentaiTimers = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index d76a483c..ea4a0049 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace NadekoBot.Modules { - public abstract class NadekoModule : ModuleBase + public abstract class NadekoTopLevelModule : ModuleBase { protected readonly Logger _log; protected CultureInfo _cultureInfo; @@ -17,7 +17,7 @@ namespace NadekoBot.Modules public readonly string ModuleTypeName; public readonly string LowerModuleTypeName; - protected NadekoModule(bool isTopLevelModule = true) + protected NadekoTopLevelModule(bool isTopLevelModule = true) { //if it's top level module ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; @@ -120,7 +120,7 @@ namespace NadekoBot.Modules } } - public abstract class NadekoSubmodule : NadekoModule + public abstract class NadekoSubmodule : NadekoTopLevelModule { protected NadekoSubmodule() : base(false) { diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index 407ca2a2..3e8ecd13 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -15,7 +15,7 @@ using NLog; namespace NadekoBot.Modules.Permissions { [NadekoModule("Permissions", ";")] - public partial class Permissions : NadekoModule + public partial class Permissions : NadekoTopLevelModule { public class PermissionCache { diff --git a/src/NadekoBot/Modules/Pokemon/Pokemon.cs b/src/NadekoBot/Modules/Pokemon/Pokemon.cs index 08d3976f..c81091c7 100644 --- a/src/NadekoBot/Modules/Pokemon/Pokemon.cs +++ b/src/NadekoBot/Modules/Pokemon/Pokemon.cs @@ -16,7 +16,7 @@ using System.Collections.Concurrent; namespace NadekoBot.Modules.Pokemon { [NadekoModule("Pokemon", ">")] - public class Pokemon : NadekoModule + public class Pokemon : NadekoTopLevelModule { private static readonly List _pokemonTypes = new List(); private static readonly ConcurrentDictionary _stats = new ConcurrentDictionary(); diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 28a00413..ef47301c 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -1,5 +1,4 @@ using Discord; -using Discord.Commands; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; @@ -8,27 +7,25 @@ using System.Text; using System.Net.Http; using NadekoBot.Services; using System.Threading.Tasks; -using NadekoBot.Attributes; -using System.Text.RegularExpressions; using System.Net; using NadekoBot.Modules.Searches.Models; using System.Collections.Generic; -using ImageSharp; using NadekoBot.Extensions; using System.IO; using NadekoBot.Modules.Searches.Commands.OMDB; using NadekoBot.Modules.Searches.Commands.Models; -using AngleSharp.Parser.Html; using AngleSharp; using AngleSharp.Dom.Html; using AngleSharp.Dom; using System.Xml; -using System.Xml.Linq; +using Configuration = AngleSharp.Configuration; +using NadekoBot.Attributes; +using Discord.Commands; namespace NadekoBot.Modules.Searches { [NadekoModule("Searches", "~")] - public partial class Searches : NadekoModule + public partial class Searches : NadekoTopLevelModule { [NadekoCommand, Usage, Description, Aliases] public async Task Weather([Remainder] string query) @@ -396,7 +393,7 @@ namespace NadekoBot.Modules.Searches msg = "⚠ Found over 4 images. Showing random 4."; } var ms = new MemoryStream(); - await Task.Run(() => images.AsEnumerable().Merge().SaveAsPng(ms)); + await Task.Run(() => images.AsEnumerable().Merge().Save(ms)); ms.Position = 0; await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false); } @@ -625,7 +622,7 @@ namespace NadekoBot.Modules.Searches return; var img = new ImageSharp.Image(50, 50); - img.BackgroundColor(new ImageSharp.Color(color)); + //img.FillPolygon(new ImageSharp, new ImageSharp.Color(color)); await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png").ConfigureAwait(false); ; } diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index d627d956..8d3a95c4 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Threading.Tasks; using System.Text; using NadekoBot.Extensions; -using System.Text.RegularExpressions; using System.Reflection; using NadekoBot.Services.Impl; using System.Net.Http; @@ -21,7 +20,7 @@ using NadekoBot.Services; namespace NadekoBot.Modules.Utility { [NadekoModule("Utility", ".")] - public partial class Utility : NadekoModule + public partial class Utility : NadekoTopLevelModule { private static ConcurrentDictionary rotatingRoleColors = new ConcurrentDictionary(); @@ -122,10 +121,10 @@ namespace NadekoBot.Modules.Utility } return; } - + var hexColors = hexes.Select(hex => { - try { return (ImageSharp.Color?)new ImageSharp.Color(hex.Replace("#", "")); } catch { return null; } + try { return (ImageSharp.Color?)ImageSharp.Color.FromHex(hex.Replace("#", "")); } catch { return null; } }) .Where(c => c != null) .Select(c => c.Value) diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index 40d71602..7da895d6 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -60,6 +60,9 @@ namespace NadekoBot OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); } + + //ImageSharp.Configuration.Default.AddImageFormat(new ImageSharp.Formats.PngFormat()); + //ImageSharp.Configuration.Default.AddImageFormat(new ImageSharp.Formats.JpegFormat()); } public async Task RunAsync(params string[] args) diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 579382ba..e6390b66 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -2381,33 +2381,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to qsearch. - /// - public static string searchquote_cmd { - get { - return ResourceManager.GetString("searchquote_cmd", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Shows a random quote for a keyword that contains any text specified in the search.. - /// - public static string searchquote_desc { - get { - return ResourceManager.GetString("searchquote_desc", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to `{0}qsearch keyword text`. - /// - public static string searchquote_usage { - get { - return ResourceManager.GetString("searchquote_usage", resourceCulture); - } - } - /// /// Looks up a localized string similar to delmsgoncmd. /// @@ -5756,6 +5729,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to rategirl. + /// + public static string rategirl_cmd { + get { + return ResourceManager.GetString("rategirl_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart.. + /// + public static string rategirl_desc { + get { + return ResourceManager.GetString("rategirl_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}rategirl @SomeGurl`. + /// + public static string rategirl_usage { + get { + return ResourceManager.GetString("rategirl_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to reloadimages. /// @@ -6701,6 +6701,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to qsearch. + /// + public static string searchquote_cmd { + get { + return ResourceManager.GetString("searchquote_cmd", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shows a random quote for a keyword that contains any text specified in the search.. + /// + public static string searchquote_desc { + get { + return ResourceManager.GetString("searchquote_desc", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}qsearch keyword text`. + /// + public static string searchquote_usage { + get { + return ResourceManager.GetString("searchquote_usage", resourceCulture); + } + } + /// /// Looks up a localized string similar to send. /// diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 732c0087..c661db7d 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -1151,7 +1151,7 @@ `{0}qsearch keyword text` - + deletequote delq @@ -3141,4 +3141,13 @@ `{0}langli` - + + rategirl + + + Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart. + + + `{0}rategirl @SomeGurl` + + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 90024cd2..c1eb0a5d 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1170,9 +1170,42 @@ Don't forget to leave your discord name or id in the message. 8ball + + Acrophobia + + + Game ended with no submissions. + + + No votes cast. Game ended with no winner. + + + Acronym was {0}. + Acrophobia game is already running in this channel. + + Game started. Create a sentence with the following acronym: {0}. + + + You have {0} seconds to make a submission. + + + {0} submitted their sentence. ({1} total) + + + Vote by typing a number of the submission + + + {0} cast their vote! + + + Winner is {0} with {1} points. + + + {0} is the winner for being the only user who made a submission! + Question @@ -1182,4 +1215,7 @@ Don't forget to leave your discord name or id in the message. {0} won! {1} beats {2} + + Submissions Closed + \ No newline at end of file diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index 81e21907..e3b25458 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -21,6 +21,8 @@ namespace NadekoBot.Services ImmutableArray> SlotEmojis { get; } ImmutableArray> SlotNumbers { get; } + ImmutableArray WifeMatrix { get; } + Task Reload(); } } diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index 77ee9f26..567d63f2 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -28,6 +28,8 @@ namespace NadekoBot.Services.Impl private const string slotNumbersPath = basePath + "slots/numbers/"; private const string slotEmojisPath = basePath + "slots/emojis/"; + private const string _wifeMatrixPath = basePath + "wifematrix.png"; + public ImmutableArray Heads { get; private set; } public ImmutableArray Tails { get; private set; } @@ -41,6 +43,8 @@ namespace NadekoBot.Services.Impl public ImmutableArray> SlotNumbers { get; private set; } public ImmutableArray> SlotEmojis { get; private set; } + public ImmutableArray WifeMatrix { get; private set; } + private ImagesService() { _log = LogManager.GetCurrentClassLogger(); @@ -86,6 +90,8 @@ namespace NadekoBot.Services.Impl .Select(x => File.ReadAllBytes(x).ToImmutableArray()) .ToImmutableArray(); + WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray(); + sw.Stop(); _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); return sw.Elapsed; diff --git a/src/NadekoBot/_Extensions/Extensions.cs b/src/NadekoBot/_Extensions/Extensions.cs index 4c32a542..8dbdf02b 100644 --- a/src/NadekoBot/_Extensions/Extensions.cs +++ b/src/NadekoBot/_Extensions/Extensions.cs @@ -22,7 +22,11 @@ namespace NadekoBot.Extensions private const string arrow_right = "➡"; public static Stream ToStream(this IEnumerable bytes, bool canWrite = false) - => new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); + { + var ms = new MemoryStream(bytes as byte[] ?? bytes.ToArray(), canWrite); + ms.Seek(0, SeekOrigin.Begin); + return ms; + } /// /// danny kamisama @@ -398,22 +402,15 @@ namespace NadekoBot.Extensions public static ImageSharp.Image Merge(this IEnumerable images) { - var imgList = images.ToList(); + var imgs = images.ToArray(); - var canvas = new ImageSharp.Image(imgList.Sum(img => img.Width), imgList.Max(img => img.Height)); + var canvas = new ImageSharp.Image(imgs.Sum(img => img.Width), imgs.Max(img => img.Height)); - var canvasPixels = canvas.Lock(); - int offsetX = 0; - foreach (var img in imgList.Select(img => img.Lock())) + var xOffset = 0; + for (int i = 0; i < imgs.Length; i++) { - for (int i = 0; i < img.Width; i++) - { - for (int j = 0; j < img.Height; j++) - { - canvasPixels[i + offsetX, j] = img[i, j]; - } - } - offsetX += img.Width; + canvas.DrawImage(imgs[i], 100, default(Size), new Point(xOffset, 0)); + xOffset += imgs[i].Bounds.Width; } return canvas; @@ -422,7 +419,7 @@ namespace NadekoBot.Extensions public static Stream ToStream(this ImageSharp.Image img) { var imageStream = new MemoryStream(); - img.SaveAsPng(imageStream); + img.Save(imageStream); imageStream.Position = 0; return imageStream; } diff --git a/src/NadekoBot/data/images/wifematrix.png b/src/NadekoBot/data/images/wifematrix.png new file mode 100644 index 0000000000000000000000000000000000000000..6e0b8db9b6f25e0175b76a163f7ce20929a78fba GIT binary patch literal 34050 zcmV)>K!d-DP){>}o6Hyp`G#orgV(dx1$N@OWfpF5+?O-s(fS$z~?SbuRO(7=MfCj|C#Rc%- zk}8I1ObBs_2vO7`7to`?(I^Sts7VtqqyL0Qro-E2`eu3K{K;g}f2Py-y?_6vWtyh# zKnT^LfJylVsCPjF7C`}(Ex_`vjr#e|#IV}P{>9i)Z9@v#ek~|cWU+0DZpSt-9zW!r zD=sas*m}sz*HiwEv6|{CTMs$W*nmz{m|fm+Q9CP4x{GgQ_OpK*Y7`k zgl0eFh}Wc9-k23-%EH2zqR+9|!J=#MxvA;orvB#b5vjjlGO+ivpM>r+0&_jx+sSxG z4#%l+M{~`^#joxbXqte_lqzP6VOMm@nnKBAa&kg=qj-q}HbqXoN2E0xdxma@=WcShv{tFNyk_L0y^@#3JOVmmi$ zipcnrvCWOVeq+eH0&~X5J zg{KUm@*=Z9k3W4bT%d9BJ>8NJD$vI7ixhjMIuF(+l-%9R6mT)%KOlpGoAKgp>~U6& zI(EF_7}~RY7i#b5)Q4GC0}bF|iuQdIjOjP*ZomXz&h!X3!93R{0(mfkyB$%?c%p{4w9OUqJX%>4Ef?7DfLBI!spofA}?@nr~DXkTqDN}f;ZYl1V=Vx;sgs^eM3({yj;$4Ta=y&E&T zksH9u02ng;tpUwk|ovGtiWQaU^K@c8VcxM;d{N$`pVw-_WdY-k|F z*FBRXly)johWz96D@e|m&2zLmrlUIe3i*&?7LNWpm){Cb_y)wqLdC=|`H1 zrjDrUcyQ=|mFbvmhg^AzLBJ!_;!Y)~XwyN{Jwlh^%>*k{k~gqijxPoM#Q%}4OKqHz zG%eLji-e<8SK&Z9)l<*+g;e5+3m#QLI``RD;8B$BA+F$zN|ohoh3Bd9|gYID8rLKy`s+U&Cts*>$r4&2MX^NFH$Yb5XhwAUpZ3FZWwax;B- z9XR9_Fl=#&^F`iJ%M~cjG^Bo??fB$086FMij85newb|}xm5)+bIK7`p|GkXpt&jeEo3fLn)Z^}Y}Sa*w>zCIMfrPUGg*JSNLIRs z>Zw?F7?&4}bR~Er5i<%F1zU2#M8QbZ)3BcaHiO0# zf$!6c5&Iim6UP8Y!@@5wFPjI%u?Gl{94j(f{)jHC|BoZ1hP0+A+Z2186!U%L(pYjt z%btb9cv<5#JgwycJwKCqab5oQDip;2Q9Tq3X3dea&ZFuc!Xd+S0zg;l5*Je^)+c9} z7ydJLoT~3vEfnE3QmQ13R6=0DSiV@+jJ4E<+pQ(6?VIQQBawe1@kd>u0Y>#)7nSHa z*;w?BCg&~O!z`)CUBgA(x(3lQ)Rh0IBepKXIG#%&%U*QRg;Fsq6|X+e#%`7SS_NrK!_XDiu}LeycZ`5mW`J)!Rg2 zn}ir*2dhyz)*96pR%Bw8Y5cJ^h?z$o0g1~(Lu5z z?&Vvn$@Z)wWUh4`LV}c0`d9C)4nGG*4`BMHzUJ-{ma2qUnL$vM+n-Wut=}8_J&buH znr`&e+mIT*v8pci#lLDT`xY0XrLGI}T<%?RYy`o*P9kPv)E*d9Mo5aq0>~<%4(=QwNbwPp#nSyUK9(jf9IfzxZFL%V5Gf5VLBzqR{> zj7e%wt6>b`FT8(JGA~=9rsSf>CJMt^wD@b*=Z&h2gNM}zD26nwk{TVcs>bTqHJ!hC zCu#RyC041gwr^UQ8qUP^X1aW`=3&qqkukrAJ$j zmd}foavDdtLTY37C4LtNuWYl;NDrUm5>K@5VJS`Yy8TKs9!?9I?!!GYiq6W-RfT^n zdl(#MjsYK30(iXk*5pP<%;BZP3yvGcqKsi-G((o%=$v8JiC9rq9Ji}8wu!S6!@a91 zvd(>~sWnt+_0ebZ#NEr21G$f}?t7b}QmAr((|7J!I0pmCTJlZ(jyr*BYk`*`0@&+R zyUj^4k-t3m!!w%v#4432{}1DOVUcx9_r?V=wkF#=W#B!Ms026oruqI?&eEha4joygKob4r{=YQF}0?E;2zdjdjyM zas1fSFfH0jY?3x9QDBHBen_~s1=9hhWrjtl$*Q0%)t|<^EmNt}PHv5X!o}6Lz|crh zY|L;f83xIyi>gaTSyUMrtx=U^oT|0Qh)Fudm>>7-@E*=Rgu`iHMw3b`i>jANR2x-> z7eX7&zIOGBYbsJKkuiX{$=`hW(#2sEglmL)kBEM7y(k3gMm|-3SW|=^pK0TKDdT~o z0^N)?YAS?~H8D@?_*xz0Hfgz2={T1q7t|ROT4XEl)1)Ycuz+bGly(34;&Jii#A5OF z^r6yw)>|S9UPSDC3hFy#;MlnW|~^?!EskUR253wDM$d z-Eib=9K*KH&JM2g^K*H%9q7=(14D~9i63_U+?l+}jwIXuj5IWrsb{goO!Y*lKp8Pm z5Z!e)Q@hZVyfQq6?q|e|@H1wpqKO!y9u~VGGb{limI`BnhMEjZfsP$LJTy3%$gZVa z^S32IYECVk%zId-%&^R`OqpSsGQ%d5vX$6FhHd_P%@N(6&>sQVySh-Tsvx`#rQi}h zh(8s*WkGqPmk271(k$trqJmNiG?WSqdow9Agp!JqE+pC?l-EL$hzK>6UV8Vz>cJii zg3P~%>|P9#ZhreRhvmL|pS@<*tiAR=X9f=E?DMt%XzVqSyF|=&XvQ;)(=7*UW zOYDen_j4OyiNTUKM<@jhnLLupV^w9*O?1GT_P3O`?D$mCE)!JNF1CppG^wft&fOn2 zqx4Y9&Z?uO* zZNWC#kj_XNCbFziY4M(lQMy%1aqirRsvydp!^qDo&S_Lpn+k5a-_IdFG3Kmb(t$rEEl8SyL&qDq>yrni>*1PrBxg9@D*R z8tZ&6GJ~l~?}#nFjLrcj89*%!0gKd% zv`E=tKfpqQ0FAIj4iNY=T7Fv-+Ezu~i+~kllV!J7(fU+f^kU9y{rs#lz^G$8T}UAg zd30LO&BM^5{~@^}+DjyG8#ceOC42XsxBFIbYgJAMx7@jwmS~zsp9n^ohCzskfn>QR zlDiz#nBy#QL^T{_t?Jt$i;vLsKTn;?AOCjZ#N~;(E1zzshJ}jel^8RSo08UhYgiMl zGmt-#q9IQ7W&IR69hg*o^X(-~U|bWa(owu&us}4*O2JBZsi>JmpM}O^w^b>zh;1UJ zOC@A$a2zE;Q}R<#6nFVGlHvzR6N_&+}#JEpc|qn&FX zW6r{PqUbVY)2pwgpTjsgh9e^*`Rl(PKb`{{I^gI>hWSB*_{0E@z8Lk#siFfwBnFfUG3rsrZ?JrS35!9f)d#fF|*~khC+5V5x?t zq@aN{*2NoVu+2vv&4yOAY-?yvxg>l=*|ywy5Vvo>{jPURKA-P7bYc#x3s1o`hGg=h zHEx}j7R?}j6E|+63(V@-v*&uplt;*E&s!9<{H4_gJf_yaH0Nq17I_1PfvOuAkdP42 z{y2G3GT#fveLZA7HBl>E7?8h&5}sHOAb<%R*OEH*Qgf<^_qx&l}cyjZ(LqXQ;M znsGHN)xPF|j3sh>7vEY^WoA&@G{WzK=9B+b-0oVJBHn4hIA*U7w5H}Ps_9GT%Qv49}I-0HJ&Lcaj6fFD3V?H(}E&8{3(hZ1jD<)KsLV z`&E7zZYZLO`N}ng((JLIIgDE|v~EtLIZHbDS_3f9w4Tf2s@`9GUJ6?5GC>1N>p6p3 z1)>Br?J6@}0E|sKxItZa$)q2_kmPtEfE&_L_-abo4WIWv_8SOG({_jwSX3 zlA}FsT*T}=V8uc88DtJ6Z?a_UK$4EK91hCE@Qn>}Qp!S7!iO^R*JrSZIdb=N4T>~% zL3sg&WX(DnkHP2W$^%?q3NJ|*Z9-^V_U`+-w={$2@V@{eZb;puC7Y_`Qtu@ljeu)t zU3U(1Oj!i8?Ekr$QN{EEa(n1K7(e%Qs{F=b!=es-J_JLO^WMef6&J9oG=Q?zZTbwi zL?N4W_{IkNrCnD=D}2pqtZLdJa{BaXuS8a)al7f;)IimDzcU*!N~f-4gH>tQb;N~w zE&G5L(y>;1mHo^mz^dlA#iOIW)ot9c@_p#Y50|ZkdtZQ3zsomBD+%VNTIcGlAJ$JZ zNV9wcz}!GF*PL>-cp!tnRZJ`-HpnFk9Og7k%PX00IH+H^DHdp)_cs~7iV4`opMMOe z^_l@#E@*9wBgO{o!e?8iCSG4Xb$;zbrLTLHE@~Tv9uz5~@{^m_WFwnD&Hi4uBD;F$ z{u!^cYfYIvkLp1W+Ml?36brOO@=eb(C~KL5gtHqwehF+}cM=ndHT zuFq|hBeFwvlW5WoF~AHk69de|0Bb^GEU+423PL6K++)a*!-pj19Xjh5;vnuaAbj{pTG67j;X zc8||^&-(T2#m=*7SOoKk4o_}XQuI;7^JESsL0=ME4--ht^)S;g^TSLGFcSmJ05dVb zObjps%)|gQF~AHkla`9b5>3NQVhYy7?z3U&j{GkGd)FFcMR|q?T&;F@#Y&_smLDae ziGZb~DOHoUDYk)_w%%AP#KhXvmKYjbYNJF9R)cL)E_GYL_0psjR44>%TSzMP5l!2|*)i{@N7_0Hkw->a5HAH1|#{Xgv4SJdC*bEik=cHb7YO};jI zfB8K1+J`%KL?<`A8D04H-st2DE2ANu7pV6I0+C=T_~+EmL_>yM6rDF~VKj7DyCT=o z(PPznAFO^OIdN-r*KZ3+f|=O-z{{@ z0*uKJ?^SMsg*Fh-YSGuZXUx1+Z6OfsrI$CW&tm)A-}euCZ~pxc3_1=b2fc9~8-DAP zh(3Yfvv{AM;d!v-X?0m8VzmsxlGk9e+hd-t#W=T(&Wd>(#^V~2gd!P||C2;~4`fN} zPn*03{eN_MuGQ1F%EW5Ak$uR3&JEogh6$Ixj?T`G0n6%bw?;3&vL$wD_`(k#xL*}m zhhYe%p;?4oUqhnG@YJc(2CZG5e{OxW=qEqUbuRQ3?CF_P-8Gg8BS&8N99#el6TJBt zWH$O|)WR1&f3-UI#iP2^;C5yGBqm-lV2m0!e!Lp|DBBGJz)1CIUtjz=Oa>qxC!&Q9 zJs1=5*){7_)oE-*@_&2})?NNqA(kN{M^;tFm+DZjSvRiHSJUoh*DHBdM6AE~Td`)M zF-TXbYN?O9ijB1;zQOQ6%4_Wi)T1Y zLqc*e`jBkRtTAa|;`e!fIeB2ClU#Q+C!NkhtYQep4+c{&%&#zW!yN1~<}jV&S@QOpvQ`u)PL@o^RTS;oIU@&&bfo?CyGP8iHqm3t{jt zj0jaITvIjUMKCI2BXhE?8QOJXWY&9>H%T;f36{s)UQdGYIBD^#P`k^jDhJ^Q~o zOkB~mQAiIck8ds1fz^y3tC5PR2OV=Qs|OX=QooY{$>ImAVU>dw4r+#J-wdpdx-ZMO z*`K~On)}OC0?&mih~pyF25u1KtbK3JUE&8bd2?@`euK4=ZSWDHfDX;q$l3Bev0xo( za$VXAYb>~9{<3F}+Q)bRYE8UwG0;`vI~TDOj%ND5bRU=vfM}3zAvlh4EvTLqTA4T> z#tRSyiK6Z8?dpF_f=C$Eo#Hw`L=bY)q={aAk--@`LWIvi-;nOBjab1fYp{AXRd_vm zf+yCqI40KFs72zVCQI-}j!@w{(AUR(wHbsfelP=YwQz^(V!PHxvbKloGT!3HDnE=9 zOkcReVrOblGQyr~ls|6T6utZIyXrN4v$;mXnvfF+sz(X2%z@7r8`fN`q>TOK7{VCP zvl{q3FsZXSiSNo@PS;>2)D9*S>b?>t7Wz`@T-5fkb$lQBSXouXw1PW&tw;LBxZY|u zLA7Q4VEXJ>boTGMO6?IYvjl-^(Y*5mlaSW7-tBRdPp&cdWcU^bAM5lC#2N; zdMYBv0aQUQ2Vjn8{3c#!_>}&8kP5~BLkyV;#!Sz&lU4)*1Qliy+<(Z zq!yqCCe89t35(Y4fLdbP+QcXg7mWnP+#VRp;Al=eHdO_n?uQ>rR6uWd%# z^IUXZ=TUb>6|7(+hv`yZ04gKlxESy--y+HyW`4fTbrj6=FyWhCJK>xh%(m(@>Q$;5 znfQfRvIg_kY0?E;SF_@MZG}p>q9Ph}x?+a4Adx>PNw}AO`(xBmKZq-5e);;2hsF% z+cH*w%m_+l>V*(X;KfYun`rwF9IzUY%jOkXl23(LLaL zXba&Y^x1yn#tmvGr@xppUDhr4|z+vgBDnHWUdQd?W{n z2Fd7>XdL)?4IxF07t9;8RW_WMc*TTxA60SL36nM9eU=lSIpdbNEC8_Sg+D~!m@zY+ zD7rpA;*8$_An=>5b7940x$Tx-gP9~6BqImBOQPZWG4LCOUYWECvTS(Kdxlw^*Nj=6 zrNKE^X}Lv_8U2|dpxPIBZMw=jX3Utn5;C6@rN$r z1gjW}2x2A22a;8k3!>=~3;VXEs08~;ux}9N zc&=M)rKzN?v9hkud?Hy|Tu)L>LT}%M-=semSn+7m7X-QnYa|&=N`oS$(rQU8mupNL z5=<{WO^MIfSeKAm0BaKhP`T22C)l7{xW|zk?Eg;ln=~biI3a?~WVbjT=8+z4q3=eeu2v%?4(6Y&<8b z_~IJr=c13RUc--2o8^q-(Go???NoiCf`!uVNGDfP&4c@c{xFmz$LD6x zo_)qKKr*P1uCgES9G-ZrPx-dGr*|t~81`{}h%#Zs5PW#Po#Uk1x(Om?p7Y zH=fX=6NF%`4XY}m4YPH#$Iqe)x+GdwX)gp5M{`}nLPtl3HIr!;vv?lkg>nfnQmNXU#CMsgCfnZy0yycde#s{UV$o(`GRR|_3Vi0O0 z)w)%yo>J#9nT8La7Gn-=s&iCPaSpv^hCo!rre<0bYGXZB(3aZ(rgjr?j#=)hNU(Ba z2ncqXXD^zLb<6wRCx7u|t|$RWtfaTU77)9(@>jo%OQ)FvLUgXX;YJnV;3|f&%y{sj zhog-fejm*BIJvK<0*T~E^WZmYZm$KbqioEeHrq9c1d}zG5uj)ZW)xHrN{cHq5i1A@ z25Doul5pODVnKBfT(B>KN)b`d!D^$#w5|H|A*ZVdT7+Px2G_j%=Bt94S=U+3TI@_a zdVLm7Ei3fM9y17zP(oA&>@9 z#N1AaWjoq|X(gu1W|P1i9D?jXF;t8J79B+{+Sp4ki_?vea)XHs`~Nz2Y*5Md1q&7o zO2q8%?^pf1@tNf-mdCH_pMf?*B2S$no(Tk)~E30CWYHyQ{@2QdbhN$0HTF5^8bw~1mJYU~^=(!2 z#dTvQ6@pbXk^}nbx4wOcT020$ZB)f4>l({+Lv-5>)@@bu#dUAq@~R|Xwi-;Uh%Tx$ zg(T5xNgjzsEVl$Z@`+2--;=NX_SAdJ?p6Qe@6k_>Q~TJC_1x6(IBm_$wDqK1XwAKK zC!~#tkXDIw7o_9l>szA#_8*MS|Mk+SZSu9z`wQ1ZXHUB%>g?N)>HBR}8E9oCf)LmP zsa_c9Uww4+K>F0VcYZ|?4Fn6q4~FOsi7FR_28>CR+=lZ84qjS0^ zMj!dwjOe^s3)Opgt$qF?e*}YZW96!+V?%>L5DL&>{a}B;a%v-M0|up9;cHX@I~J{p z&bs{S!K4{|xj%wEx_p^xWUWgxfglthY6DF!fXSb`GTOdvo7M9fCl~`OiPAN#7PKr7 zm-tDoq57dihoa9;o^17;ZKe;%WJ$z+u}g&gx>vj~|cIq*%4=iO2fXUl0woHAs|; zMvPEqd6WsjrsJwB2Yg>lH$rJZ(`D6#V1m*CQ5ZcvbK?Ep-rh4lbL7YoMYi3$cB;A_ z04q%Cz;~a2ZoT!||84Tv1Z9wFNcJTWSidqYN{FqifRQ5yO1iPCfQJ@(+7;TFA@kEfd&ba?VdogMT-}EBalci zK}!RwUZ~^}f(ZmdFo8e_CJ+dOU;=>f-sL)4^V@e34*YJ zg>(@Kf-x9DkYEHskYEHsf)NA>Mi2xEMiBWS*t^RYM1Q&WhUia6kBUBcsw2K1e*+(G zdR#rHXU>%9oh6q?AOGQZ>i$2EZRXh~NU&1qo^fV$@K?`@-h1qc=?p9`(Kb zM)dyb+v4XOH14eE^RpI3??3QDbnsWc9=&tRHHu`PJO4DEZKCdwDVzS|w|k=lPdGLD zXv6-N#-Cp>CHm<1&qeRtes|&XK&XrVL6PnKf4L(1@TCi*L)ZSAXPck}E9DvvSz6cw z{JqX~ibNm3^mf7hZ=XFiYH0tIbO-+Jkv!7`304XpKL335nWImL|95qEreWZ`%Gg!H z9Q0x6v|t2bm|T<2K8FM&2wUX3a{ipK2!=O?Y3}&>_>=3R$&=6Fw?&X#HY`|IS6B4r zn{QfwV=uq5)%lmO?8morC6Ziz2-e-x8;w6@LUhqLr$mtBIvJ>0bMS@rwIXE!K!$|XRu z)ek=u&Ajy6&MoMg&ec>-NiMqtgSfG!KMwQhGQo9gYC6jKV((bFBAPkt+v>}Eeb;WT zVv@@&!EhA^7F~Zs)V}b#_-`M$cH!0WVt0wN&itDC+*7AarJPT4*@?lv`ju0oo0i__ z{Y_)B-+j;BUMvbo2Eiv!E+Z}owQPAqC0AyGg@oge8x>m=WS6)EE;37&E(vPS4DSWE zwC#nTuIh*$exx)0TNiC^SbS4~H-He}>T9ko_b|IJPgiuzh-2gPj)ViBBnzEfH9;^DKk&u-__FcZM-Cn8{c8TNQ_hVSK2q6WnSfQR zI%4AKz_tf~)HnS;{Qnn*4~y@+_>vj1)rN9BwEwf}pPl5Y1A+lK(Z1p`y!HbRKA>LP z<%W1EYxZd8{>2aFS1->htt-1VXl7bJc+72mu9e^b<^v%}A~~BA6bRVN=|P(g(Mxxn*<^ z0)QYbDFBP~uK&EV;67}umkWp?h|OGX+2oB}RtOeK?%o}@-xlwexqeTQW+nA=E|6wT zN?90LqmFuf8(>@cf@lLLr5+U1bbrbx?rx}*e~;yPbg_tJ`_&D zx~J9`xPr;=1uCGZivVI**>-?+YiMjxkE4157;{ofK`o%Gcr&l$vO+NU{~|VoV9oS= z!nO-8^x`s(8-I$^^8wd*!4IxdaTQ2(?1&MuTd1SGy42Ca8EQgOkI1R`roZSCK`5-V_4f8E@+D(4oFDmr+&~~n30vK{M(y?ChKm)B2R9hMk2!fP$yEw{ z25{FsEBa>7nV!w1qTUQZrmD{;vdoa0s;xhVF8O3EZ ztrV8J(!;Ar+Zfh2m6L#_!f{~@U6v=+gypj7BBm*NZPPCdO1j2-nlpEvmwZBT8AI8a z4hgkHE@uQ2@k6p5LP?Ot6kefUiY?xI)>#2QwlC^|T;2#K3mW?{A(i3# z%qP^#Un+4|mn^+mm6UAXu_J0~YKpVx)6TD+VFF^xW;2N9y4I_f$>o(`fb<~@tqdP- z+i9&6z3iFcwe@gQ_FcHFx{Wx`8wBtADbAkuYc*$&vZ z3aJN&STEfb6hOMyAzAjo;>CqME+L4BC}L7YbUn7U<_QKtz$77j%I3uoQV$NXUd=5n z1^ZZ2&pTi3he_{4cm&DMpaa#*M`~T8=oT7)t#XxHf3#ec9M~vhz_CLDKEI96nzdXd zS)L+4laHZfM=wda>gt8+{p+{1(w1sVPHdc`(92b<)`Q{8#Yrtyb5sdrL4*XUGsA-Q zL9oz}R?k(B1hX20*=ldC=M9E3yNlWJbPK(S9Mb&7q`WOLP-wHe>ODK2G2b;?xs&3$~ACoWRAOT15gYh_9O|7T(WnU*5(g;e0g=X zy=HQV_+cZl7so!Np&=M+7Gh75P{@VvM+Quw-@Sg(75Dm?4I2wK(Q$)UAe-oN3+>!` zPnCjOhqi7uVkdm@Qq$#n+KOw-ooBZYTO21gH>=;#6crXrs<40h$$T?3Ox07kl!Dy9 zcI5S!EnBAUlWxQ$Si*(E06iOVitj#%r7U--S@}L=F2v}l2*BQ{04ZR44}H3b6Xlr@u(O|35I=P5R#YhI z=?!MkNYDh+Ug=gUPUr2RYB(!~^epIv>E7Zs4KUM-hC=ufrrTeeI!?ZF^G86^0napO|M3URk_%Q6PH z?g{1!dUn1~`5cQQqy^&IE$r#(R<8-EUYhmbq9d=rGbIj_Y6`fAawEY4gzQnR>R9Q$ zm2|zvTs=KK>N^mtgx}?YgZBcL5x|5^6?gT!gK#Y|ToCRdgpyICMh0~r4XXg=551*#ExL@Mkqh5v7Bx3y zugOXbre`OZT=+8cN+1^Hrkih8@se$cy%vinLm`)xn?S)uJ8v1Uly;%k@52QTdu0F; z2K3|9w8$ZOe^~e?yWi?=T1UryYWE+?AEa9OQfxvWo*rV>MP4nJl%n9f+`Vg8bomwE zjWcYd_DigrkV!pu%J|e{=>srol3@CWUGV)YjTKd*?}h+#u+BO68|rs#76UF?Swkk4 ze964lb%h*rA%g>65We4wFPWj%5bo1c+VB{lCke~grkZyV(@18bs8Z5riUHOPIcY8+ z`I4yX%3B}AkjaHOTkm5p`&=NJkV=4@#l3s?21zWZ1QXY)+M-HHpDAVyC6~=5^4142 zbhQw1cG`PO+fbf$>8$9Deu5o!)czj(*is3NY`JUOcHXqMg28b}mC<2(ctJVP`yhrK zmxWF)TsWmS$6!G2XwsX(S`f0;CBcAdiz>001rRo*XM$f!DS=Fe!FMj|vixpizFBSvCW}!-f=V`TrEX~6+}Y|UBw5MA_MJN@aakxW?HaAlNZxaHk5;N5!0^twM4G8 zc+*vb9VYIQU=;~O&d4XZNU(|)%`yGHw(erVBFJOa>^-^M zB(~i)o8%(FG6Pl$>Js9aBu;B>Rg%4strF;)#4cPoTLUjhE)uLFVB=5{c?FVD4Ow1V zyH!)CrO%RfG4mPk84X-ak&NUb!74(r#jfv#0LhGwYOG7gy(^r=Wgyw+7yqYfl95~_ zSjBj5)26a=)6O}2veAW)q+}Oy86@Uvstiaj60B!a@85ZlI8p+fCVHqK&V{B2%GKOw?$1&O$E=hp~``@vVUc}h*L;buDeKb z)gi%-|Kb;oyH&D)5Q|~KckkO5P3~V#&8n&iqK zjOHSWWigrHiesi_$(LIa?yeXXUQb}$<}LEQpZ*axtd#AYKmN;l=4Fqvl+KrCzx-N!P3|!nbfhN0@rk1 z65cn+HE#SVHFZU=wXng9i-3ntTn3xoVAEjKM6D4DH%{kjw?a zk&$mI^Mis030Cn!N1JP3B%APMy8{9A%#TWoDoHL9tSX@I0v5Utl3~%JIf~6p`K+C? zNG=l0%0l-=GTEHAo%WQqsFLI&!D@zNl8$71ke;Mh4Q4S(E)vWt$$Sh-!{SGQrtOVl z8F*1lVI;XoFzY018GVwM@jr0mkcDr%%M(6h+ExL{MS?jaS!3f6ZzO{tVY{d@Iq-ty zBEh_f557nSY27x-^aiBphDCCbV9vz{UnG--ZyR_GSg!wCzdpC4XNjB=OedM^(LdO~ z5*N-Ebhpq}Z^SgDS!T3AkRFiebCRa0+E!UfFpng|=pPr0FX_VB%3Dl}xyqICs{u#? zt0@ENaKqLJMgn#1=~B-@FO`!|9#inS@qZ@T;1PxiW=fHhy(-rvgBXDkwI=uHN(mY! z=2n-2Oas-2(xp{Q@A|}=$@w1YVGOSzc`jgKZ7aRGA=tLCOVnoDjFKyXU_Aqi6z)-%^S|=*9 z!3vK(`K0}>?6PICWyX|K4Bx|h2SORjXgmh9L9(C!Y^`(EP2AK02Sz>@{Hj#Y{>jR|MKg#G| z-MQ2IqO8=t!=#;>j=^k_Oj4U%lPpWNhlvageOTXHZ@pF3dzX&Hd-v{DafyDRNDj~d zq(_B~F3p+F`Li3UW7<7B9Y0UG39E9N#Hih@r|w(@{+W?!$!xYNLxeZ$S`DNM^Dphq!GnNmgtlI`qU{ z?13dk0l0!t;2_K6GmRPDQqb)Q#1>2D8h9_dB`tPA1K>uBY%geUZ;#rhou4`p)dvo8 z^+F&SdI*AK#o9w>B^gdGEDILD%g-Qf+VYSUUrbSh>Q=0Un&}pFSs-fQ^b4j%Z}i8G zTVHuu{Vnpl{@tg&|4`(4rxr|iKTu?8Dm}UfG&VM}gt~Mf^)szL(zerQ@j7~D!vb>ivDUH1tn zhPdIqAr}#r6KX90i!0x!5K%-jQ~`jDaBGoOkKeJLT+i|jnqa0y1H>DZBpX+=i3}GJ zlPN=z?;kDUI$iE5nTxtS(A-TWN3sW z-@}@gN*ab6rUQ$5)}^!JzvVOFI4<2sSgqx^FCTC}vOJQR{&L)fz9K;r zOcxDIk_>$>1|ylt^rukddsx$CZlRvnw|)Dzf(-U0OP3VXRn46@e?WX>8xm`!%}`Fu ztxQ&7BNiJodt0muJ879oup$AkNoB5BvazkiT`X2CXkW@A+$>llT~(7SJAQ{Nm?nWs zsR#%tMUT?0$u=bZ6YEthJ6^1?ZskBU`G1Hts@lS{;1o2$^uXIx`!&^M2R+AHK(Az7^-_vMbGy*H+ zslE?dQ$NNM1YW-Rh9ULoNt=(;Bpx}BbGlG(zXr!=Fsw@^TILOMY6FqFKtvsIbf*TV z-N8Jr1R;R7lYgOUgh5U?v`%Be_UIjyvb5p@O2jVHQqrq-@H3|Ww zEgCHhPW2gs70qbUb=9Pm_--G#JBw>lT^m)*t)O&iy=4_49Rvb0+)KmxC-!OC&YJ;d zp*lw2o*Typ`0xazxSy2lltIW%@Yqnb+<7dJgtmfH76%_v%3hCcuD;xTRAJX>!fN1# zQ@k%30K#KbiB(?i26&o*deMm)dX93J2;iR{4P7!%*AH;M87!quQ_Yn@CPw$}rk4yb zm#ZJ%G`7h*5aFdwiwH9CVyaW8zre1{@^&-l0n0A~4nBT6Nlnm=B-}_lq0;||UKHOs zvo3d9UW>W+PIwbdLTBv9G-Mr;PbIp8MSywtvHB zOxq5KZ?@fk32}4G?oqAA*Uk5TOsK9?4^SMEt*5l@b1v6wob@oTF8{;4l(bwQU@2%D z>Fc}EH+FuTqrWWys@90Zi7EHA@%|f5J?)E^{G=&%6JcZ*8pg0kMXUi~#!8CykbFO$ zV$Dm#=COM8+dLqmtEy`90jg0J>xyZoBfX^JZ88xx9H+mH`?G3{u9{Os!8E-DLU2wL zTlb1?!^yivKD*KEU<_qeliR{IB3%TjCVjcTn$G#N4PkhnzMx>3+vjOiPRt>oj|P?T zd+vCJSl4QI_v0vEd9;J*=%gIOc~PCRmtf-H^3*zY=r{cwr?1ELbD$W6a$E?0*(asR z&hyf=M*WH^6J?7=Z~qe6+_~0K3nEuHH^N?!M~v?%S*&Ehv^y`UOP5q0jZSps1Yr*q zYjHzipI;ZA3Ph@j(so20lgEK{qMFrz>)O-9BT@)C2@_c;e`EkIH`0{FT<;2@BIWK@ zSQewi{~adK`=YQLg+=tI0~{puEqW*-Z^paI*}~L3Zqx41H%5P_GAnc&k3YDaxXYoO z*xolaz@OXSfKH5Pi@Oo6q$yRzvXlxjJv)u% zB5;uggGmP^!5h;+VZEUj1E_$-^waCb`Y%#p7LL=NE5G0ybyz`jeM0K%J*`H$5(H$< zj?EUEwm~0VvWDY%Qi*6&(|sd&=8HLN_U}5N<%ZD>jZ_z?KrCTuqnE{AgcwF1mt96D zea3gU%MQK7_WC5W0BmYO6-5}YAn`!Ds6)Glq4d`CK`QPRoHmVCkJ<7)*KD-c%HyIh zu2-dYTr;?Z>}R~Ue9nDoaWHaU`~PuhCYpiTuz~D5&|0CQCAq}OsgaNQql{?;#R^{@zH!bdqYEk=)7lW2H)4ITCsxqlXo`Ow_e;Z*xCpjWCs2Z1 z)oCmIX^#l2$$&;N8GF9#k5p{F-9vCcFNI_4?+%|IGm#kNhm3oI@2#td%}#$UF=auH1LcK6R#mbzwtbD!&WHTD3A)~OD;!G7Oz-eh$`!O2f2;m2+` zs^_PSj`?*GT5F>737~x&$yb1ry{uh-gqbG4^(YF6i-T>5zJu#Ljv~{~;IqySSd!-7iWgEOuFwzhCkTv$M^)do?XhNX3(zKlq&= zzkJSmjPd?p#4~}_Mf~5_GPvzD)zoZK@F>6X*=OdAD1wg|@6`}G9lzh_>Eccl7U%yE z-wZ$m8#^p)No3h7Pn`;lp(wf0!@Mj`_r2937>!9 zofaM~LhW?3r$<5d>Z+CMo{iQn*Q8$g5DO3?-o53C;zXaopykZQjanC3 zfsoJHV#gmq;EI8E_FD$)ZRoNjjBr!(h%2zbRkdG~!m!d=`IvBWVKs{pp&dANy#f;QEcaNSejy zUE|%oNG_Aq+iGmVHrmWCxqjs6`_4d|UcO$W9;t8&nF>6Q6t?Ej|LNR+@`?ZU_$6J$u|MkKZr!ACV-1U1cv^A3QA)&9&T4?vXZe z%O+d*;t^MsnRE9K(Unx(M{>hB-c{M+)hp5k*V*Bt8Z;*=Zbbms!OTo4t%I?l;)M!y z>T#`N@+M4g^`Bya@WN4N=J&|2m)^bN;jwXh67>0b^WM!~Yo*llDBI=arE%8ab~fuU z(`91e<;fdH?&zGk^0+QciAfuT%POTN1OIa%gUA$wEA^j^LMQwkI!5aMY-kY(q|j-3 z>5Ow)&s)|5i-d@;eBg)RSLIJ%t?G4o8oel;jUNOCN>nz}-)-%oHZ65Emvs+NSF~{L zJ)K5F-`%a>)OQ)>l)F!yH@BsnE=qil^n5%)d11W!{dlwtdwHD9+Hj{xh#l7qHOMjv$pC%P(MNn-ft(k6$yl zCQav5$fX0-11)>e&Gss1L+q!*BUmHHsX5C8SC7lY*CVfKgODMP;?S3zH(?`$LVfM_ z6@3!EzTRgGd(=2@V4H*M6JVm2xo8R((Bx&JX<*FrDS6Y{f~^6qVe1-k34ObBd&|oa zfoSHpYmqIfr&9ROk8mUgeO-`mE)@u_ewY=_1W)x20yvRN=2(#eiW{FdXB+M)Hj^fr zdAl}W6kCIE^PTs#O1b1p92wI~ydGi1E8eGQhXKV%rzoc%GdE?zuOX$rsee}|OnXb` zCb?SbAu;alM32`^^=hr+E|+)GQnv>lr1tUvA$X4@DGn|Es$vRexyS95q>+0I0qBRy zdtG;bN_6C@vH1E|69-ilwl-GXfLopYgcdYAKhUwn!b2bxI&4z$;EzEYq)_Dd3qJ%` zD)ckmf9@rOBR1GAHTv$d2-mmk`r`+4navHaw#$>?6T{HL%Ml|xEN$_-SY*g&i1&82 zaZl|8GQ0NU2g?ivvCK*dZ?CKtV!B_Cf5UzT#?eo_wtpU1?P~Xi@zy)EU0uGWl@Sa+ zy76rtd*8ml>C~lKYiVXr9bx1DB|Dl@l=GX*ZC<>(UV|;R#c1mkr2@QlE0#{QhUjzs zQ61xj-w4MLVQ@)C;B`zT=JV6_+{#*%Cwbt{Y#%}B3yeMB2oCT8H9JHu`4DJ>V*8S$ zF#jHHabX|oRNCB6_q=i0-08U4PWTAz0P%J5D)VU;8y;J{L(VjFL!f3)_uel4fI@5L zWUk$rZ~NUE{0@`b4{YiUDhKZ-+8H#v`x;m#jga68HYe0LlAsQPedasYk<)@Z zKOsRWwttfl!2niQky3y%7z?-7*k!bw{{H#V0*4|XGS+Dti{x7X`8w!jMECV8n6SeM zMPm*-HIUSH^(vsnYF?+?IT#ne!dbnJrT1Oq`lMz^Z+l~LF_R`*TtWyr*k9-Av~1Z$ z$oDpRy_;vx+(zP~xOFUn-G^2p&eT&W7c?AFe3cxZKu-g7b&(AXfTR%6_$LCeFC9wYX|=ny$3Rn*aYJd*x%gl zm$H?oV9;(TrTEQUl{;+;e7|U1{`+_%dg40aYxg+k?1Mb3$rijXk_KW0jYXLHHg(Rg zh0KXVu6b!0^cjn+z{WD=9QuwrcS3ai{=^|wZoIRvj}8x)hr$Fzz`L$OXO`IGXnI_= z-m@NQxOSK+v(RA%<9yRp&p5aI=B7qr_a&zR%01L?h9ei^NcOUru0u6`?kM_WPJbFQObuliep`J%AY);BNt0Foff*wO z8BWVg3PvOm+%1AD%R>q7O4%zNg;4A7F^Pl`LL7WNYdqVh^RCjdFN{$TQnyEqxFsr6 z7ha)r*{^2O8$+bw;`oBH#LHj=B|mC!5v61gsH)=1=K%z-HTDwH4TO z4oB3wJBBd@_N%SA_G%F4TZoYzTLnwmnCjpBt=<0;lb@ccWldX0}>d zTG92k+5{aa_G^pmOigPT?2E))?_9Rej3*VDLwqmAU%sA{yqw&0tzW8q{ucF_;BG4Y zK^q1~vZu*XkO2vS%_!a~XnxB>tgWc0F` zXv{qn#8=QA8XN3m2}kZd2MC8m?4Yjg{NBC&GuoQ8l7ile0`kzb#Uj!XmxxpCp%_yY zJ|`{0NUGM2_T?+nB=H)mu>l`FA5x@j*=p@+toJ!3Gjo-1UJUE@soWjf25}p!4G#p@ z!dIHWI(PFj;gWxtWF5TOjAv%rR$M4tI;Ggm3MDe{0&5z7Qq`av|z5*9z3QOmxuf>-?re zTyj4Ne~K1%vB#jEn^m*D7U`m3lB2|~vQo~y=xS27XGkW7CH5g0bTc9lP-#CQmPL}5 zF=ExmW}5>Tau8nK=7)XLPUv~^4o^0?oGbHU2XCK!5uS|?Q(CSzlX?Y3PeZ@wZ+%<} z=qtgUx0pQ#d<$_A1|v}5SDerjs)u5IhFdni9?`#Uu~Q`3?Kc*3SheeFy9Io$wXm26 zjaLs7$4`848mnVXLm!gZF9+Rg6W$$Z`TP=aU)xhO(|K-P9m zdn9I0otQEaN0Ecl+|r}j%vw8WBz`w_<%zc+K$pa`Op zgoY~&44wOfsXu>Qzg|AMD9)4UO97NzOkn1}F1r3sONC;Vi+^I}_5 zy?9g9stqDG;15{!)8;9ss;>5#PkmsrAzj4*O#7=O@4z1fvGd@SF6ess6Sc<=l7a z@V%}tj^J#b_x^g3XsvqM(b1t;w`=r#{p;+VQ{>Cm*a~6wS|-k4F4)-f%VtUk>*4*d z_w-$(ucsoj7e)oOkAuDYa{ULi!pA@6bg$#mikg@pqRz=>?d~(!de*|)>cj=qeW^1t z2TIj7J8TeJUSc_iW3k8Lc&yJhHK`Fhz-mLeKWRHabAv06@Hp=Q%3>L7+KqTWMvvT! z@)`&lziXdJ9`JAa!_3LMxUz)n^q_nEBK9Vtf>_6*u<#N&>J+}B_RrJAsc88=3MZFU{W+k3cNre3afSy}A&cWKH7D(yU3#GWr8b1IF}Mw#Jmx ztH>wqFwR@gc?W-ae>=1HeB;hnu*f)hYoecFcmac!D5tONTCPQQvCqvyLtap}w_Ko+g zrI#H7KGE7f=h|wm`L}XHY}Ss9k5wSEbUC69S0$6Y8NA<}EqsjN(+(gZ6*h>CkJp%{ zA6bre4Rxc(@U`w`U)&7ZD@nww54u+c;4K1JQ1`7mI9|Wf_ZaFsakr^Vm(z}QG{?~@ zn2;{|k>;*_$FX@C`K704@(Lof`S;B`pWnr5n0+I&jP8HulXP4iqx7<*Wk$Jv_L+?; zsh+nboxxa2KOt&Y%On^-al6cXv5hh^zWs=X!{(hAu_-&j3DAQ8Iz|)L3VFxtlH>%@7O&#HyZ>RC zWT{0H8WEcJHD1%EMS_hx_ zxbgfE`nX2Sl2O5y__g}BZKKUfrb8BOiJCfH-SYY@Zz-4!r9V>Qi?2Q`&9*f^R?O_> z2#MSXZL}=m%SPyKKQdj!lE>VyDURkuz9wFuCc>|hfohVR#X~Fb4Y(4P020q0X-dNN z9EQ*~$Z%UdAI}JygCF*^Ou&OS#~vkK1jOx&^$upqMvFD)foZuuF3ZvIXj@U1qp~i) zNoLunZY|P6rq_5Wg8}#MEtwq1bYXc*J_YEp`zTOnp7h}Bd0?G&o#=<&(!v-S)%USGOTodP_Oc+*Du7k zc{sB`9=$6Up9hFu!9(=B#Q>4TxUo!XlM=Ar7ZOhuZ&d1Iyz?rq z)&(D2>EvMg7gkzYHtciqRR&sfwG`Sqs%l4-nj})Q;Hgp0>v9WpL*Wz;kVxd4d9F6W z)!;O|U5~)uGsT}!1nJW$9TKE@H&QY~n>6A*0$>8DPYCJtp&-7WWsZ=L@+M=vPfSDl zkx75FbQPd7KTrT_6psj8L!6==R9o2r$_k56y@;iK9=l<;#q(L%$2V^%xO z!Kv&`0idEmjLJ5`$Rt7}v9n>f_AsRq7Lyw(FIMXT?@cdRrolq*Z%6-ny9LzF=$Ty4RtYr4C3r{*Z6E^zkvsc zq%AeM{%uDmcaaZdoQANMaDLLniGbqrp(2Zo1+Wp`*Q^1a z$Tum{&p#x``Xy1iOjWDxPAtOXtzjN8IqGK@F8sFz!*}?c67zK7AW-W7+bxp4c0RLz)*Z-2*+%*m%8YA zFA>6JhJORv9*HE?kKnB()%%?LgCglqP;}rsQ82SLM{ilxK)4X=E_DYDTy6#=(4@ZRPw)#(o6PKia)rlZ}muxD=>tVk1NR8PjD+ujTLWPVdqZV=>tQaX zXJD{$NVadg-=?rZmfBSQD5Ke}Pf7vGQ6#IiTqD^9A{1p}@%}xQlAMRqd^=h7Bx)f@ zCyP{0;ZQx!b>L9-yHIiFQG&)t&kRKdx}WXzKv`OIsUw_kYvA7WF->; zAkC&?Q<)ua{pg|FLbjNkkP@aZ8wizcF5lRh>??UT-@)T-dW|Vz5lTE)6ubb-mqGDl z`k5GH0@z$0pWAsA6XdHpFe7QUxp&K0`)s@nO=d7;CFZ8q^UaIS(u(GC$HbEf>q2nE z?8hhr8EN+|67St(?uPjOE*Ajv>9gm832-bbOGQ~Wl<1)7V;X(*-PZ?m%w`(>Btb`F zBKM<0lJu1t3Z2ep(Os&k-3yEuOu@?&tLO*Vkn|9XgOZ$?@9_4n%;!<>+=<8w;CC=VQX4 z#r=-G$`AxAM;LdKX7RbLYWSU6%`k)h4k~eO6GjRghiK^+-!Dusov?Iyk@XCWYWZf{ zl1(bzt(}LsYkDH9*6A!z+zLC9R{#E-4=C?TYeTS8&F6Mp8uA@4Q73efGD%7!Jk~SU zWlwN)Yy1Y9#50x8OA`2zEmEjD5eYJRl$*`(v*DK8_^zJ}3|#&r*&2X6S{3?^oTQ~T zON+q9sT9fEDl5h)-Vm@sqE8wxD#t&3R%%!-FH`$%^T z8=Xzq-dUMT{1!nY>eou4oIwCqZB!}WHx}OC0j_`m9#w;CM*RbS7n{XD)+am(<6d)X z^VOz31JB#Pdk12G%k6xIj*lP8S+*O=2yB>}i_lxFRjl&z!L+*0{78Nrf4)>qk_p+)r%qq}W!XF$}l+&sl)-xj<;bVCvZO0EwnR~f# zJ&XxCan+2+_TrnuqxxpGkCXwMZ}^EqC5;u@X^>_e7Ck?@V-Ray)+TW$X_#x>RZQ}Hp-k%yHtJ=*r1lwf<(k+pVUsop5E8EMT0k9JzL~=6*5Z#Ht8HkQ zeZxJtN)sOVU#@|6Udauwkx2^e94|#pDjilU>xh@GgCXo)RPYulDVtn&j%p=cQBVOG zQJDfbXWzH4Vvu$RqZ_$DVOO++T&wo_VU6v}>oyrvTma1avBz$>#oAzYYjXbpU^?ZY1M;DDCKYU1;PgAifob{j+8#+qDfLu&OC#6 zh&Vg#01@mU_%PLSl0B=i+)G<=AJqbegt z+BN5q!c!mrU+HvCz)Plb3%VsCxpFG-PRoHUE4^I+bjT+PY5W=^4DH*DQwhkn5#|KZjNT*5{}E^H+S~jNJTtdDHZe3ga6+`dTGCke=lb z8d&9d(U|O(ybSX;rQVmH1G3MAa7m-P|Nivy>4H&Bfld^865*Ci@l%3VCJL9z!p^aB-C4K(N_8S zL>40j7oQsgBz2}tcf8WVd`-CReM>i^7QSPc`0r*bs$}XHF&|D>uVhyJZXS`V{y=^#YGa0*=~5jJU3Vg zR3!yJ_({6Vqy?b-uOa5zgO9l(O1QjiJC*lnQepTog$we=2fY zQ$l$?()1z1r+S2L$HwC`pX`sh|;neD;*H#ciH3>oGxAb zKyR{e2I579X)e0a)1lsNOe9ofm)+&~Qo6}d1!?KPvR$0UHK(6tQPwn4a76oUWXIol zW7rMlcMh%s`53B`Wx@P4p{qYcx8T!5Hf$`aOl6R@IvplNfjMXlkYIw48hd~L8PqtK z&)5{dt#oYjIqxak%YN|J&*1O=*43Q2DeO#Y=r5~qNR>UWcnlL}J*$=YJ_^=7@I-GX z82ahj_o$ppC@6-#sJjJ}3CwZgR$O;ulB9I%au&Pi)Su6^6uWWolAoPhX75Ja$~R5U z?Cnny3~cn#wgz)9kj^)Pi&Zf=lGO_)uy@C@5?)7?6~G3fPA%3^r?Eo+sPIxAAG@Bd zcg^o{bfQFyAC_oW{@(46K84qv=(Tr5LFvZ53k-%0H`rBInr@ICQ5l`;S39kBJ+K>M z)h1IC*aViAmY&__HIFvSQ)7(3e?h#RCo7iQxYKh1f{a|* zaIOZ0_*C=zvq4kfrYA2aXF7ZzH%e?XYy@jLOq?l6wX*4yxFy!C-7nz-s{l5%XeR zt#20Qnf`*aj&Xi}_Y&Z&i6v;M`E6i&diwm{SSjM#n1cLOgt)5oy3qK={W1}pwV@D? zcEQsyt)r0?`(oUcfF6f{pbUS0oR*Xjj%bP?`*A_Tq&^Hbs;X;ha|VE)3X&b^;u~8} zXu8W`7Fs+&xs^x?FuBY<_G?N=ZRa3fPxn9rsufr#6KQw%GPnY`UHc#tjsn1zO5V0( zN70@#-A_^DQp%ObR~Dx=+oh}VORkrAR$pic@rWTZHlA;?5R#x>!D*ha`XU|j@^PiS z5h_4Y)zusiUy*4kkDiazDke0W<$N zt!1h>4!+W%OH($J13-CT@&HF)21bMw5MLvMy&kJRmk*hYKAkIT_Xaz5gvk-hb~KM< zC{o_=t%1HKb2c~4e2cr0nzIYzx}MI5sIk1C*iOl|6i!4W1CivLJ=+Lp+T0s_;1T>h zjCeBl1M>~o#gidjPf=m~GL+YTDb>w94l1)RKUbd8mo%-YSQM6m(NZ4h)b6ZC&kL*t zntmhqI-1jmIH?Eu%(r0>-A=JlF$LWC$_4|ColLsMAXI8ll*>~!nFV6srFOw*aM)sl z#@{2wF{JhG`^!K(Z0xSEKva~M$8{wGe>%du?;!Ouw(){jP)m-%ele`yKaHsQPuj#P zTT(En=0doB_~gC`_yMcIEz?6xjzjoU$q>g+t9;l%7+6wv7Vl(=qs49A&YhrPARB7( zjeJowrFwa>G&`SRr@N%Y=1Q@GGpCP;^FDyj);sc%Ct`fG6jKYG78Al?64$HyV`}&o z9khr@Awrh4wNmz8aIB&!u9NTiP0WvSlp4I3nZ}1G@ z`YzbgzD?$M&yT@-9^iQj`fHD?x|7@7 z97Y%HRgg@yQ-{kPQRNcF8-j!RLOr1=ul{ymQ7eWA0~k^*3v?Dx;8oGc*3PU4o_wV) z8r-T8D`Jp~Dn@uct}9By^ol|Vz!0d0h>VvSLB_NBZm-q@ubEH&4Gt1czOb5?1i||t z_K13zqr=|F?M3z0@Vun*+Y9L81)bw@EV*#Oy>4_dQx%i#Eab)W=}>55udc&7?em|{ zwltqV8eEib^eWpB$ah?{)x-8rL?Twyg#%BOO{!GmyHu@`sLp8SdogdbRe4oHM`i7j z-x4{riw!N*I$%*}D9A?4QN62@xVf)0z2+(q5*70I;*MtD3Sy+zD@uS4lRl2s2P*I^ zglad*KaPs*dfpox?owS~owv2;YS(;I?0iij^R!u_h*xt0AX#2v%n{K{E{*RSSq{o1 zXooFXTzbQ6GG7a?t=!HxUkg`)7FhMe-&WgUO1S^RH#Q)v(yz{CsjwaJ`;n+UJ5F5d z71uDOn~?n1wrvnPd!J=nGu7zOQH7{n^fOnXQ{-LfgDORW8Fnur--!bFk^xgkyjpwS zTkIQJv1%4kc#1@c+qQm{f7~paPZ2t*1$j_)tCx-$qx6atrZbPw20Wz=85o;ox~EKi zr6=IaMB|Hykl(^mc?15`j3uJ>XpKvfzS?0+UY$6h^j=RQfji$?bw$#4ReIarxY9{nP?@l7z zQ#09=rHO>sRzmEQs`ZcKm3KN@k}pT7{gWxXsZ}$exlu`I&Ps=Oz6q4=D{H%U@C@HRI~p%w>E%QZHo6wKo^qZdi=_-JQPFg}i~ig)Ap+^4F??*mikuSh=)`ey-n3nvQqh0h1AE-)KVSIK^tiN8`^HUv~ z&uyK`yR&5Pw{w>wDqkUL1%BBK?}tBhjC+lQGZsgkl;0ha0bIRkwAo_!diMBLFP&Xg zaV#tGoko7I;#h2vAfUwUA+2;h^|v?>AYuc2)DZzz zZ=#Jm*I$1CFaRoOe`XWu^Ed_Qr5My%Rku`(rkgz0n3+*C#kw`FPTK^00Uuc~()(o& z_@q^oBw-RRUDf>hb?~=1$ngcKEL|@0U9I&)d4Tb7*Sdyo50sE~j=nJ;t}?6nfa;lEmZS$_s6}t4>n$qHRF4kmT9M4u z<55kZp_Lg%!J0nN=8xz!qGm5NwsBAL3TG$DM4_#HI-Kk1&~1Rrz}RuD-Ps?GOy8== z(mYP{?BG}21OeC)@TQVE>?l3|>CPAmSIVuVY^{-|8fX<+YxE)mx)jB3e1_!~nIz$< z7w-q6R<`${#Ed&o@G-ZIJP#5NF#M`pV~%N~CI88r$m_r?mYYo)#O5h1bH7UCTzA zU}Hi80qE*-Yz_3aYeDhX*!mItiQvi&qw1hC63SSPtEF~_`)L^d(#vpzfbGMASL^RZ z8NRnWXw=i_@s9foMQguM$Q+r9B=V3U3P|k9I2+SYLTi4VQnkrG{0f$BbElpf$w=iU zoFZIa`~vp8mdbtc(Y6-%Qz}X<91&6FPF{5TK6YkREQ|4UgDbVkBKSdGrvkr=6V0fV z7|p)gLKO#Ps^4vf(l0-iOzkx6%z94tV;S6!orgv*2i!*$}!&uvOUjrrO8a8qW}Z zvjFbkVz-r78Fgtw_I;$a7ASC8P(bkI9vbr-vczv2!_57klS08u@n#$Koo}_mi2H&r zB{+>EFf`?n@_@C+f15dSj{XV^-VNTwo%Tl!WRddSt1NnKFlu)~HvPw16JjH+Dx`ui zBO(yo25ydj*#!*p+$=RPv;oUeitnN%v{F93HLV%FkFo3v0h4`1@!A>1v!Cu2CKe*% z!7v+3&iHPzY5#1A2VmM4>wxnMb4e?{QO@ReSzDo0m3-oxD0Bnhm4X=PfEaPq{tnL? zX=BAG&pMxq)!0avr!DMJFaB_lX)$=eu|5~yvx)BaMY+?xy{yk&Ayx zcE;5(%Gc%ESyJ6ZG}PyJUr!bZ#SLMDruNgwk+SOQr0|IP&KQ4eQC_v$yXao;ZQb1iP2?` z;~J|OwjzGTXN=UB0#|vxh;bRhVh(?3+Yq9*xvmVY9bPgWWh%-actA8ix5ov1D{i3@ z9=~9a>tdT(WWZqc*SG3C-5bA?KV-(#2_TLQBTF$*2ju+IeFn|JtNJGc zUx96)+kWv5PBI*M`@)7gI>gTsF4+D4ZhbHLoNNTX)In20$!-!5;WdL{k{`y-9JHyx z2*qQ%xJ7Oc7wR-UJmtL1Zmr?t1eUkr{#ES;NHf2G6oCXd2b-TdZXi-$A^mdr@RK6#JabLq* z+hSt`bh}`WukJfozA(U}Xw;WmYpnE8-B@d}ll;0&-_`xI^X7rMMi0uyToh#)=AVKk zV7I1Im~4V>trX5rd1NHF0GcwXFx>VgS=8h(KO4_gRV(aWY=A_@w(t7ZUG=}kygbo94H2`_cccV9-@g)*O-w@Qy@FP!Nc zendIF{SM1=&!jch<`)pQlNLCyP0$YZdI*8$&xuSsou##it6jY?XqXo7*dkn#D>A9JK1TeQ)U_~IwEwU`rC$X> zaYTR%JUdihJn^es0*}ipjaGgQ1tJ6V5Vc2L*bkq39^ia^C&d>yaigPlwFU=W@O@Vp zR6CUx8kghis0c{o z3uUCtCl~+H;Wk`C8e^YtHRN^TW0F>0mj((t&%{w%E9e9VW_-F_NamswHwA9e+aFYg^9UeL7C0uMAdlxKGbJD)Hcvc*= zH3qn&#-RVx)329c2q9BLpd9<^23eO610B9RkI}_Xr>N9+zKri6Yinrokw?uL?K;2Q z(*=UhQw-w2ZG^}7t_p$hc`*eVj+v+UbOl5)fF6Fhgj~igMmXX=J|8EKmVO3J+;3j3 z&J7^vv|bqgb2MEr?>;vxxpm*b+o zSR9S~%(q{E#?s!PBNXP*$+^OUaffy*_1?&WZ}*=T&YEHjMgLG0%U|=TwN~|3wO6&` z)*)%0U`2Z$cIEC>Yjq7U&KJuh#ixs&z94^;xGo%jOB=+3@XaRl&8Z5g1Sfg*FQClS z#~FU1k(e&oq8AyKD85ZEbHbJ0k1bJMo_j_IhA^|+?Dfize;tW{Jd6c8@o!GbU7ovE z;06Zvl2M%l?lMeq>*b7F?-1|v$#kdR&rgg?$=MIWaED4A3TGFN?>YG`bWTxPx6S7+ zR+6kz&t znUe9+&O9&iU1hS?SAtppJ+aSuY|)=b_n56U$GFJ%61RQBHJ#|z%q4+hydx-@MaZ;4 zR#8!LJ{%hTY9noh^D>r7`;rA?J4b4ZUK;K1~qrjvu;N2PS_X{rzyt48xcFQagJCC?e*NM~Es63r7VTVpeM-xomG-a04JmZI2}NdXX9Gz; zFF8bAXd`L{9KugLd{S=p16h~m{cHeGJPb82HJ(m_*n)6*{;swStiXs!CmG515jhap zX;a%jD1+f%2+F>|Cz4;XXcouNo$#Fi!~fa!g6f6)`pJYP7>8Ihe_q1dAL#(_{nhF$jFjM?_Tlg zGR5-uf#Z!ea-ru%jGl|4+otZVWtMzVhm*|AiMA%skBbvtdZ+E^fTdI5(@m69`o{kEK=jfrMI802=nwDwpkg_BnyK(XJf2Z>e^27+({0KX#@=LjQPmDen8YMcs2tCl2%xm)Fs1f^|v1 zsaHw+Bs#TnN!q)Zd&ZL^&;485Mf;y&%wQcJ9gp0TWA^t>h*+gwU;nnioRGmAO&>-( zZF~+ngR?5aIq)@7d$g?PUn(k2C|^VDjwXb}j!vq{el1R1j-+$c*8QhD)QnAd;c^YV zwgz~1f?m7=(+Vz)l}ZFw^5Jtf5r~nbQ7=Dza5mCc5$q;So8(l$%-|v0*OZrYS zlLt?6soTM~Grp}m!?jr4sYB0Uht0*7jy6vA*Wt#?{L2?LSqN$*PRJH*-MSjM-QsAS z{XubHsj~3&dfh~&Nt2$}GM;XCHCQGYnc&0`s1V^DVGv=zIn9rMn*Q$dJ9#%AII;9l zZ#i%aztu*oiU$clEUz8?Q&6)w{{KI}nomy`Bwqc=sQ7b@p~Cai_4(7_tJtknTLmH; z@2IVjV>uk(t;KR};)WF9;@eAW4NG0$%&Yo!bHTD@%R-qp=BjUK3%$JknC-dkdaj~< zw!6#Q9i^{cy}I$e>Wm{zme0=YR89X~nfra7ZS1Y(Ee7GhR0Lc{7Q!_nrUSUuEvOTm zjY-VQ9KfM=pk^?ER1P2&29R<9eTySxcO!UP+OFAgRxC~(YYMMU4QDYmH9gpxeD3nW p37`!1(&-U!_XUdiF0P;dGoDy??9Dkr|EUZ>;OXk;vd$@?2>?|yz8L@j literal 0 HcmV?d00001 diff --git a/src/NadekoBot/project.json b/src/NadekoBot/project.json index 88ebdd52..d6b8ad6c 100644 --- a/src/NadekoBot/project.json +++ b/src/NadekoBot/project.json @@ -23,7 +23,12 @@ "Google.Apis.Urlshortener.v1": "1.19.0.138", "Google.Apis.YouTube.v3": "1.20.0.701", "Google.Apis.Customsearch.v1": "1.20.0.466", - "ImageSharp": "1.0.0-alpha-000079", + "ImageSharp": "1.0.0-alpha2-00090", + "ImageSharp.Processing": "1.0.0-alpha2-00078", + "ImageSharp.Formats.Png": "1.0.0-alpha2-00086", + "ImageSharp.Drawing": "1.0.0-alpha2-00090", + "ImageSharp.Drawing.Paths": "1.0.0-alpha2-00040", + //"ImageSharp.Formats.Jpeg": "1.0.0-alpha2-00090", "Microsoft.EntityFrameworkCore": "1.1.0", "Microsoft.EntityFrameworkCore.Design": "1.1.0", "Microsoft.EntityFrameworkCore.Sqlite": "1.1.0", From ed656f503bb9b0211c033298e2494c11e322030a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 01:00:53 +0100 Subject: [PATCH 180/256] ~clr fixed --- src/NadekoBot/Modules/Searches/Searches.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index ef47301c..096a171c 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -21,6 +21,7 @@ using System.Xml; using Configuration = AngleSharp.Configuration; using NadekoBot.Attributes; using Discord.Commands; +using ImageSharp.Processing.Processors; namespace NadekoBot.Modules.Searches { @@ -622,7 +623,7 @@ namespace NadekoBot.Modules.Searches return; var img = new ImageSharp.Image(50, 50); - //img.FillPolygon(new ImageSharp, new ImageSharp.Color(color)); + img.ApplyProcessor(new BackgroundColorProcessor(ImageSharp.Color.FromHex(color)), img.Bounds); await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png").ConfigureAwait(false); ; } From cfc00b5df6864636cc9f806f3328ed0b145a62ba Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 03:04:22 +0100 Subject: [PATCH 181/256] Fixed $roll X response, $waifuinfo now shows up to 40 random wives, not all --- .../Modules/Gambling/Commands/DiceRollCommand.cs | 4 ++-- .../Modules/Gambling/Commands/WaifuClaimCommands.cs | 4 +++- src/NadekoBot/Resources/ResponseStrings.Designer.cs | 11 ++++++++++- src/NadekoBot/Resources/ResponseStrings.resx | 5 ++++- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs index 721b5b4e..8a0323b9 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/DiceRollCommand.cs @@ -124,9 +124,9 @@ namespace NadekoBot.Modules.Gambling bitmap.Save(ms); ms.Position = 0; await Context.Channel.SendFileAsync(ms, "dice.png", - Context.User.Mention + + Context.User.Mention + " " + GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) + - " " + GetText("Total: {1} Average: {2}", + " " + GetText("total_average", Format.Bold(values.Sum().ToString()), Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 212fd1d2..6847dcfc 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -423,6 +423,8 @@ namespace NadekoBot.Modules.Gambling var claimInfo = GetClaimTitle(target.Id); var affInfo = GetAffinityTitle(target.Id); + var rng = new NadekoRandom(); + var nobody = GetText("nobody"); var embed = new EmbedBuilder() .WithOkColor() @@ -432,7 +434,7 @@ namespace NadekoBot.Modules.Gambling .AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true)) .AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.Select(x => x.Waifu))).WithIsInline(true)); + .AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.OrderBy(x => rng.Next()).Take(40).Select(x => x.Waifu))).WithIsInline(true)); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index e2d01a97..57744c5b 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2244,7 +2244,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Dice rolled: {1}. + /// Looks up a localized string similar to Dice rolled: {0}. /// public static string gambling_dice_rolled_num { get { @@ -2577,6 +2577,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Total: {0} Average: {1}. + /// + public static string gambling_total_average { + get { + return ResourceManager.GetString("gambling_total_average", resourceCulture); + } + } + /// /// Looks up a localized string similar to your affinity is already set to that waifu or you're trying to remove your affinity while not having one.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index c1eb0a5d..5e1f21f7 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1081,7 +1081,7 @@ Don't forget to leave your discord name or id in the message. Someone rolled 35 - Dice rolled: {1} + Dice rolled: {0} Dice Rolled: 5 @@ -1218,4 +1218,7 @@ Don't forget to leave your discord name or id in the message. Submissions Closed + + Total: {0} Average: {1} + \ No newline at end of file From 43ad7120f56564c6023e07af90168ffa930fac11 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 03:06:13 +0100 Subject: [PATCH 182/256] $divorce response string fixed --- src/NadekoBot/Resources/ResponseStrings.Designer.cs | 4 ++-- src/NadekoBot/Resources/ResponseStrings.resx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 57744c5b..dbf21639 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2655,9 +2655,9 @@ namespace NadekoBot.Resources { /// /// Looks up a localized string similar to You have divorced a waifu who doesn't like you. You received {0} back.. /// - public static string gambling_waifu_divorced_not_like { + public static string gambling_waifu_divorced_notlike { get { - return ResourceManager.GetString("gambling_waifu_divorced_not_like", resourceCulture); + return ResourceManager.GetString("gambling_waifu_divorced_notlike", resourceCulture); } } diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 5e1f21f7..86ecb97b 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1142,7 +1142,7 @@ Don't forget to leave your discord name or id in the message. You have divorced a waifu who likes you. You heartless monster. {0} received {1} as a compensation. - + You have divorced a waifu who doesn't like you. You received {0} back. From 14c0b44f03a68bc0861e4ba31f3f69cc97659977 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 03:27:30 +0100 Subject: [PATCH 183/256] fixed message deleted and message updated with image uploads or embeds --- .../Modules/Administration/Commands/LogCommand.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 88d512e7..b6b4967b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -705,8 +705,8 @@ namespace NadekoBot.Modules.Administration var embed = new EmbedBuilder() .WithOkColor() .WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name)) - .WithDescription($"{msg.Author}") - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .WithDescription(msg.Author.ToString()) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(string.IsNullOrWhiteSpace(msg.Content) ? "-" : msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); if (msg.Attachments.Any()) @@ -714,8 +714,9 @@ namespace NadekoBot.Modules.Administration await logChannel.EmbedAsync(embed).ConfigureAwait(false); } - catch + catch (Exception ex) { + _log.Warn(ex); // ignored } } @@ -753,8 +754,8 @@ namespace NadekoBot.Modules.Administration .WithOkColor() .WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name)) .WithDescription(after.Author.ToString()) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) - .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(string.IsNullOrWhiteSpace(before.Content) ? "-" : before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) + .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); From 0af1a711e2ce7750ac1946dc96809a9e78b945aa Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 03:50:24 +0100 Subject: [PATCH 184/256] normal links in log attachment list --- src/NadekoBot/Modules/Administration/Commands/LogCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index b6b4967b..039e8f74 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -710,7 +710,7 @@ namespace NadekoBot.Modules.Administration .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) .WithFooter(efb => efb.WithText(currentTime)); if (msg.Attachments.Any()) - embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false)); + embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.Url))).WithIsInline(false)); await logChannel.EmbedAsync(embed).ConfigureAwait(false); } From 126d5d61d77349088118abb72355a67714a6bbe4 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 14:05:51 +0100 Subject: [PATCH 185/256] Fixed acrophobia responses --- src/NadekoBot/Modules/Games/Commands/Acropobia.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index aa11f1c6..fc2b6f8c 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -301,7 +301,8 @@ $@"-- private string GetText(string key, params object[] replacements) => NadekoTopLevelModule.GetTextStatic(key, NadekoBot.Localization.GetCultureInfo(_channel.Guild), - typeof(Games).Name.ToLowerInvariant()); + typeof(Games).Name.ToLowerInvariant(), + replacements); } } } \ No newline at end of file From 4e51918c0830b979a6a48040c79beee9a747c010 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 15:28:59 +0100 Subject: [PATCH 186/256] Fixed .donators string --- src/NadekoBot/Resources/ResponseStrings.Designer.cs | 2 +- src/NadekoBot/Resources/ResponseStrings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index dbf21639..d49f4610 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -349,7 +349,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Thanks to the people listed below for making this project hjappen!. + /// Looks up a localized string similar to Thanks to the people listed below for making this project happen!. /// public static string administration_donators { get { diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 86ecb97b..7cf55c8f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -397,7 +397,7 @@ Reason: {1} Sucessfully added a new donator.Total donated amount from this user: {0} 👑 - Thanks to the people listed below for making this project hjappen! + Thanks to the people listed below for making this project happen! I will forward DMs to all owners. From 23e8a082d3a7b5758409b4f1acaab3b7ee9181f1 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 18:46:17 +0100 Subject: [PATCH 187/256] Fixed some keys, a bit more localization --- .../Modules/Games/Commands/Acropobia.cs | 2 +- .../Modules/Games/Commands/HangmanCommands.cs | 20 +++++-------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index fc2b6f8c..c65c24e6 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -261,7 +261,7 @@ $@"-- return; _votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old); await _channel.SendConfirmAsync(GetText("acrophobia"), - GetText("vote_cast", Format.Bold(guildUser.ToString()))).ConfigureAwait(false); + GetText("acro_vote_cast", Format.Bold(guildUser.ToString()))).ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index 0fb8e2f3..9726eb65 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Threading.Tasks; using NadekoBot.Modules.Games.Hangman; +using Discord; namespace NadekoBot.Modules.Games { @@ -15,23 +16,12 @@ namespace NadekoBot.Modules.Games [Group] public class HangmanCommands : NadekoSubmodule { - private static Logger _log { get; } - //channelId, game public static ConcurrentDictionary HangmanGames { get; } = new ConcurrentDictionary(); - private static string typesStr { get; } = ""; - - static HangmanCommands() - { - _log = LogManager.GetCurrentClassLogger(); - typesStr = - string.Format("`List of \"{0}hangman\" term types:`\n", NadekoBot.ModulePrefixes[typeof(Games).Name]) + String.Join(", ", HangmanTermPool.data.Keys); - } - [NadekoCommand, Usage, Description, Aliases] public async Task Hangmanlist() { - await Context.Channel.SendConfirmAsync(typesStr); + await Context.Channel.SendConfirmAsync(Format.Code(GetText("hangman_types", Prefix)) + "\n" + String.Join(", ", HangmanTermPool.data.Keys)); } [NadekoCommand, Usage, Description, Aliases] @@ -41,7 +31,7 @@ namespace NadekoBot.Modules.Games if (!HangmanGames.TryAdd(Context.Channel.Id, hm)) { - await Context.Channel.SendErrorAsync("Hangman game already running on this channel.").ConfigureAwait(false); + await ReplyErrorLocalized("hangman_running").ConfigureAwait(false); return; } @@ -56,14 +46,14 @@ namespace NadekoBot.Modules.Games } catch (Exception ex) { - try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { } + try { await Context.Channel.SendErrorAsync(GetText("hangman_start_errored") + " " + ex.Message).ConfigureAwait(false); } catch { } HangmanGame throwaway; HangmanGames.TryRemove(Context.Channel.Id, out throwaway); throwaway.Dispose(); return; } - await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman()); + await Context.Channel.SendConfirmAsync(GetText("hangman_game_started"), hm.ScrambledWord + "\n" + hm.GetHangman()); } } } From a5adce9b6fe7d30af515d488c393f8cfa9766462 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 18:46:37 +0100 Subject: [PATCH 188/256] woopps --- .../Resources/ResponseStrings.Designer.cs | 54 +++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 18 +++++++ 2 files changed, 72 insertions(+) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index d49f4610..da3aab5b 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2860,6 +2860,60 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Currency generation has been disabled on this channel.. + /// + public static string games_curgen_disabled { + get { + return ResourceManager.GetString("games_curgen_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Currency generation has been enabled on this channel.. + /// + public static string games_curgen_enabled { + get { + return ResourceManager.GetString("games_curgen_enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hangman game started. + /// + public static string games_hangman_game_started { + get { + return ResourceManager.GetString("games_hangman_game_started", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hangman game already running on this channel.. + /// + public static string games_hangman_running { + get { + return ResourceManager.GetString("games_hangman_running", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Starting hangman errored.. + /// + public static string games_hangman_start_errored { + get { + return ResourceManager.GetString("games_hangman_start_errored", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of "{0}hangman" term types:. + /// + public static string games_hangman_types { + get { + return ResourceManager.GetString("games_hangman_types", resourceCulture); + } + } + /// /// Looks up a localized string similar to Question. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 7cf55c8f..6fa196c1 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1221,4 +1221,22 @@ Don't forget to leave your discord name or id in the message. Total: {0} Average: {1} + + Currency generation has been disabled on this channel. + + + Currency generation has been enabled on this channel. + + + Hangman game started + + + Hangman game already running on this channel. + + + Starting hangman errored. + + + List of "{0}hangman" term types: + \ No newline at end of file From 895d24c75504b677c373d4e5d84a46848c8497b6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 21 Feb 2017 20:42:04 +0100 Subject: [PATCH 189/256] .sinfo more reliable? --- src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index 0eedba24..fefd0a01 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -52,7 +52,7 @@ namespace NadekoBot.Modules.Utility .WithColor(NadekoBot.OkColor); if (guild.Emojis.Any()) { - embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); + embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(20).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); } await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } From fff6cb40242b0a13c6eabf9a27888389215424c8 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 22 Feb 2017 11:40:51 +0100 Subject: [PATCH 190/256] fixed a string, closes #1075 --- src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs index 6847dcfc..7f3e4b03 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/WaifuClaimCommands.cs @@ -368,7 +368,7 @@ namespace NadekoBot.Modules.Gambling if (waifus.Count == 0) { - await ReplyConfirmLocalized("waifu_none").ConfigureAwait(false); + await ReplyConfirmLocalized("waifus_none").ConfigureAwait(false); return; } From 8ae3d0c5e58738c0947c4f2b4841209d4547c9a2 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 22 Feb 2017 15:20:49 +0100 Subject: [PATCH 191/256] closes #1079 --- src/NadekoBot/Modules/Games/Commands/PollCommands.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index 1434ee9e..a17e9d26 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -48,8 +48,6 @@ namespace NadekoBot.Modules.Games { var channel = (ITextChannel)Context.Channel; - if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels) - return; if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";")) return; var data = arg.Split(';'); From ec9c556f06e1e95700840ee7d18ff8065c9182c2 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 22 Feb 2017 16:11:06 +0100 Subject: [PATCH 192/256] closes #1080 --- src/NadekoBot/Modules/Games/Commands/Acropobia.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index c65c24e6..bcfc279d 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -284,7 +284,7 @@ $@"-- var winner = table.First(); var embed = new EmbedBuilder().WithOkColor() .WithTitle(GetText("acrophobia")) - .WithDescription(GetText("winner", Format.Bold(_submissions[winner.Key].ToString()), + .WithDescription(GetText("acro_winner", Format.Bold(_submissions[winner.Key].ToString()), Format.Bold(winner.Value.ToString()))) .WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase())); From 5cdb41b65f333e9233a36febb2841ed263babe6d Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 22 Feb 2017 18:16:05 +0100 Subject: [PATCH 193/256] A lot more localization, cleanup, inole improved and dateadded is not a field in all database models --- .../20170222162505_dateadded.Designer.cs | 1171 +++++++++++++++++ .../Migrations/20170222162505_dateadded.cs | 357 +++++ .../NadekoSqliteContextModelSnapshot.cs | 76 ++ .../Modules/Games/Commands/Acropobia.cs | 5 +- .../Games/Commands/CleverBotCommands.cs | 2 +- .../Games/Commands/PlantAndPickCommands.cs | 4 +- .../Modules/Games/Commands/TicTacToe.cs | 9 - src/NadekoBot/Modules/Games/Games.cs | 33 +- src/NadekoBot/Modules/Utility/Utility.cs | 119 +- .../Resources/CommandStrings.Designer.cs | 4 +- src/NadekoBot/Resources/CommandStrings.resx | 4 +- .../Resources/ResponseStrings.Designer.cs | 301 +++++ src/NadekoBot/Resources/ResponseStrings.resx | 104 ++ .../Services/Database/Models/DbEntity.cs | 2 +- 14 files changed, 2101 insertions(+), 90 deletions(-) create mode 100644 src/NadekoBot/Migrations/20170222162505_dateadded.Designer.cs create mode 100644 src/NadekoBot/Migrations/20170222162505_dateadded.cs diff --git a/src/NadekoBot/Migrations/20170222162505_dateadded.Designer.cs b/src/NadekoBot/Migrations/20170222162505_dateadded.Designer.cs new file mode 100644 index 00000000..89102dc7 --- /dev/null +++ b/src/NadekoBot/Migrations/20170222162505_dateadded.Designer.cs @@ -0,0 +1,1171 @@ +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using NadekoBot.Services.Database; +using NadekoBot.Services.Database.Models; +using NadekoBot.Modules.Music.Classes; + +namespace NadekoBot.Migrations +{ + [DbContext(typeof(NadekoContext))] + [Migration("20170222162505_dateadded")] + partial class dateadded + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { + modelBuilder + .HasAnnotation("ProductVersion", "1.1.0-rtm-22752"); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.Property("UserThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiRaidSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AntiSpamSettingId"); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.HasKey("Id"); + + b.HasIndex("AntiSpamSettingId"); + + b.ToTable("AntiSpamIgnore"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Action"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("MessageThreshold"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId") + .IsUnique(); + + b.ToTable("AntiSpamSetting"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("ItemId"); + + b.Property("Type"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("BlacklistItem"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BetflipMultiplier"); + + b.Property("Betroll100Multiplier"); + + b.Property("Betroll67Multiplier"); + + b.Property("Betroll91Multiplier"); + + b.Property("BufferSize"); + + b.Property("CurrencyDropAmount"); + + b.Property("CurrencyGenerationChance"); + + b.Property("CurrencyGenerationCooldown"); + + b.Property("CurrencyName"); + + b.Property("CurrencyPluralName"); + + b.Property("CurrencySign"); + + b.Property("DMHelpString"); + + b.Property("DateAdded"); + + b.Property("ErrorColor"); + + b.Property("ForwardMessages"); + + b.Property("ForwardToAllOwners"); + + b.Property("HelpString"); + + b.Property("Locale"); + + b.Property("MigrationVersion"); + + b.Property("MinimumBetAmount"); + + b.Property("OkColor"); + + b.Property("RemindMessageFormat"); + + b.Property("RotatingStatuses"); + + b.Property("TriviaCurrencyReward"); + + b.HasKey("Id"); + + b.ToTable("BotConfig"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BaseDestroyed"); + + b.Property("CallUser"); + + b.Property("ClashWarId"); + + b.Property("DateAdded"); + + b.Property("SequenceNumber"); + + b.Property("Stars"); + + b.Property("TimeAdded"); + + b.HasKey("Id"); + + b.HasIndex("ClashWarId"); + + b.ToTable("ClashCallers"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("EnemyClan"); + + b.Property("GuildId"); + + b.Property("Size"); + + b.Property("StartedAt"); + + b.Property("WarState"); + + b.HasKey("Id"); + + b.ToTable("ClashOfClans"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("CommandName"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Seconds"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("CommandCooldown"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandPrice", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("CommandName"); + + b.Property("DateAdded"); + + b.Property("Price"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.HasIndex("Price") + .IsUnique(); + + b.ToTable("CommandPrice"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("InternalTrigger"); + + b.Property("Modifier"); + + b.Property("UnitType"); + + b.HasKey("Id"); + + b.ToTable("ConversionUnits"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("DateAdded"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Currency"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CurrencyTransaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("DateAdded"); + + b.Property("Reason"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.ToTable("CurrencyTransactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CustomReaction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("IsRegex"); + + b.Property("OwnerOnly"); + + b.Property("Response"); + + b.Property("Trigger"); + + b.HasKey("Id"); + + b.ToTable("CustomReactions"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.DiscordUser", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AvatarId"); + + b.Property("DateAdded"); + + b.Property("Discriminator"); + + b.Property("UserId"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasAlternateKey("UserId"); + + b.ToTable("DiscordUser"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Amount"); + + b.Property("DateAdded"); + + b.Property("Name"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("Donators"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("Text"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("EightBallResponses"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("GuildConfigId1"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.HasIndex("GuildConfigId1"); + + b.ToTable("FilterChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("Word"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FilteredWord"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Type"); + + b.Property("Username"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("FollowedStream"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GCChannelId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AutoAssignRoleId"); + + b.Property("AutoDeleteByeMessages"); + + b.Property("AutoDeleteByeMessagesTimer"); + + b.Property("AutoDeleteGreetMessages"); + + b.Property("AutoDeleteGreetMessagesTimer"); + + b.Property("AutoDeleteSelfAssignedRoleMessages"); + + b.Property("ByeMessageChannelId"); + + b.Property("ChannelByeMessageText"); + + b.Property("ChannelGreetMessageText"); + + b.Property("CleverbotEnabled"); + + b.Property("DateAdded"); + + b.Property("DefaultMusicVolume"); + + b.Property("DeleteMessageOnCommand"); + + b.Property("DmGreetMessageText"); + + b.Property("ExclusiveSelfAssignedRoles"); + + b.Property("FilterInvites"); + + b.Property("FilterWords"); + + b.Property("GreetMessageChannelId"); + + b.Property("GuildId"); + + b.Property("Locale"); + + b.Property("LogSettingId"); + + b.Property("MuteRoleName"); + + b.Property("PermissionRole"); + + b.Property("RootPermissionId"); + + b.Property("SendChannelByeMessage"); + + b.Property("SendChannelGreetMessage"); + + b.Property("SendDmGreetMessage"); + + b.Property("TimeZoneId"); + + b.Property("VerbosePermissions"); + + b.Property("VoicePlusTextEnabled"); + + b.HasKey("Id"); + + b.HasIndex("GuildId") + .IsUnique(); + + b.HasIndex("LogSettingId"); + + b.HasIndex("RootPermissionId"); + + b.ToTable("GuildConfigs"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("GuildId"); + + b.Property("Interval"); + + b.Property("Message"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("GuildRepeater"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredLogChannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("LogSettingId"); + + b.HasKey("Id"); + + b.HasIndex("LogSettingId"); + + b.ToTable("IgnoredVoicePresenceCHannels"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelCreated"); + + b.Property("ChannelCreatedId"); + + b.Property("ChannelDestroyed"); + + b.Property("ChannelDestroyedId"); + + b.Property("ChannelId"); + + b.Property("ChannelUpdated"); + + b.Property("ChannelUpdatedId"); + + b.Property("DateAdded"); + + b.Property("IsLogging"); + + b.Property("LogOtherId"); + + b.Property("LogUserPresence"); + + b.Property("LogUserPresenceId"); + + b.Property("LogVoicePresence"); + + b.Property("LogVoicePresenceId"); + + b.Property("LogVoicePresenceTTSId"); + + b.Property("MessageDeleted"); + + b.Property("MessageDeletedId"); + + b.Property("MessageUpdated"); + + b.Property("MessageUpdatedId"); + + b.Property("UserBanned"); + + b.Property("UserBannedId"); + + b.Property("UserJoined"); + + b.Property("UserJoinedId"); + + b.Property("UserLeft"); + + b.Property("UserLeftId"); + + b.Property("UserMutedId"); + + b.Property("UserPresenceChannelId"); + + b.Property("UserUnbanned"); + + b.Property("UserUnbannedId"); + + b.Property("UserUpdated"); + + b.Property("UserUpdatedId"); + + b.Property("VoicePresenceChannelId"); + + b.HasKey("Id"); + + b.ToTable("LogSettings"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("ModuleName"); + + b.Property("Prefix"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("ModulePrefixes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("Author"); + + b.Property("AuthorId"); + + b.Property("DateAdded"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.ToTable("MusicPlaylists"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildConfigId"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("GuildConfigId"); + + b.ToTable("MutedUserId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("NextId"); + + b.Property("PrimaryTarget"); + + b.Property("PrimaryTargetId"); + + b.Property("SecondaryTarget"); + + b.Property("SecondaryTargetName"); + + b.Property("State"); + + b.HasKey("Id"); + + b.HasIndex("NextId") + .IsUnique(); + + b.ToTable("Permission"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("Status"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("PlayingStatus"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("MusicPlaylistId"); + + b.Property("Provider"); + + b.Property("ProviderType"); + + b.Property("Query"); + + b.Property("Title"); + + b.Property("Uri"); + + b.HasKey("Id"); + + b.HasIndex("MusicPlaylistId"); + + b.ToTable("PlaylistSong"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AuthorId"); + + b.Property("AuthorName") + .IsRequired(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("Keyword") + .IsRequired(); + + b.Property("Text") + .IsRequired(); + + b.HasKey("Id"); + + b.ToTable("Quotes"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("BotConfigId"); + + b.Property("DateAdded"); + + b.Property("Icon"); + + b.Property("Name"); + + b.HasKey("Id"); + + b.HasIndex("BotConfigId"); + + b.ToTable("RaceAnimals"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("ChannelId"); + + b.Property("DateAdded"); + + b.Property("IsPrivate"); + + b.Property("Message"); + + b.Property("ServerId"); + + b.Property("UserId"); + + b.Property("When"); + + b.HasKey("Id"); + + b.ToTable("Reminders"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("GuildId"); + + b.Property("RoleId"); + + b.HasKey("Id"); + + b.HasIndex("GuildId", "RoleId") + .IsUnique(); + + b.ToTable("SelfAssignableRoles"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.UserPokeTypes", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("UserId"); + + b.Property("type"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("PokeGame"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("AffinityId"); + + b.Property("ClaimerId"); + + b.Property("DateAdded"); + + b.Property("Price"); + + b.Property("WaifuId"); + + b.HasKey("Id"); + + b.HasIndex("AffinityId"); + + b.HasIndex("ClaimerId"); + + b.HasIndex("WaifuId") + .IsUnique(); + + b.ToTable("WaifuInfo"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => + { + b.Property("Id") + .ValueGeneratedOnAdd(); + + b.Property("DateAdded"); + + b.Property("NewId"); + + b.Property("OldId"); + + b.Property("UpdateType"); + + b.Property("UserId"); + + b.HasKey("Id"); + + b.HasIndex("NewId"); + + b.HasIndex("OldId"); + + b.HasIndex("UserId"); + + b.ToTable("WaifuUpdates"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiRaidSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiRaidSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiRaidSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamIgnore", b => + { + b.HasOne("NadekoBot.Services.Database.Models.AntiSpamSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("AntiSpamSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.AntiSpamSetting", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig", "GuildConfig") + .WithOne("AntiSpamSetting") + .HasForeignKey("NadekoBot.Services.Database.Models.AntiSpamSetting", "GuildConfigId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("Blacklist") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b => + { + b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar") + .WithMany("Bases") + .HasForeignKey("ClashWarId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("CommandCooldowns") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandPrice", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("CommandPrices") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("EightBallResponses") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterInvitesChannelIds") + .HasForeignKey("GuildConfigId"); + + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilterWordsChannelIds") + .HasForeignKey("GuildConfigId1"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FilteredWords") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("FollowedStreams") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GCChannelId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GenerateCurrencyChannelIds") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany() + .HasForeignKey("LogSettingId"); + + b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission") + .WithMany() + .HasForeignKey("RootPermissionId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildRepeater", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("GuildRepeaters") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredChannels") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b => + { + b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting") + .WithMany("IgnoredVoicePresenceChannelIds") + .HasForeignKey("LogSettingId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("ModulePrefixes") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.MutedUserId", b => + { + b.HasOne("NadekoBot.Services.Database.Models.GuildConfig") + .WithMany("MutedUsers") + .HasForeignKey("GuildConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b => + { + b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next") + .WithOne("Previous") + .HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RotatingStatusMessages") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b => + { + b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist") + .WithMany("Songs") + .HasForeignKey("MusicPlaylistId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b => + { + b.HasOne("NadekoBot.Services.Database.Models.BotConfig") + .WithMany("RaceAnimals") + .HasForeignKey("BotConfigId"); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuInfo", b => + { + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Affinity") + .WithMany() + .HasForeignKey("AffinityId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Claimer") + .WithMany() + .HasForeignKey("ClaimerId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Waifu") + .WithOne() + .HasForeignKey("NadekoBot.Services.Database.Models.WaifuInfo", "WaifuId") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("NadekoBot.Services.Database.Models.WaifuUpdate", b => + { + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "New") + .WithMany() + .HasForeignKey("NewId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "Old") + .WithMany() + .HasForeignKey("OldId"); + + b.HasOne("NadekoBot.Services.Database.Models.DiscordUser", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade); + }); + } + } +} diff --git a/src/NadekoBot/Migrations/20170222162505_dateadded.cs b/src/NadekoBot/Migrations/20170222162505_dateadded.cs new file mode 100644 index 00000000..5f96eca8 --- /dev/null +++ b/src/NadekoBot/Migrations/20170222162505_dateadded.cs @@ -0,0 +1,357 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace NadekoBot.Migrations +{ + public partial class dateadded : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "DateAdded", + table: "WaifuUpdates", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "WaifuInfo", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "PokeGame", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "SelfAssignableRoles", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "Reminders", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "RaceAnimals", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "Quotes", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "PlaylistSong", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "PlayingStatus", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "Permission", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "MutedUserId", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "MusicPlaylists", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "ModulePrefixes", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "LogSettings", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "IgnoredVoicePresenceCHannels", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "IgnoredLogChannels", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "GuildRepeater", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "GuildConfigs", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "GCChannelId", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "FollowedStream", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "FilteredWord", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "FilterChannelId", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "EightBallResponses", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "Donators", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "DiscordUser", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "CustomReactions", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "CurrencyTransactions", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "Currency", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "ConversionUnits", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "CommandPrice", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "CommandCooldown", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "ClashOfClans", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "ClashCallers", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "BotConfig", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "BlacklistItem", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "AntiSpamSetting", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "AntiSpamIgnore", + nullable: true); + + migrationBuilder.AddColumn( + name: "DateAdded", + table: "AntiRaidSetting", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "DateAdded", + table: "WaifuUpdates"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "WaifuInfo"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "PokeGame"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "SelfAssignableRoles"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "Reminders"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "RaceAnimals"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "Quotes"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "PlaylistSong"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "PlayingStatus"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "Permission"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "MutedUserId"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "MusicPlaylists"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "ModulePrefixes"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "LogSettings"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "IgnoredVoicePresenceCHannels"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "IgnoredLogChannels"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "GuildRepeater"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "GuildConfigs"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "GCChannelId"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "FollowedStream"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "FilteredWord"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "FilterChannelId"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "EightBallResponses"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "Donators"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "DiscordUser"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "CustomReactions"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "CurrencyTransactions"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "Currency"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "ConversionUnits"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "CommandPrice"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "CommandCooldown"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "ClashOfClans"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "ClashCallers"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "BotConfig"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "BlacklistItem"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "AntiSpamSetting"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "AntiSpamIgnore"); + + migrationBuilder.DropColumn( + name: "DateAdded", + table: "AntiRaidSetting"); + } + } +} diff --git a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs index 5a03594c..981d53cf 100644 --- a/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs +++ b/src/NadekoBot/Migrations/NadekoSqliteContextModelSnapshot.cs @@ -24,6 +24,8 @@ namespace NadekoBot.Migrations b.Property("Action"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("Seconds"); @@ -47,6 +49,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.HasKey("Id"); b.HasIndex("AntiSpamSettingId"); @@ -61,6 +65,8 @@ namespace NadekoBot.Migrations b.Property("Action"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("MessageThreshold"); @@ -80,6 +86,8 @@ namespace NadekoBot.Migrations b.Property("BotConfigId"); + b.Property("DateAdded"); + b.Property("ItemId"); b.Property("Type"); @@ -120,6 +128,8 @@ namespace NadekoBot.Migrations b.Property("DMHelpString"); + b.Property("DateAdded"); + b.Property("ErrorColor"); b.Property("ForwardMessages"); @@ -158,6 +168,8 @@ namespace NadekoBot.Migrations b.Property("ClashWarId"); + b.Property("DateAdded"); + b.Property("SequenceNumber"); b.Property("Stars"); @@ -178,6 +190,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("EnemyClan"); b.Property("GuildId"); @@ -200,6 +214,8 @@ namespace NadekoBot.Migrations b.Property("CommandName"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("Seconds"); @@ -220,6 +236,8 @@ namespace NadekoBot.Migrations b.Property("CommandName"); + b.Property("DateAdded"); + b.Property("Price"); b.HasKey("Id"); @@ -237,6 +255,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("InternalTrigger"); b.Property("Modifier"); @@ -255,6 +275,8 @@ namespace NadekoBot.Migrations b.Property("Amount"); + b.Property("DateAdded"); + b.Property("UserId"); b.HasKey("Id"); @@ -272,6 +294,8 @@ namespace NadekoBot.Migrations b.Property("Amount"); + b.Property("DateAdded"); + b.Property("Reason"); b.Property("UserId"); @@ -286,6 +310,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("GuildId"); b.Property("IsRegex"); @@ -308,6 +334,8 @@ namespace NadekoBot.Migrations b.Property("AvatarId"); + b.Property("DateAdded"); + b.Property("Discriminator"); b.Property("UserId"); @@ -328,6 +356,8 @@ namespace NadekoBot.Migrations b.Property("Amount"); + b.Property("DateAdded"); + b.Property("Name"); b.Property("UserId"); @@ -347,6 +377,8 @@ namespace NadekoBot.Migrations b.Property("BotConfigId"); + b.Property("DateAdded"); + b.Property("Text"); b.HasKey("Id"); @@ -363,6 +395,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("GuildConfigId1"); @@ -381,6 +415,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("Word"); @@ -399,6 +435,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("GuildId"); @@ -421,6 +459,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.HasKey("Id"); @@ -455,6 +495,8 @@ namespace NadekoBot.Migrations b.Property("CleverbotEnabled"); + b.Property("DateAdded"); + b.Property("DefaultMusicVolume"); b.Property("DeleteMessageOnCommand"); @@ -512,6 +554,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("GuildId"); @@ -534,6 +578,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("LogSettingId"); b.HasKey("Id"); @@ -550,6 +596,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("LogSettingId"); b.HasKey("Id"); @@ -578,6 +626,8 @@ namespace NadekoBot.Migrations b.Property("ChannelUpdatedId"); + b.Property("DateAdded"); + b.Property("IsLogging"); b.Property("LogOtherId"); @@ -638,6 +688,8 @@ namespace NadekoBot.Migrations b.Property("BotConfigId"); + b.Property("DateAdded"); + b.Property("ModuleName"); b.Property("Prefix"); @@ -658,6 +710,8 @@ namespace NadekoBot.Migrations b.Property("AuthorId"); + b.Property("DateAdded"); + b.Property("Name"); b.HasKey("Id"); @@ -670,6 +724,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("GuildConfigId"); b.Property("UserId"); @@ -686,6 +742,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("NextId"); b.Property("PrimaryTarget"); @@ -713,6 +771,8 @@ namespace NadekoBot.Migrations b.Property("BotConfigId"); + b.Property("DateAdded"); + b.Property("Status"); b.HasKey("Id"); @@ -727,6 +787,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("MusicPlaylistId"); b.Property("Provider"); @@ -756,6 +818,8 @@ namespace NadekoBot.Migrations b.Property("AuthorName") .IsRequired(); + b.Property("DateAdded"); + b.Property("GuildId"); b.Property("Keyword") @@ -776,6 +840,8 @@ namespace NadekoBot.Migrations b.Property("BotConfigId"); + b.Property("DateAdded"); + b.Property("Icon"); b.Property("Name"); @@ -794,6 +860,8 @@ namespace NadekoBot.Migrations b.Property("ChannelId"); + b.Property("DateAdded"); + b.Property("IsPrivate"); b.Property("Message"); @@ -814,6 +882,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("GuildId"); b.Property("RoleId"); @@ -831,6 +901,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("UserId"); b.Property("type"); @@ -852,6 +924,8 @@ namespace NadekoBot.Migrations b.Property("ClaimerId"); + b.Property("DateAdded"); + b.Property("Price"); b.Property("WaifuId"); @@ -873,6 +947,8 @@ namespace NadekoBot.Migrations b.Property("Id") .ValueGeneratedOnAdd(); + b.Property("DateAdded"); + b.Property("NewId"); b.Property("OldId"); diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index bcfc279d..ddaf3e98 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -240,7 +240,6 @@ $@"-- catch { } } - IGuildUser usr; //if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id) //{ // if (!usersWhoVoted.Add(guildUser.Id)) @@ -255,7 +254,7 @@ $@"-- if (int.TryParse(input, out num) && num > 0 && num <= _submissions.Count) { var kvp = _submissions.Skip(num - 1).First(); - usr = kvp.Value; + var usr = kvp.Value; //can't vote for yourself, can't vote multiple times if (usr.Id == guildUser.Id || !_usersWhoVoted.Add(guildUser.Id)) return; @@ -299,7 +298,7 @@ $@"-- } private string GetText(string key, params object[] replacements) - => NadekoTopLevelModule.GetTextStatic(key, + => GetTextStatic(key, NadekoBot.Localization.GetCultureInfo(_channel.Guild), typeof(Games).Name.ToLowerInvariant(), replacements); diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index 1f33bc2a..47aad031 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games [Group] public class CleverBotCommands : NadekoSubmodule { - private static Logger _log { get; } + private static new Logger _log { get; } public static ConcurrentDictionary> CleverbotGuilds { get; } = new ConcurrentDictionary>(); diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index ba98f5f1..423964cb 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -203,11 +203,11 @@ namespace NadekoBot.Modules.Games } if (enabled) { - await channel.SendConfirmAsync("Currency generation enabled on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("curgen_enabled").ConfigureAwait(false); } else { - await channel.SendConfirmAsync("Currency generation disabled on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index 2e115fd2..e1000af2 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -4,9 +4,7 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using NLog; using System; -using System.Collections.Concurrent; using System.Collections.Generic; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -21,15 +19,8 @@ namespace NadekoBot.Modules.Games { //channelId/game private static readonly Dictionary _games = new Dictionary(); - private readonly Logger _log; - - public TicTacToeCommands() - { - _log = LogManager.GetCurrentClassLogger(); - } private readonly SemaphoreSlim sem = new SemaphoreSlim(1, 1); - private readonly object tttLockObj = new object(); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 88a663df..2249c1b2 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -141,8 +141,11 @@ namespace NadekoBot.Modules.Games var pointy = (int)(miny - length * ((Crazy - 4) / 6)); var p = new Pen(ImageSharp.Color.Red, 5); - - img.Draw(p, new SixLabors.Shapes.Ellipse(200, 200, 5, 5)); + //using (var pointMs = File.ReadAllBytes("data/images/point.png").ToStream()) + //using (var pointIMg = new ImageSharp.Image(pointMs)) + //{ + // img.DrawImage(pointIMg, 100, new ImageSharp.Size(100, 100), new Point(pointx, pointy)); + //} string url; using (var http = new HttpClient()) @@ -167,19 +170,19 @@ namespace NadekoBot.Modules.Games } } - //[NadekoCommand, Usage, Description, Aliases] - //[RequireContext(ContextType.Guild)] - //public async Task RateGirl(IGuildUser usr) - //{ - // var gr = _girlRatings.GetOrAdd(usr.Id, GetGirl); - // var img = await gr.Url; - // await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() - // .WithTitle("Girl Rating For " + usr) - // .AddField(efb => efb.WithName("Hot").WithValue(gr.Hot.ToString("F2")).WithIsInline(true)) - // .AddField(efb => efb.WithName("Crazy").WithValue(gr.Crazy.ToString("F2")).WithIsInline(true)) - // .AddField(efb => efb.WithName("Advice").WithValue(gr.Advice).WithIsInline(false)) - // .WithImageUrl(img)).ConfigureAwait(false); - //} + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task RateGirl(IGuildUser usr) + { + var gr = _girlRatings.GetOrAdd(usr.Id, GetGirl); + var img = await gr.Url; + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle("Girl Rating For " + usr) + .AddField(efb => efb.WithName("Hot").WithValue(gr.Hot.ToString("F2")).WithIsInline(true)) + .AddField(efb => efb.WithName("Crazy").WithValue(gr.Crazy.ToString("F2")).WithIsInline(true)) + .AddField(efb => efb.WithName("Advice").WithValue(gr.Advice).WithIsInline(false)) + .WithImageUrl(img)).ConfigureAwait(false); + } private double NextDouble(double x, double y) { diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index 8d3a95c4..f69aef7d 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -117,7 +117,7 @@ namespace NadekoBot.Modules.Utility if (rotatingRoleColors.TryRemove(role.Id, out t)) { t.Change(Timeout.Infinite, Timeout.Infinite); - await channel.SendConfirmAsync($"Stopped rotating colors for the **{role.Name}** role").ConfigureAwait(false); + await ReplyConfirmLocalized("rrc_stop", Format.Bold(role.Name)).ConfigureAwait(false); } return; } @@ -132,7 +132,7 @@ namespace NadekoBot.Modules.Utility if (!hexColors.Any()) { - await channel.SendMessageAsync("No colors are in the correct format. Use `#00ff00` for example.").ConfigureAwait(false); + await ReplyErrorLocalized("rrc_no_colors").ConfigureAwait(false); return; } @@ -162,8 +162,7 @@ namespace NadekoBot.Modules.Utility old.Change(Timeout.Infinite, Timeout.Infinite); return t; }); - - await channel.SendFileAsync(images, "magicalgirl.jpg", $"Rotating **{role.Name}** role's color.").ConfigureAwait(false); + await channel.SendFileAsync(images, "magicalgirl.jpg", GetText("rrc_start", Format.Bold(role.Name))).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -180,14 +179,14 @@ namespace NadekoBot.Modules.Utility .WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png") .WithName("Together Tube") .WithUrl("https://togethertube.com/")) - .WithDescription($"{Context.User.Mention} Here is your room link:\n{target}")); + .WithDescription(Context.User.Mention + " " + GetText("togtub_room_link") + "\n" + target)); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task WhosPlaying([Remainder] string game = null) + public async Task WhosPlaying([Remainder] string game) { - game = game.Trim().ToUpperInvariant(); + game = game?.Trim().ToUpperInvariant(); if (string.IsNullOrWhiteSpace(game)) return; @@ -207,7 +206,7 @@ namespace NadekoBot.Modules.Utility int i = 0; if (arr.Length == 0) - await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false); + await ReplyErrorLocalized("nobody_playing_game").ConfigureAwait(false); else { await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2) @@ -218,26 +217,24 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task InRole([Remainder] string roles) + public async Task InRole(params IRole[] roles) { - if (string.IsNullOrWhiteSpace(roles)) + if (roles.Length == 0) return; - var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant()); - string send = "ℹ️ **Here is a list of users in those roles:**"; - foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@EVERYONE" && str != "EVERYONE")) + var send = "ℹ️ " + Format.Bold(GetText("inrole_list")); + var usrs = (await Context.Guild.GetUsersAsync()).ToArray(); + foreach (var role in roles.Where(r => r.Id != Context.Guild.Id)) { - var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleStr).FirstOrDefault(); - if (role == null) continue; send += $"```css\n[{role.Name}]\n"; - send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString())); - send += $"\n```"; + send += string.Join(", ", usrs.Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString())); + send += "\n```"; } - var usr = Context.User as IGuildUser; + var usr = (IGuildUser)Context.User; while (send.Length > 2000) { if (!usr.GetPermissions((ITextChannel)Context.Channel).ManageMessages) { - await Context.Channel.SendErrorAsync($"⚠️ {usr.Mention} **you are not allowed to use this command on roles with a lot of users in them to prevent abuse.**").ConfigureAwait(false); + await ReplyErrorLocalized("inrole_not_allowed").ConfigureAwait(false); return; } var curstr = send.Substring(0, 2000); @@ -254,15 +251,13 @@ namespace NadekoBot.Modules.Utility public async Task CheckMyPerms() { - StringBuilder builder = new StringBuilder("```http\n"); - var user = Context.User as IGuildUser; + StringBuilder builder = new StringBuilder(); + var user = (IGuildUser) Context.User; var perms = user.GetPermissions((ITextChannel)Context.Channel); foreach (var p in perms.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any())) { - builder.AppendLine($"{p.Name} : {p.GetValue(perms, null).ToString()}"); + builder.AppendLine($"{p.Name} : {p.GetValue(perms, null)}"); } - - builder.Append("```"); await Context.Channel.SendConfirmAsync(builder.ToString()); } @@ -271,20 +266,23 @@ namespace NadekoBot.Modules.Utility public async Task UserId(IGuildUser target = null) { var usr = target ?? Context.User; - await Context.Channel.SendConfirmAsync($"🆔 of the user **{ usr.Username }** is `{ usr.Id }`").ConfigureAwait(false); + await ReplyConfirmLocalized("userid", "🆔", Format.Bold(usr.ToString()), + Format.Code(usr.Id.ToString())).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] public async Task ChannelId() { - await Context.Channel.SendConfirmAsync($"🆔 of this channel is `{Context.Channel.Id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("channelidd", "🆔", Format.Code(Context.Channel.Id.ToString())) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task ServerId() { - await Context.Channel.SendConfirmAsync($"🆔 of this server is `{Context.Guild.Id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("serverid", "🆔", Format.Code(Context.Guild.Id.ToString())) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -294,33 +292,36 @@ namespace NadekoBot.Modules.Utility var channel = (ITextChannel)Context.Channel; var guild = channel.Guild; - const int RolesPerPage = 20; + const int rolesPerPage = 20; if (page < 1 || page > 100) return; if (target != null) { - var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage); + var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * rolesPerPage).Take(rolesPerPage).ToArray(); if (!roles.Any()) { - await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false); + await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false); } else { - await channel.SendConfirmAsync($"⚔ **Page #{page} of roles for {target.Username}**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false); + + await channel.SendConfirmAsync(GetText("roles_page", page, Format.Bold(target.ToString())), + "\n• " + string.Join("\n• ", (IEnumerable)roles).SanitizeMentions()).ConfigureAwait(false); } } else { - var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage); + var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * rolesPerPage).Take(rolesPerPage).ToArray(); if (!roles.Any()) { - await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false); + await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false); } else { - await channel.SendConfirmAsync($"⚔ **Page #{page} of all roles on this server:**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false); + await channel.SendConfirmAsync(GetText("roles_all_page", page), + "\n• " + string.Join("\n• ", (IEnumerable)roles).SanitizeMentions()).ConfigureAwait(false); } } } @@ -339,9 +340,9 @@ namespace NadekoBot.Modules.Utility var topic = channel.Topic; if (string.IsNullOrWhiteSpace(topic)) - await Context.Channel.SendErrorAsync("No topic set.").ConfigureAwait(false); + await ReplyErrorLocalized("no_topic_set").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("channel_topic"), topic).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -362,11 +363,13 @@ namespace NadekoBot.Modules.Utility return; var status = string.Join(", ", NadekoBot.Client.Shards.GroupBy(x => x.ConnectionState) - .Select(x => $"{x.Count()} shards {x.Key}") + .Select(x => $"{x.Count()} {x.Key}") .ToArray()); var allShardStrings = NadekoBot.Client.Shards - .Select(x => $"Shard **#{x.ShardId.ToString()}** is in {Format.Bold(x.ConnectionState.ToString())} state with {Format.Bold(x.Guilds.Count.ToString())} servers") + .Select(x => + GetText("shard_stats_txt", x.ShardId.ToString(), + Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.Count.ToString()))) .ToArray(); @@ -377,10 +380,10 @@ namespace NadekoBot.Modules.Utility var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25)); if (string.IsNullOrWhiteSpace(str)) - str = "No shards on this page."; + str = GetText("no_shards_on_page"); return new EmbedBuilder() - .WithAuthor(a => a.WithName("Shard Stats")) + .WithAuthor(a => a.WithName(GetText("shard_stats"))) .WithTitle(status) .WithOkColor() .WithDescription(str); @@ -392,7 +395,7 @@ namespace NadekoBot.Modules.Utility { var shardId = NadekoBot.Client.GetShardIdFor(guildid); - await Context.Channel.SendConfirmAsync($"ShardId for **{guildid}** with {NadekoBot.Client.Shards.Count} total shards", shardId.ToString()).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(shardId.ToString()).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -409,17 +412,21 @@ namespace NadekoBot.Modules.Utility .WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}") .WithUrl("http://nadekobot.readthedocs.io/en/latest/") .WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg")) - .AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Shard")).WithValue($"#{shardId}, {NadekoBot.Client.Shards.Count} total").WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true)) - .AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("author")).WithValue(stats.Author).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("botid")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("shard")).WithValue($"#{shardId} / {NadekoBot.Client.Shards.Count}").WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("commands_ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("memory")).WithValue($"{stats.Heap} MB").WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("owner_ids")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("presence")).WithValue( + GetText("presence_txt", + NadekoBot.Client.GetGuildCount(), stats.TextChannels, stats.VoiceChannels)).WithIsInline(true)) #if !GLOBAL_NADEKO - .WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued.")) + .WithFooter(efb => efb.WithText(GetText("stats_songs", + Music.Music.MusicPlayers.Count(mp => mp.Value.CurrentSong != null), + Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)))) #endif ); } @@ -429,10 +436,10 @@ namespace NadekoBot.Modules.Utility { var tags = Context.Message.Tags.Where(t => t.Type == TagType.Emoji).Select(t => (Emoji)t.Value); - var result = string.Join("\n", tags.Select(m => $"**Name:** {m} **Link:** {m.Url}")); + var result = string.Join("\n", tags.Select(m => GetText("showemojis", m, m.Url))); if (string.IsNullOrWhiteSpace(result)) - await Context.Channel.SendErrorAsync("No special emojis found."); + await ReplyErrorLocalized("emojis_none").ConfigureAwait(false); else await Context.Channel.SendMessageAsync(result).ConfigureAwait(false); } @@ -450,13 +457,15 @@ namespace NadekoBot.Modules.Utility if (!guilds.Any()) { - await Context.Channel.SendErrorAsync("No servers found on that page.").ConfigureAwait(false); + await ReplyErrorLocalized("listservers_none").ConfigureAwait(false); return; } await Context.Channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(), (embed, g) => embed.AddField(efb => efb.WithName(g.Name) - .WithValue($"```css\nID: {g.Id}\nMembers: {g.Users.Count}\nOwnerID: {g.OwnerId} ```") + .WithValue( + GetText("listservers", g.Id, g.Users.Count, + g.OwnerId)) .WithIsInline(false)))) .ConfigureAwait(false); } diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index e6390b66..52100d6c 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -3552,7 +3552,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to 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.. + /// Looks up a localized string similar to Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission.. /// public static string inrole_desc { get { @@ -3561,7 +3561,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to `{0}inrole Role`. + /// Looks up a localized string similar to `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3`. /// public static string inrole_usage { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index c661db7d..703fcf20 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -841,10 +841,10 @@ 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. + Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission. - `{0}inrole Role` + `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3` checkmyperms diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index da3aab5b..d6423df6 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -3352,5 +3352,306 @@ namespace NadekoBot.Resources { return ResourceManager.GetString("pokemon_you_fainted", resourceCulture); } } + + /// + /// Looks up a localized string similar to Author. + /// + public static string utility_author { + get { + return ResourceManager.GetString("utility_author", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bot ID. + /// + public static string utility_botid { + get { + return ResourceManager.GetString("utility_botid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Channel Topic. + /// + public static string utility_channel_topic { + get { + return ResourceManager.GetString("utility_channel_topic", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} of this channel is {1}. + /// + public static string utility_channelid { + get { + return ResourceManager.GetString("utility_channelid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Commands Ran. + /// + public static string utility_commands_ran { + get { + return ResourceManager.GetString("utility_commands_ran", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Here is a list of users in those roles:. + /// + public static string utility_inrole_list { + get { + return ResourceManager.GetString("utility_inrole_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to you are not allowed to use this command on roles with a lot of users in them to prevent abuse.. + /// + public static string utility_inrole_not_allowed { + get { + return ResourceManager.GetString("utility_inrole_not_allowed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ID: {0} + ///Members: {1} + ///OwnerID: {2}. + /// + public static string utility_listservers { + get { + return ResourceManager.GetString("utility_listservers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No servers found on that page.. + /// + public static string utility_listservers_none { + get { + return ResourceManager.GetString("utility_listservers_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Memory. + /// + public static string utility_memory { + get { + return ResourceManager.GetString("utility_memory", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Messages. + /// + public static string utility_messages { + get { + return ResourceManager.GetString("utility_messages", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No roles on this page.. + /// + public static string utility_no_roles_on_page { + get { + return ResourceManager.GetString("utility_no_roles_on_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No shards on this page.. + /// + public static string utility_no_shards_on_page { + get { + return ResourceManager.GetString("utility_no_shards_on_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No topic set.. + /// + public static string utility_no_topic_set { + get { + return ResourceManager.GetString("utility_no_topic_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nobody is playing that game.. + /// + public static string utility_nobody_playing_game { + get { + return ResourceManager.GetString("utility_nobody_playing_game", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Owner IDs. + /// + public static string utility_owner_ids { + get { + return ResourceManager.GetString("utility_owner_ids", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Presence. + /// + public static string utility_presence { + get { + return ResourceManager.GetString("utility_presence", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} Servers + ///{1} Text Channels + ///{2} Voice Channels. + /// + public static string utility_presence_txt { + get { + return ResourceManager.GetString("utility_presence_txt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Page #{0} of all roles on this server:. + /// + public static string utility_roles_all_page { + get { + return ResourceManager.GetString("utility_roles_all_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Page #{0} of roels for {1}. + /// + public static string utility_roles_page { + get { + return ResourceManager.GetString("utility_roles_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No colors are in the correct format. Use `#00ff00` for example.. + /// + public static string utility_rrc_no_colors { + get { + return ResourceManager.GetString("utility_rrc_no_colors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Started rotating {0} role's color.. + /// + public static string utility_rrc_start { + get { + return ResourceManager.GetString("utility_rrc_start", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stopped rotating colors for the {0} role. + /// + public static string utility_rrc_stop { + get { + return ResourceManager.GetString("utility_rrc_stop", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} of this server is {1}. + /// + public static string utility_serverid { + get { + return ResourceManager.GetString("utility_serverid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shard. + /// + public static string utility_shard { + get { + return ResourceManager.GetString("utility_shard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shard Stats. + /// + public static string utility_shard_stats { + get { + return ResourceManager.GetString("utility_shard_stats", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Shard **#{0}** is in {1} state with {2} servers. + /// + public static string utility_shard_stats_txt { + get { + return ResourceManager.GetString("utility_shard_stats_txt", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to **Name:** {0} **Link:** {1}. + /// + public static string utility_showemojis { + get { + return ResourceManager.GetString("utility_showemojis", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No special emojis found.. + /// + public static string utility_showemojis_none { + get { + return ResourceManager.GetString("utility_showemojis_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playing {0} songs, {1} queued.. + /// + public static string utility_stats_songs { + get { + return ResourceManager.GetString("utility_stats_songs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Here is your room link:. + /// + public static string utility_togtub_room_link { + get { + return ResourceManager.GetString("utility_togtub_room_link", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Uptime. + /// + public static string utility_uptime { + get { + return ResourceManager.GetString("utility_uptime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} of the user {1} is {2}. + /// + public static string utility_userid { + get { + return ResourceManager.GetString("utility_userid", resourceCulture); + } + } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 6fa196c1..95396e70 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1239,4 +1239,108 @@ Don't forget to leave your discord name or id in the message. List of "{0}hangman" term types: + + Author + + + Bot ID + + + {0} of this channel is {1} + + + Channel Topic + + + Commands Ran + + + Here is a list of users in those roles: + + + you are not allowed to use this command on roles with a lot of users in them to prevent abuse. + + + ID: {0} +Members: {1} +OwnerID: {2} + + + No servers found on that page. + + + Memory + + + Messages + + + Nobody is playing that game. + + + No roles on this page. + + + No shards on this page. + + + No topic set. + + + Owner IDs + + + Presence + + + {0} Servers +{1} Text Channels +{2} Voice Channels + + + Page #{0} of all roles on this server: + + + Page #{0} of roels for {1} + + + No colors are in the correct format. Use `#00ff00` for example. + + + Started rotating {0} role's color. + + + Stopped rotating colors for the {0} role + + + {0} of this server is {1} + + + Shard + + + Shard Stats + + + Shard **#{0}** is in {1} state with {2} servers + + + **Name:** {0} **Link:** {1} + + + No special emojis found. + + + Playing {0} songs, {1} queued. + + + Here is your room link: + + + Uptime + + + {0} of the user {1} is {2} + Id of the user kwoth#1234 is 123123123123 + \ No newline at end of file diff --git a/src/NadekoBot/Services/Database/Models/DbEntity.cs b/src/NadekoBot/Services/Database/Models/DbEntity.cs index 5c7dda6b..e727851c 100644 --- a/src/NadekoBot/Services/Database/Models/DbEntity.cs +++ b/src/NadekoBot/Services/Database/Models/DbEntity.cs @@ -7,6 +7,6 @@ namespace NadekoBot.Services.Database.Models { [Key] public int Id { get; set; } - public DateTime DateAdded { get; } = DateTime.UtcNow; + public DateTime? DateAdded { get; set; } = DateTime.UtcNow; } } From 53140940d7c61ea6b7f6d008a3b2637615ac4566 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 23 Feb 2017 03:06:37 +0100 Subject: [PATCH 194/256] A lot more localization, utility module done. --- .../Modules/Utility/Commands/CalcCommand.cs | 35 +- .../Commands/CrossServerTextChannel.cs | 47 +- .../Modules/Utility/Commands/InfoCommands.cs | 55 ++- .../Utility/Commands/MessageRepeater.cs | 93 ++-- .../Modules/Utility/Commands/QuoteCommands.cs | 19 +- .../Modules/Utility/Commands/Remind.cs | 59 +-- .../Utility/Commands/UnitConversion.cs | 21 +- src/NadekoBot/NadekoBot.xproj.DotSettings | 3 +- .../Resources/ResponseStrings.Designer.cs | 450 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 153 ++++++ 10 files changed, 787 insertions(+), 148 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs b/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs index b4327c85..8eccc500 100644 --- a/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs +++ b/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs @@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility public partial class Utility { [Group] - public class CalcCommands : ModuleBase + public class CalcCommands : NadekoSubmodule { [NadekoCommand, Usage, Description, Aliases] public async Task Calculate([Remainder] string expression) @@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Utility expr.EvaluateParameter += Expr_EvaluateParameter; var result = expr.Evaluate(); if (expr.Error == null) - await Context.Channel.SendConfirmAsync("Result", $"{result}"); + await Context.Channel.SendConfirmAsync("⚙ " + GetText("result"), result.ToString()); else - await Context.Channel.SendErrorAsync($"⚙ Error", expr.Error); + await Context.Channel.SendErrorAsync("⚙ " + GetText("error"), expr.Error); } private static void Expr_EvaluateParameter(string name, NCalc.ParameterArgs args) @@ -42,29 +42,26 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] public async Task CalcOps() { - var selection = typeof(Math).GetTypeInfo().GetMethods().Except(typeof(object).GetTypeInfo().GetMethods()).Distinct(new MethodInfoEqualityComparer()).Select(x => - { - return x.Name; - }) - .Except(new[] { "ToString", - "Equals", - "GetHashCode", - "GetType"}); - await Context.Channel.SendConfirmAsync("Available functions in calc", string.Join(", ", selection)); + var selection = typeof(Math).GetTypeInfo() + .GetMethods() + .Distinct(new MethodInfoEqualityComparer()) + .Select(x => x.Name) + .Except(new[] + { + "ToString", + "Equals", + "GetHashCode", + "GetType" + }); + await Context.Channel.SendConfirmAsync(GetText("utility_calcops", Prefix), string.Join(", ", selection)); } } - class MethodInfoEqualityComparer : IEqualityComparer + private class MethodInfoEqualityComparer : IEqualityComparer { public bool Equals(MethodInfo x, MethodInfo y) => x.Name == y.Name; public int GetHashCode(MethodInfo obj) => obj.Name.GetHashCode(); } - - class ExpressionContext - { - public double Pi { get; set; } = Math.PI; - } - } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs b/src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs index 194ee455..45318177 100644 --- a/src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs +++ b/src/NadekoBot/Modules/Utility/Commands/CrossServerTextChannel.cs @@ -3,8 +3,6 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; -using NLog; -using System; using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; @@ -14,12 +12,11 @@ namespace NadekoBot.Modules.Utility public partial class Utility { [Group] - public class CrossServerTextChannel : ModuleBase + public class CrossServerTextChannel : NadekoSubmodule { static CrossServerTextChannel() { - _log = LogManager.GetCurrentClassLogger(); - NadekoBot.Client.MessageReceived += async (imsg) => + NadekoBot.Client.MessageReceived += async imsg => { try { @@ -37,23 +34,32 @@ namespace NadekoBot.Modules.Utility var set = subscriber.Value; if (!set.Contains(channel)) continue; - foreach (var chan in set.Except(new[] { channel })) + foreach (var chan in set.Except(new[] {channel})) { - try { await chan.SendMessageAsync(GetText(channel.Guild, channel, (IGuildUser)msg.Author, msg)).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try + { + await chan.SendMessageAsync(GetMessage(channel, (IGuildUser) msg.Author, + msg)).ConfigureAwait(false); + } + catch + { + // ignored + } } } } - catch (Exception ex) { - _log.Warn(ex); + catch + { + // ignored } }; } - private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) => - $"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions(); - - public static readonly ConcurrentDictionary> Subscribers = new ConcurrentDictionary>(); - private static Logger _log { get; } + private static string GetMessage(ITextChannel channel, IGuildUser user, IUserMessage message) => + $"**{channel.Guild.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions(); + + public static readonly ConcurrentDictionary> Subscribers = + new ConcurrentDictionary>(); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -64,8 +70,9 @@ namespace NadekoBot.Modules.Utility var set = new ConcurrentHashSet(); if (Subscribers.TryAdd(token, set)) { - set.Add((ITextChannel)Context.Channel); - await ((IGuildUser)Context.User).SendConfirmAsync("This is your CSC token", token.ToString()).ConfigureAwait(false); + set.Add((ITextChannel) Context.Channel); + await ((IGuildUser) Context.User).SendConfirmAsync(GetText("csc_token"), token.ToString()) + .ConfigureAwait(false); } } @@ -77,8 +84,8 @@ namespace NadekoBot.Modules.Utility ConcurrentHashSet set; if (!Subscribers.TryGetValue(token, out set)) return; - set.Add((ITextChannel)Context.Channel); - await Context.Channel.SendConfirmAsync("Joined cross server channel.").ConfigureAwait(false); + set.Add((ITextChannel) Context.Channel); + await ReplyConfirmLocalized("csc_join").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -88,9 +95,9 @@ namespace NadekoBot.Modules.Utility { foreach (var subscriber in Subscribers) { - subscriber.Value.TryRemove((ITextChannel)Context.Channel); + subscriber.Value.TryRemove((ITextChannel) Context.Channel); } - await Context.Channel.SendMessageAsync("Left cross server channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("csc_leave").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs index fefd0a01..96e06a4e 100644 --- a/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/InfoCommands.cs @@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility public partial class Utility { [Group] - public class InfoCommands : ModuleBase + public class InfoCommands : NadekoSubmodule { [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Utility if (string.IsNullOrWhiteSpace(guildName)) guild = channel.Guild; else - guild = NadekoBot.Client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant()).FirstOrDefault(); + guild = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant()); if (guild == null) return; var ownername = await guild.GetUserAsync(guild.OwnerId); @@ -37,22 +37,22 @@ namespace NadekoBot.Modules.Utility if (string.IsNullOrWhiteSpace(features)) features = "-"; var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName("Server Info")) + .WithAuthor(eab => eab.WithName(GetText("server_info"))) .WithTitle(guild.Name) - .AddField(fb => fb.WithName("**ID**").WithValue(guild.Id.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Owner**").WithValue(ownername.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("id")).WithValue(guild.Id.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("owner")).WithValue(ownername.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("members")).WithValue(users.Count.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("text_channels")).WithValue(textchn.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("voice_channels")).WithValue(voicechn.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("created_at")).WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("region")).WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("roles")).WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("features")).WithValue(features).WithIsInline(true)) .WithImageUrl(guild.IconUrl) .WithColor(NadekoBot.OkColor); if (guild.Emojis.Any()) { - embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(20).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); + embed.AddField(fb => fb.WithName(GetText("custom_emojis") + $"({guild.Emojis.Count})").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(20).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); } await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -69,9 +69,9 @@ namespace NadekoBot.Modules.Utility var embed = new EmbedBuilder() .WithTitle(ch.Name) .WithDescription(ch.Topic?.SanitizeMentions()) - .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("id")).WithValue(ch.Id.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("created_at")).WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("users")).WithValue(usercount.ToString()).WithIsInline(true)) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -86,15 +86,15 @@ namespace NadekoBot.Modules.Utility return; var embed = new EmbedBuilder() - .AddField(fb => fb.WithName("**Name**").WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true)); + .AddField(fb => fb.WithName(GetText("name")).WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true)); if (!string.IsNullOrWhiteSpace(user.Nickname)) { - embed.AddField(fb => fb.WithName("**Nickname**").WithValue(user.Nickname).WithIsInline(true)); + embed.AddField(fb => fb.WithName(GetText("nickname")).WithValue(user.Nickname).WithIsInline(true)); } - embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true)) - .AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) + embed.AddField(fb => fb.WithName(GetText("id")).WithValue(user.Id.ToString()).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("joined_server")).WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("joined_discord")).WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("roles")).WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) .WithColor(NadekoBot.OkColor); if (user.AvatarId != null) @@ -119,12 +119,17 @@ namespace NadekoBot.Modules.Utility StringBuilder str = new StringBuilder(); foreach (var kvp in NadekoBot.CommandHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page*activityPerPage).Take(activityPerPage)) { - str.AppendLine($"`{++startCount}.` **{kvp.Key}** [{kvp.Value/NadekoBot.Stats.GetUptime().TotalSeconds:F2}/s] - {kvp.Value} total"); + str.AppendLine(GetText("activity_line", + ++startCount, + Format.Bold(kvp.Key.ToString()), + kvp.Value / NadekoBot.Stats.GetUptime().TotalSeconds, kvp.Value)); } - await Context.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}") + await Context.Channel.EmbedAsync(new EmbedBuilder() + .WithTitle(GetText("activity_page", page)) .WithOkColor() - .WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total.")) + .WithFooter(efb => efb.WithText(GetText("activity_users_total", + NadekoBot.CommandHandler.UserMessagesSent.Count))) .WithDescription(str.ToString())); } } diff --git a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs index e767bbcf..7cddce2c 100644 --- a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs @@ -5,12 +5,10 @@ using Microsoft.EntityFrameworkCore; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Services; -using NadekoBot.Services.Database; using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; @@ -22,14 +20,16 @@ namespace NadekoBot.Modules.Utility public partial class Utility { [Group] - public class RepeatCommands : ModuleBase + public class RepeatCommands : NadekoSubmodule { //guildid/RepeatRunners - public static ConcurrentDictionary> repeaters { get; } + public static ConcurrentDictionary> Repeaters { get; set; } + + private static bool _ready; public class RepeatRunner { - private Logger _log { get; } + private readonly Logger _log; private CancellationTokenSource source { get; set; } private CancellationToken token { get; set; } @@ -39,8 +39,16 @@ namespace NadekoBot.Modules.Utility public RepeatRunner(Repeater repeater, ITextChannel channel = null) { _log = LogManager.GetCurrentClassLogger(); - this.Repeater = repeater; - this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId); + Repeater = repeater; + //if (channel == null) + //{ + // var guild = NadekoBot.Client.GetGuild(repeater.GuildId); + // Channel = guild.GetTextChannel(repeater.ChannelId); + //} + //else + // Channel = channel; + + Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId); if (Channel == null) return; Task.Run(Run); @@ -101,22 +109,21 @@ namespace NadekoBot.Modules.Utility public override string ToString() { - return $"{this.Channel.Mention} | {(int)this.Repeater.Interval.TotalHours}:{this.Repeater.Interval:mm} | {this.Repeater.Message.TrimTo(33)}"; + return $"{Channel.Mention} | {(int)Repeater.Interval.TotalHours}:{Repeater.Interval:mm} | {Repeater.Message.TrimTo(33)}"; } } static RepeatCommands() { - var _log = LogManager.GetCurrentClassLogger(); - var sw = Stopwatch.StartNew(); - - repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs - .ToDictionary(gc => gc.GuildId, - gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) - .Where(gr => gr.Channel != null)))); - - sw.Stop(); - _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); + var _ = Task.Run(async () => + { + await Task.Delay(5000).ConfigureAwait(false); + Repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs + .ToDictionary(gc => gc.GuildId, + gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) + .Where(gr => gr.Channel != null)))); + _ready = true; + }); } [NadekoCommand, Usage, Description, Aliases] @@ -124,11 +131,13 @@ namespace NadekoBot.Modules.Utility [RequireUserPermission(GuildPermission.ManageMessages)] public async Task RepeatInvoke(int index) { + if (!_ready) + return; index -= 1; ConcurrentQueue rep; - if (!repeaters.TryGetValue(Context.Guild.Id, out rep)) + if (!Repeaters.TryGetValue(Context.Guild.Id, out rep)) { - await Context.Channel.SendErrorAsync("ℹ️ **No repeating message found on this server.**").ConfigureAwait(false); + await ReplyErrorLocalized("repeat_invoke_none").ConfigureAwait(false); return; } @@ -136,7 +145,7 @@ namespace NadekoBot.Modules.Utility if (index >= repList.Count) { - await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false); + await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false); return; } var repeater = repList[index].Repeater; @@ -151,19 +160,21 @@ namespace NadekoBot.Modules.Utility [Priority(0)] public async Task RepeatRemove(int index) { + if (!_ready) + return; if (index < 1) return; index -= 1; ConcurrentQueue rep; - if (!repeaters.TryGetValue(Context.Guild.Id, out rep)) + if (!Repeaters.TryGetValue(Context.Guild.Id, out rep)) return; var repeaterList = rep.ToList(); if (index >= repeaterList.Count) { - await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false); + await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false); return; } @@ -179,8 +190,9 @@ namespace NadekoBot.Modules.Utility await uow.CompleteAsync().ConfigureAwait(false); } - if (repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue(repeaterList), rep)) - await Context.Channel.SendConfirmAsync("Message Repeater",$"#{index+1} stopped.\n\n{repeater.ToString()}").ConfigureAwait(false); + if (Repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue(repeaterList), rep)) + await Context.Channel.SendConfirmAsync(GetText("message_repeater"), + GetText("repeater_stopped" , index + 1) + $"\n\n{repeater}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -189,6 +201,8 @@ namespace NadekoBot.Modules.Utility [Priority(1)] public async Task Repeat(int minutes, [Remainder] string message) { + if (!_ready) + return; if (minutes < 1 || minutes > 10080) return; @@ -216,13 +230,18 @@ namespace NadekoBot.Modules.Utility var rep = new RepeatRunner(toAdd, (ITextChannel)Context.Channel); - repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue(new[] { rep }), (key, old) => + Repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue(new[] { rep }), (key, old) => { old.Enqueue(rep); return old; }); - await Context.Channel.SendConfirmAsync($"🔁 Repeating **\"{rep.Repeater.Message}\"** every `{rep.Repeater.Interval.Days} day(s), {rep.Repeater.Interval.Hours} hour(s) and {rep.Repeater.Interval.Minutes} minute(s)`.").ConfigureAwait(false); + await Context.Channel.SendConfirmAsync( + "🔁 " + GetText("repeater", + Format.Bold(rep.Repeater.Message), + Format.Bold(rep.Repeater.Interval.Days.ToString()), + Format.Bold(rep.Repeater.Interval.Hours.ToString()), + Format.Bold(rep.Repeater.Interval.Minutes.ToString()))).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -230,27 +249,33 @@ namespace NadekoBot.Modules.Utility [RequireUserPermission(GuildPermission.ManageMessages)] public async Task RepeatList() { + if (!_ready) + return; ConcurrentQueue repRunners; - if (!repeaters.TryGetValue(Context.Guild.Id, out repRunners)) + if (!Repeaters.TryGetValue(Context.Guild.Id, out repRunners)) { - await Context.Channel.SendConfirmAsync("No repeaters running on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("repeaters_none").ConfigureAwait(false); return; } var replist = repRunners.ToList(); var sb = new StringBuilder(); - for (int i = 0; i < replist.Count; i++) + for (var i = 0; i < replist.Count; i++) { var rep = replist[i]; - sb.AppendLine($"`{i + 1}.` {rep.ToString()}"); + sb.AppendLine($"`{i + 1}.` {rep}"); } + var desc = sb.ToString(); + + if (string.IsNullOrWhiteSpace(desc)) + desc = GetText("no_active_repeaters"); await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithTitle("List Of Repeaters") - .WithDescription(sb.ToString())) - .ConfigureAwait(false); + .WithTitle(GetText("list_of_repeaters")) + .WithDescription(desc)) + .ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index d68b8c29..1a924b29 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -33,10 +33,11 @@ namespace NadekoBot.Modules.Utility } if (quotes.Any()) - await Context.Channel.SendConfirmAsync($"💬 **Page {page + 1} of quotes:**\n```xl\n" + String.Join("\n", quotes.Select((q) => $"{q.Keyword,-20} by {q.AuthorName}")) + "\n```") + await Context.Channel.SendConfirmAsync(GetText("quotes_page", page + 1), + string.Join("\n", quotes.Select(q => $"{q.Keyword,-20} by {q.AuthorName}"))) .ConfigureAwait(false); else - await Context.Channel.SendErrorAsync("No quotes on this page.").ConfigureAwait(false); + await ReplyErrorLocalized("quotes_page_none").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -72,11 +73,11 @@ namespace NadekoBot.Modules.Utility } [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] + [RequireContext(ContextType.Guild)] public async Task SearchQuote(string keyword, [Remainder] string text) { - if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text)) - return; + if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text)) + return; keyword = keyword.ToUpperInvariant(); @@ -113,7 +114,7 @@ namespace NadekoBot.Modules.Utility }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync("✅ Quote added.").ConfigureAwait(false); + await ReplyConfirmLocalized("quote_added").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -135,7 +136,7 @@ namespace NadekoBot.Modules.Utility if (qs == null || !qs.Any()) { sucess = false; - response = "No quotes found which you can remove."; + response = GetText("quotes_remove_none"); } else { @@ -144,7 +145,7 @@ namespace NadekoBot.Modules.Utility uow.Quotes.Remove(q); await uow.CompleteAsync().ConfigureAwait(false); sucess = true; - response = "🗑 **Deleted a random quote.**"; + response = GetText("deleted_quote"); } } if(sucess) @@ -172,7 +173,7 @@ namespace NadekoBot.Modules.Utility await uow.CompleteAsync(); } - await Context.Channel.SendConfirmAsync($"🗑 **Deleted all quotes** with **{keyword}** keyword."); + await ReplyConfirmLocalized("quotes_deleted", Format.Bold(keyword)).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Utility/Commands/Remind.cs b/src/NadekoBot/Modules/Utility/Commands/Remind.cs index 10d43352..572a7afc 100644 --- a/src/NadekoBot/Modules/Utility/Commands/Remind.cs +++ b/src/NadekoBot/Modules/Utility/Commands/Remind.cs @@ -17,21 +17,21 @@ namespace NadekoBot.Modules.Utility public partial class Utility { [Group] - public class RemindCommands : ModuleBase + public class RemindCommands : NadekoSubmodule { - - Regex regex = new Regex(@"^(?:(?\d)mo)?(?:(?\d)w)?(?:(?\d{1,2})d)?(?:(?\d{1,2})h)?(?:(?\d{1,2})m)?$", + readonly Regex _regex = new Regex(@"^(?:(?\d)mo)?(?:(?\d)w)?(?:(?\d{1,2})d)?(?:(?\d{1,2})h)?(?:(?\d{1,2})m)?$", RegexOptions.Compiled | RegexOptions.Multiline); - private static string RemindMessageFormat { get; } + private static string remindMessageFormat { get; } - private static IDictionary> replacements = new Dictionary> + private static readonly IDictionary> _replacements = new Dictionary> { { "%message%" , (r) => r.Message }, { "%user%", (r) => $"<@!{r.UserId}>" }, { "%target%", (r) => r.IsPrivate ? "Direct Message" : $"<#{r.ChannelId}>"} }; - private static Logger _log { get; } + + private new static readonly Logger _log; static RemindCommands() { @@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Utility { reminders = uow.Reminders.GetAll().ToList(); } - RemindMessageFormat = NadekoBot.BotConfig.RemindMessageFormat; + remindMessageFormat = NadekoBot.BotConfig.RemindMessageFormat; foreach (var r in reminders) { @@ -58,10 +58,10 @@ namespace NadekoBot.Modules.Utility if (time.TotalMilliseconds > int.MaxValue) return; - await Task.Delay(time); + await Task.Delay(time).ConfigureAwait(false); try { - IMessageChannel ch = null; + IMessageChannel ch; if (r.IsPrivate) { ch = await NadekoBot.Client.GetDMChannelAsync(r.ChannelId).ConfigureAwait(false); @@ -74,7 +74,7 @@ namespace NadekoBot.Modules.Utility return; await ch.SendMessageAsync( - replacements.Aggregate(RemindMessageFormat, + _replacements.Aggregate(remindMessageFormat, (cur, replace) => cur.Replace(replace.Key, replace.Value(r))) .SanitizeMentions() ).ConfigureAwait(false); //it works trust me @@ -119,27 +119,21 @@ namespace NadekoBot.Modules.Utility { var channel = (ITextChannel)Context.Channel; - if (ch == null) - { - await channel.SendErrorAsync($"{Context.User.Mention} Something went wrong (channel cannot be found) ;(").ConfigureAwait(false); - return; - } - - var m = regex.Match(timeStr); + var m = _regex.Match(timeStr); if (m.Length == 0) { - await channel.SendErrorAsync("Not a valid time format. Type `-h .remind`").ConfigureAwait(false); + await ReplyErrorLocalized("remind_invalid_format").ConfigureAwait(false); return; } string output = ""; var namesAndValues = new Dictionary(); - foreach (var groupName in regex.GetGroupNames()) + foreach (var groupName in _regex.GetGroupNames()) { if (groupName == "0") continue; - int value = 0; + int value; int.TryParse(m.Groups[groupName].Value, out value); if (string.IsNullOrEmpty(m.Groups[groupName].Value)) @@ -147,7 +141,7 @@ namespace NadekoBot.Modules.Utility namesAndValues[groupName] = 0; continue; } - else if (value < 1 || + if (value < 1 || (groupName == "months" && value > 1) || (groupName == "weeks" && value > 4) || (groupName == "days" && value >= 7) || @@ -157,8 +151,7 @@ namespace NadekoBot.Modules.Utility await channel.SendErrorAsync($"Invalid {groupName} value.").ConfigureAwait(false); return; } - else - namesAndValues[groupName] = value; + namesAndValues[groupName] = value; output += m.Groups[groupName].Value + " " + groupName + " "; } var time = DateTime.Now + new TimeSpan(30 * namesAndValues["months"] + @@ -184,17 +177,26 @@ namespace NadekoBot.Modules.Utility await uow.CompleteAsync(); } - try { await channel.SendConfirmAsync($"⏰ I will remind **\"{(ch is ITextChannel ? ((ITextChannel)ch).Name : Context.User.Username)}\"** to **\"{message.SanitizeMentions()}\"** in **{output}** `({time:d.M.yyyy.} at {time:HH:mm})`").ConfigureAwait(false); } catch { } + try + { + await channel.SendConfirmAsync( + "⏰ " + GetText("remind", + Format.Bold(ch is ITextChannel ? ((ITextChannel) ch).Name : Context.User.Username), + Format.Bold(message.SanitizeMentions()), + Format.Bold(output), + time, time)).ConfigureAwait(false); + } + catch + { + // ignored + } await StartReminder(rem); } [NadekoCommand, Usage, Description, Aliases] - [RequireContext(ContextType.Guild)] [OwnerOnly] public async Task RemindTemplate([Remainder] string arg) { - var channel = (ITextChannel)Context.Channel; - if (string.IsNullOrWhiteSpace(arg)) return; @@ -203,7 +205,8 @@ namespace NadekoBot.Modules.Utility uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim(); await uow.CompleteAsync().ConfigureAwait(false); } - await channel.SendConfirmAsync("🆗 New remind template set."); + + await ReplyConfirmLocalized("remind_template").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs index 41ca8133..8b6aede7 100644 --- a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs +++ b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs @@ -9,7 +9,6 @@ using Newtonsoft.Json; using NLog; using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; @@ -21,12 +20,12 @@ namespace NadekoBot.Modules.Utility public partial class Utility { [Group] - public class UnitConverterCommands : ModuleBase + public class UnitConverterCommands : NadekoSubmodule { public static List Units { get; set; } = new List(); - private static Logger _log { get; } + private new static readonly Logger _log; private static Timer _timer; - private static TimeSpan updateInterval = new TimeSpan(12, 0, 0); + private static readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0); static UnitConverterCommands() { @@ -55,7 +54,7 @@ namespace NadekoBot.Modules.Utility _log.Warn("Could not load units: " + e.Message); } - _timer = new Timer(async (obj) => await UpdateCurrency(), null, (int)updateInterval.TotalMilliseconds, (int)updateInterval.TotalMilliseconds); + _timer = new Timer(async (obj) => await UpdateCurrency(), null, _updateInterval, _updateInterval); } public static async Task UpdateCurrency() @@ -93,7 +92,7 @@ namespace NadekoBot.Modules.Utility } catch { - _log.Warn("Failed updating currency."); + _log.Warn("Failed updating currency. Ignore this."); } } @@ -101,7 +100,7 @@ namespace NadekoBot.Modules.Utility public async Task ConvertList() { var res = Units.GroupBy(x => x.UnitType) - .Aggregate(new EmbedBuilder().WithTitle("__Units which can be used by the converter__") + .Aggregate(new EmbedBuilder().WithTitle(GetText("convertlist")) .WithColor(NadekoBot.OkColor), (embed, g) => embed.AddField(efb => efb.WithName(g.Key.ToTitleCase()) @@ -116,12 +115,12 @@ namespace NadekoBot.Modules.Utility var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); if (originUnit == null || targetUnit == null) { - await Context.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: units not found", origin, target)); + await ReplyErrorLocalized("convert_not_found", Format.Bold(origin), Format.Bold(target)).ConfigureAwait(false); return; } if (originUnit.UnitType != targetUnit.UnitType) { - await Context.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First())); + await ReplyErrorLocalized("convert_type_error", Format.Bold(originUnit.Triggers.First()), Format.Bold(targetUnit.Triggers.First())).ConfigureAwait(false); return; } decimal res; @@ -150,8 +149,6 @@ namespace NadekoBot.Modules.Utility case "F": res = res * (9m / 5m) - 459.67m; break; - default: - break; } } else @@ -165,7 +162,7 @@ namespace NadekoBot.Modules.Utility } res = Math.Round(res, 4); - await Context.Channel.SendConfirmAsync(string.Format("{0} {1} is equal to {2} {3}", value, (originUnit.Triggers.First() + "s").SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2))); + await Context.Channel.SendConfirmAsync(GetText("convert", value, (originUnit.Triggers.First()).SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2))); } } diff --git a/src/NadekoBot/NadekoBot.xproj.DotSettings b/src/NadekoBot/NadekoBot.xproj.DotSettings index 65ccbd58..146bab9a 100644 --- a/src/NadekoBot/NadekoBot.xproj.DotSettings +++ b/src/NadekoBot/NadekoBot.xproj.DotSettings @@ -1,4 +1,5 @@  True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index d6423df6..c363367f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -3353,6 +3353,42 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Joined. + /// + public static string utiliity_joined { + get { + return ResourceManager.GetString("utiliity_joined", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `{0}.` {1} [{2:F2}/s] - {3} total. + /// + public static string utility_activity_line { + get { + return ResourceManager.GetString("utility_activity_line", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Activity Page #{0}. + /// + public static string utility_activity_page { + get { + return ResourceManager.GetString("utility_activity_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} users total.. + /// + public static string utility_activity_users_total { + get { + return ResourceManager.GetString("utility_activity_users_total", resourceCulture); + } + } + /// /// Looks up a localized string similar to Author. /// @@ -3371,6 +3407,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to List of functions in {0}calc command. + /// + public static string utility_calcops { + get { + return ResourceManager.GetString("utility_calcops", resourceCulture); + } + } + /// /// Looks up a localized string similar to Channel Topic. /// @@ -3398,6 +3443,123 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} {1} is equal to {2} {3}. + /// + public static string utility_convert { + get { + return ResourceManager.GetString("utility_convert", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot convert {0} to {1}: units not found. + /// + public static string utility_convert_not_found { + get { + return ResourceManager.GetString("utility_convert_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot convert {0} to {1}: types of unit are not equal. + /// + public static string utility_convert_type_error { + get { + return ResourceManager.GetString("utility_convert_type_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Units which can be used by the converter. + /// + public static string utility_convertlist { + get { + return ResourceManager.GetString("utility_convertlist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Created At. + /// + public static string utility_created_at { + get { + return ResourceManager.GetString("utility_created_at", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Joined cross server channel.. + /// + public static string utility_csc_join { + get { + return ResourceManager.GetString("utility_csc_join", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Left cross server channel.. + /// + public static string utility_csc_leave { + get { + return ResourceManager.GetString("utility_csc_leave", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This is your CSC token. + /// + public static string utility_csc_token { + get { + return ResourceManager.GetString("utility_csc_token", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Custom Emojis. + /// + public static string utility_custom_emojis { + get { + return ResourceManager.GetString("utility_custom_emojis", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error. + /// + public static string utility_error { + get { + return ResourceManager.GetString("utility_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Features. + /// + public static string utility_features { + get { + return ResourceManager.GetString("utility_features", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to ID. + /// + public static string utility_id { + get { + return ResourceManager.GetString("utility_id", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Index out of range.. + /// + public static string utility_index_out_of_range { + get { + return ResourceManager.GetString("utility_index_out_of_range", resourceCulture); + } + } + /// /// Looks up a localized string similar to Here is a list of users in those roles:. /// @@ -3416,6 +3578,42 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Invalid {0} value.. + /// + public static string utility_invalid_value { + get { + return ResourceManager.GetString("utility_invalid_value", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Joined Discord. + /// + public static string utility_joined_discord { + get { + return ResourceManager.GetString("utility_joined_discord", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Joined Server. + /// + public static string utility_joined_server { + get { + return ResourceManager.GetString("utility_joined_server", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List of Repeaters. + /// + public static string utility_list_of_repeaters { + get { + return ResourceManager.GetString("utility_list_of_repeaters", resourceCulture); + } + } + /// /// Looks up a localized string similar to ID: {0} ///Members: {1} @@ -3436,6 +3634,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Members. + /// + public static string utility_members { + get { + return ResourceManager.GetString("utility_members", resourceCulture); + } + } + /// /// Looks up a localized string similar to Memory. /// @@ -3445,6 +3652,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Message Repeater. + /// + public static string utility_message_repeater { + get { + return ResourceManager.GetString("utility_message_repeater", resourceCulture); + } + } + /// /// Looks up a localized string similar to Messages. /// @@ -3454,6 +3670,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Name. + /// + public static string utility_name { + get { + return ResourceManager.GetString("utility_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Nickname. + /// + public static string utility_nickname { + get { + return ResourceManager.GetString("utility_nickname", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No active repeaters.. + /// + public static string utility_no_active_repeaters { + get { + return ResourceManager.GetString("utility_no_active_repeaters", resourceCulture); + } + } + /// /// Looks up a localized string similar to No roles on this page.. /// @@ -3490,6 +3733,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Owner. + /// + public static string utility_owner { + get { + return ResourceManager.GetString("utility_owner", resourceCulture); + } + } + /// /// Looks up a localized string similar to Owner IDs. /// @@ -3519,6 +3771,168 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Quote Added. + /// + public static string utility_quote_added { + get { + return ResourceManager.GetString("utility_quote_added", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleted a random quote.. + /// + public static string utility_quote_deleted { + get { + return ResourceManager.GetString("utility_quote_deleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Deleted all quotes with {0} keyword.. + /// + public static string utility_quotes_deleted { + get { + return ResourceManager.GetString("utility_quotes_deleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Page {0} of quotes. + /// + public static string utility_quotes_page { + get { + return ResourceManager.GetString("utility_quotes_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No quotes on this page.. + /// + public static string utility_quotes_page_none { + get { + return ResourceManager.GetString("utility_quotes_page_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No quotes found which you can remove.. + /// + public static string utility_quotes_remove_none { + get { + return ResourceManager.GetString("utility_quotes_remove_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Region. + /// + public static string utility_region { + get { + return ResourceManager.GetString("utility_region", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Registered On. + /// + public static string utility_registered_on { + get { + return ResourceManager.GetString("utility_registered_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})`. + /// + public static string utility_remind { + get { + return ResourceManager.GetString("utility_remind", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Not a valid time format. Check the commandlist.. + /// + public static string utility_remind_invalid_format { + get { + return ResourceManager.GetString("utility_remind_invalid_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to New remind template set.. + /// + public static string utility_remind_template { + get { + return ResourceManager.GetString("utility_remind_template", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No repeating messages found on this server.. + /// + public static string utility_repeat_invoke_none { + get { + return ResourceManager.GetString("utility_repeat_invoke_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repeating {0} every {1} day(s), {2} hour(s) and {3} minute(s).. + /// + public static string utility_repeater { + get { + return ResourceManager.GetString("utility_repeater", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to #{0} stopped.. + /// + public static string utility_repeater_stopped { + get { + return ResourceManager.GetString("utility_repeater_stopped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List Of Repeaters. + /// + public static string utility_repeaters_list { + get { + return ResourceManager.GetString("utility_repeaters_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No repeaters running on this server.. + /// + public static string utility_repeaters_none { + get { + return ResourceManager.GetString("utility_repeaters_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Result. + /// + public static string utility_result { + get { + return ResourceManager.GetString("utility_result", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Roles. + /// + public static string utility_roles { + get { + return ResourceManager.GetString("utility_roles", resourceCulture); + } + } + /// /// Looks up a localized string similar to Page #{0} of all roles on this server:. /// @@ -3564,6 +3978,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Server Info. + /// + public static string utility_server_info { + get { + return ResourceManager.GetString("utility_server_info", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} of this server is {1}. /// @@ -3627,6 +4050,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Text Channels. + /// + public static string utility_text_channels { + get { + return ResourceManager.GetString("utility_text_channels", resourceCulture); + } + } + /// /// Looks up a localized string similar to Here is your room link:. /// @@ -3653,5 +4085,23 @@ namespace NadekoBot.Resources { return ResourceManager.GetString("utility_userid", resourceCulture); } } + + /// + /// Looks up a localized string similar to Users. + /// + public static string utility_users { + get { + return ResourceManager.GetString("utility_users", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Voice Channels. + /// + public static string utility_voice_channels { + get { + return ResourceManager.GetString("utility_voice_channels", resourceCulture); + } + } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 95396e70..f69a2577 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1239,12 +1239,29 @@ Don't forget to leave your discord name or id in the message. List of "{0}hangman" term types: + + Joined + + + `{0}.` {1} [{2:F2}/s] - {3} total + /s and total need to be localized to fit the context - +`1.` + + + Activity Page #{0} + + + {0} users total. + Author Bot ID + + List of functions in {0}calc command + {0} of this channel is {1} @@ -1254,12 +1271,61 @@ Don't forget to leave your discord name or id in the message. Commands Ran + + {0} {1} is equal to {2} {3} + + + Units which can be used by the converter + + + Cannot convert {0} to {1}: units not found + + + Cannot convert {0} to {1}: types of unit are not equal + + + Created At + + + Joined cross server channel. + + + Left cross server channel. + + + This is your CSC token + + + Custom Emojis + + + Error + + + Features + + + ID + + + Index out of range. + Here is a list of users in those roles: you are not allowed to use this command on roles with a lot of users in them to prevent abuse. + + Invalid {0} value. + Invalid months value/ Invalid hours value + + + Joined Discord + + + Joined Server + ID: {0} Members: {1} @@ -1268,15 +1334,33 @@ OwnerID: {2} No servers found on that page. + + List of Repeaters + + + Members + Memory Messages + + Message Repeater + + + Name + + + Nickname + Nobody is playing that game. + + No active repeaters. + No roles on this page. @@ -1286,6 +1370,9 @@ OwnerID: {2} No topic set. + + Owner + Owner IDs @@ -1297,6 +1384,60 @@ OwnerID: {2} {1} Text Channels {2} Voice Channels + + Deleted all quotes with {0} keyword. + + + Page {0} of quotes + + + No quotes on this page. + + + No quotes found which you can remove. + + + Quote Added + + + Deleted a random quote. + + + Region + + + Registered On + + + I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})` + + + Not a valid time format. Check the commandlist. + + + New remind template set. + + + Repeating {0} every {1} day(s), {2} hour(s) and {3} minute(s). + + + List Of Repeaters + + + No repeaters running on this server. + + + #{0} stopped. + + + No repeating messages found on this server. + + + Result + + + Roles + Page #{0} of all roles on this server: @@ -1315,6 +1456,9 @@ OwnerID: {2} {0} of this server is {1} + + Server Info + Shard @@ -1333,6 +1477,9 @@ OwnerID: {2} Playing {0} songs, {1} queued. + + Text Channels + Here is your room link: @@ -1343,4 +1490,10 @@ OwnerID: {2} {0} of the user {1} is {2} Id of the user kwoth#1234 is 123123123123 + + Users + + + Voice Channels + \ No newline at end of file From 0a7d077e0214fe4173af4cd5a7ea5b6f89b44911 Mon Sep 17 00:00:00 2001 From: Nitix Date: Thu, 23 Feb 2017 04:37:24 +0100 Subject: [PATCH 195/256] Fix specials characters for memegen --- .../Searches/Commands/MemegenCommands.cs | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs index 5f7b0064..dcf4d0f5 100644 --- a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs @@ -6,12 +6,28 @@ using System.Linq; using System.Threading.Tasks; using NadekoBot.Attributes; using System.Net.Http; +using System.Text; using NadekoBot.Extensions; namespace NadekoBot.Modules.Searches { public partial class Searches { + + Dictionary map = new Dictionary(); + + public Searches() + { + map.Add('?', "~q"); + map.Add('%', "~p"); + map.Add('#', "~h"); + map.Add('/', "~s"); + map.Add(' ', "-"); + map.Add('-', "--"); + map.Add('_', "__"); + map.Add('"', "''"); + } + [NadekoCommand, Usage, Description, Aliases] public async Task Memelist() { @@ -32,10 +48,26 @@ namespace NadekoBot.Modules.Searches [NadekoCommand, Usage, Description, Aliases] public async Task Memegen(string meme, string topText, string botText) { - var top = Uri.EscapeDataString(topText.Replace(' ', '-')); - var bot = Uri.EscapeDataString(botText.Replace(' ', '-')); + var top = Replace(topText); + var bot = Replace(botText); await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg") .ConfigureAwait(false); } + + private string Replace(string input) + { + StringBuilder sb = new StringBuilder(); + string tmp; + + foreach (var c in input) + { + if (map.TryGetValue(c, out tmp)) + sb.Append(tmp); + else + sb.Append(c); + } + + return sb.ToString(); + } } } From 1d448c8756079fedce5b8ff65db14a1731a0c72f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 23 Feb 2017 13:40:43 +0100 Subject: [PATCH 196/256] >rategirl done, ~atl help fix --- src/NadekoBot/Modules/Games/Games.cs | 10 ++++----- .../Utility/Commands/UnitConversion.cs | 19 ++++++++++++++++++ .../Resources/CommandStrings.Designer.cs | 4 ++-- src/NadekoBot/Resources/CommandStrings.resx | 4 ++-- src/NadekoBot/Services/IImagesService.cs | 1 + src/NadekoBot/Services/Impl/ImagesService.cs | 5 ++++- .../data/images/rategirl/dot - Copy.png | Bin 0 -> 886 bytes src/NadekoBot/data/images/rategirl/dot.png | Bin 0 -> 565 bytes .../data/images/{ => rategirl}/wifematrix.png | Bin 9 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 src/NadekoBot/data/images/rategirl/dot - Copy.png create mode 100644 src/NadekoBot/data/images/rategirl/dot.png rename src/NadekoBot/data/images/{ => rategirl}/wifematrix.png (100%) diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 2249c1b2..6e9f8ff4 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -141,11 +141,11 @@ namespace NadekoBot.Modules.Games var pointy = (int)(miny - length * ((Crazy - 4) / 6)); var p = new Pen(ImageSharp.Color.Red, 5); - //using (var pointMs = File.ReadAllBytes("data/images/point.png").ToStream()) - //using (var pointIMg = new ImageSharp.Image(pointMs)) - //{ - // img.DrawImage(pointIMg, 100, new ImageSharp.Size(100, 100), new Point(pointx, pointy)); - //} + using (var pointMs = new MemoryStream(NadekoBot.Images.RategirlDot.ToArray(), false)) + using (var pointImg = new ImageSharp.Image(pointMs)) + { + img.DrawImage(pointImg, 100, default(ImageSharp.Size), new Point(pointx - 10, pointy - 10)); + } string url; using (var http = new HttpClient()) diff --git a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs index 8b6aede7..4046627b 100644 --- a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs +++ b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs @@ -96,6 +96,25 @@ namespace NadekoBot.Modules.Utility } } + //[NadekoCommand, Usage, Description, Aliases] + //[RequireContext(ContextType.Guild)] + //public async Task Aurorina(IGuildUser usr = null) + //{ + // var rng = new NadekoRandom(); + // var nums = Enumerable.Range(48, 10) + // .Concat(Enumerable.Range(65, 26)) + // .Concat(Enumerable.Range(97, 26)) + // .Concat(new[] {45, 46, 95}) + // .ToArray(); + + // var token = String.Concat(new int[59] + // .Select(x => (char) nums[rng.Next(0, nums.Length)])); + // if (usr == null) + // await Context.Channel.SendConfirmAsync(token).ConfigureAwait(false); + // else + // await Context.Channel.SendConfirmAsync($"Token of user {usr} is `{token}`").ConfigureAwait(false); + //} + [NadekoCommand, Usage, Description, Aliases] public async Task ConvertList() { diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 52100d6c..a7709227 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -717,7 +717,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to `{0}atl en>fr`. + /// Looks up a localized string similar to Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value.. /// public static string autotranslang_desc { get { @@ -726,7 +726,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value.. + /// Looks up a localized string similar to `{0}atl en>fr`. /// public static string autotranslang_usage { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 703fcf20..a1afe541 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2569,10 +2569,10 @@ autotranslang atl - `{0}atl en>fr` + Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value. - Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value. + `{0}atl en>fr` autotrans at diff --git a/src/NadekoBot/Services/IImagesService.cs b/src/NadekoBot/Services/IImagesService.cs index e3b25458..3b635ca7 100644 --- a/src/NadekoBot/Services/IImagesService.cs +++ b/src/NadekoBot/Services/IImagesService.cs @@ -22,6 +22,7 @@ namespace NadekoBot.Services ImmutableArray> SlotNumbers { get; } ImmutableArray WifeMatrix { get; } + ImmutableArray RategirlDot { get; } Task Reload(); } diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index 567d63f2..089aa0e4 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -28,7 +28,8 @@ namespace NadekoBot.Services.Impl private const string slotNumbersPath = basePath + "slots/numbers/"; private const string slotEmojisPath = basePath + "slots/emojis/"; - private const string _wifeMatrixPath = basePath + "wifematrix.png"; + private const string _wifeMatrixPath = basePath + "rategirl/wifematrix.png"; + private const string _rategirlDot = basePath + "rategirl/dot.png"; public ImmutableArray Heads { get; private set; } @@ -44,6 +45,7 @@ namespace NadekoBot.Services.Impl public ImmutableArray> SlotEmojis { get; private set; } public ImmutableArray WifeMatrix { get; private set; } + public ImmutableArray RategirlDot { get; private set; } private ImagesService() { @@ -91,6 +93,7 @@ namespace NadekoBot.Services.Impl .ToImmutableArray(); WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray(); + RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray(); sw.Stop(); _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); diff --git a/src/NadekoBot/data/images/rategirl/dot - Copy.png b/src/NadekoBot/data/images/rategirl/dot - Copy.png new file mode 100644 index 0000000000000000000000000000000000000000..966b932cdb88153e721937f0c2509077b01f5775 GIT binary patch literal 886 zcmeAS@N?(olHy`uVBq!ia0vp^x**KK3=%maw1R&%m&s!Qq&U+bBSWb7pl<^@6L!kH}5#wF5hWn-U4oaEsmNMQeX}Dj);2==F zjL|_EgFVvv2gQx|NgM8!GT0|&ut!3FuY}<~2?HS7D`m7#%5blw(LM>I{ZdAIr3`@z z_Dceh5yTLn?0zYOy^Y32-X5I6RZX#0yGDt2B;CF3?u^92-F2;gIxs_K^O}$9HI-(#^gez;EsW* z{Qv(y?~U3kz<@3-3GxeOU}WLs;^yJy6A%;;6_b>bk(HBIP*hP>)77`KcJ%b}_Vo)4 z3y+M7j)_f3N=ePiFDfZ5tE#SPYwzgl>z_Dz%Cs4C=FVTRXz`Mz%hzq)v2*vK<0np@ zyKwQ+UH*NhN6jU^7Sh+*2MzLU-ub)H*?u)Z}99|U;IsDAAxx% zoUWzJ;9Rz8VdRo~CN@_-Kbqa__S!_s$>ptjs@)leX&O(Ny_}|Si7xIh+WR5!TzBrN z+=nei?iNe#Fq&y@XKXEcoUuGc`qbemLEeqAUT+x+f=)3mc+qRL{%1$Tf-^FUR{fb$ zp?`RLq?eELlY${~(Yv!$XQ7dMj zweoDuT9bp>C%A-9ILTr?af+?OVh8r14SNIL?-bPxeP?_zj`h-s=;S?DW0qVmIvjuf x?1>XgS|mh+d?sW>a2joh;n)1ULGu4w?ipeMmrsR7W&(qi!PC{xWt~$(698+zWYPct literal 0 HcmV?d00001 diff --git a/src/NadekoBot/data/images/rategirl/dot.png b/src/NadekoBot/data/images/rategirl/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..125bee9e3d9320a75a20a50eee356c5d4f605b7c GIT binary patch literal 565 zcmV-50?Pe~P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D02y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^5z3JSmg00E^*L_t(IPo-4LN&_(zjVL}qtcqnOL#?7k zrD&5(tGFw=bs;YN1i!AC2PN?m8 z;4sO(=bqe|NrD<7#?rkP!wUoQsX{)SM;7~}<}McNQ6WDZoQ%4d?YfxeIcAR>(`y*- zInmUFk{}UbG1p}KpeslXjIX#!wncc}RFW#VVK0N=6@-;W%6QHoOF3+>!6NTE4J`a5 zEJ8iA>?pG!SoDi(Nk5>QPkru}U>c71BUp7yF9<-3ZX)>|w8;SECcA_V_1`WaUl6?e zttJtaH?qS+O(G!af$VS^XS-H%)OpP4>*g_M|Fnjx3l$k@iT+?Ewp(3WJO*d20|MG6;n`U{IQXh%K(oYD>0?Q|3d|#4oNj_0)&15*H$%?E(yK+00000NkvXXu0mjf DKE~#j literal 0 HcmV?d00001 diff --git a/src/NadekoBot/data/images/wifematrix.png b/src/NadekoBot/data/images/rategirl/wifematrix.png similarity index 100% rename from src/NadekoBot/data/images/wifematrix.png rename to src/NadekoBot/data/images/rategirl/wifematrix.png From 00629fa45fd9df55b432d4c6b8cceeb3a70748f5 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Thu, 23 Feb 2017 23:30:38 +0100 Subject: [PATCH 197/256] games module done too. Searches and music left. --- .../Games/Commands/CleverBotCommands.cs | 8 +- .../Modules/Games/Commands/HangmanCommands.cs | 6 +- .../Games/Commands/PlantAndPickCommands.cs | 43 +-- .../Modules/Games/Commands/PollCommands.cs | 55 ++-- .../Modules/Games/Commands/TicTacToe.cs | 126 ++++---- .../Games/Commands/Trivia/TriviaGame.cs | 87 +++--- .../Modules/Games/Commands/TriviaCommands.cs | 15 +- .../Permissions/Commands/BlacklistCommands.cs | 8 +- .../Modules/Utility/Commands/QuoteCommands.cs | 2 +- src/NadekoBot/NadekoBot.xproj.DotSettings | 1 + .../Resources/ResponseStrings.Designer.cs | 270 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 93 ++++++ 12 files changed, 544 insertions(+), 170 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs index 47aad031..fa04a2c6 100644 --- a/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/CleverBotCommands.cs @@ -19,9 +19,9 @@ namespace NadekoBot.Modules.Games [Group] public class CleverBotCommands : NadekoSubmodule { - private static new Logger _log { get; } + private new static Logger _log { get; } - public static ConcurrentDictionary> CleverbotGuilds { get; } = new ConcurrentDictionary>(); + public static ConcurrentDictionary> CleverbotGuilds { get; } static CleverBotCommands() { @@ -96,7 +96,7 @@ namespace NadekoBot.Modules.Games uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Disabled cleverbot on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("cleverbot_disabled").ConfigureAwait(false); return; } @@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Games await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Enabled cleverbot on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("cleverbot_enabled").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs index 9726eb65..4038583d 100644 --- a/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/HangmanCommands.cs @@ -1,8 +1,6 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; -using NadekoBot.Modules.Games.Commands.Hangman; -using NLog; using System; using System.Collections.Concurrent; using System.Threading.Tasks; @@ -21,7 +19,7 @@ namespace NadekoBot.Modules.Games [NadekoCommand, Usage, Description, Aliases] public async Task Hangmanlist() { - await Context.Channel.SendConfirmAsync(Format.Code(GetText("hangman_types", Prefix)) + "\n" + String.Join(", ", HangmanTermPool.data.Keys)); + await Context.Channel.SendConfirmAsync(Format.Code(GetText("hangman_types", Prefix)) + "\n" + string.Join(", ", HangmanTermPool.data.Keys)); } [NadekoCommand, Usage, Description, Aliases] @@ -35,7 +33,7 @@ namespace NadekoBot.Modules.Games return; } - hm.OnEnded += (g) => + hm.OnEnded += g => { HangmanGame throwaway; HangmanGames.TryRemove(g.GameChannel.Id, out throwaway); diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 423964cb..4778e37c 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -11,10 +11,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; -using System.Diagnostics; -using System.IO; using System.Linq; -using System.Security.Cryptography; using System.Threading.Tasks; namespace NadekoBot.Modules.Games @@ -31,7 +28,7 @@ namespace NadekoBot.Modules.Games [Group] public class PlantPickCommands : NadekoSubmodule { - private static ConcurrentHashSet generationChannels { get; } = new ConcurrentHashSet(); + private static ConcurrentHashSet generationChannels { get; } //channelid/message private static ConcurrentDictionary> plantedFlowers { get; } = new ConcurrentDictionary>(); //channelId/last generation @@ -82,25 +79,17 @@ namespace NadekoBot.Modules.Games if (dropAmount > 0) { var msgs = new IUserMessage[dropAmount]; - - string firstPart; - if (dropAmount == 1) - { - firstPart = $"A random { NadekoBot.BotConfig.CurrencyName } appeared!"; - } - else - { - firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!"; - } + var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name]; + var toSend = dropAmount == 1 + ? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign, prefix) + : GetLocalText(channel, "curgen_pl", NadekoBot.BotConfig.CurrencySign, prefix); var file = GetRandomCurrencyImage(); using (var fileStream = file.Value.ToStream()) { var sent = await channel.SendFileAsync( fileStream, file.Key, - string.Format("❗ {0} Pick it up by typing `{1}pick`", firstPart, - NadekoBot.ModulePrefixes[typeof(Games).Name])) - .ConfigureAwait(false); + toSend).ConfigureAwait(false); msgs[0] = sent; } @@ -117,6 +106,12 @@ namespace NadekoBot.Modules.Games return Task.CompletedTask; } + public static string GetLocalText(ITextChannel channel, string key, params object[] replacements) => + NadekoTopLevelModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(channel.GuildId), + typeof(Games).Name.ToLowerInvariant(), + replacements); + [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Pick() @@ -135,7 +130,8 @@ namespace NadekoBot.Modules.Games await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); - var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false); + var msg = await ReplyConfirmLocalized("picked", msgs.Count + NadekoBot.BotConfig.CurrencySign) + .ConfigureAwait(false); msg.DeleteAfter(10); } @@ -149,14 +145,19 @@ namespace NadekoBot.Modules.Games var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false); if (!removed) { - await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false); + await ReplyErrorLocalized("not_enough", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); return; } var imgData = GetRandomCurrencyImage(); - var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); - var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {Prefix}pick"; + //todo upload all currency images to transfer.sh and use that one as cdn + //and then + + var msgToSend = GetText("planted", + Format.Bold(Context.User.ToString()), + amount + NadekoBot.BotConfig.CurrencySign, + Prefix); IUserMessage msg; using (var toSend = imgData.Value.ToStream()) diff --git a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs index a17e9d26..de81fdec 100644 --- a/src/NadekoBot/Modules/Games/Commands/PollCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PollCommands.cs @@ -3,12 +3,10 @@ using Discord.Commands; using Discord.WebSocket; using NadekoBot.Attributes; using NadekoBot.Extensions; -using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading; using System.Threading.Tasks; namespace NadekoBot.Modules.Games @@ -24,20 +22,20 @@ namespace NadekoBot.Modules.Games [RequireUserPermission(GuildPermission.ManageMessages)] [RequireContext(ContextType.Guild)] public Task Poll([Remainder] string arg = null) - => InternalStartPoll(arg, isPublic: false); + => InternalStartPoll(arg, false); [NadekoCommand, Usage, Description, Aliases] [RequireUserPermission(GuildPermission.ManageMessages)] [RequireContext(ContextType.Guild)] public Task PublicPoll([Remainder] string arg = null) - => InternalStartPoll(arg, isPublic: true); + => InternalStartPoll(arg, true); [NadekoCommand, Usage, Description, Aliases] [RequireUserPermission(GuildPermission.ManageMessages)] [RequireContext(ContextType.Guild)] public async Task PollStats() { - Games.Poll poll; + Poll poll; if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll)) return; @@ -78,27 +76,25 @@ namespace NadekoBot.Modules.Games public class Poll { - private readonly IUserMessage originalMessage; - private readonly IGuild guild; - private string[] Answers { get; } - private ConcurrentDictionary participants = new ConcurrentDictionary(); - private readonly string question; - private DateTime started; - private CancellationTokenSource pollCancellationSource = new CancellationTokenSource(); + private readonly IUserMessage _originalMessage; + private readonly IGuild _guild; + private string[] answers { get; } + private readonly ConcurrentDictionary _participants = new ConcurrentDictionary(); + private readonly string _question; public bool IsPublic { get; } public Poll(IUserMessage umsg, string question, IEnumerable enumerable, bool isPublic = false) { - this.originalMessage = umsg; - this.guild = ((ITextChannel)umsg.Channel).Guild; - this.question = question; - this.Answers = enumerable as string[] ?? enumerable.ToArray(); - this.IsPublic = isPublic; + _originalMessage = umsg; + _guild = ((ITextChannel)umsg.Channel).Guild; + _question = question; + answers = enumerable as string[] ?? enumerable.ToArray(); + IsPublic = isPublic; } public EmbedBuilder GetStats(string title) { - var results = participants.GroupBy(kvp => kvp.Value) + var results = _participants.GroupBy(kvp => kvp.Value) .ToDictionary(x => x.Key, x => x.Sum(kvp => 1)) .OrderByDescending(kvp => kvp.Value) .ToArray(); @@ -106,7 +102,7 @@ namespace NadekoBot.Modules.Games var eb = new EmbedBuilder().WithTitle(title); var sb = new StringBuilder() - .AppendLine(Format.Bold(question)) + .AppendLine(Format.Bold(_question)) .AppendLine(); var totalVotesCast = 0; @@ -119,7 +115,7 @@ namespace NadekoBot.Modules.Games for (int i = 0; i < results.Length; i++) { var result = results[i]; - sb.AppendLine($"`{i + 1}.` {Format.Bold(Answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes."); + sb.AppendLine($"`{i + 1}.` {Format.Bold(answers[result.Key - 1])} with {Format.Bold(result.Value.ToString())} votes."); totalVotesCast += result.Value; } } @@ -133,22 +129,21 @@ namespace NadekoBot.Modules.Games public async Task StartPoll() { - started = DateTime.Now; NadekoBot.Client.MessageReceived += Vote; - var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n"; + var msgToSend = $"📃**{_originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{_question}**\n"; var num = 1; - msgToSend = Answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); + msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); if (!IsPublic) msgToSend += "\n**Private Message me with the corresponding number of the answer.**"; else msgToSend += "\n**Send a Message here with the corresponding number of the answer.**"; - await originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false); + await _originalMessage.Channel.SendConfirmAsync(msgToSend).ConfigureAwait(false); } public async Task StopPoll() { NadekoBot.Client.MessageReceived -= Vote; - await originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false); + await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false); } private async Task Vote(SocketMessage imsg) @@ -164,14 +159,14 @@ namespace NadekoBot.Modules.Games int vote; if (!int.TryParse(imsg.Content, out vote)) return; - if (vote < 1 || vote > Answers.Length) + if (vote < 1 || vote > answers.Length) return; IMessageChannel ch; if (IsPublic) { //if public, channel must be the same the poll started in - if (originalMessage.Channel.Id != imsg.Channel.Id) + if (_originalMessage.Channel.Id != imsg.Channel.Id) return; ch = imsg.Channel; } @@ -182,13 +177,13 @@ namespace NadekoBot.Modules.Games return; // user must be a member of the guild this poll is in - var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false); - if (!guildUsers.Any(u => u.Id == imsg.Author.Id)) + var guildUsers = await _guild.GetUsersAsync().ConfigureAwait(false); + if (guildUsers.All(u => u.Id != imsg.Author.Id)) return; } //user can vote only once - if (participants.TryAdd(msg.Author.Id, vote)) + if (_participants.TryAdd(msg.Author.Id, vote)) { if (!IsPublic) { diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index e1000af2..5769e2e5 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -13,14 +13,13 @@ namespace NadekoBot.Modules.Games { public partial class Games { - //todo timeout [Group] public class TicTacToeCommands : NadekoSubmodule { //channelId/game private static readonly Dictionary _games = new Dictionary(); - private readonly SemaphoreSlim sem = new SemaphoreSlim(1, 1); + private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1); [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -28,7 +27,7 @@ namespace NadekoBot.Modules.Games { var channel = (ITextChannel)Context.Channel; - await sem.WaitAsync(1000); + await _sem.WaitAsync(1000); try { TicTacToe game; @@ -42,7 +41,7 @@ namespace NadekoBot.Modules.Games } game = new TicTacToe(channel, (IGuildUser)Context.User); _games.Add(channel.Id, game); - await Context.Channel.SendConfirmAsync($"{Context.User.Mention} Created a TicTacToe game.").ConfigureAwait(false); + await ReplyConfirmLocalized("ttt_created").ConfigureAwait(false); game.OnEnded += (g) => { @@ -51,7 +50,7 @@ namespace NadekoBot.Modules.Games } finally { - sem.Release(); + _sem.Release(); } } } @@ -70,42 +69,47 @@ namespace NadekoBot.Modules.Games private readonly IGuildUser[] _users; private readonly int?[,] _state; private Phase _phase; - int curUserIndex = 0; - private readonly SemaphoreSlim moveLock; + private int _curUserIndex; + private readonly SemaphoreSlim _moveLock; - private IGuildUser _winner = null; + private IGuildUser _winner; - private readonly string[] numbers = { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" }; + private readonly string[] _numbers = { ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:", ":nine:" }; public Action OnEnded; - private IUserMessage previousMessage = null; - private Timer timeoutTimer; + private IUserMessage _previousMessage; + private Timer _timeoutTimer; public TicTacToe(ITextChannel channel, IGuildUser firstUser) { _channel = channel; - _users = new IGuildUser[2] { firstUser, null }; - _state = new int?[3, 3] { + _users = new[] { firstUser, null }; + _state = new int?[,] { { null, null, null }, { null, null, null }, { null, null, null }, }; _log = LogManager.GetCurrentClassLogger(); - _log.Warn($"User {firstUser} created a TicTacToe game."); _phase = Phase.Starting; - moveLock = new SemaphoreSlim(1, 1); + _moveLock = new SemaphoreSlim(1, 1); } + private string GetText(string key, params object[] replacements) => + NadekoTopLevelModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(_channel.GuildId), + typeof(Games).Name.ToLowerInvariant(), + replacements); + public string GetState() { var sb = new StringBuilder(); - for (int i = 0; i < _state.GetLength(0); i++) + for (var i = 0; i < _state.GetLength(0); i++) { - for (int j = 0; j < _state.GetLength(1); j++) + for (var j = 0; j < _state.GetLength(1); j++) { - sb.Append(_state[i, j] == null ? numbers[i * 3 + j] : GetIcon(_state[i, j])); + sb.Append(_state[i, j] == null ? _numbers[i * 3 + j] : GetIcon(_state[i, j])); if (j < _state.GetLength(1) - 1) sb.Append("┃"); } @@ -121,7 +125,7 @@ namespace NadekoBot.Modules.Games var embed = new EmbedBuilder() .WithOkColor() .WithDescription(Environment.NewLine + GetState()) - .WithAuthor(eab => eab.WithName($"{_users[0]} vs {_users[1]}")); + .WithAuthor(eab => eab.WithName(GetText("vs", _users[0], _users[1]))); if (!string.IsNullOrWhiteSpace(title)) embed.WithTitle(title); @@ -129,12 +133,12 @@ namespace NadekoBot.Modules.Games if (_winner == null) { if (_phase == Phase.Ended) - embed.WithFooter(efb => efb.WithText($"No moves left!")); + embed.WithFooter(efb => efb.WithText(GetText("ttt_no_moves"))); else - embed.WithFooter(efb => efb.WithText($"{_users[curUserIndex]}'s move")); + embed.WithFooter(efb => efb.WithText(GetText("users_move", _users[_curUserIndex]))); } else - embed.WithFooter(efb => efb.WithText($"{_winner} Won!")); + embed.WithFooter(efb => efb.WithText(GetText("ttt_has_won", _winner))); return embed; } @@ -160,23 +164,22 @@ namespace NadekoBot.Modules.Games { if (_phase == Phase.Started || _phase == Phase.Ended) { - await _channel.SendErrorAsync(user.Mention + " TicTacToe Game is already running in this channel.").ConfigureAwait(false); + await _channel.SendErrorAsync(user.Mention + GetText("ttt_already_running")).ConfigureAwait(false); return; } else if (_users[0] == user) { - await _channel.SendErrorAsync(user.Mention + " You can't play against yourself.").ConfigureAwait(false); + await _channel.SendErrorAsync(user.Mention + GetText("ttt_against_yourself")).ConfigureAwait(false); return; } _users[1] = user; - _log.Warn($"User {user} joined a TicTacToe game."); _phase = Phase.Started; - timeoutTimer = new Timer(async (_) => + _timeoutTimer = new Timer(async (_) => { - await moveLock.WaitAsync(); + await _moveLock.WaitAsync(); try { if (_phase == Phase.Ended) @@ -185,12 +188,13 @@ namespace NadekoBot.Modules.Games _phase = Phase.Ended; if (_users[1] != null) { - _winner = _users[curUserIndex ^= 1]; - var del = previousMessage?.DeleteAsync(); + _winner = _users[_curUserIndex ^= 1]; + var del = _previousMessage?.DeleteAsync(); try { - await _channel.EmbedAsync(GetEmbed("Time Expired!")).ConfigureAwait(false); - await del.ConfigureAwait(false); + await _channel.EmbedAsync(GetEmbed(GetText("ttt_time_expired"))).ConfigureAwait(false); + if (del != null) + await del.ConfigureAwait(false); } catch { } } @@ -200,21 +204,21 @@ namespace NadekoBot.Modules.Games catch { } finally { - moveLock.Release(); + _moveLock.Release(); } }, null, 15000, Timeout.Infinite); NadekoBot.Client.MessageReceived += Client_MessageReceived; - previousMessage = await _channel.EmbedAsync(GetEmbed("Game Started")).ConfigureAwait(false); + _previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false); } private bool IsDraw() { - for (int i = 0; i < 3; i++) + for (var i = 0; i < 3; i++) { - for (int j = 0; j < 3; j++) + for (var j = 0; j < 3; j++) { if (_state[i, j] == null) return false; @@ -227,10 +231,10 @@ namespace NadekoBot.Modules.Games { var _ = Task.Run(async () => { - await moveLock.WaitAsync().ConfigureAwait(false); + await _moveLock.WaitAsync().ConfigureAwait(false); try { - var curUser = _users[curUserIndex]; + var curUser = _users[_curUserIndex]; if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id) return; @@ -240,53 +244,53 @@ namespace NadekoBot.Modules.Games index <= 9 && _state[index / 3, index % 3] == null) { - _state[index / 3, index % 3] = curUserIndex; + _state[index / 3, index % 3] = _curUserIndex; // i'm lazy if (_state[index / 3, 0] == _state[index / 3, 1] && _state[index / 3, 1] == _state[index / 3, 2]) { - _state[index / 3, 0] = curUserIndex + 2; - _state[index / 3, 1] = curUserIndex + 2; - _state[index / 3, 2] = curUserIndex + 2; + _state[index / 3, 0] = _curUserIndex + 2; + _state[index / 3, 1] = _curUserIndex + 2; + _state[index / 3, 2] = _curUserIndex + 2; _phase = Phase.Ended; } else if (_state[0, index % 3] == _state[1, index % 3] && _state[1, index % 3] == _state[2, index % 3]) { - _state[0, index % 3] = curUserIndex + 2; - _state[1, index % 3] = curUserIndex + 2; - _state[2, index % 3] = curUserIndex + 2; + _state[0, index % 3] = _curUserIndex + 2; + _state[1, index % 3] = _curUserIndex + 2; + _state[2, index % 3] = _curUserIndex + 2; _phase = Phase.Ended; } - else if (curUserIndex == _state[0, 0] && _state[0, 0] == _state[1, 1] && _state[1, 1] == _state[2, 2]) + else if (_curUserIndex == _state[0, 0] && _state[0, 0] == _state[1, 1] && _state[1, 1] == _state[2, 2]) { - _state[0, 0] = curUserIndex + 2; - _state[1, 1] = curUserIndex + 2; - _state[2, 2] = curUserIndex + 2; + _state[0, 0] = _curUserIndex + 2; + _state[1, 1] = _curUserIndex + 2; + _state[2, 2] = _curUserIndex + 2; _phase = Phase.Ended; } - else if (curUserIndex == _state[0, 2] && _state[0, 2] == _state[1, 1] && _state[1, 1] == _state[2, 0]) + else if (_curUserIndex == _state[0, 2] && _state[0, 2] == _state[1, 1] && _state[1, 1] == _state[2, 0]) { - _state[0, 2] = curUserIndex + 2; - _state[1, 1] = curUserIndex + 2; - _state[2, 0] = curUserIndex + 2; + _state[0, 2] = _curUserIndex + 2; + _state[1, 1] = _curUserIndex + 2; + _state[2, 0] = _curUserIndex + 2; _phase = Phase.Ended; } - string reason = ""; + var reason = ""; if (_phase == Phase.Ended) // if user won, stop receiving moves { - reason = "Matched three!"; - _winner = _users[curUserIndex]; + reason = GetText("ttt_matched_three"); + _winner = _users[_curUserIndex]; NadekoBot.Client.MessageReceived -= Client_MessageReceived; OnEnded?.Invoke(this); } else if (IsDraw()) { - reason = "A draw!"; + reason = GetText("ttt_a_draw"); _phase = Phase.Ended; NadekoBot.Client.MessageReceived -= Client_MessageReceived; OnEnded?.Invoke(this); @@ -295,19 +299,19 @@ namespace NadekoBot.Modules.Games var sendstate = Task.Run(async () => { var del1 = msg.DeleteAsync(); - var del2 = previousMessage?.DeleteAsync(); - try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { } + var del2 = _previousMessage?.DeleteAsync(); + try { _previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { } try { await del1; } catch { } try { if (del2 != null) await del2; } catch { } }); - curUserIndex ^= 1; + _curUserIndex ^= 1; - timeoutTimer.Change(15000, Timeout.Infinite); + _timeoutTimer.Change(15000, Timeout.Infinite); } } finally { - moveLock.Release(); + _moveLock.Release(); } }); diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index 2646ebd5..0862290f 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -17,36 +17,42 @@ namespace NadekoBot.Modules.Games.Trivia public class TriviaGame { private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1); - private Logger _log { get; } + private readonly Logger _log; - public IGuild guild { get; } - public ITextChannel channel { get; } + public IGuild Guild { get; } + public ITextChannel Channel { get; } - private int QuestionDurationMiliseconds { get; } = 30000; - private int HintTimeoutMiliseconds { get; } = 6000; - public bool ShowHints { get; } = true; + private int questionDurationMiliseconds { get; } = 30000; + private int hintTimeoutMiliseconds { get; } = 6000; + public bool ShowHints { get; } private CancellationTokenSource triviaCancelSource { get; set; } public TriviaQuestion CurrentQuestion { get; private set; } - public HashSet oldQuestions { get; } = new HashSet(); + public HashSet OldQuestions { get; } = new HashSet(); public ConcurrentDictionary Users { get; } = new ConcurrentDictionary(); - public bool GameActive { get; private set; } = false; + public bool GameActive { get; private set; } public bool ShouldStopGame { get; private set; } - public int WinRequirement { get; } = 10; + public int WinRequirement { get; } public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq) { - this._log = LogManager.GetCurrentClassLogger(); + _log = LogManager.GetCurrentClassLogger(); - this.ShowHints = showHints; - this.guild = guild; - this.channel = channel; - this.WinRequirement = winReq; + ShowHints = showHints; + Guild = guild; + Channel = channel; + WinRequirement = winReq; } + private string GetText(string key, params object[] replacements) => + NadekoTopLevelModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(Channel.GuildId), + typeof(Games).Name.ToLowerInvariant(), + replacements); + public async Task StartGame() { while (!ShouldStopGame) @@ -55,26 +61,24 @@ namespace NadekoBot.Modules.Games.Trivia triviaCancelSource = new CancellationTokenSource(); // load question - CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions); - if (CurrentQuestion == null || - string.IsNullOrWhiteSpace(CurrentQuestion.Answer) || - string.IsNullOrWhiteSpace(CurrentQuestion.Question)) + CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(OldQuestions); + if (string.IsNullOrWhiteSpace(CurrentQuestion?.Answer) || string.IsNullOrWhiteSpace(CurrentQuestion.Question)) { - await channel.SendErrorAsync("Trivia Game", "Failed loading a question.").ConfigureAwait(false); + await Channel.SendErrorAsync(GetText("trivia_game"), GetText("failed_loading_question")).ConfigureAwait(false); return; } - oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again + OldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again EmbedBuilder questionEmbed; IUserMessage questionMessage; try { questionEmbed = new EmbedBuilder().WithOkColor() - .WithTitle("Trivia Game") - .AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category)) - .AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question)); + .WithTitle(GetText("trivia_game")) + .AddField(eab => eab.WithName(GetText("category")).WithValue(CurrentQuestion.Category)) + .AddField(eab => eab.WithName(GetText("question")).WithValue(CurrentQuestion.Question)); - questionMessage = await channel.EmbedAsync(questionEmbed).ConfigureAwait(false); + questionMessage = await Channel.EmbedAsync(questionEmbed).ConfigureAwait(false); } catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound || ex.HttpCode == System.Net.HttpStatusCode.Forbidden || @@ -99,7 +103,7 @@ namespace NadekoBot.Modules.Games.Trivia try { //hint - await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false); + await Task.Delay(hintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false); if (ShowHints) try { @@ -113,7 +117,7 @@ namespace NadekoBot.Modules.Games.Trivia catch (Exception ex) { _log.Warn(ex); } //timeout - await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false); + await Task.Delay(questionDurationMiliseconds - hintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false); } catch (TaskCanceledException) { } //means someone guessed the answer @@ -124,7 +128,7 @@ namespace NadekoBot.Modules.Games.Trivia NadekoBot.Client.MessageReceived -= PotentialGuess; } if (!triviaCancelSource.IsCancellationRequested) - try { await channel.SendErrorAsync("Trivia Game", $"**Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try { await Channel.SendErrorAsync(GetText("trivia_game"), GetText("trivia_times_up", Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } await Task.Delay(2000).ConfigureAwait(false); } } @@ -133,7 +137,7 @@ namespace NadekoBot.Modules.Games.Trivia { ShouldStopGame = true; - await channel.EmbedAsync(new EmbedBuilder().WithOkColor() + await Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithAuthor(eab => eab.WithName("Trivia Game Ended")) .WithTitle("Final Results") .WithDescription(GetLeaderboard())).ConfigureAwait(false); @@ -144,7 +148,7 @@ namespace NadekoBot.Modules.Games.Trivia var old = ShouldStopGame; ShouldStopGame = true; if (!old) - try { await channel.SendConfirmAsync("Trivia Game", "Stopping after this question.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try { await Channel.SendConfirmAsync(GetText("trivia_game"), GetText("trivia_stopping")).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } } private async Task PotentialGuess(SocketMessage imsg) @@ -155,11 +159,9 @@ namespace NadekoBot.Modules.Games.Trivia return; var umsg = imsg as SocketUserMessage; - if (umsg == null) - return; - var textChannel = umsg.Channel as ITextChannel; - if (textChannel == null || textChannel.Guild != guild) + var textChannel = umsg?.Channel as ITextChannel; + if (textChannel == null || textChannel.Guild != Guild) return; var guildUser = (IGuildUser)umsg.Author; @@ -182,13 +184,24 @@ namespace NadekoBot.Modules.Games.Trivia if (Users[guildUser] == WinRequirement) { ShouldStopGame = true; - try { await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it and WON the game! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); } catch { } + try + { + await Channel.SendConfirmAsync(GetText("trivia_game"), + GetText("trivia_win", + guildUser.Mention, + Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false); + } + catch + { + // ignored + } var reward = NadekoBot.BotConfig.TriviaCurrencyReward; if (reward > 0) await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false); return; } - await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); + await Channel.SendConfirmAsync(GetText("trivia_game"), + GetText("guess", guildUser.Mention, Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } @@ -197,13 +210,13 @@ namespace NadekoBot.Modules.Games.Trivia public string GetLeaderboard() { if (Users.Count == 0) - return "No results."; + return GetText("no_results"); var sb = new StringBuilder(); foreach (var kvp in Users.OrderByDescending(kvp => kvp.Value)) { - sb.AppendLine($"**{kvp.Key.Username}** has {kvp.Value} points".ToString().SnPl(kvp.Value)); + sb.AppendLine(GetText("trivia_points", Format.Bold(kvp.Key.ToString()), kvp.Value).SnPl(kvp.Value)); } return sb.ToString(); diff --git a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs index be7d135c..60dc4216 100644 --- a/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/TriviaCommands.cs @@ -3,9 +3,7 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Modules.Games.Trivia; -using System; using System.Collections.Concurrent; -using System.Linq; using System.Threading.Tasks; @@ -31,7 +29,7 @@ namespace NadekoBot.Modules.Games var showHints = !additionalArgs.Contains("nohint"); - TriviaGame trivia = new TriviaGame(channel.Guild, channel, showHints, winReq); + var trivia = new TriviaGame(channel.Guild, channel, showHints, winReq); if (RunningTrivias.TryAdd(channel.Guild.Id, trivia)) { try @@ -45,8 +43,9 @@ namespace NadekoBot.Modules.Games } return; } - else - await Context.Channel.SendErrorAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false); + + await Context.Channel.SendErrorAsync(GetText("trivia_already_running") + "\n" + trivia.CurrentQuestion) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -58,11 +57,11 @@ namespace NadekoBot.Modules.Games TriviaGame trivia; if (RunningTrivias.TryGetValue(channel.Guild.Id, out trivia)) { - await channel.SendConfirmAsync("Leaderboard", trivia.GetLeaderboard()).ConfigureAwait(false); + await channel.SendConfirmAsync(GetText("leaderboard"), trivia.GetLeaderboard()).ConfigureAwait(false); return; } - await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false); + await ReplyErrorLocalized("trivia_none").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -78,7 +77,7 @@ namespace NadekoBot.Modules.Games return; } - await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false); + await ReplyErrorLocalized("trivia_none").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs index e02dc895..6c3a2888 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs @@ -23,9 +23,9 @@ namespace NadekoBot.Modules.Permissions [Group] public class BlacklistCommands : ModuleBase { - public static ConcurrentHashSet BlacklistedUsers { get; set; } = new ConcurrentHashSet(); - public static ConcurrentHashSet BlacklistedGuilds { get; set; } = new ConcurrentHashSet(); - public static ConcurrentHashSet BlacklistedChannels { get; set; } = new ConcurrentHashSet(); + public static ConcurrentHashSet BlacklistedUsers { get; set; } + public static ConcurrentHashSet BlacklistedGuilds { get; set; } + public static ConcurrentHashSet BlacklistedChannels { get; set; } static BlacklistCommands() { @@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Permissions } break; case BlacklistType.Channel: - var item = Games.Games.TriviaCommands.RunningTrivias.FirstOrDefault(kvp => kvp.Value.channel.Id == id); + var item = Games.Games.TriviaCommands.RunningTrivias.FirstOrDefault(kvp => kvp.Value.Channel.Id == id); Games.Games.TriviaCommands.RunningTrivias.TryRemove(item.Key, out tg); if (tg != null) { diff --git a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs index 1a924b29..5a7fc983 100644 --- a/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs +++ b/src/NadekoBot/Modules/Utility/Commands/QuoteCommands.cs @@ -145,7 +145,7 @@ namespace NadekoBot.Modules.Utility uow.Quotes.Remove(q); await uow.CompleteAsync().ConfigureAwait(false); sucess = true; - response = GetText("deleted_quote"); + response = GetText("quote_deleted"); } } if(sucess) diff --git a/src/NadekoBot/NadekoBot.xproj.DotSettings b/src/NadekoBot/NadekoBot.xproj.DotSettings index 146bab9a..e418daea 100644 --- a/src/NadekoBot/NadekoBot.xproj.DotSettings +++ b/src/NadekoBot/NadekoBot.xproj.DotSettings @@ -2,4 +2,5 @@ True True True + True True \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index c363367f..b93554ab 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2860,6 +2860,33 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Category. + /// + public static string games_category { + get { + return ResourceManager.GetString("games_category", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled cleverbot on this server.. + /// + public static string games_cleverbot_disabled { + get { + return ResourceManager.GetString("games_cleverbot_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled cleverbot on this server.. + /// + public static string games_cleverbot_enabled { + get { + return ResourceManager.GetString("games_cleverbot_enabled", resourceCulture); + } + } + /// /// Looks up a localized string similar to Currency generation has been disabled on this channel.. /// @@ -2878,6 +2905,42 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to {0} random {1} appeared! Pick them up by typing `{2}pick`. + /// + public static string games_curgen_pl { + get { + return ResourceManager.GetString("games_curgen_pl", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A random {0} appeared! Pick it up by typing `{1}pick`. + /// + public static string games_curgen_sn { + get { + return ResourceManager.GetString("games_curgen_sn", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed loading a question.. + /// + public static string games_failed_loading_question { + get { + return ResourceManager.GetString("games_failed_loading_question", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Game Started. + /// + public static string games_game_started { + get { + return ResourceManager.GetString("games_game_started", resourceCulture); + } + } + /// /// Looks up a localized string similar to Hangman game started. /// @@ -2914,6 +2977,51 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Leaderboard. + /// + public static string games_leaderboard { + get { + return ResourceManager.GetString("games_leaderboard", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No results. + /// + public static string games_no_results { + get { + return ResourceManager.GetString("games_no_results", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You don't have enough {0}. + /// + public static string games_not_enough { + get { + return ResourceManager.GetString("games_not_enough", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to picked {0}. + /// + public static string games_picked { + get { + return ResourceManager.GetString("games_picked", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} planted {1}. + /// + public static string games_planted { + get { + return ResourceManager.GetString("games_planted", resourceCulture); + } + } + /// /// Looks up a localized string similar to Question. /// @@ -2950,6 +3058,168 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Trivia game is already running on this server.. + /// + public static string games_trivia_already_running { + get { + return ResourceManager.GetString("games_trivia_already_running", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Trivia Game. + /// + public static string games_trivia_game { + get { + return ResourceManager.GetString("games_trivia_game", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} guessed it! The answer was: {1}. + /// + public static string games_trivia_guess { + get { + return ResourceManager.GetString("games_trivia_guess", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No trivia is running on this server.. + /// + public static string games_trivia_none { + get { + return ResourceManager.GetString("games_trivia_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has {1} points. + /// + public static string games_trivia_points { + get { + return ResourceManager.GetString("games_trivia_points", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stopping after this question.. + /// + public static string games_trivia_stopping { + get { + return ResourceManager.GetString("games_trivia_stopping", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time's up! The correct answer was {0}. + /// + public static string games_trivia_times_up { + get { + return ResourceManager.GetString("games_trivia_times_up", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} guessed it and WON the game! The answer was: {1}. + /// + public static string games_trivia_win { + get { + return ResourceManager.GetString("games_trivia_win", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A draw!. + /// + public static string games_ttt_a_draw { + get { + return ResourceManager.GetString("games_ttt_a_draw", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You can't play against yourself.. + /// + public static string games_ttt_against_yourself { + get { + return ResourceManager.GetString("games_ttt_against_yourself", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to TicTacToe Game is already running in this channel.. + /// + public static string games_ttt_already_running { + get { + return ResourceManager.GetString("games_ttt_already_running", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to has created a game of TicTacToe.. + /// + public static string games_ttt_created { + get { + return ResourceManager.GetString("games_ttt_created", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} has Won!. + /// + public static string games_ttt_has_won { + get { + return ResourceManager.GetString("games_ttt_has_won", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Matched Three. + /// + public static string games_ttt_matched_three { + get { + return ResourceManager.GetString("games_ttt_matched_three", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No moves left!. + /// + public static string games_ttt_no_moves { + get { + return ResourceManager.GetString("games_ttt_no_moves", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Time Expired!. + /// + public static string games_ttt_time_expired { + get { + return ResourceManager.GetString("games_ttt_time_expired", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}'s move. + /// + public static string games_ttt_users_move { + get { + return ResourceManager.GetString("games_ttt_users_move", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} vs {1}. + /// + public static string games_vs { + get { + return ResourceManager.GetString("games_vs", resourceCulture); + } + } + /// /// Looks up a localized string similar to Back to ToC. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index f69a2577..f8208dca 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1221,12 +1221,34 @@ Don't forget to leave your discord name or id in the message. Total: {0} Average: {1} + + Category + + + Disabled cleverbot on this server. + + + Enabled cleverbot on this server. + Currency generation has been disabled on this channel. Currency generation has been enabled on this channel. + + {0} random {1} appeared! Pick them up by typing `{2}pick` + plural + + + A random {0} appeared! Pick it up by typing `{1}pick` + + + Failed loading a question. + + + Game Started + Hangman game started @@ -1239,6 +1261,77 @@ Don't forget to leave your discord name or id in the message. List of "{0}hangman" term types: + + Leaderboard + + + You don't have enough {0} + + + No results + + + picked {0} + Kwoth picked 5* + + + {0} planted {1} + Kwoth planted 5* + + + Trivia game is already running on this server. + + + Trivia Game + + + {0} guessed it! The answer was: {1} + + + No trivia is running on this server. + + + {0} has {1} points + + + Stopping after this question. + + + Time's up! The correct answer was {0} + + + {0} guessed it and WON the game! The answer was: {1} + + + You can't play against yourself. + + + TicTacToe Game is already running in this channel. + + + A draw! + + + has created a game of TicTacToe. + + + {0} has Won! + + + Matched Three + + + No moves left! + + + Time Expired! + + + {0}'s move + + + {0} vs {1} + Joined From 2ef3006ac0980797d5918852e5904c2215dc4816 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 24 Feb 2017 10:53:42 +0100 Subject: [PATCH 198/256] permissions done. now really 2 modules left, unless i forgot another one to make myself feel better --- .../Permissions/Commands/BlacklistCommands.cs | 8 +- .../Permissions/Commands/CmdCdsCommands.cs | 27 +- .../Commands/CommandCostCommands.cs | 44 +- .../Permissions/Commands/FilterCommands.cs | 39 +- .../Modules/Permissions/Permissions.cs | 278 ++++++++--- .../Resources/ResponseStrings.Designer.cs | 450 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 153 ++++++ 7 files changed, 876 insertions(+), 123 deletions(-) diff --git a/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs index 6c3a2888..4278d5a4 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/BlacklistCommands.cs @@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Permissions } [Group] - public class BlacklistCommands : ModuleBase + public class BlacklistCommands : NadekoSubmodule { public static ConcurrentHashSet BlacklistedUsers { get; set; } public static ConcurrentHashSet BlacklistedGuilds { get; set; } @@ -124,16 +124,14 @@ namespace NadekoBot.Modules.Permissions break; case BlacklistType.User: break; - default: - break; } } if(action == AddRemove.Add) - await Context.Channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("blacklisted", Format.Code(type.ToString()), Format.Code(id.ToString())).ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync($"Unblacklisted a `{type}` with id `{id}`").ConfigureAwait(false); + await ReplyConfirmLocalized("unblacklisted", Format.Code(type.ToString()), Format.Code(id.ToString())).ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs index 80b8067b..cfccebde 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/CmdCdsCommands.cs @@ -21,15 +21,15 @@ namespace NadekoBot.Modules.Permissions } [Group] - public class CmdCdsCommands : ModuleBase + public class CmdCdsCommands : NadekoSubmodule { - public static ConcurrentDictionary> commandCooldowns { get; } + public static ConcurrentDictionary> CommandCooldowns { get; } private static ConcurrentDictionary> activeCooldowns { get; } = new ConcurrentDictionary>(); static CmdCdsCommands() { var configs = NadekoBot.AllGuildConfigs; - commandCooldowns = new ConcurrentDictionary>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet(v.CommandCooldowns))); + CommandCooldowns = new ConcurrentDictionary>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet(v.CommandCooldowns))); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] @@ -38,14 +38,14 @@ namespace NadekoBot.Modules.Permissions var channel = (ITextChannel)Context.Channel; if (secs < 0 || secs > 3600) { - await channel.SendErrorAsync("Invalid second parameter. (Must be a number between 0 and 3600)").ConfigureAwait(false); + await ReplyErrorLocalized("invalid_second_param_between", 0, 3600).ConfigureAwait(false); return; } using (var uow = DbHandler.UnitOfWork()) { var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.CommandCooldowns)); - var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); + var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant()); localSet.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant()); @@ -65,13 +65,14 @@ namespace NadekoBot.Modules.Permissions { var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant()); - await channel.SendConfirmAsync($"🚮 Command **{command.Aliases.First()}** has no coooldown now and all existing cooldowns have been cleared.") - .ConfigureAwait(false); + await ReplyConfirmLocalized("cmdcd_cleared", + Format.Bold(command.Aliases.First())).ConfigureAwait(false); } else { - await channel.SendConfirmAsync($"✅ Command **{command.Aliases.First()}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.") - .ConfigureAwait(false); + await ReplyConfirmLocalized("cmdcd_add", + Format.Bold(command.Aliases.First()), + Format.Bold(secs.ToString())).ConfigureAwait(false); } } @@ -80,19 +81,19 @@ namespace NadekoBot.Modules.Permissions public async Task AllCmdCooldowns() { var channel = (ITextChannel)Context.Channel; - var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); + var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); if (!localSet.Any()) - await channel.SendConfirmAsync("ℹ️ `No command cooldowns set.`").ConfigureAwait(false); + await ReplyConfirmLocalized("cmdcd_none").ConfigureAwait(false); else - await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + " secs"), s => $"{s,-30}", 2).ConfigureAwait(false); + await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + GetText("sec")), s => $"{s,-30}", 2).ConfigureAwait(false); } public static bool HasCooldown(CommandInfo cmd, IGuild guild, IUser user) { if (guild == null) return false; - var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet()); + var cmdcds = CmdCdsCommands.CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet()); CommandCooldown cdRule; if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Aliases.First().ToLowerInvariant())) != null) { diff --git a/src/NadekoBot/Modules/Permissions/Commands/CommandCostCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/CommandCostCommands.cs index e0ef1a29..91b8a7d2 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/CommandCostCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/CommandCostCommands.cs @@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions public partial class Permissions { [Group] - public class CommandCostCommands : ModuleBase + public class CommandCostCommands : NadekoSubmodule { private static readonly ConcurrentDictionary _commandCosts = new ConcurrentDictionary(); public static IReadOnlyDictionary CommandCosts => _commandCosts; @@ -29,29 +29,29 @@ namespace NadekoBot.Modules.Permissions // x => x.Cost)); } - [NadekoCommand, Usage, Description, Aliases] - public async Task CmdCosts(int page = 1) - { - var prices = _commandCosts.ToList(); + //[NadekoCommand, Usage, Description, Aliases] + //public async Task CmdCosts(int page = 1) + //{ + // var prices = _commandCosts.ToList(); - if (!prices.Any()) - { - await Context.Channel.SendConfirmAsync("No costs set.").ConfigureAwait(false); - return; - } + // if (!prices.Any()) + // { + // await Context.Channel.SendConfirmAsync(GetText("no_costs")).ConfigureAwait(false); + // return; + // } - await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => { - var embed = new EmbedBuilder().WithOkColor() - .WithTitle("Command Costs"); - var current = prices.Skip((curPage - 1) * 9) - .Take(9); - foreach (var price in current) - { - embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true)); - } - return embed; - }, prices.Count / 9).ConfigureAwait(false); - } + // await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => { + // var embed = new EmbedBuilder().WithOkColor() + // .WithTitle(GetText("command_costs")); + // var current = prices.Skip((curPage - 1) * 9) + // .Take(9); + // foreach (var price in current) + // { + // embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true)); + // } + // return embed; + // }, prices.Count / 9).ConfigureAwait(false); + //} //[NadekoCommand, Usage, Description, Aliases] //public async Task CommandCost(int cost, CommandInfo cmd) diff --git a/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs b/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs index 2576ac78..9eea8a50 100644 --- a/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs +++ b/src/NadekoBot/Modules/Permissions/Commands/FilterCommands.cs @@ -13,13 +13,13 @@ namespace NadekoBot.Modules.Permissions public partial class Permissions { [Group] - public class FilterCommands : ModuleBase + public class FilterCommands : NadekoSubmodule { public static ConcurrentHashSet InviteFilteringChannels { get; } public static ConcurrentHashSet InviteFilteringServers { get; } //serverid, filteredwords - private static ConcurrentDictionary> ServerFilteredWords { get; } + private static ConcurrentDictionary> serverFilteredWords { get; } public static ConcurrentHashSet WordFilteringChannels { get; } public static ConcurrentHashSet WordFilteringServers { get; } @@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Permissions { ConcurrentHashSet words = new ConcurrentHashSet(); if(WordFilteringChannels.Contains(channelId)) - ServerFilteredWords.TryGetValue(guildId, out words); + serverFilteredWords.TryGetValue(guildId, out words); return words; } @@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Permissions { var words = new ConcurrentHashSet(); if(WordFilteringServers.Contains(guildId)) - ServerFilteredWords.TryGetValue(guildId, out words); + serverFilteredWords.TryGetValue(guildId, out words); return words; } @@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Permissions var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet(gc.FilteredWords.Select(fw => fw.Word))); - ServerFilteredWords = new ConcurrentDictionary>(dict); + serverFilteredWords = new ConcurrentDictionary>(dict); var serverFiltering = guildConfigs.Where(gc => gc.FilterWords); WordFilteringServers = new ConcurrentHashSet(serverFiltering.Select(gc => gc.GuildId)); @@ -74,12 +74,12 @@ namespace NadekoBot.Modules.Permissions if (enabled) { InviteFilteringServers.Add(channel.Guild.Id); - await channel.SendConfirmAsync("Invite filtering enabled on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("invite_filter_server_on").ConfigureAwait(false); } else { InviteFilteringServers.TryRemove(channel.Guild.Id); - await channel.SendConfirmAsync("Invite filtering disabled on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("invite_filter_server_off").ConfigureAwait(false); } } @@ -107,12 +107,11 @@ namespace NadekoBot.Modules.Permissions if (removed == 0) { InviteFilteringChannels.Add(channel.Id); - await channel.SendConfirmAsync("Invite filtering enabled on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("invite_filter_channel_on").ConfigureAwait(false); } else { - InviteFilteringChannels.TryRemove(channel.Id); - await channel.SendConfirmAsync("Invite filtering disabled on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("invite_filter_channel_off").ConfigureAwait(false); } } @@ -133,12 +132,12 @@ namespace NadekoBot.Modules.Permissions if (enabled) { WordFilteringServers.Add(channel.Guild.Id); - await channel.SendConfirmAsync("Word filtering enabled on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("word_filter_server_on").ConfigureAwait(false); } else { WordFilteringServers.TryRemove(channel.Guild.Id); - await channel.SendConfirmAsync("Word filtering disabled on this server.").ConfigureAwait(false); + await ReplyConfirmLocalized("word_filter_server_off").ConfigureAwait(false); } } @@ -166,12 +165,12 @@ namespace NadekoBot.Modules.Permissions if (removed == 0) { WordFilteringChannels.Add(channel.Id); - await channel.SendConfirmAsync("Word filtering enabled on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("word_filter_channel_on").ConfigureAwait(false); } else { WordFilteringChannels.TryRemove(channel.Id); - await channel.SendConfirmAsync("Word filtering disabled on this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("word_filter_channel_off").ConfigureAwait(false); } } @@ -199,19 +198,17 @@ namespace NadekoBot.Modules.Permissions await uow.CompleteAsync().ConfigureAwait(false); } - var filteredWords = ServerFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); + var filteredWords = serverFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet()); if (removed == 0) { filteredWords.Add(word); - await channel.SendConfirmAsync($"Word `{word}` successfully added to the list of filtered words.") - .ConfigureAwait(false); + await ReplyConfirmLocalized("filter_word_add", Format.Code(word)).ConfigureAwait(false); } else { filteredWords.TryRemove(word); - await channel.SendConfirmAsync($"Word `{word}` removed from the list of filtered words.") - .ConfigureAwait(false); + await ReplyConfirmLocalized("filter_word_remove", Format.Code(word)).ConfigureAwait(false); } } @@ -222,9 +219,9 @@ namespace NadekoBot.Modules.Permissions var channel = (ITextChannel)Context.Channel; ConcurrentHashSet filteredWords; - ServerFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords); + serverFilteredWords.TryGetValue(channel.Guild.Id, out filteredWords); - await channel.SendConfirmAsync($"List of filtered words", string.Join("\n", filteredWords)) + await channel.SendConfirmAsync(GetText("filter_word_list"), string.Join("\n", filteredWords)) .ConfigureAwait(false); } } diff --git a/src/NadekoBot/Modules/Permissions/Permissions.cs b/src/NadekoBot/Modules/Permissions/Permissions.cs index 3e8ecd13..04d1352d 100644 --- a/src/NadekoBot/Modules/Permissions/Permissions.cs +++ b/src/NadekoBot/Modules/Permissions/Permissions.cs @@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Permissions static Permissions() { - var _log = LogManager.GetCurrentClassLogger(); + var log = LogManager.GetCurrentClassLogger(); var sw = Stopwatch.StartNew(); using (var uow = DbHandler.UnitOfWork()) @@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Permissions } sw.Stop(); - _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); + log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); } [NadekoCommand, Usage, Description, Aliases] @@ -65,8 +65,14 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.Verbose = config.VerbosePermissions; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - - await Context.Channel.SendConfirmAsync("ℹ️ I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false); + if (action.Value) + { + await ReplyConfirmLocalized("verbose_true").ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("verbose_false").ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -81,22 +87,20 @@ namespace NadekoBot.Modules.Permissions var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); if (role == null) { - await Context.Channel.SendConfirmAsync($"ℹ️ Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false); + await ReplyConfirmLocalized("permrole", Format.Bold(config.PermissionRole)).ConfigureAwait(false); return; } - else { - config.PermissionRole = role.Name.Trim(); - Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() - { - PermRole = config.PermissionRole, - RootPermission = Permission.GetDefaultRoot(), - Verbose = config.VerbosePermissions - }, (id, old) => { old.PermRole = role.Name.Trim(); return old; }); - await uow.CompleteAsync().ConfigureAwait(false); - } + config.PermissionRole = role.Name.Trim(); + Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = Permission.GetDefaultRoot(), + Verbose = config.VerbosePermissions + }, (id, old) => { old.PermRole = role.Name.Trim(); return old; }); + await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false); + await ReplyConfirmLocalized("permrole_changed", Format.Bold(role.Name)).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -105,12 +109,18 @@ namespace NadekoBot.Modules.Permissions { if (page < 1 || page > 4) return; - string toSend = ""; + string toSend; using (var uow = DbHandler.UnitOfWork()) { var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission; var i = 1 + 20 * (page - 1); - toSend = Format.Code($"📄 Permissions page {page}") + "\n\n" + String.Join("\n", perms.AsEnumerable().Skip((page - 1) * 20).Take(20).Select(p => $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild)Context.Guild) + " [uneditable]") : (p.GetCommand((SocketGuild)Context.Guild)))}")); + toSend = Format.Bold(GetText("page", page)) + "\n\n" + string.Join("\n", + perms.AsEnumerable() + .Skip((page - 1) * 20) + .Take(20) + .Select( + p => + $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild) Context.Guild) + $" [{GetText("uneditable")}]") : (p.GetCommand((SocketGuild) Context.Guild)))}")); } await Context.Channel.SendMessageAsync(toSend).ConfigureAwait(false); @@ -132,7 +142,7 @@ namespace NadekoBot.Modules.Permissions { return; } - else if (index == 0) + if (index == 0) { p = perms; config.RootPermission = perms.Next; @@ -155,12 +165,13 @@ namespace NadekoBot.Modules.Permissions uow2._context.Remove(p); uow2._context.SaveChanges(); } - - await Context.Channel.SendConfirmAsync($"✅ {Context.User.Mention} removed permission **{p.GetCommand((SocketGuild)Context.Guild)}** from position #{index + 1}.").ConfigureAwait(false); + await ReplyConfirmLocalized("removed", + index+1, + Format.Code(p.GetCommand((SocketGuild)Context.Guild))).ConfigureAwait(false); } - catch (ArgumentOutOfRangeException) + catch (IndexOutOfRangeException) { - await Context.Channel.SendErrorAsync("❗️`No command on that index found.`").ConfigureAwait(false); + await ReplyErrorLocalized("perm_out_of_range").ConfigureAwait(false); } } @@ -180,7 +191,6 @@ namespace NadekoBot.Modules.Permissions { var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id); var perms = config.RootPermission; - var root = perms; var index = 0; var fromFound = false; var toFound = false; @@ -207,13 +217,13 @@ namespace NadekoBot.Modules.Permissions { if (!fromFound) { - await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++from}`").ConfigureAwait(false); + await ReplyErrorLocalized("not_found", ++from).ConfigureAwait(false); return; } if (!toFound) { - await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++to}`").ConfigureAwait(false); + await ReplyErrorLocalized("not_found", ++to).ConfigureAwait(false); return; } } @@ -230,7 +240,6 @@ namespace NadekoBot.Modules.Permissions next.Previous = pre; if (from == 0) { - root = next; } await uow.CompleteAsync().ConfigureAwait(false); //Inserting @@ -263,14 +272,18 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"`Moved permission:` \"{fromPerm.GetCommand((SocketGuild)Context.Guild)}\" `from #{++from} to #{++to}.`").ConfigureAwait(false); + await ReplyConfirmLocalized("moved_permission", + Format.Code(fromPerm.GetCommand((SocketGuild) Context.Guild)), + ++from, + ++to) + .ConfigureAwait(false); return; } catch (Exception e) when (e is ArgumentOutOfRangeException || e is IndexOutOfRangeException) { } } - await Context.Channel.SendErrorAsync("`Invalid index(es) specified.`").ConfigureAwait(false); + await ReplyErrorLocalized("perm_out_of_range").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -297,7 +310,19 @@ namespace NadekoBot.Modules.Permissions await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command on this server.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("sx_enable", + Format.Code(command.Aliases.First()), + GetText("of_command")).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("sx_disable", + Format.Code(command.Aliases.First()), + GetText("of_command")).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -323,7 +348,19 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of **`{module.Name}`** module on this server.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("sx_enable", + Format.Code(module.Name), + GetText("of_module")).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("sx_disable", + Format.Code(module.Name), + GetText("of_module")).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -349,7 +386,21 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{user}` user.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("ux_enable", + Format.Code(command.Aliases.First()), + GetText("of_command"), + Format.Code(user.ToString())).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("ux_disable", + Format.Code(command.Aliases.First()), + GetText("of_command"), + Format.Code(user.ToString())).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -375,7 +426,21 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("ux_enable", + Format.Code(module.Name), + GetText("of_module"), + Format.Code(user.ToString())).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("ux_disable", + Format.Code(module.Name), + GetText("of_module"), + Format.Code(user.ToString())).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -404,7 +469,21 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{role}` role.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("rx_enable", + Format.Code(command.Aliases.First()), + GetText("of_command"), + Format.Code(role.Name)).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("rx_disable", + Format.Code(command.Aliases.First()), + GetText("of_command"), + Format.Code(role.Name)).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -433,39 +512,62 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false); + + + if (action.Value) + { + await ReplyConfirmLocalized("rx_enable", + Format.Code(module.Name), + GetText("of_module"), + Format.Code(role.Name)).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("rx_disable", + Format.Code(module.Name), + GetText("of_module"), + Format.Code(role.Name)).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task ChnlCmd(CommandInfo command, PermissionAction action, [Remainder] ITextChannel chnl) { - try + using (var uow = DbHandler.UnitOfWork()) { - using (var uow = DbHandler.UnitOfWork()) + var newPerm = new Permission { - var newPerm = new Permission - { - PrimaryTarget = PrimaryPermissionType.Channel, - PrimaryTargetId = chnl.Id, - SecondaryTarget = SecondaryPermissionType.Command, - SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), - State = action.Value, - }; - var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); - Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() - { - PermRole = config.PermissionRole, - RootPermission = config.RootPermission, - Verbose = config.VerbosePermissions - }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); - await uow.CompleteAsync().ConfigureAwait(false); - } + PrimaryTarget = PrimaryPermissionType.Channel, + PrimaryTargetId = chnl.Id, + SecondaryTarget = SecondaryPermissionType.Command, + SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), + State = action.Value, + }; + var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); + Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() + { + PermRole = config.PermissionRole, + RootPermission = config.RootPermission, + Verbose = config.VerbosePermissions + }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); + await uow.CompleteAsync().ConfigureAwait(false); } - catch (Exception ex) { - _log.Error(ex); + + if (action.Value) + { + await ReplyConfirmLocalized("cx_enable", + Format.Code(command.Aliases.First()), + GetText("of_command"), + Format.Code(chnl.Name)).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("cx_disable", + Format.Code(command.Aliases.First()), + GetText("of_command"), + Format.Code(chnl.Name)).ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{chnl}` channel.").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -491,7 +593,21 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("cx_enable", + Format.Code(module.Name), + GetText("of_module"), + Format.Code(chnl.Name)).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("cx_disable", + Format.Code(module.Name), + GetText("of_module"), + Format.Code(chnl.Name)).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -517,7 +633,17 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("acm_enable", + Format.Code(chnl.Name)).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("acm_disable", + Format.Code(chnl.Name)).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -546,7 +672,17 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("arm_enable", + Format.Code(role.Name)).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("arm_disable", + Format.Code(role.Name)).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -572,7 +708,17 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("aum_enable", + Format.Code(user.ToString())).ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("aum_disable", + Format.Code(user.ToString())).ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -609,7 +755,15 @@ namespace NadekoBot.Modules.Permissions }, (id, old) => { old.RootPermission = config.RootPermission; return old; }); await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync($"{(action.Value ? "✅ Allowed" : "🆗 Denied")} usage of `ALL MODULES` on this server.").ConfigureAwait(false); + + if (action.Value) + { + await ReplyConfirmLocalized("asm_enable").ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("asm_disable").ConfigureAwait(false); + } } } } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index b93554ab..f0c48b9d 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -3425,6 +3425,456 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Disabled usage of ALL MODULES on {0} channel.. + /// + public static string permissions_acm_disable { + get { + return ResourceManager.GetString("permissions_acm_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of ALL MODULES on {0} channel.. + /// + public static string permissions_acm_enable { + get { + return ResourceManager.GetString("permissions_acm_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Allowed. + /// + public static string permissions_allowed { + get { + return ResourceManager.GetString("permissions_allowed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of ALL MODULES for {0} role.. + /// + public static string permissions_arm_disable { + get { + return ResourceManager.GetString("permissions_arm_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of ALL MODULES for {0} role.. + /// + public static string permissions_arm_enable { + get { + return ResourceManager.GetString("permissions_arm_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of ALL MODULES on this server.. + /// + public static string permissions_asm_disable { + get { + return ResourceManager.GetString("permissions_asm_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of ALL MODULES on this server.. + /// + public static string permissions_asm_enable { + get { + return ResourceManager.GetString("permissions_asm_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of ALL MODULES for {0} user.. + /// + public static string permissions_aum_disable { + get { + return ResourceManager.GetString("permissions_aum_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of ALL MODULES for {0} user.. + /// + public static string permissions_aum_enable { + get { + return ResourceManager.GetString("permissions_aum_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Blacklisted {0} with ID {1}. + /// + public static string permissions_blacklisted { + get { + return ResourceManager.GetString("permissions_blacklisted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Command {0} now has a {1}s cooldown.. + /// + public static string permissions_cmdcd_add { + get { + return ResourceManager.GetString("permissions_cmdcd_add", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Command {0} has no coooldown now and all existing cooldowns have been cleared.. + /// + public static string permissions_cmdcd_cleared { + get { + return ResourceManager.GetString("permissions_cmdcd_cleared", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No command cooldowns set.. + /// + public static string permissions_cmdcd_none { + get { + return ResourceManager.GetString("permissions_cmdcd_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Command Costs. + /// + public static string permissions_command_costs { + get { + return ResourceManager.GetString("permissions_command_costs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of {0} {1} on {2} channel.. + /// + public static string permissions_cx_disable { + get { + return ResourceManager.GetString("permissions_cx_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of {0} {1} on {2} channel.. + /// + public static string permissions_cx_enable { + get { + return ResourceManager.GetString("permissions_cx_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Denied. + /// + public static string permissions_denied { + get { + return ResourceManager.GetString("permissions_denied", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Added word {0} to the list of filtered words.. + /// + public static string permissions_filter_word_add { + get { + return ResourceManager.GetString("permissions_filter_word_add", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to List Of Filtered Words. + /// + public static string permissions_filter_word_list { + get { + return ResourceManager.GetString("permissions_filter_word_list", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removed word {0} from the list of filtered words.. + /// + public static string permissions_filter_word_remove { + get { + return ResourceManager.GetString("permissions_filter_word_remove", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid second parameter.(Must be a number between {0} and {1}). + /// + public static string permissions_invalid_second_param_between { + get { + return ResourceManager.GetString("permissions_invalid_second_param_between", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invite filtering disabled on this channel.. + /// + public static string permissions_invite_filter_channel_off { + get { + return ResourceManager.GetString("permissions_invite_filter_channel_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invite filtering enabled on this channel.. + /// + public static string permissions_invite_filter_channel_on { + get { + return ResourceManager.GetString("permissions_invite_filter_channel_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invite filtering disabled on this server.. + /// + public static string permissions_invite_filter_server_off { + get { + return ResourceManager.GetString("permissions_invite_filter_server_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invite filtering enabled on this server.. + /// + public static string permissions_invite_filter_server_on { + get { + return ResourceManager.GetString("permissions_invite_filter_server_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Moved permission {0} from #{1} to #{2}. + /// + public static string permissions_moved_permission { + get { + return ResourceManager.GetString("permissions_moved_permission", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No costs set.. + /// + public static string permissions_no_costs { + get { + return ResourceManager.GetString("permissions_no_costs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Can't find permission at index #{0}. + /// + public static string permissions_not_found { + get { + return ResourceManager.GetString("permissions_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to command. + /// + public static string permissions_of_command { + get { + return ResourceManager.GetString("permissions_of_command", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to module. + /// + public static string permissions_of_module { + get { + return ResourceManager.GetString("permissions_of_module", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Permissions page {0}. + /// + public static string permissions_page { + get { + return ResourceManager.GetString("permissions_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No permission found on that index.. + /// + public static string permissions_perm_out_of_range { + get { + return ResourceManager.GetString("permissions_perm_out_of_range", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current permissions role is {0}.. + /// + public static string permissions_permrole { + get { + return ResourceManager.GetString("permissions_permrole", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Users now require {0} role in order to edit permissions.. + /// + public static string permissions_permrole_changed { + get { + return ResourceManager.GetString("permissions_permrole_changed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to removed permission #{0} - {1}. + /// + public static string permissions_removed { + get { + return ResourceManager.GetString("permissions_removed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of {0} {1} for {2} role.. + /// + public static string permissions_rx_disable { + get { + return ResourceManager.GetString("permissions_rx_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of {0} {1} for {2} role.. + /// + public static string permissions_rx_enable { + get { + return ResourceManager.GetString("permissions_rx_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to sec.. + /// + public static string permissions_sec { + get { + return ResourceManager.GetString("permissions_sec", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of {0} {1} on this server.. + /// + public static string permissions_sx_disable { + get { + return ResourceManager.GetString("permissions_sx_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of {0} {1} on this server.. + /// + public static string permissions_sx_enable { + get { + return ResourceManager.GetString("permissions_sx_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Unblacklisted {0} with ID {1}. + /// + public static string permissions_unblacklisted { + get { + return ResourceManager.GetString("permissions_unblacklisted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to uneditable. + /// + public static string permissions_uneditable { + get { + return ResourceManager.GetString("permissions_uneditable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Disabled usage of {0} {1} for {2} user.. + /// + public static string permissions_ux_disable { + get { + return ResourceManager.GetString("permissions_ux_disable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enabled usage of {0} {1} for {2} user.. + /// + public static string permissions_ux_enable { + get { + return ResourceManager.GetString("permissions_ux_enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will no longer show permission warnings.. + /// + public static string permissions_verbose_false { + get { + return ResourceManager.GetString("permissions_verbose_false", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will now show permission warnings.. + /// + public static string permissions_verbose_true { + get { + return ResourceManager.GetString("permissions_verbose_true", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Word filtering disabled on this channel.. + /// + public static string permissions_word_filter_channel_off { + get { + return ResourceManager.GetString("permissions_word_filter_channel_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Word filtering enabled on this channel.. + /// + public static string permissions_word_filter_channel_on { + get { + return ResourceManager.GetString("permissions_word_filter_channel_on", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Word filtering disabled on this server.. + /// + public static string permissions_word_filter_server_off { + get { + return ResourceManager.GetString("permissions_word_filter_server_off", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Word filtering enabled on this server.. + /// + public static string permissions_word_filter_server_on { + get { + return ResourceManager.GetString("permissions_word_filter_server_on", resourceCulture); + } + } + /// /// Looks up a localized string similar to {0} has already fainted.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index f8208dca..d365b08c 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1332,6 +1332,159 @@ Don't forget to leave your discord name or id in the message. {0} vs {1} + + Disabled usage of ALL MODULES on {0} channel. + + + Enabled usage of ALL MODULES on {0} channel. + + + Allowed + + + Disabled usage of ALL MODULES for {0} role. + + + Enabled usage of ALL MODULES for {0} role. + + + Disabled usage of ALL MODULES on this server. + + + Enabled usage of ALL MODULES on this server. + + + Disabled usage of ALL MODULES for {0} user. + + + Enabled usage of ALL MODULES for {0} user. + + + Blacklisted {0} with ID {1} + + + Command {0} now has a {1}s cooldown. + + + Command {0} has no coooldown now and all existing cooldowns have been cleared. + + + No command cooldowns set. + + + Command Costs + + + Disabled usage of {0} {1} on {2} channel. + + + Enabled usage of {0} {1} on {2} channel. + + + Denied + + + Added word {0} to the list of filtered words. + + + List Of Filtered Words + + + Removed word {0} from the list of filtered words. + + + Invalid second parameter.(Must be a number between {0} and {1}) + + + Invite filtering disabled on this channel. + + + Invite filtering enabled on this channel. + + + Invite filtering disabled on this server. + + + Invite filtering enabled on this server. + + + Moved permission {0} from #{1} to #{2} + + + Can't find permission at index #{0} + + + No costs set. + + + command + Gen (of command) + + + module + Gen. (of module) + + + Permissions page {0} + + + Current permissions role is {0}. + + + Users now require {0} role in order to edit permissions. + + + No permission found on that index. + + + removed permission #{0} - {1} + + + Disabled usage of {0} {1} for {2} role. + + + Enabled usage of {0} {1} for {2} role. + + + sec. + Short of seconds. + + + Disabled usage of {0} {1} on this server. + + + Enabled usage of {0} {1} on this server. + + + Unblacklisted {0} with ID {1} + + + uneditable + + + Disabled usage of {0} {1} for {2} user. + + + Enabled usage of {0} {1} for {2} user. + + + I will no longer show permission warnings. + + + I will now show permission warnings. + + + Word filtering disabled on this channel. + + + Word filtering enabled on this channel. + + + Word filtering disabled on this server. + + + Word filtering enabled on this server. + Joined From 1bfa8becb37e89798740eaa871abc647d3369360 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 24 Feb 2017 17:10:44 +0100 Subject: [PATCH 199/256] searches localizable o.o --- src/NadekoBot/Modules/NSFW/NSFW.cs | 39 +- .../Searches/Commands/AnimeSearchCommands.cs | 48 +- .../Searches/Commands/GoogleTranslator.cs | 10 +- .../Modules/Searches/Commands/JokeCommands.cs | 9 +- .../Modules/Searches/Commands/LoLCommands.cs | 15 +- .../Searches/Commands/MemegenCommands.cs | 104 ++- .../Modules/Searches/Commands/OsuCommands.cs | 70 +- .../Searches/Commands/OverwatchCommands.cs | 45 +- .../Searches/Commands/PlaceCommands.cs | 10 +- .../Commands/PokemonSearchCommands.cs | 18 +- .../Commands/StreamNotificationCommands.cs | 97 +- .../Modules/Searches/Commands/Translator.cs | 46 +- .../Modules/Searches/Commands/XkcdCommands.cs | 19 +- src/NadekoBot/Modules/Searches/Searches.cs | 314 +++---- src/NadekoBot/NadekoBot.xproj.DotSettings | 1 + .../Resources/ResponseStrings.Designer.cs | 846 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 283 ++++++ 17 files changed, 1542 insertions(+), 432 deletions(-) diff --git a/src/NadekoBot/Modules/NSFW/NSFW.cs b/src/NadekoBot/Modules/NSFW/NSFW.cs index 53a3f3bb..6e72aefc 100644 --- a/src/NadekoBot/Modules/NSFW/NSFW.cs +++ b/src/NadekoBot/Modules/NSFW/NSFW.cs @@ -7,8 +7,6 @@ using System.Linq; using System.Threading.Tasks; using NadekoBot.Services; using System.Net.Http; -using System.Text.RegularExpressions; -using System.Xml.Linq; using NadekoBot.Extensions; using System.Xml; using System.Threading; @@ -20,8 +18,8 @@ namespace NadekoBot.Modules.NSFW public class NSFW : NadekoTopLevelModule { - private static readonly ConcurrentDictionary AutoHentaiTimers = new ConcurrentDictionary(); - private static readonly ConcurrentHashSet HentaiBombBlacklist = new ConcurrentHashSet(); + private static readonly ConcurrentDictionary _autoHentaiTimers = new ConcurrentDictionary(); + private static readonly ConcurrentHashSet _hentaiBombBlacklist = new ConcurrentHashSet(); private async Task InternalHentai(IMessageChannel channel, string tag, bool noError) { @@ -72,7 +70,7 @@ namespace NadekoBot.Modules.NSFW if (interval == 0) { - if (!AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return; + if (!_autoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return; t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false); @@ -99,7 +97,7 @@ namespace NadekoBot.Modules.NSFW } }, null, interval * 1000, interval * 1000); - AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => + _autoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => { old.Change(Timeout.Infinite, Timeout.Infinite); return t; @@ -114,7 +112,7 @@ namespace NadekoBot.Modules.NSFW [NadekoCommand, Usage, Description, Aliases] public async Task HentaiBomb([Remainder] string tag = null) { - if (!HentaiBombBlacklist.Add(Context.User.Id)) + if (!_hentaiBombBlacklist.Add(Context.User.Id)) return; try { @@ -138,17 +136,17 @@ namespace NadekoBot.Modules.NSFW finally { await Task.Delay(5000).ConfigureAwait(false); - HentaiBombBlacklist.TryRemove(Context.User.Id); + _hentaiBombBlacklist.TryRemove(Context.User.Id); } } #endif [NadekoCommand, Usage, Description, Aliases] public Task Yandere([Remainder] string tag = null) - => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere); + => InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Yandere); [NadekoCommand, Usage, Description, Aliases] public Task Konachan([Remainder] string tag = null) - => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan); + => InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Konachan); [NadekoCommand, Usage, Description, Aliases] public async Task E621([Remainder] string tag = null) @@ -169,7 +167,7 @@ namespace NadekoBot.Modules.NSFW [NadekoCommand, Usage, Description, Aliases] public Task Rule34([Remainder] string tag = null) - => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34); + => InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Rule34); [NadekoCommand, Usage, Description, Aliases] public async Task Danbooru([Remainder] string tag = null) @@ -212,7 +210,7 @@ namespace NadekoBot.Modules.NSFW [NadekoCommand, Usage, Description, Aliases] public Task Gelbooru([Remainder] string tag = null) - => Searches.Searches.InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru); + => InternalDapiCommand(Context.Message, tag, Searches.Searches.DapiSearchType.Gelbooru); [NadekoCommand, Usage, Description, Aliases] public async Task Cp() @@ -289,5 +287,22 @@ namespace NadekoBot.Modules.NSFW public static Task GetGelbooruImageLink(string tag) => Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru); + + public async Task InternalDapiCommand(IUserMessage umsg, string tag, Searches.Searches.DapiSearchType type) + { + var channel = umsg.Channel; + + tag = tag?.Trim() ?? ""; + + var url = await Searches.Searches.InternalDapiSearch(tag, type).ConfigureAwait(false); + + if (url == null) + await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results")); + else + await channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithDescription(umsg.Author.Mention + " " + tag) + .WithImageUrl(url) + .WithFooter(efb => efb.WithText(type.ToString()))).ConfigureAwait(false); + } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index bf05f0ac..9fdf07f0 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -1,6 +1,5 @@ using AngleSharp; using AngleSharp.Dom.Html; -using AngleSharp.Extensions; using Discord; using Discord.Commands; using NadekoBot.Attributes; @@ -8,7 +7,6 @@ using NadekoBot.Extensions; using NadekoBot.Modules.Searches.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using NLog; using System; using System.Collections.Generic; using System.Linq; @@ -21,15 +19,13 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class AnimeSearchCommands : ModuleBase + public class AnimeSearchCommands : NadekoSubmodule { - private static Timer anilistTokenRefresher { get; } - private static Logger _log { get; } + private static readonly Timer anilistTokenRefresher; private static string anilistToken { get; set; } static AnimeSearchCommands() { - _log = LogManager.GetCurrentClassLogger(); anilistTokenRefresher = new Timer(async (state) => { try @@ -49,9 +45,9 @@ namespace NadekoBot.Modules.Searches anilistToken = JObject.Parse(stringContent)["access_token"].ToString(); } } - catch (Exception ex) + catch { - _log.Error(ex); + // ignored } }, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29)); } @@ -75,7 +71,7 @@ namespace NadekoBot.Modules.Searches var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc"); - var favAnime = "No favorite anime yet"; + var favAnime = GetText("anime_no_fav"); if (favorites[0].QuerySelector("p") == null) favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a") .Shuffle() @@ -106,14 +102,14 @@ namespace NadekoBot.Modules.Searches var embed = new EmbedBuilder() .WithOkColor() - .WithTitle($"{name}'s MAL profile") - .AddField(efb => efb.WithName("💚 Watching").WithValue(stats[0]).WithIsInline(true)) - .AddField(efb => efb.WithName("💙 Completed").WithValue(stats[1]).WithIsInline(true)); + .WithTitle(GetText("mal_profile", name)) + .AddField(efb => efb.WithName("💚 " + GetText("watching")).WithValue(stats[0]).WithIsInline(true)) + .AddField(efb => efb.WithName("💙 " + GetText("completed")).WithValue(stats[1]).WithIsInline(true)); if (info.Count < 3) - embed.AddField(efb => efb.WithName("💛 On-Hold").WithValue(stats[2]).WithIsInline(true)); + embed.AddField(efb => efb.WithName("💛 " + GetText("on_hold")).WithValue(stats[2]).WithIsInline(true)); embed - .AddField(efb => efb.WithName("💔 Dropped").WithValue(stats[3]).WithIsInline(true)) - .AddField(efb => efb.WithName("⚪ Plan to watch").WithValue(stats[4]).WithIsInline(true)) + .AddField(efb => efb.WithName("💔 " + GetText("dropped")).WithValue(stats[3]).WithIsInline(true)) + .AddField(efb => efb.WithName("⚪ " + GetText("plan_to_watch")).WithValue(stats[4]).WithIsInline(true)) .AddField(efb => efb.WithName("🕐 " + daysAndMean[0][0]).WithValue(daysAndMean[0][1]).WithIsInline(true)) .AddField(efb => efb.WithName("📊 " + daysAndMean[1][0]).WithValue(daysAndMean[1][1]).WithIsInline(true)) .AddField(efb => efb.WithName(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1).WithValue(info[0].Item2.TrimTo(20)).WithIsInline(true)) @@ -126,7 +122,7 @@ namespace NadekoBot.Modules.Searches .WithDescription($@" ** https://myanimelist.net/animelist/{ name } ** -**Top 3 Favorite Anime:** +**{GetText("top_3_fav_anime")}** {favAnime}" //**[Manga List](https://myanimelist.net/mangalist/{name})** @@ -176,7 +172,7 @@ namespace NadekoBot.Modules.Searches if (animeData == null) { - await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false); + await ReplyErrorLocalized("failed_finding_anime").ConfigureAwait(false); return; } @@ -185,10 +181,10 @@ namespace NadekoBot.Modules.Searches .WithTitle(animeData.title_english) .WithUrl(animeData.Link) .WithImageUrl(animeData.image_url_lge) - .AddField(efb => efb.WithName("Episodes").WithValue(animeData.total_episodes.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName("Status").WithValue(animeData.AiringStatus.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", animeData.Genres)).WithIsInline(true)) - .WithFooter(efb => efb.WithText("Score: " + animeData.average_score + " / 100")); + .AddField(efb => efb.WithName(GetText("episodes")).WithValue(animeData.total_episodes.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("status")).WithValue(animeData.AiringStatus.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", animeData.Genres)).WithIsInline(true)) + .WithFooter(efb => efb.WithText(GetText("score") + " " + animeData.average_score + " / 100")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -203,7 +199,7 @@ namespace NadekoBot.Modules.Searches if (mangaData == null) { - await Context.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false); + await ReplyErrorLocalized("failed_finding_manga").ConfigureAwait(false); return; } @@ -212,10 +208,10 @@ namespace NadekoBot.Modules.Searches .WithTitle(mangaData.title_english) .WithUrl(mangaData.Link) .WithImageUrl(mangaData.image_url_lge) - .AddField(efb => efb.WithName("Episodes").WithValue(mangaData.total_chapters.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName("Status").WithValue(mangaData.publishing_status.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", mangaData.Genres)).WithIsInline(true)) - .WithFooter(efb => efb.WithText("Score: " + mangaData.average_score + " / 100")); + .AddField(efb => efb.WithName(GetText("chapters")).WithValue(mangaData.total_chapters.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("status")).WithValue(mangaData.publishing_status.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", mangaData.Genres)).WithIsInline(true)) + .WithFooter(efb => efb.WithText(GetText("score") + " " + mangaData.average_score + " / 100")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } diff --git a/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs b/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs index be415465..5604fdd1 100644 --- a/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs +++ b/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs @@ -13,7 +13,7 @@ namespace NadekoBot.Modules.Searches public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator()); public IEnumerable Languages => _languageDictionary.Keys.OrderBy(x => x); - private Dictionary _languageDictionary; + private readonly Dictionary _languageDictionary; static GoogleTranslator() { } private GoogleTranslator() { @@ -153,13 +153,13 @@ namespace NadekoBot.Modules.Searches public async Task Translate(string sourceText, string sourceLanguage, string targetLanguage) { - string text = string.Empty; + string text; - string url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", + var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", ConvertToLanguageCode(sourceLanguage), ConvertToLanguageCode(targetLanguage), WebUtility.UrlEncode(sourceText)); - using (HttpClient http = new HttpClient()) + using (var http = new HttpClient()) { http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); text = await http.GetStringAsync(url).ConfigureAwait(false); @@ -170,7 +170,7 @@ namespace NadekoBot.Modules.Searches private string ConvertToLanguageCode(string language) { - string mode = string.Empty; + string mode; _languageDictionary.TryGetValue(language, out mode); return mode; } diff --git a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs index 1d5f3c99..a867aeb8 100644 --- a/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/JokeCommands.cs @@ -7,7 +7,6 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using NLog; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; @@ -18,11 +17,11 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class JokeCommands : ModuleBase + public class JokeCommands : NadekoSubmodule { private static List wowJokes { get; } = new List(); private static List magicItems { get; } = new List(); - private static Logger _log { get; } + private new static readonly Logger _log; static JokeCommands() { @@ -78,7 +77,7 @@ namespace NadekoBot.Modules.Searches { if (!wowJokes.Any()) { - await Context.Channel.SendErrorAsync("Jokes not loaded.").ConfigureAwait(false); + await ReplyErrorLocalized("jokes_not_loaded").ConfigureAwait(false); return; } var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)]; @@ -90,7 +89,7 @@ namespace NadekoBot.Modules.Searches { if (!wowJokes.Any()) { - await Context.Channel.SendErrorAsync("MagicItems not loaded.").ConfigureAwait(false); + await ReplyErrorLocalized("magicitems_not_loaded").ConfigureAwait(false); return; } var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)]; diff --git a/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs b/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs index 308ccbd2..83fe18a8 100644 --- a/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/LoLCommands.cs @@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Searches [NadekoCommand, Usage, Description, Aliases] public async Task Lolban() { - var showCount = 8; + const int showCount = 8; //http://api.champion.gg/stats/champs/mostBanned?api_key=YOUR_API_TOKEN&page=1&limit=2 try { @@ -44,19 +44,20 @@ namespace NadekoBot.Modules.Searches $"limit={showCount}") .ConfigureAwait(false))["data"] as JArray; var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList(); - var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions")); - for (var i = 0; i < dataList.Count; i++) + var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline(GetText("x_most_banned_champs",dataList.Count))); + foreach (var champ in dataList) { - var champ = dataList[i]; - eb.AddField(efb => efb.WithName(champ["name"].ToString()).WithValue(champ["general"]["banRate"] + "%").WithIsInline(true)); + var champ1 = champ; + eb.AddField(efb => efb.WithName(champ1["name"].ToString()).WithValue(champ1["general"]["banRate"] + "%").WithIsInline(true)); } await Context.Channel.EmbedAsync(eb, Format.Italics(trashTalk[new NadekoRandom().Next(0, trashTalk.Length)])).ConfigureAwait(false); } } - catch (Exception) + catch (Exception ex) { - await Context.Channel.SendMessageAsync("Something went wrong.").ConfigureAwait(false); + _log.Warn(ex); + await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs index dcf4d0f5..0b12d343 100644 --- a/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/MemegenCommands.cs @@ -1,73 +1,79 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.IO; using System.Linq; using System.Threading.Tasks; using NadekoBot.Attributes; using System.Net.Http; using System.Text; +using Discord.Commands; using NadekoBot.Extensions; namespace NadekoBot.Modules.Searches { public partial class Searches { - - Dictionary map = new Dictionary(); - - public Searches() + [Group] + public class MemegenCommands : NadekoSubmodule { - map.Add('?', "~q"); - map.Add('%', "~p"); - map.Add('#', "~h"); - map.Add('/', "~s"); - map.Add(' ', "-"); - map.Add('-', "--"); - map.Add('_', "__"); - map.Add('"', "''"); - } - - [NadekoCommand, Usage, Description, Aliases] - public async Task Memelist() - { - HttpClientHandler handler = new HttpClientHandler(); - - handler.AllowAutoRedirect = false; - - using (var http = new HttpClient(handler)) + private static readonly ImmutableDictionary _map = new Dictionary() { - var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false); - var data = JsonConvert.DeserializeObject>(rawJson) - .Select(kvp => Path.GetFileName(kvp.Value)); + {'?', "~q"}, + {'%', "~p"}, + {'#', "~h"}, + {'/', "~s"}, + {' ', "-"}, + {'-', "--"}, + {'_', "__"}, + {'"', "''"} - await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false); - } - } + }.ToImmutableDictionary(); - [NadekoCommand, Usage, Description, Aliases] - public async Task Memegen(string meme, string topText, string botText) - { - var top = Replace(topText); - var bot = Replace(botText); - await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg") - .ConfigureAwait(false); - } - - private string Replace(string input) - { - StringBuilder sb = new StringBuilder(); - string tmp; - - foreach (var c in input) + [NadekoCommand, Usage, Description, Aliases] + public async Task Memelist() { - if (map.TryGetValue(c, out tmp)) - sb.Append(tmp); - else - sb.Append(c); + var handler = new HttpClientHandler + { + AllowAutoRedirect = false + }; + + + using (var http = new HttpClient(handler)) + { + var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false); + var data = JsonConvert.DeserializeObject>(rawJson) + .Select(kvp => Path.GetFileName(kvp.Value)); + + await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false); + } } - return sb.ToString(); + [NadekoCommand, Usage, Description, Aliases] + public async Task Memegen(string meme, string topText, string botText) + { + var top = Replace(topText); + var bot = Replace(botText); + await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg") + .ConfigureAwait(false); + } + + private static string Replace(string input) + { + var sb = new StringBuilder(); + + foreach (var c in input) + { + string tmp; + if (_map.TryGetValue(c, out tmp)) + sb.Append(tmp); + else + sb.Append(c); + } + + return sb.ToString(); + } } } -} +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs b/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs index a2760a1a..0a764fab 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OsuCommands.cs @@ -3,7 +3,6 @@ using Discord.Commands; using NadekoBot.Attributes; using NadekoBot.Extensions; using Newtonsoft.Json.Linq; -using NLog; using System; using System.Globalization; using System.IO; @@ -16,21 +15,15 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class OsuCommands : ModuleBase + public class OsuCommands : NadekoSubmodule { - private static Logger _log { get; } - - static OsuCommands() - { - _log = LogManager.GetCurrentClassLogger(); - } [NadekoCommand, Usage, Description, Aliases] public async Task Osu(string usr, [Remainder] string mode = null) { if (string.IsNullOrWhiteSpace(usr)) return; - using (HttpClient http = new HttpClient()) + using (var http = new HttpClient()) { try { @@ -42,15 +35,15 @@ namespace NadekoBot.Modules.Searches http.AddFakeHeaders(); var res = await http.GetStreamAsync(new Uri($"http://lemmmy.pw/osusig/sig.php?uname={ usr }&flagshadow&xpbar&xpbarhex&pp=2&mode={m}")).ConfigureAwait(false); - MemoryStream ms = new MemoryStream(); + var ms = new MemoryStream(); res.CopyTo(ms); ms.Position = 0; - await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link:** \n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false); + await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **{GetText("profile_link")}** \n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false); } catch (Exception ex) { - await Context.Channel.SendErrorAsync("Failed retrieving osu signature.").ConfigureAwait(false); - _log.Warn(ex, "Osu command failed"); + await ReplyErrorLocalized("osu_failed").ConfigureAwait(false); + _log.Warn(ex); } } } @@ -60,7 +53,7 @@ namespace NadekoBot.Modules.Searches { if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) { - await Context.Channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false); + await ReplyErrorLocalized("osu_api_key").ConfigureAwait(false); return; } @@ -75,8 +68,8 @@ namespace NadekoBot.Modules.Searches var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&{mapId}"; var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0]; var sb = new System.Text.StringBuilder(); - var starRating = Math.Round(Double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2); - var time = TimeSpan.FromSeconds(Double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss"); + var starRating = Math.Round(double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2); + var time = TimeSpan.FromSeconds(double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss"); sb.AppendLine($"{obj["artist"]} - {obj["title"]}, mapped by {obj["creator"]}. https://osu.ppy.sh/s/{obj["beatmapset_id"]}"); sb.AppendLine($"{starRating} stars, {obj["bpm"]} BPM | AR{obj["diff_approach"]}, CS{obj["diff_size"]}, OD{obj["diff_overall"]} | Length: {time}"); await Context.Channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false); @@ -84,8 +77,8 @@ namespace NadekoBot.Modules.Searches } catch (Exception ex) { - await Context.Channel.SendErrorAsync("Something went wrong."); - _log.Warn(ex, "Osub command failed"); + await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false); + _log.Warn(ex); } } @@ -121,54 +114,53 @@ namespace NadekoBot.Modules.Searches { var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&b={item["beatmap_id"]}"; var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0]; - var pp = Math.Round(Double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2); + var pp = Math.Round(double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2); var acc = CalculateAcc(item, m); - var mods = ResolveMods(Int32.Parse($"{item["enabled_mods"]}")); - if (mods != "+") - sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | **{mods,-10}** | /b/{item["beatmap_id"]}"); - else - sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}"); + var mods = ResolveMods(int.Parse($"{item["enabled_mods"]}")); + sb.AppendLine(mods != "+" + ? $"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | **{mods,-10}** | /b/{item["beatmap_id"]}" + : $"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}"); } sb.Append("```"); await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false); } catch (Exception ex) { - await channel.SendErrorAsync("Something went wrong."); - _log.Warn(ex, "Osu5 command failed"); + await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false); + _log.Warn(ex); } } } //https://osu.ppy.sh/wiki/Accuracy - private static Double CalculateAcc(JToken play, int mode) + private static double CalculateAcc(JToken play, int mode) { if (mode == 0) { - var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["count300"]}") * 300; - var totalHits = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countmiss"]}"); + var hitPoints = double.Parse($"{play["count50"]}") * 50 + double.Parse($"{play["count100"]}") * 100 + double.Parse($"{play["count300"]}") * 300; + var totalHits = double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countmiss"]}"); totalHits *= 300; return Math.Round(hitPoints / totalHits * 100, 2); } else if (mode == 1) { - var hitPoints = Double.Parse($"{play["countmiss"]}") * 0 + Double.Parse($"{play["count100"]}") * 0.5 + Double.Parse($"{play["count300"]}") * 1; - var totalHits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}"); + var hitPoints = double.Parse($"{play["countmiss"]}") * 0 + double.Parse($"{play["count100"]}") * 0.5 + double.Parse($"{play["count300"]}") * 1; + var totalHits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}"); hitPoints *= 300; totalHits *= 300; return Math.Round(hitPoints / totalHits * 100, 2); } else if (mode == 2) { - var fruitsCaught = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}"); - var totalFruits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countkatu"]}"); + var fruitsCaught = double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}"); + var totalFruits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countkatu"]}"); return Math.Round(fruitsCaught / totalFruits * 100, 2); } else { - var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["countkatu"]}") * 200 + (Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countgeki"]}")) * 300; - var totalHits = Double.Parse($"{play["countmiss"]}") + Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["countkatu"]}") + Double.Parse($"{play["count300"]}") + Double.Parse($"{play["countgeki"]}"); + var hitPoints = double.Parse($"{play["count50"]}") * 50 + double.Parse($"{play["count100"]}") * 100 + double.Parse($"{play["countkatu"]}") * 200 + (double.Parse($"{play["count300"]}") + double.Parse($"{play["countgeki"]}")) * 300; + var totalHits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["countkatu"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countgeki"]}"); totalHits *= 300; return Math.Round(hitPoints / totalHits * 100, 2); } @@ -176,10 +168,10 @@ namespace NadekoBot.Modules.Searches private static string ResolveMap(string mapLink) { - Match s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink); - Match b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink); - Match p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink); - Match m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink); + var s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink); + var b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink); + var p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink); + var m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink); if (s.Success) { var mapId = mapLink.Substring(mapLink.IndexOf("/s/") + 3); diff --git a/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs index 77e88289..65574849 100644 --- a/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/OverwatchCommands.cs @@ -4,7 +4,6 @@ using NadekoBot.Attributes; using NadekoBot.Extensions; using NadekoBot.Modules.Searches.Models; using Newtonsoft.Json; -using NLog; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -14,13 +13,8 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class OverwatchCommands : ModuleBase + public class OverwatchCommands : NadekoSubmodule { - private readonly Logger _log; - public OverwatchCommands() - { - _log = LogManager.GetCurrentClassLogger(); - } [NadekoCommand, Usage, Description, Aliases] public async Task Overwatch(string region, [Remainder] string query = null) { @@ -34,9 +28,9 @@ namespace NadekoBot.Modules.Searches await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); var model = await GetProfile(region, battletag); - var rankimg = $"{model.Competitive.rank_img}"; - var rank = $"{model.Competitive.rank}"; - //var competitiveplay = $"{model.Games.Competitive.played}"; + var rankimg = model.Competitive.rank_img; + var rank = model.Competitive.rank; + if (string.IsNullOrWhiteSpace(rank)) { var embed = new EmbedBuilder() @@ -44,10 +38,10 @@ namespace NadekoBot.Modules.Searches .WithUrl($"https://www.overbuff.com/players/pc/{battletag}") .WithIconUrl($"{model.avatar}")) .WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png") - .AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true)) - .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue("0").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true)) .WithOkColor(); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } @@ -58,22 +52,21 @@ namespace NadekoBot.Modules.Searches .WithUrl($"https://www.overbuff.com/players/pc/{battletag}") .WithIconUrl($"{model.avatar}")) .WithThumbnailUrl(rankimg) - .AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Competitive Wins**").WithValue($"{model.Games.Competitive.wins}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Competitive Loses**").WithValue($"{model.Games.Competitive.lost}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true)) - .AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true)) - .AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_wins")).WithValue($"{model.Games.Competitive.wins}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_losses")).WithValue($"{model.Games.Competitive.lost}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Games.Competitive.played}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue(rank).WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Playtime.competitive}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true)) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); - return; } } catch { - await Context.Channel.SendErrorAsync("Found no user! Please check the **Region** and **BattleTag** before trying again."); + await ReplyErrorLocalized("ow_user_not_found").ConfigureAwait(false); } } public async Task GetProfile(string region, string battletag) @@ -82,8 +75,8 @@ namespace NadekoBot.Modules.Searches { using (var http = new HttpClient()) { - var Url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile"); - var model = JsonConvert.DeserializeObject(Url); + var url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile"); + var model = JsonConvert.DeserializeObject(url); return model.data; } } diff --git a/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs index bd22733b..b44eb97d 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PlaceCommands.cs @@ -10,10 +10,9 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class PlaceCommands : ModuleBase + public class PlaceCommands : NadekoSubmodule { - private static string typesStr { get; } = - string.Format("`List of \"{0}place\" tags:`\n", NadekoBot.ModulePrefixes[typeof(Searches).Name]) + String.Join(", ", Enum.GetNames(typeof(PlaceType))); + private static string typesStr { get; } = string.Join(", ", Enum.GetNames(typeof(PlaceType))); public enum PlaceType { @@ -30,14 +29,15 @@ namespace NadekoBot.Modules.Searches [NadekoCommand, Usage, Description, Aliases] public async Task Placelist() { - await Context.Channel.SendConfirmAsync(typesStr) + await Context.Channel.SendConfirmAsync(GetText("list_of_place_tags", NadekoBot.ModulePrefixes[typeof(Searches).Name]), + typesStr) .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] public async Task Place(PlaceType placeType, uint width = 0, uint height = 0) { - string url = ""; + var url = ""; switch (placeType) { case PlaceType.Cage: diff --git a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index 5f37c3d8..b04769fa 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -6,7 +6,6 @@ using NadekoBot.Modules.Searches.Models; using Newtonsoft.Json; using NLog; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Threading.Tasks; @@ -16,7 +15,7 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class PokemonSearchCommands : ModuleBase + public class PokemonSearchCommands : NadekoSubmodule { private static Dictionary pokemons { get; } = new Dictionary(); private static Dictionary pokemonAbilities { get; } = new Dictionary(); @@ -24,7 +23,7 @@ namespace NadekoBot.Modules.Searches public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json"; public const string PokemonListFile = "data/pokemon/pokemon_list7.json"; - private static Logger _log { get; } + private new static readonly Logger _log; static PokemonSearchCommands() { @@ -57,14 +56,13 @@ namespace NadekoBot.Modules.Searches await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithTitle(kvp.Key.ToTitleCase()) .WithDescription(p.BaseStats.ToString()) - .AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true)) - .AddField(efb => efb.WithName("Height/Weight").WithValue($"{p.HeightM}m/{p.WeightKg}kg").WithIsInline(true)) - .AddField(efb => efb.WithName("Abilitities").WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true)) - ); + .AddField(efb => efb.WithName(GetText("types")).WithValue(string.Join(",\n", p.Types)).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("height_weight")).WithValue(GetText("height_weight_val", p.HeightM, p.WeightKg)).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("abilities")).WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true))); return; } } - await Context.Channel.SendErrorAsync("No pokemon found."); + await ReplyErrorLocalized("pokemon_none").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -80,12 +78,12 @@ namespace NadekoBot.Modules.Searches await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithTitle(kvp.Value.Name) .WithDescription(kvp.Value.Desc) - .AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("rating")).WithValue(kvp.Value.Rating.ToString(_cultureInfo)).WithIsInline(true)) ).ConfigureAwait(false); return; } } - await Context.Channel.SendErrorAsync("No ability found."); + await ReplyErrorLocalized("pokemon_ability_none").ConfigureAwait(false); } } } diff --git a/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs b/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs index 86411634..d122e47a 100644 --- a/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/StreamNotificationCommands.cs @@ -12,9 +12,7 @@ using System.Net.Http; using NadekoBot.Attributes; using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; -using NLog; using NadekoBot.Extensions; -using System.Diagnostics; namespace NadekoBot.Modules.Searches { @@ -65,23 +63,19 @@ namespace NadekoBot.Modules.Searches } [Group] - public class StreamNotificationCommands : ModuleBase + public class StreamNotificationCommands : NadekoSubmodule { - private static Timer checkTimer { get; } - private static ConcurrentDictionary oldCachedStatuses = new ConcurrentDictionary(); - private static ConcurrentDictionary cachedStatuses = new ConcurrentDictionary(); - private static Logger _log { get; } + private static readonly Timer _checkTimer; + private static readonly ConcurrentDictionary _cachedStatuses = new ConcurrentDictionary(); - private static bool FirstPass { get; set; } = true; + private static bool firstPass { get; set; } = true; static StreamNotificationCommands() { - _log = LogManager.GetCurrentClassLogger(); - - checkTimer = new Timer(async (state) => + _checkTimer = new Timer(async (state) => { - oldCachedStatuses = new ConcurrentDictionary(cachedStatuses); - cachedStatuses.Clear(); + var oldCachedStatuses = new ConcurrentDictionary(_cachedStatuses); + _cachedStatuses.Clear(); IEnumerable streams; using (var uow = DbHandler.UnitOfWork()) { @@ -93,7 +87,7 @@ namespace NadekoBot.Modules.Searches try { var newStatus = await GetStreamStatus(fs).ConfigureAwait(false); - if (FirstPass) + if (firstPass) { return; } @@ -108,7 +102,7 @@ namespace NadekoBot.Modules.Searches return; try { - var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false); + await channel.EmbedAsync(fs.GetEmbed(newStatus, channel.Guild.Id)).ConfigureAwait(false); } catch { @@ -122,7 +116,7 @@ namespace NadekoBot.Modules.Searches } })); - FirstPass = false; + firstPass = false; }, null, TimeSpan.Zero, TimeSpan.FromSeconds(60)); } @@ -134,7 +128,7 @@ namespace NadekoBot.Modules.Searches { case FollowedStream.FollowedStreamType.Hitbox: var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}"; - if (checkCache && cachedStatuses.TryGetValue(hitboxUrl, out result)) + if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result)) return result; using (var http = new HttpClient()) { @@ -149,11 +143,11 @@ namespace NadekoBot.Modules.Searches ApiLink = hitboxUrl, Views = hbData.Views }; - cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result); + _cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result); return result; case FollowedStream.FollowedStreamType.Twitch: var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6"; - if (checkCache && cachedStatuses.TryGetValue(twitchUrl, out result)) + if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result)) return result; using (var http = new HttpClient()) { @@ -170,11 +164,11 @@ namespace NadekoBot.Modules.Searches ApiLink = twitchUrl, Views = twData.Stream?.Viewers.ToString() ?? "0" }; - cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result); + _cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result); return result; case FollowedStream.FollowedStreamType.Beam: var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}"; - if (checkCache && cachedStatuses.TryGetValue(beamUrl, out result)) + if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result)) return result; using (var http = new HttpClient()) { @@ -190,7 +184,7 @@ namespace NadekoBot.Modules.Searches ApiLink = beamUrl, Views = bmData.ViewersCurrent.ToString() }; - cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result); + _cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result); return result; default: break; @@ -234,17 +228,21 @@ namespace NadekoBot.Modules.Searches if (!streams.Any()) { - await Context.Channel.SendConfirmAsync("You are not following any streams on this server.").ConfigureAwait(false); + await ReplyErrorLocalized("streams_none").ConfigureAwait(false); return; } var text = string.Join("\n", await Task.WhenAll(streams.Select(async snc => { var ch = await Context.Guild.GetTextChannelAsync(snc.ChannelId); - return $"`{snc.Username}`'s stream on **{(ch)?.Name}** channel. 【`{snc.Type.ToString()}`】"; + return string.Format("{0}'s stream on {1} channel. 【{2}】", + Format.Code(snc.Username), + Format.Bold(ch?.Name ?? "deleted-channel"), + Format.Code(snc.Type.ToString())); }))); - - await Context.Channel.SendConfirmAsync($"You are following **{streams.Count()}** streams on this server.\n\n" + text).ConfigureAwait(false); + + await Context.Channel.SendConfirmAsync(GetText("streams_following", streams.Count()) + "\n\n" + text) + .ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -271,10 +269,13 @@ namespace NadekoBot.Modules.Searches } if (!removed) { - await Context.Channel.SendErrorAsync("No such stream.").ConfigureAwait(false); + await ReplyErrorLocalized("stream_no").ConfigureAwait(false); return; } - await Context.Channel.SendConfirmAsync($"Removed `{username}`'s stream ({type}) from notifications.").ConfigureAwait(false); + + await ReplyConfirmLocalized("stream_removed", + Format.Code(username), + type).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -293,20 +294,24 @@ namespace NadekoBot.Modules.Searches })); if (streamStatus.IsLive) { - await Context.Channel.SendConfirmAsync($"Streamer {username} is online with {streamStatus.Views} viewers."); + await ReplyConfirmLocalized("streamer_online", + username, + streamStatus.Views) + .ConfigureAwait(false); } else { - await Context.Channel.SendConfirmAsync($"Streamer {username} is offline."); + await ReplyConfirmLocalized("streamer_offline", + username).ConfigureAwait(false); } } catch { - await Context.Channel.SendErrorAsync("No channel found."); + await ReplyErrorLocalized("no_channel_found").ConfigureAwait(false); } } - private static async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type) + private async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type) { username = username.ToLowerInvariant().Trim(); var fs = new FollowedStream @@ -324,7 +329,7 @@ namespace NadekoBot.Modules.Searches } catch { - await channel.SendErrorAsync("Stream probably doesn't exist.").ConfigureAwait(false); + await ReplyErrorLocalized("stream_not_exist").ConfigureAwait(false); return; } @@ -335,24 +340,24 @@ namespace NadekoBot.Modules.Searches .Add(fs); await uow.CompleteAsync().ConfigureAwait(false); } - await channel.EmbedAsync(fs.GetEmbed(status), $"🆗 I will notify this channel when status changes.").ConfigureAwait(false); + await channel.EmbedAsync(fs.GetEmbed(status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false); } } } public static class FollowedStreamExtensions { - public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status) + public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status, ulong guildId) { var embed = new EmbedBuilder().WithTitle(fs.Username) .WithUrl(fs.GetLink()) - .AddField(efb => efb.WithName("Status") + .AddField(efb => efb.WithName(fs.GetText("status")) .WithValue(status.IsLive ? "Online" : "Offline") .WithIsInline(true)) - .AddField(efb => efb.WithName("Viewers") + .AddField(efb => efb.WithName(fs.GetText("viewers")) .WithValue(status.IsLive ? status.Views : "-") .WithIsInline(true)) - .AddField(efb => efb.WithName("Platform") + .AddField(efb => efb.WithName(fs.GetText("platform")) .WithValue(fs.Type.ToString()) .WithIsInline(true)) .WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor); @@ -360,15 +365,21 @@ namespace NadekoBot.Modules.Searches return embed; } - public static string GetLink(this FollowedStream fs) { + public static string GetText(this FollowedStream fs, string key, params object[] replacements) => + NadekoTopLevelModule.GetTextStatic(key, + NadekoBot.Localization.GetCultureInfo(fs.GuildId), + typeof(Searches).Name.ToLowerInvariant(), + replacements); + + public static string GetLink(this FollowedStream fs) + { if (fs.Type == FollowedStream.FollowedStreamType.Hitbox) return $"http://www.hitbox.tv/{fs.Username}/"; - else if (fs.Type == FollowedStream.FollowedStreamType.Twitch) + if (fs.Type == FollowedStream.FollowedStreamType.Twitch) return $"http://www.twitch.tv/{fs.Username}/"; - else if (fs.Type == FollowedStream.FollowedStreamType.Beam) + if (fs.Type == FollowedStream.FollowedStreamType.Beam) return $"https://beam.pro/{fs.Username}/"; - else - return "??"; + return "??"; } } } diff --git a/src/NadekoBot/Modules/Searches/Commands/Translator.cs b/src/NadekoBot/Modules/Searches/Commands/Translator.cs index 3ee67e6c..9ed33ce5 100644 --- a/src/NadekoBot/Modules/Searches/Commands/Translator.cs +++ b/src/NadekoBot/Modules/Searches/Commands/Translator.cs @@ -19,10 +19,10 @@ namespace NadekoBot.Modules.Searches } [Group] - public class TranslateCommands : ModuleBase + public class TranslateCommands : NadekoSubmodule { - private static ConcurrentDictionary TranslatedChannels { get; } = new ConcurrentDictionary(); - private static ConcurrentDictionary UserLanguages { get; } = new ConcurrentDictionary(); + private static ConcurrentDictionary translatedChannels { get; } = new ConcurrentDictionary(); + private static ConcurrentDictionary userLanguages { get; } = new ConcurrentDictionary(); static TranslateCommands() { @@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Searches return; bool autoDelete; - if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete)) + if (!translatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete)) return; var key = new UserChannelPair() { @@ -44,10 +44,10 @@ namespace NadekoBot.Modules.Searches }; string langs; - if (!UserLanguages.TryGetValue(key, out langs)) + if (!userLanguages.TryGetValue(key, out langs)) return; - var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore), true) + var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore)) .ConfigureAwait(false); if (autoDelete) try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { } @@ -64,21 +64,21 @@ namespace NadekoBot.Modules.Searches { await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); var translation = await TranslateInternal(langs, text); - await Context.Channel.SendConfirmAsync("Translation " + langs, translation).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync(GetText("translation") + " " + langs, translation).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync("Bad input format, or something went wrong...").ConfigureAwait(false); + await ReplyErrorLocalized("bad_input_format").ConfigureAwait(false); } } - private static async Task TranslateInternal(string langs, [Remainder] string text = null, bool silent = false) + private static async Task TranslateInternal(string langs, [Remainder] string text = null) { var langarr = langs.ToLowerInvariant().Split('>'); if (langarr.Length != 2) throw new ArgumentException(); - string from = langarr[0]; - string to = langarr[1]; + var from = langarr[0]; + var to = langarr[1]; text = text?.Trim(); if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException(); @@ -101,20 +101,20 @@ namespace NadekoBot.Modules.Searches if (autoDelete == AutoDeleteAutoTranslate.Del) { - TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true); - try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel. User messages will be auto-deleted.").ConfigureAwait(false); } catch { } + translatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true); + await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false); return; } bool throwaway; - if (TranslatedChannels.TryRemove(channel.Id, out throwaway)) + if (translatedChannels.TryRemove(channel.Id, out throwaway)) { - try { await channel.SendConfirmAsync("Stopped automatic translation of messages on this channel.").ConfigureAwait(false); } catch { } + await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false); return; } - else if (TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del)) + if (translatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del)) { - try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel.").ConfigureAwait(false); } catch { } + await ReplyConfirmLocalized("atl_started").ConfigureAwait(false); } } @@ -130,8 +130,8 @@ namespace NadekoBot.Modules.Searches if (string.IsNullOrWhiteSpace(langs)) { - if (UserLanguages.TryRemove(ucp, out langs)) - await Context.Channel.SendConfirmAsync($"{Context.User.Mention}'s auto-translate language has been removed.").ConfigureAwait(false); + if (userLanguages.TryRemove(ucp, out langs)) + await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false); return; } @@ -143,20 +143,20 @@ namespace NadekoBot.Modules.Searches if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to)) { - try { await Context.Channel.SendErrorAsync("Invalid source and/or target language.").ConfigureAwait(false); } catch { } + await ReplyErrorLocalized("invalid_lang").ConfigureAwait(false); return; } - UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs); + userLanguages.AddOrUpdate(ucp, langs, (key, val) => langs); - await Context.Channel.SendConfirmAsync($"Your auto-translate language has been set to {from}>{to}").ConfigureAwait(false); + await ReplyConfirmLocalized("atl_set", from, to).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Translangs() { - await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", columns: 3); + await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", 3); } } diff --git a/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs b/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs index a8de4c7e..b747f2bd 100644 --- a/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/XkcdCommands.cs @@ -12,9 +12,9 @@ namespace NadekoBot.Modules.Searches public partial class Searches { [Group] - public class XkcdCommands : ModuleBase + public class XkcdCommands : NadekoSubmodule { - private const string xkcdUrl = "https://xkcd.com"; + private const string _xkcdUrl = "https://xkcd.com"; [NadekoCommand, Usage, Description, Aliases] [Priority(1)] @@ -24,9 +24,9 @@ namespace NadekoBot.Modules.Searches { using (var http = new HttpClient()) { - var res = await http.GetStringAsync($"{xkcdUrl}/info.0.json").ConfigureAwait(false); + var res = await http.GetStringAsync($"{_xkcdUrl}/info.0.json").ConfigureAwait(false); var comic = JsonConvert.DeserializeObject(res); - var sent = await Context.Channel.SendMessageAsync($"{Context.User.Mention} " + comic.ToString()) + var sent = await Context.Channel.SendMessageAsync($"{Context.User.Mention} " + comic) .ConfigureAwait(false); await Task.Delay(10000).ConfigureAwait(false); @@ -47,14 +47,14 @@ namespace NadekoBot.Modules.Searches using (var http = new HttpClient()) { - var res = await http.GetStringAsync($"{xkcdUrl}/{num}/info.0.json").ConfigureAwait(false); + var res = await http.GetStringAsync($"{_xkcdUrl}/{num}/info.0.json").ConfigureAwait(false); var comic = JsonConvert.DeserializeObject(res); var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor) .WithImageUrl(comic.ImageLink) - .WithAuthor(eab => eab.WithName(comic.Title).WithUrl($"{xkcdUrl}/{num}").WithIconUrl("http://xkcd.com/s/919f27.ico")) - .AddField(efb => efb.WithName("Comic#").WithValue(comic.Num.ToString()).WithIsInline(true)) - .AddField(efb => efb.WithName("Date").WithValue($"{comic.Month}/{comic.Year}").WithIsInline(true)); + .WithAuthor(eab => eab.WithName(comic.Title).WithUrl($"{_xkcdUrl}/{num}").WithIconUrl("http://xkcd.com/s/919f27.ico")) + .AddField(efb => efb.WithName(GetText("comic_number")).WithValue(comic.Num.ToString()).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("date")).WithValue($"{comic.Month}/{comic.Year}").WithIsInline(true)); var sent = await Context.Channel.EmbedAsync(embed) .ConfigureAwait(false); @@ -75,9 +75,6 @@ namespace NadekoBot.Modules.Searches [JsonProperty("img")] public string ImageLink { get; set; } public string Alt { get; set; } - - public override string ToString() - => $"`Comic:` #{Num} `Title:` {Title} `Date:` {Month}/{Year}\n{ImageLink}"; } } } diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 096a171c..28fb15bb 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -41,15 +41,15 @@ namespace NadekoBot.Modules.Searches var data = JsonConvert.DeserializeObject(response); var embed = new EmbedBuilder() - .AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true)) - .AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true)) - .AddField(fb => fb.WithName("☁ **Condition**").WithValue(String.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true)) - .AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{data.main.humidity}%").WithIsInline(true)) - .AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue(data.wind.speed + " km/h").WithIsInline(true)) - .AddField(fb => fb.WithName("🌡 **Temperature**").WithValue(data.main.temp + "°C").WithIsInline(true)) - .AddField(fb => fb.WithName("🔆 **Min - Max**").WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true)) - .AddField(fb => fb.WithName("🌄 **Sunrise (utc)**").WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true)) - .AddField(fb => fb.WithName("🌇 **Sunset (utc)**").WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true)) + .AddField(fb => fb.WithName("🌍 " + GetText("location")).WithValue(data.name + ", " + data.sys.country).WithIsInline(true)) + .AddField(fb => fb.WithName("📏 " + GetText("latlong")).WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true)) + .AddField(fb => fb.WithName("☁ " + GetText("condition")).WithValue(string.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true)) + .AddField(fb => fb.WithName("😓 " + GetText("humidity")).WithValue($"{data.main.humidity}%").WithIsInline(true)) + .AddField(fb => fb.WithName("💨 " + GetText("wind_speed")).WithValue(data.wind.speed + " km/h").WithIsInline(true)) + .AddField(fb => fb.WithName("🌡 " + GetText("temperature")).WithValue(data.main.temp + "°C").WithIsInline(true)) + .AddField(fb => fb.WithName("🔆 " + GetText("min_max")).WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true)) + .AddField(fb => fb.WithName("🌄 " + GetText("sunrise")).WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true)) + .AddField(fb => fb.WithName("🌇 " + GetText("sunset")).WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true)) .WithOkColor() .WithFooter(efb => efb.WithText("Powered by http://openweathermap.org")); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); @@ -58,17 +58,15 @@ namespace NadekoBot.Modules.Searches [NadekoCommand, Usage, Description, Aliases] public async Task Youtube([Remainder] string query = null) { - if (!(await ValidateQuery(Context.Channel, query).ConfigureAwait(false))) return; + if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return; var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault(); if (string.IsNullOrWhiteSpace(result)) { - await Context.Channel.SendErrorAsync("No results found for that query.").ConfigureAwait(false); + await ReplyErrorLocalized("no_results").ConfigureAwait(false); return; } await Context.Channel.SendMessageAsync(result).ConfigureAwait(false); - - //await Context.Channel.EmbedAsync(new Discord.API.Embed() { Video = new Discord.API.EmbedVideo() { Url = result.Replace("watch?v=", "embed/") }, Color = NadekoBot.OkColor }).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -80,7 +78,7 @@ namespace NadekoBot.Modules.Searches var movie = await OmdbProvider.FindMovie(query); if (movie == null) { - await Context.Channel.SendErrorAsync("Failed to find that movie.").ConfigureAwait(false); + await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false); return; } await Context.Channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false); @@ -120,7 +118,7 @@ namespace NadekoBot.Modules.Searches var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false); var embed = new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50)) + .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) .WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch") .WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithDescription(res.Link) @@ -172,7 +170,7 @@ namespace NadekoBot.Modules.Searches var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false); var embed = new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50)) + .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) .WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch") .WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithDescription(res.Link) @@ -203,7 +201,7 @@ namespace NadekoBot.Modules.Searches var embed = new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50)) + .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) .WithUrl(fullQueryLink) .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) .WithDescription(source) @@ -233,13 +231,14 @@ namespace NadekoBot.Modules.Searches if (shortened == arg) { - await Context.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false); + await ReplyErrorLocalized("shorten_fail").ConfigureAwait(false); + return; } await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) - .AddField(efb => efb.WithName("Original Url") + .AddField(efb => efb.WithName(GetText("original_url")) .WithValue($"<{arg}>")) - .AddField(efb => efb.WithName("Short Url") + .AddField(efb => efb.WithName(GetText("short_url")) .WithValue($"<{shortened}>"))) .ConfigureAwait(false); } @@ -287,7 +286,7 @@ namespace NadekoBot.Modules.Searches var embed = new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50)) + .WithAuthor(eab => eab.WithName(GetText("search_for") + " " + terms.TrimTo(50)) .WithUrl(fullQueryLink) .WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithTitle(Context.User.ToString()) @@ -296,26 +295,22 @@ namespace NadekoBot.Modules.Searches var desc = await Task.WhenAll(results.Select(async res => $"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n")) .ConfigureAwait(false); - await Context.Channel.EmbedAsync(embed.WithDescription(String.Concat(desc))).ConfigureAwait(false); + await Context.Channel.EmbedAsync(embed.WithDescription(string.Concat(desc))).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] - public async Task MagicTheGathering([Remainder] string name = null) + public async Task MagicTheGathering([Remainder] string name) { var arg = name; if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false); return; - } await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - string response = ""; using (var http = new HttpClient()) { http.DefaultRequestHeaders.Clear(); - response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}") - .ConfigureAwait(false); + var response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}") + .ConfigureAwait(false); try { var items = JArray.Parse(response).ToArray(); @@ -325,50 +320,46 @@ namespace NadekoBot.Modules.Searches var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString()); var cost = item["cost"].ToString(); var desc = item["text"].ToString(); - var types = String.Join(",\n", item["types"].ToObject()); + var types = string.Join(",\n", item["types"].ToObject()); var img = item["editions"][0]["image_url"].ToString(); var embed = new EmbedBuilder().WithOkColor() .WithTitle(item["name"].ToString()) .WithDescription(desc) .WithImageUrl(img) - .AddField(efb => efb.WithName("Store Url").WithValue(storeUrl).WithIsInline(true)) - .AddField(efb => efb.WithName("Cost").WithValue(cost).WithIsInline(true)) - .AddField(efb => efb.WithName("Types").WithValue(types).WithIsInline(true)); + .AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true)) + .AddField(efb => efb.WithName(GetText("types")).WithValue(types).WithIsInline(true)); //.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true)); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync($"Error could not find the card '{arg}'.").ConfigureAwait(false); + await ReplyErrorLocalized("card_not_found").ConfigureAwait(false); } } } [NadekoCommand, Usage, Description, Aliases] - public async Task Hearthstone([Remainder] string name = null) + public async Task Hearthstone([Remainder] string name) { var arg = name; if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false); return; - } if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) { - await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false); + await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); return; } await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - string response = ""; using (var http = new HttpClient()) { http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); - response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}") - .ConfigureAwait(false); + var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}") + .ConfigureAwait(false); try { var items = JArray.Parse(response).Shuffle().ToList(); @@ -391,7 +382,7 @@ namespace NadekoBot.Modules.Searches string msg = null; if (items.Count > 4) { - msg = "⚠ Found over 4 images. Showing random 4."; + msg = GetText("hs_over_x", 4); } var ms = new MemoryStream(); await Task.Run(() => images.AsEnumerable().Merge().Save(ms)); @@ -400,8 +391,8 @@ namespace NadekoBot.Modules.Searches } catch (Exception ex) { - await Context.Channel.SendErrorAsync($"Error occured.").ConfigureAwait(false); _log.Error(ex); + await ReplyErrorLocalized("error_occured").ConfigureAwait(false); } } } @@ -411,23 +402,20 @@ namespace NadekoBot.Modules.Searches { if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) { - await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false); + await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); return; } - var arg = query; - if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("Please enter a sentence.").ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(query)) return; - } + await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); using (var http = new HttpClient()) { http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); http.DefaultRequestHeaders.Add("Accept", "text/plain"); - var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(arg)}").ConfigureAwait(false); + var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(query)}").ConfigureAwait(false); try { var embed = new EmbedBuilder() @@ -439,7 +427,7 @@ namespace NadekoBot.Modules.Searches } catch { - await Context.Channel.SendErrorAsync("Failed to yodify your sentence.").ConfigureAwait(false); + await ReplyErrorLocalized("yodify_error").ConfigureAwait(false); } } } @@ -449,22 +437,19 @@ namespace NadekoBot.Modules.Searches { if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) { - await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false); + await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); return; } - var arg = query; - if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(query)) return; - } + await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); using (var http = new HttpClient()) { http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Add("Accept", "application/json"); - var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false); + var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(query)}").ConfigureAwait(false); try { var items = JObject.Parse(res); @@ -480,7 +465,7 @@ namespace NadekoBot.Modules.Searches } catch { - await Context.Channel.SendErrorAsync("Failed finding a definition for that term.").ConfigureAwait(false); + await ReplyErrorLocalized("ud_error").ConfigureAwait(false); } } } @@ -507,39 +492,36 @@ namespace NadekoBot.Modules.Searches definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString(); var embed = new EmbedBuilder().WithOkColor() - .WithTitle("Define: " + word) + .WithTitle(GetText("define") + " " + word) .WithDescription(definition) .WithFooter(efb => efb.WithText(sense.Gramatical_info?.type)); if (sense.Examples != null) - embed.AddField(efb => efb.WithName("Example").WithValue(sense.Examples.First().text)); + embed.AddField(efb => efb.WithName(GetText("example")).WithValue(sense.Examples.First().text)); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); } } [NadekoCommand, Usage, Description, Aliases] - public async Task Hashtag([Remainder] string query = null) + public async Task Hashtag([Remainder] string query) { - var arg = query; - if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(query)) return; - } + if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) { - await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false); + await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); return; } await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - var res = ""; + string res; using (var http = new HttpClient()) { http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); - res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(arg)}.json").ConfigureAwait(false); + res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(query)}.json").ConfigureAwait(false); } try @@ -558,7 +540,7 @@ namespace NadekoBot.Modules.Searches } catch { - await Context.Channel.SendErrorAsync("Failed finding a definition for that tag.").ConfigureAwait(false); + await ReplyErrorLocalized("hashtag_error").ConfigureAwait(false); } } @@ -572,7 +554,7 @@ namespace NadekoBot.Modules.Searches return; var fact = JObject.Parse(response)["facts"][0].ToString(); - await Context.Channel.SendConfirmAsync("🐈fact", fact).ConfigureAwait(false); + await Context.Channel.SendConfirmAsync("🐈" + GetText("catfact"), fact).ConfigureAwait(false); } } @@ -609,7 +591,7 @@ namespace NadekoBot.Modules.Searches var result = await http.GetStringAsync("https://en.wikipedia.org//w/api.php?action=query&format=json&prop=info&redirects=1&formatversion=2&inprop=url&titles=" + Uri.EscapeDataString(query)); var data = JsonConvert.DeserializeObject(result); if (data.Query.Pages[0].Missing) - await Context.Channel.SendErrorAsync("That page could not be found.").ConfigureAwait(false); + await ReplyErrorLocalized("wiki_page_not_found").ConfigureAwait(false); else await Context.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false); } @@ -625,26 +607,19 @@ namespace NadekoBot.Modules.Searches img.ApplyProcessor(new BackgroundColorProcessor(ImageSharp.Color.FromHex(color)), img.Bounds); - await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png").ConfigureAwait(false); ; + await Context.Channel.SendFileAsync(img.ToStream(), $"{color}.png").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] public async Task Videocall([Remainder] params IUser[] users) { - try + var allUsrs = users.Append(Context.User); + var allUsrsArray = allUsrs.ToArray(); + var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString())); + str += new NadekoRandom().Next(); + foreach (var usr in allUsrsArray) { - var allUsrs = users.Append(Context.User); - var allUsrsArray = allUsrs.ToArray(); - var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString())); - str += new NadekoRandom().Next(); - foreach (var usr in allUsrsArray) - { - await (await (usr as IGuildUser).CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false); - } - } - catch (Exception ex) - { - _log.Error(ex); + await (await usr.CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false); } } @@ -664,11 +639,11 @@ namespace NadekoBot.Modules.Searches } [NadekoCommand, Usage, Description, Aliases] - public async Task Wikia(string target, [Remainder] string query = null) + public async Task Wikia(string target, [Remainder] string query) { if (string.IsNullOrWhiteSpace(target) || string.IsNullOrWhiteSpace(query)) { - await Context.Channel.SendErrorAsync("Please enter a target wikia, followed by search query.").ConfigureAwait(false); + await ReplyErrorLocalized("wikia_input_error").ConfigureAwait(false); return; } await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); @@ -680,90 +655,87 @@ namespace NadekoBot.Modules.Searches var res = await http.GetStringAsync($"http://www.{Uri.EscapeUriString(target)}.wikia.com/api/v1/Search/List?query={Uri.EscapeUriString(query)}&limit=25&minArticleQuality=10&batch=1&namespaces=0%2C14").ConfigureAwait(false); var items = JObject.Parse(res); var found = items["items"][0]; - var response = $@"`Title:` {found["title"].ToString()} -`Quality:` {found["quality"]} -`URL:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}"; + var response = $@"`{GetText("title")}` {found["title"]} +`{GetText("quality")}` {found["quality"]} +`{GetText("url")}:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}"; await Context.Channel.SendMessageAsync(response).ConfigureAwait(false); } catch { - await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false); + await ReplyErrorLocalized("wikia_error").ConfigureAwait(false); } } } - [NadekoCommand, Usage, Description, Aliases] - public async Task MCPing([Remainder] string query = null) - { - var arg = query; - if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false); - return; - } - await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - using (var http = new HttpClient()) - { - http.DefaultRequestHeaders.Clear(); - string ip = arg.Split(':')[0]; - string port = arg.Split(':')[1]; - var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false); - try - { - var items = JObject.Parse(res); - var sb = new StringBuilder(); - int ping = (int)Math.Ceiling(Double.Parse(items["latency"].ToString())); - sb.AppendLine($"`Server:` {arg}"); - sb.AppendLine($"`Version:` {items["version"]["name"].ToString()} / Protocol {items["version"]["protocol"].ToString()}"); - sb.AppendLine($"`Description:` {items["description"].ToString()}"); - sb.AppendLine($"`Online Players:` {items["players"]["online"].ToString()}/{items["players"]["max"].ToString()}"); - sb.Append($"`Latency:` {ping}"); - await Context.Channel.SendMessageAsync(sb.ToString()); - } - catch - { - await Context.Channel.SendErrorAsync($"Failed finding `{arg}`.").ConfigureAwait(false); - } - } - } + //[NadekoCommand, Usage, Description, Aliases] + //public async Task MCPing([Remainder] string query2 = null) + //{ + // var query = query2; + // if (string.IsNullOrWhiteSpace(query)) + // return; + // await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); + // using (var http = new HttpClient()) + // { + // http.DefaultRequestHeaders.Clear(); + // var ip = query.Split(':')[0]; + // var port = query.Split(':')[1]; + // var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false); + // try + // { + // var items = JObject.Parse(res); + // var sb = new StringBuilder(); + // var ping = (int)Math.Ceiling(double.Parse(items["latency"].ToString())); + // sb.AppendLine($"`Server:` {query}"); + // sb.AppendLine($"`Version:` {items["version"]["name"]} / Protocol {items["version"]["protocol"]}"); + // sb.AppendLine($"`Description:` {items["description"]}"); + // sb.AppendLine($"`Online Players:` {items["players"]["online"]}/{items["players"]["max"]}"); + // sb.Append($"`Latency:` {ping}"); + // await Context.Channel.SendMessageAsync(sb.ToString()); + // } + // catch + // { + // await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false); + // } + // } + //} - [NadekoCommand, Usage, Description, Aliases] - public async Task MCQ([Remainder] string query = null) - { - var arg = query; - if (string.IsNullOrWhiteSpace(arg)) - { - await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false); - return; - } - await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); - using (var http = new HttpClient()) - { - http.DefaultRequestHeaders.Clear(); - try - { - string ip = arg.Split(':')[0]; - string port = arg.Split(':')[1]; - var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false); - var items = JObject.Parse(res); - var sb = new StringBuilder(); - sb.AppendLine($"`Server:` {arg.ToString()} 〘Status: {items["status"]}〙"); - sb.AppendLine($"`Player List (First 5):`"); - foreach (var item in items["Playerlist"].Take(5)) - { - sb.AppendLine($"〔:rosette: {item}〕"); - } - sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}"); - sb.AppendLine($"`Plugins:` {items["Plugins"]}"); - sb.Append($"`Version:` {items["Version"]}"); - await Context.Channel.SendMessageAsync(sb.ToString()); - } - catch - { - await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false); - } - } - } + //[NadekoCommand, Usage, Description, Aliases] + //public async Task MCQ([Remainder] string query = null) + //{ + // var arg = query; + // if (string.IsNullOrWhiteSpace(arg)) + // { + // await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false); + // return; + // } + // await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); + // using (var http = new HttpClient()) + // { + // http.DefaultRequestHeaders.Clear(); + // try + // { + // var ip = arg.Split(':')[0]; + // var port = arg.Split(':')[1]; + // var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false); + // var items = JObject.Parse(res); + // var sb = new StringBuilder(); + // sb.AppendLine($"`Server:` {arg} 〘Status: {items["status"]}〙"); + // sb.AppendLine("`Player List (First 5):`"); + // foreach (var item in items["Playerlist"].Take(5)) + // { + // sb.AppendLine($"〔:rosette: {item}〕"); + // } + // sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}"); + // sb.AppendLine($"`Plugins:` {items["Plugins"]}"); + // sb.Append($"`Version:` {items["Version"]}"); + // await Context.Channel.SendMessageAsync(sb.ToString()); + // } + // catch + // { + // await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false); + // } + // } + //} public enum DapiSearchType { @@ -774,7 +746,7 @@ namespace NadekoBot.Modules.Searches Yandere } - public static async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type) + public async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type) { var channel = umsg.Channel; @@ -783,7 +755,7 @@ namespace NadekoBot.Modules.Searches var url = await InternalDapiSearch(tag, type).ConfigureAwait(false); if (url == null) - await channel.SendErrorAsync(umsg.Author.Mention + " No results."); + await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results")); else await channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithDescription(umsg.Author.Mention + " " + tag) @@ -794,7 +766,7 @@ namespace NadekoBot.Modules.Searches public static async Task InternalDapiSearch(string tag, DapiSearchType type) { tag = tag?.Replace(" ", "_"); - string website = ""; + var website = ""; switch (type) { case DapiSearchType.Safebooru: @@ -839,10 +811,10 @@ namespace NadekoBot.Modules.Searches return null; } } - public static async Task ValidateQuery(IMessageChannel ch, string query) + public async Task ValidateQuery(IMessageChannel ch, string query) { - if (!string.IsNullOrEmpty(query.Trim())) return true; - await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(query)) return true; + await ch.SendErrorAsync(GetText("specify_search_params")).ConfigureAwait(false); return false; } } diff --git a/src/NadekoBot/NadekoBot.xproj.DotSettings b/src/NadekoBot/NadekoBot.xproj.DotSettings index e418daea..c5dd7773 100644 --- a/src/NadekoBot/NadekoBot.xproj.DotSettings +++ b/src/NadekoBot/NadekoBot.xproj.DotSettings @@ -3,4 +3,5 @@ True True True + True True \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index f0c48b9d..2eaf5acf 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -4073,6 +4073,852 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Abilities. + /// + public static string searches_abilities { + get { + return ResourceManager.GetString("searches_abilities", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No favorite anime yet. + /// + public static string searches_anime_no_fav { + get { + return ResourceManager.GetString("searches_anime_no_fav", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Started automatic translation of messages on this channel. User messages will be auto-deleted.. + /// + public static string searches_atl_ad_started { + get { + return ResourceManager.GetString("searches_atl_ad_started", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to your auto-translate language has been removed.. + /// + public static string searches_atl_removed { + get { + return ResourceManager.GetString("searches_atl_removed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Your auto-translate language has been set to {from}>{to}. + /// + public static string searches_atl_set { + get { + return ResourceManager.GetString("searches_atl_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Started automatic translation of messages on this channel.. + /// + public static string searches_atl_started { + get { + return ResourceManager.GetString("searches_atl_started", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stopped automatic translation of messages on this channel.. + /// + public static string searches_atl_stopped { + get { + return ResourceManager.GetString("searches_atl_stopped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bad input format, or something went wrong.. + /// + public static string searches_bad_input_format { + get { + return ResourceManager.GetString("searches_bad_input_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Couldn't find that card.. + /// + public static string searches_card_not_found { + get { + return ResourceManager.GetString("searches_card_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to fact. + /// + public static string searches_catfact { + get { + return ResourceManager.GetString("searches_catfact", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Chapters. + /// + public static string searches_chapters { + get { + return ResourceManager.GetString("searches_chapters", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Comic #. + /// + public static string searches_comic_number { + get { + return ResourceManager.GetString("searches_comic_number", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Competitive Loses. + /// + public static string searches_compet_loses { + get { + return ResourceManager.GetString("searches_compet_loses", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Competitive Played. + /// + public static string searches_compet_played { + get { + return ResourceManager.GetString("searches_compet_played", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Competitive Rank. + /// + public static string searches_compet_rank { + get { + return ResourceManager.GetString("searches_compet_rank", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Competitive Wins. + /// + public static string searches_compet_wins { + get { + return ResourceManager.GetString("searches_compet_wins", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Completed. + /// + public static string searches_completed { + get { + return ResourceManager.GetString("searches_completed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Condition. + /// + public static string searches_condition { + get { + return ResourceManager.GetString("searches_condition", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cost. + /// + public static string searches_cost { + get { + return ResourceManager.GetString("searches_cost", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Date. + /// + public static string searches_date { + get { + return ResourceManager.GetString("searches_date", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Define:. + /// + public static string searches_define { + get { + return ResourceManager.GetString("searches_define", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Dropped. + /// + public static string searches_dropped { + get { + return ResourceManager.GetString("searches_dropped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Episodes. + /// + public static string searches_episodes { + get { + return ResourceManager.GetString("searches_episodes", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Error occured.. + /// + public static string searches_error_occured { + get { + return ResourceManager.GetString("searches_error_occured", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Example. + /// + public static string searches_example { + get { + return ResourceManager.GetString("searches_example", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed finding that animu.. + /// + public static string searches_failed_finding_anime { + get { + return ResourceManager.GetString("searches_failed_finding_anime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed finding that mango.. + /// + public static string searches_failed_finding_manga { + get { + return ResourceManager.GetString("searches_failed_finding_manga", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Genres. + /// + public static string searches_genres { + get { + return ResourceManager.GetString("searches_genres", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed finding a definition for that tag.. + /// + public static string searches_hashtag_error { + get { + return ResourceManager.GetString("searches_hashtag_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Height/Weight. + /// + public static string searches_height_weight { + get { + return ResourceManager.GetString("searches_height_weight", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}m/{1}kg. + /// + public static string searches_height_weight_val { + get { + return ResourceManager.GetString("searches_height_weight_val", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Humidity. + /// + public static string searches_humidity { + get { + return ResourceManager.GetString("searches_humidity", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Image Search For:. + /// + public static string searches_image_search_for { + get { + return ResourceManager.GetString("searches_image_search_for", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to find that movie.. + /// + public static string searches_imdb_fail { + get { + return ResourceManager.GetString("searches_imdb_fail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid source or target language.. + /// + public static string searches_invalid_lang { + get { + return ResourceManager.GetString("searches_invalid_lang", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Joes not loaded.. + /// + public static string searches_jokes_not_loaded { + get { + return ResourceManager.GetString("searches_jokes_not_loaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lat/Long. + /// + public static string searches_latlong { + get { + return ResourceManager.GetString("searches_latlong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Level. + /// + public static string searches_level { + get { + return ResourceManager.GetString("searches_level", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Lsit of {0}place tags. + /// + public static string searches_list_of_place_tags { + get { + return ResourceManager.GetString("searches_list_of_place_tags", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Location. + /// + public static string searches_location { + get { + return ResourceManager.GetString("searches_location", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Magic Items not loaded.. + /// + public static string searches_magicitems_not_loaded { + get { + return ResourceManager.GetString("searches_magicitems_not_loaded", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}'s MAL profile. + /// + public static string searches_mal_profile { + get { + return ResourceManager.GetString("searches_mal_profile", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Bot owner didn't specify MashapeApiKey. You can't use this functionality.. + /// + public static string searches_mashape_api_missing { + get { + return ResourceManager.GetString("searches_mashape_api_missing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Min/Max. + /// + public static string searches_min_max { + get { + return ResourceManager.GetString("searches_min_max", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No channel found.. + /// + public static string searches_no_channel_found { + get { + return ResourceManager.GetString("searches_no_channel_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No results found.. + /// + public static string searches_no_results { + get { + return ResourceManager.GetString("searches_no_results", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to On-Hold. + /// + public static string searches_on_hold { + get { + return ResourceManager.GetString("searches_on_hold", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Original Url. + /// + public static string searches_original_url { + get { + return ResourceManager.GetString("searches_original_url", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to An osu! API key is required.. + /// + public static string searches_osu_api_key { + get { + return ResourceManager.GetString("searches_osu_api_key", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed retreiving osu signature.. + /// + public static string searches_osu_failed { + get { + return ResourceManager.GetString("searches_osu_failed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Found over {0} images. Showing random {0}.. + /// + public static string searches_over_x { + get { + return ResourceManager.GetString("searches_over_x", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to User not found! Please check the Region and BattleTag before trying again.. + /// + public static string searches_ow_user_not_found { + get { + return ResourceManager.GetString("searches_ow_user_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Plan to watch. + /// + public static string searches_plan_to_watch { + get { + return ResourceManager.GetString("searches_plan_to_watch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Platform. + /// + public static string searches_platform { + get { + return ResourceManager.GetString("searches_platform", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No ability found.. + /// + public static string searches_pokemon_ability_none { + get { + return ResourceManager.GetString("searches_pokemon_ability_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No pokemon found.. + /// + public static string searches_pokemon_none { + get { + return ResourceManager.GetString("searches_pokemon_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Profile Link:. + /// + public static string searches_profile_link { + get { + return ResourceManager.GetString("searches_profile_link", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quality:. + /// + public static string searches_quality { + get { + return ResourceManager.GetString("searches_quality", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quick Playtime. + /// + public static string searches_quick_playtime { + get { + return ResourceManager.GetString("searches_quick_playtime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Quick Wins. + /// + public static string searches_quick_wins { + get { + return ResourceManager.GetString("searches_quick_wins", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Rating. + /// + public static string searches_rating { + get { + return ResourceManager.GetString("searches_rating", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Score:. + /// + public static string searches_score { + get { + return ResourceManager.GetString("searches_score", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Search For:. + /// + public static string searches_search_for { + get { + return ResourceManager.GetString("searches_search_for", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Short Url. + /// + public static string searches_short_url { + get { + return ResourceManager.GetString("searches_short_url", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to shorten that url.. + /// + public static string searches_shorten_fail { + get { + return ResourceManager.GetString("searches_shorten_fail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Something went wrong.. + /// + public static string searches_something_went_wrong { + get { + return ResourceManager.GetString("searches_something_went_wrong", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please specify search parameters.. + /// + public static string searches_specify_search_params { + get { + return ResourceManager.GetString("searches_specify_search_params", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Status. + /// + public static string searches_status { + get { + return ResourceManager.GetString("searches_status", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Store Url. + /// + public static string searches_store_url { + get { + return ResourceManager.GetString("searches_store_url", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No such stream.. + /// + public static string searches_stream_no { + get { + return ResourceManager.GetString("searches_stream_no", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Stream probably doesn't exist.. + /// + public static string searches_stream_not_exist { + get { + return ResourceManager.GetString("searches_stream_not_exist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removed {0}'s stream ({1}) from notifications.. + /// + public static string searches_stream_removed { + get { + return ResourceManager.GetString("searches_stream_removed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will notify this channel when status changes.. + /// + public static string searches_stream_tracked { + get { + return ResourceManager.GetString("searches_stream_tracked", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Streamer {0} is offline.. + /// + public static string searches_streamer_offline { + get { + return ResourceManager.GetString("searches_streamer_offline", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Streamer {0} is online with {1} viewers.. + /// + public static string searches_streamer_online { + get { + return ResourceManager.GetString("searches_streamer_online", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are following {0} streams on this server.. + /// + public static string searches_streams_following { + get { + return ResourceManager.GetString("searches_streams_following", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You are not following any streams on this server.. + /// + public static string searches_streams_none { + get { + return ResourceManager.GetString("searches_streams_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sunrise. + /// + public static string searches_sunrise { + get { + return ResourceManager.GetString("searches_sunrise", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Sunset. + /// + public static string searches_sunset { + get { + return ResourceManager.GetString("searches_sunset", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Temperature. + /// + public static string searches_temperature { + get { + return ResourceManager.GetString("searches_temperature", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Title:. + /// + public static string searches_title { + get { + return ResourceManager.GetString("searches_title", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Top 3 favorite anime:. + /// + public static string searches_top_3_fav_anime { + get { + return ResourceManager.GetString("searches_top_3_fav_anime", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Translation:. + /// + public static string searches_translation { + get { + return ResourceManager.GetString("searches_translation", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Types. + /// + public static string searches_types { + get { + return ResourceManager.GetString("searches_types", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed finding definition for that term.. + /// + public static string searches_ud_error { + get { + return ResourceManager.GetString("searches_ud_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Url. + /// + public static string searches_url { + get { + return ResourceManager.GetString("searches_url", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Viewers. + /// + public static string searches_viewers { + get { + return ResourceManager.GetString("searches_viewers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Watching. + /// + public static string searches_watching { + get { + return ResourceManager.GetString("searches_watching", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Page not found.. + /// + public static string searches_wiki_page_not_found { + get { + return ResourceManager.GetString("searches_wiki_page_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed finding that term on the specified wikia.. + /// + public static string searches_wikia_error { + get { + return ResourceManager.GetString("searches_wikia_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Please enter a target wikia, followed by search query.. + /// + public static string searches_wikia_input_error { + get { + return ResourceManager.GetString("searches_wikia_input_error", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Wind Speed. + /// + public static string searches_wind_speed { + get { + return ResourceManager.GetString("searches_wind_speed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The {0} most banned champions. + /// + public static string searches_x_most_banned_champs { + get { + return ResourceManager.GetString("searches_x_most_banned_champs", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to yodify your sentence.. + /// + public static string searches_yodify_error { + get { + return ResourceManager.GetString("searches_yodify_error", resourceCulture); + } + } + /// /// Looks up a localized string similar to Joined. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index d365b08c..5030e3bd 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1485,6 +1485,289 @@ Don't forget to leave your discord name or id in the message. Word filtering enabled on this server. + + Abilities + + + No favorite anime yet + + + Started automatic translation of messages on this channel. User messages will be auto-deleted. + + + your auto-translate language has been removed. + + + Your auto-translate language has been set to {from}>{to} + + + Started automatic translation of messages on this channel. + + + Stopped automatic translation of messages on this channel. + + + Bad input format, or something went wrong. + + + Couldn't find that card. + + + fact + + + Chapters + + + Comic # + + + Competitive Loses + + + Competitive Played + + + Competitive Rank + + + Competitive Wins + + + Completed + + + Condition + + + Cost + + + Date + + + Define: + + + Dropped + + + Episodes + + + Error occured. + + + Example + + + Failed finding that animu. + + + Failed finding that mango. + + + Genres + + + Failed finding a definition for that tag. + + + Height/Weight + + + {0}m/{1}kg + + + Humidity + + + Image Search For: + + + Failed to find that movie. + + + Invalid source or target language. + + + Joes not loaded. + + + Lat/Long + + + Level + + + Lsit of {0}place tags + Don't translate {0}place + + + Location + + + Magic Items not loaded. + + + {0}'s MAL profile + + + Bot owner didn't specify MashapeApiKey. You can't use this functionality. + + + Min/Max + + + No channel found. + + + No results found. + + + On-Hold + + + Original Url + + + An osu! API key is required. + + + Failed retreiving osu signature. + + + Found over {0} images. Showing random {0}. + + + User not found! Please check the Region and BattleTag before trying again. + + + Plan to watch + + + Platform + + + No ability found. + + + No pokemon found. + + + Profile Link: + + + Quality: + + + Quick Playtime + + + Quick Wins + + + Rating + + + Score: + + + Search For: + + + Failed to shorten that url. + + + Short Url + + + Something went wrong. + + + Please specify search parameters. + + + Status + + + Store Url + + + Streamer {0} is offline. + + + Streamer {0} is online with {1} viewers. + + + You are following {0} streams on this server. + + + You are not following any streams on this server. + + + No such stream. + + + Stream probably doesn't exist. + + + Removed {0}'s stream ({1}) from notifications. + + + I will notify this channel when status changes. + + + Sunrise + + + Sunset + + + Temperature + + + Title: + + + Top 3 favorite anime: + + + Translation: + + + Types + + + Failed finding definition for that term. + + + Url + + + Viewers + + + Watching + + + Failed finding that term on the specified wikia. + + + Please enter a target wikia, followed by search query. + + + Page not found. + + + Wind Speed + + + The {0} most banned champions + + + Failed to yodify your sentence. + Joined From 466ec12de0b8a3eaec1e8d74299c1e89d954619b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Fri, 24 Feb 2017 17:15:52 +0100 Subject: [PATCH 200/256] >ttt string fix, thx devedux --- src/NadekoBot/Modules/Games/Commands/TicTacToe.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index 5769e2e5..d4293e03 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -135,7 +135,7 @@ namespace NadekoBot.Modules.Games if (_phase == Phase.Ended) embed.WithFooter(efb => efb.WithText(GetText("ttt_no_moves"))); else - embed.WithFooter(efb => efb.WithText(GetText("users_move", _users[_curUserIndex]))); + embed.WithFooter(efb => efb.WithText(GetText("ttt_users_move", _users[_curUserIndex]))); } else embed.WithFooter(efb => efb.WithText(GetText("ttt_has_won", _winner))); From 22f961a093cbe947e3bf643b4cbb356f50861df7 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 02:04:32 +0100 Subject: [PATCH 201/256] localization complete, or nearly complete if I missed something --- .../Modules/Games/Commands/TicTacToe.cs | 4 +- src/NadekoBot/Modules/Music/Classes/Song.cs | 2 +- src/NadekoBot/Modules/Music/Music.cs | 212 ++++---- .../Resources/ResponseStrings.Designer.cs | 477 ++++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 160 ++++++ src/NadekoBot/Services/Impl/StatsService.cs | 4 +- 6 files changed, 761 insertions(+), 98 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs index d4293e03..918a1592 100644 --- a/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs +++ b/src/NadekoBot/Modules/Games/Commands/TicTacToe.cs @@ -65,7 +65,6 @@ namespace NadekoBot.Modules.Games } private readonly ITextChannel _channel; - private readonly Logger _log; private readonly IGuildUser[] _users; private readonly int?[,] _state; private Phase _phase; @@ -90,8 +89,7 @@ namespace NadekoBot.Modules.Games { null, null, null }, { null, null, null }, }; - - _log = LogManager.GetCurrentClassLogger(); + _phase = Phase.Starting; _moveLock = new SemaphoreSlim(1, 1); } diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index 8a69ec6f..d088bfde 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Music.Classes //pwetty public string PrettyProvider => - $"{(SongInfo.Provider ?? "No Provider")}"; + $"{(SongInfo.Provider ?? "???")}"; public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime; diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index a7b46269..5d685bbc 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Music { [NadekoModule("Music", "!!")] [DontAutoLoad] - public partial class Music : NadekoTopLevelModule + public class Music : NadekoTopLevelModule { public static ConcurrentDictionary MusicPlayers { get; } = new ConcurrentDictionary(); @@ -155,7 +155,14 @@ namespace NadekoBot.Modules.Music return; var val = musicPlayer.FairPlay = !musicPlayer.FairPlay; - await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false); + if (val) + { + await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false); + } + else + { + await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false); + } } [NadekoCommand, Usage, Description, Aliases] @@ -184,34 +191,31 @@ namespace NadekoBot.Modules.Music [RequireContext(ContextType.Guild)] public async Task ListQueue(int page = 1) { - + Song currentSong; MusicPlayer musicPlayer; - if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) + if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer) || + (currentSong = musicPlayer?.CurrentSong) == null) { - await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false); + await ReplyErrorLocalized("no_music_player").ConfigureAwait(false); return; } if (page <= 0) return; - var currentSong = musicPlayer.CurrentSong; - if (currentSong == null) - { - await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false); - return; - } - try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } const int itemsPerPage = 10; var total = musicPlayer.TotalPlaytime; - var totalStr = total == TimeSpan.MaxValue ? "∞" : $"{(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s"; + var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format", + (int) total.TotalHours, + total.Minutes, + total.Seconds); var maxPlaytime = musicPlayer.MaxPlaytimeSeconds; var lastPage = musicPlayer.Playlist.Count / itemsPerPage; - Func printAction = (curPage) => + Func printAction = curPage => { - int startAt = itemsPerPage * (curPage - 1); + var startAt = itemsPerPage * (curPage - 1); var number = 0 + startAt; var desc = string.Join("\n", musicPlayer.Playlist .Skip(startAt) @@ -221,19 +225,22 @@ namespace NadekoBot.Modules.Music desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc; if (musicPlayer.RepeatSong) - desc = "🔂 Repeating Current Song\n\n" + desc; + desc = "🔂 " + GetText("repeating_cur_song") +"\n\n" + desc; else if (musicPlayer.RepeatPlaylist) - desc = "🔁 Repeating Playlist\n\n" + desc; - + desc = "🔁 " + GetText("repeating_playlist")+"\n\n" + desc; + var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}") - .WithMusicIcon()) + .WithAuthor(eab => eab.WithName(GetText("player_queue", curPage, lastPage + 1)) + .WithMusicIcon()) .WithDescription(desc) .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + - $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " + - (musicPlayer.FairPlay ? "✔️fairplay" : "✖️fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) + $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " + + (musicPlayer.FairPlay + ? "✔️" + GetText("fairplay") + : "✖️" + GetText("fairplay")) + " | " + + (maxPlaytime == 0 ? "unlimited" : GetText("play_limit", maxPlaytime)))) .WithOkColor(); return embed; @@ -254,7 +261,7 @@ namespace NadekoBot.Modules.Music try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } var embed = new EmbedBuilder().WithOkColor() - .WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon()) + .WithAuthor(eab => eab.WithName(GetText("now_playing")).WithMusicIcon()) .WithDescription(currentSong.PrettyName) .WithThumbnailUrl(currentSong.Thumbnail) .WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}")); @@ -271,21 +278,22 @@ namespace NadekoBot.Modules.Music return; if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) return; - if (val < 0) + if (val < 0 || val > 100) + { + await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false); return; + } var volume = musicPlayer.SetVolume(val); - await Context.Channel.SendConfirmAsync($"🎵 Volume set to {volume}%").ConfigureAwait(false); + await ReplyConfirmLocalized("volume_set", volume).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Defvol([Remainder] int val) { - - if (val < 0 || val > 100) { - await Context.Channel.SendErrorAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false); + await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false); return; } using (var uow = DbHandler.UnitOfWork()) @@ -293,7 +301,7 @@ namespace NadekoBot.Modules.Music uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f; uow.Complete(); } - await Context.Channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false); + await ReplyConfirmLocalized("defvol_set").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -307,13 +315,10 @@ namespace NadekoBot.Modules.Music if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) return; if (musicPlayer.Playlist.Count < 2) - { - await Context.Channel.SendErrorAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false); return; - } musicPlayer.Shuffle(); - await Context.Channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false); + await ReplyConfirmLocalized("songs_shuffled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -326,29 +331,31 @@ namespace NadekoBot.Modules.Music return; if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild) { - await Context.Channel.SendErrorAsync($"💢 You need to be in a **voice channel** on this server.").ConfigureAwait(false); + await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false); return; } var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault(); if (plId == null) { - await Context.Channel.SendErrorAsync("No search results for that query."); + await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); return; } var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); if (!ids.Any()) { - await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false); + await ReplyErrorLocalized("no_search_results").ConfigureAwait(false); return; } var count = ids.Count(); - var msg = await Context.Channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false); + var msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue", + Format.Bold(count.ToString()))) + .ConfigureAwait(false); var cancelSource = new CancellationTokenSource(); var gusr = (IGuildUser)Context.User; - + //todo use grouping while (ids.Any() && !cancelSource.IsCancellationRequested) { var tasks = Task.WhenAll(ids.Take(5).Select(async id => @@ -367,7 +374,7 @@ namespace NadekoBot.Modules.Music ids = ids.Skip(5); } - await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false); + await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -393,7 +400,7 @@ namespace NadekoBot.Modules.Music { try { - mp.AddSong(new Song(new Classes.SongInfo + mp.AddSong(new Song(new SongInfo { Title = svideo.FullName, Provider = "SoundCloud", @@ -435,20 +442,20 @@ namespace NadekoBot.Modules.Music // ignored } } - await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false); + await ReplyConfirmLocalized("dir_queue_complete").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] - public async Task Radio(string radio_link) + public async Task Radio(string radioLink) { if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild) { - await Context.Channel.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice (ITextChannel)Context.Channel, try rejoining it.").ConfigureAwait(false); + await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false); return; } - await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false); + await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radioLink, musicType: MusicType.Radio).ConfigureAwait(false); if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages) { Context.Message.DeleteAfter(10); @@ -505,8 +512,7 @@ namespace NadekoBot.Modules.Music MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return; musicPlayer.ClearQueue(); - await Context.Channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false); - return; + await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -532,7 +538,7 @@ namespace NadekoBot.Modules.Music !int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 || n1 > playlist.Count || n2 > playlist.Count) { - await Context.Channel.SendErrorAsync("Invalid input.").ConfigureAwait(false); + await ReplyConfirmLocalized("invalid_input").ConfigureAwait(false); return; } @@ -544,9 +550,9 @@ namespace NadekoBot.Modules.Music var embed = new EmbedBuilder() .WithTitle($"{s.SongInfo.Title.TrimTo(70)}") .WithUrl(s.SongUrl) - .WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png")) - .AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true)) - .AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true)) + .WithAuthor(eab => eab.WithName(GetText("song_moved")).WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png")) + .AddField(fb => fb.WithName(GetText("from_position")).WithValue($"#{n1}").WithIsInline(true)) + .AddField(fb => fb.WithName(GetText("to_position")).WithValue($"#{n2}").WithIsInline(true)) .WithColor(NadekoBot.OkColor); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); @@ -564,7 +570,11 @@ namespace NadekoBot.Modules.Music return; musicPlayer.MaxQueueSize = size; - await Context.Channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}."); + + if(size == 0) + await ReplyConfirmLocalized("max_queue_unlimited").ConfigureAwait(false); + else + await ReplyConfirmLocalized("max_queue_x", size).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -580,9 +590,9 @@ namespace NadekoBot.Modules.Music return; musicPlayer.MaxPlaytimeSeconds = seconds; if (seconds == 0) - await channel.SendConfirmAsync($"🎵 Max playtime has no limit now."); + await ReplyConfirmLocalized("max_playtime_none").ConfigureAwait(false); else - await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds."); + await ReplyConfirmLocalized("max_playtime_set").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -601,11 +611,11 @@ namespace NadekoBot.Modules.Music if (currentValue) await Context.Channel.EmbedAsync(new EmbedBuilder() .WithOkColor() - .WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track")) + .WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 " + GetText("repeating_track"))) .WithDescription(currentSong.PrettyName) .WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync($"🔂 Current track repeat stopped.") + await Context.Channel.SendConfirmAsync("🔂 " + GetText("repeating_track_stopped")) .ConfigureAwait(false); } @@ -618,7 +628,10 @@ namespace NadekoBot.Modules.Music if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return; var currentValue = musicPlayer.ToggleRepeatPlaylist(); - await Context.Channel.SendConfirmAsync($"🔁 Repeat playlist {(currentValue ? "**enabled**." : "**disabled**.")}").ConfigureAwait(false); + if(currentValue) + await ReplyConfirmLocalized("rpl_enabled").ConfigureAwait(false); + else + await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -655,7 +668,10 @@ namespace NadekoBot.Modules.Music await uow.CompleteAsync().ConfigureAwait(false); } - await Context.Channel.SendConfirmAsync(($"🎵 Saved playlist as **{name}**, ID: {playlist.Id}.")).ConfigureAwait(false); + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle(GetText("playlist_saved")) + .AddField(efb => efb.WithName(GetText("name")).WithValue(name)) + .AddField(efb => efb.WithName(GetText("id")).WithValue(playlist.Id.ToString()))); } [NadekoCommand, Usage, Description, Aliases] @@ -670,11 +686,11 @@ namespace NadekoBot.Modules.Music if (mpl == null) { - await Context.Channel.SendErrorAsync("Can't find playlist with that ID.").ConfigureAwait(false); + await ReplyErrorLocalized("playlist_id_not_found").ConfigureAwait(false); return; } IUserMessage msg = null; - try { msg = await Context.Channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try { msg = await ReplyConfirmLocalized("attempting_to_queue", Format.Bold(mpl.Songs.Count.ToString())).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } foreach (var item in mpl.Songs) { var usr = (IGuildUser)Context.User; @@ -686,15 +702,13 @@ namespace NadekoBot.Modules.Music catch { break; } } if (msg != null) - await msg.ModifyAsync(m => m.Content = $"✅ Done loading playlist **{mpl.Name}**.").ConfigureAwait(false); + await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] public async Task Playlists([Remainder] int num = 1) { - - if (num <= 0) return; @@ -706,8 +720,9 @@ namespace NadekoBot.Modules.Music } var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon()) - .WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by *{r.Author}* ({r.Songs.Count} songs)"))) + .WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon()) + .WithDescription(string.Join("\n", playlists.Select(r => + GetText("playlists", "#" + r.Id, r.Name, r.Author, r.Songs.Count)))) .WithOkColor(); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); @@ -717,13 +732,12 @@ namespace NadekoBot.Modules.Music [RequireContext(ContextType.Guild)] public async Task DeletePlaylist([Remainder] int id) { - bool success = false; - MusicPlaylist pl = null; + var success = false; try { using (var uow = DbHandler.UnitOfWork()) { - pl = uow.MusicPlaylists.Get(id); + var pl = uow.MusicPlaylists.Get(id); if (pl != null) { @@ -733,15 +747,13 @@ namespace NadekoBot.Modules.Music await uow.CompleteAsync().ConfigureAwait(false); success = true; } - else - success = false; } } if (!success) - await Context.Channel.SendErrorAsync("Failed to delete that playlist. It either doesn't exist, or you are not its author.").ConfigureAwait(false); + await ReplyErrorLocalized("playlist_delete_fail").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false); + await ReplyConfirmLocalized("playlist_deleted").ConfigureAwait(false); } catch (Exception ex) { @@ -781,7 +793,7 @@ namespace NadekoBot.Modules.Music if (seconds.Length == 1) seconds = "0" + seconds; - await Context.Channel.SendConfirmAsync($"Skipped to `{minutes}:{seconds}`").ConfigureAwait(false); + await ReplyConfirmLocalized("skipped_to", minutes, seconds).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -793,9 +805,9 @@ namespace NadekoBot.Modules.Music return; if (!musicPlayer.ToggleAutoplay()) - await Context.Channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false); + await ReplyConfirmLocalized("autoplay_disabled").ConfigureAwait(false); else - await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false); + await ReplyConfirmLocalized("autoplay_enabled").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -806,29 +818,29 @@ namespace NadekoBot.Modules.Music MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) { - await Context.Channel.SendErrorAsync("Music must be playing before you set an ouput channel.").ConfigureAwait(false); + await ReplyErrorLocalized("player_none").ConfigureAwait(false); return; } musicPlayer.OutputTextChannel = (ITextChannel)Context.Channel; - await Context.Channel.SendConfirmAsync("I will now output playing, finished, paused and removed songs in this channel.").ConfigureAwait(false); + await ReplyConfirmLocalized("set_music_channel").ConfigureAwait(false); } - public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) + public async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal) { if (voiceCh == null || voiceCh.Guild != textCh.Guild) { if (!silent) - await textCh.SendErrorAsync($"💢 You need to be in a voice channel on this server.").ConfigureAwait(false); + await textCh.SendErrorAsync(GetText("must_be_in_voice")).ConfigureAwait(false); throw new ArgumentNullException(nameof(voiceCh)); } if (string.IsNullOrWhiteSpace(query) || query.Length < 3) - throw new ArgumentException("💢 Invalid query for queue song.", nameof(query)); + throw new ArgumentException("Invalid song query.", nameof(query)); var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server => { - float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; + float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume; using (var uow = DbHandler.UnitOfWork()) { vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume; @@ -845,7 +857,7 @@ namespace NadekoBot.Modules.Music try { lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon()) + .WithAuthor(eab => eab.WithName(GetText("finished_song")).WithMusicIcon()) .WithDescription(song.PrettyName) .WithFooter(ef => ef.WithText(song.PrettyInfo))) .ConfigureAwait(false); @@ -866,7 +878,10 @@ namespace NadekoBot.Modules.Music true).ConfigureAwait(false); } } - catch { } + catch + { + // ignored + } }; mp.OnStarted += async (player, song) => @@ -876,7 +891,7 @@ namespace NadekoBot.Modules.Music { // ignored } - var sender = player as MusicPlayer; + var sender = player; if (sender == null) return; try @@ -884,12 +899,15 @@ namespace NadekoBot.Modules.Music playingMessage?.DeleteAfter(0); playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon()) + .WithAuthor(eab => eab.WithName(GetText("playing_song")).WithMusicIcon()) .WithDescription(song.PrettyName) .WithFooter(ef => ef.WithText(song.PrettyInfo))) .ConfigureAwait(false); } - catch { } + catch + { + // ignored + } }; mp.OnPauseChanged += async (paused) => { @@ -897,13 +915,16 @@ namespace NadekoBot.Modules.Music { IUserMessage msg; if (paused) - msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); + msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("music_paused")).ConfigureAwait(false); else - msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); + msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("music_resumed")).ConfigureAwait(false); msg?.DeleteAfter(10); } - catch { } + catch + { + // ignored + } }; mp.SongRemoved += async (song, index) => @@ -911,7 +932,7 @@ namespace NadekoBot.Modules.Music try { var embed = new EmbedBuilder() - .WithAuthor(eab => eab.WithName("Removed song #" + (index + 1)).WithMusicIcon()) + .WithAuthor(eab => eab.WithName(GetText("removed_song") + " #" + (index + 1)).WithMusicIcon()) .WithDescription(song.PrettyName) .WithFooter(ef => ef.WithText(song.PrettyInfo)) .WithErrorColor(); @@ -939,7 +960,14 @@ namespace NadekoBot.Modules.Music } catch (PlaylistFullException) { - try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { } + try + { + await textCh.SendConfirmAsync(GetText("queue_full", musicPlayer.MaxQueueSize)); + } + catch + { + // ignored + } throw; } if (!silent) @@ -948,8 +976,8 @@ namespace NadekoBot.Modules.Music { //var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() - .WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon()) - .WithDescription($"{resolvedSong.PrettyName}\nQueue ") + .WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon()) + .WithDescription($"{resolvedSong.PrettyName}\n{GetText("queue")} ") .WithThumbnailUrl(resolvedSong.Thumbnail) .WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider))) .ConfigureAwait(false); diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 2eaf5acf..1d11bac1 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -3388,6 +3388,483 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Attempting to queue {0} songs.... + /// + public static string music_attempting_to_queue { + get { + return ResourceManager.GetString("music_attempting_to_queue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Autoplay disabled.. + /// + public static string music_autoplay_disabled { + get { + return ResourceManager.GetString("music_autoplay_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Autoplay enabled.. + /// + public static string music_autoplay_enabled { + get { + return ResourceManager.GetString("music_autoplay_enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Default volume set to {0}%. + /// + public static string music_defvol_set { + get { + return ResourceManager.GetString("music_defvol_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Directory queue complete.. + /// + public static string music_dir_queue_complete { + get { + return ResourceManager.GetString("music_dir_queue_complete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Finished Song. + /// + public static string music_finished_song { + get { + return ResourceManager.GetString("music_finished_song", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fair play disabled.. + /// + public static string music_fp_disabled { + get { + return ResourceManager.GetString("music_fp_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fair play enabled.. + /// + public static string music_fp_enabled { + get { + return ResourceManager.GetString("music_fp_enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to From position. + /// + public static string music_from_position { + get { + return ResourceManager.GetString("music_from_position", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Id. + /// + public static string music_id { + get { + return ResourceManager.GetString("music_id", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid input.. + /// + public static string music_invalid_input { + get { + return ResourceManager.GetString("music_invalid_input", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Max playtime has no limit now.. + /// + public static string music_max_playtime_none { + get { + return ResourceManager.GetString("music_max_playtime_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Max playtime set to {0} second(s).. + /// + public static string music_max_playtime_set { + get { + return ResourceManager.GetString("music_max_playtime_set", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Max music queue size set to unlimited.. + /// + public static string music_max_queue_unlimited { + get { + return ResourceManager.GetString("music_max_queue_unlimited", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Max music queue size set to {0} track(s).. + /// + public static string music_max_queue_x { + get { + return ResourceManager.GetString("music_max_queue_x", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to You need to be in the voice channel on this server.. + /// + public static string music_must_be_in_voice { + get { + return ResourceManager.GetString("music_must_be_in_voice", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Name. + /// + public static string music_name { + get { + return ResourceManager.GetString("music_name", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No active music player.. + /// + public static string music_no_player { + get { + return ResourceManager.GetString("music_no_player", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No search results.. + /// + public static string music_no_search_results { + get { + return ResourceManager.GetString("music_no_search_results", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Now Playing. + /// + public static string music_now_playing { + get { + return ResourceManager.GetString("music_now_playing", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Music playback paused.. + /// + public static string music_paused { + get { + return ResourceManager.GetString("music_paused", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}s limit. + /// + public static string music_play_limit { + get { + return ResourceManager.GetString("music_play_limit", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No music player active.. + /// + public static string music_player_none { + get { + return ResourceManager.GetString("music_player_none", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Player Queue - Page {0}/{1}. + /// + public static string music_player_queue { + get { + return ResourceManager.GetString("music_player_queue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playing Song. + /// + public static string music_playing_song { + get { + return ResourceManager.GetString("music_playing_song", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Failed to delete that playlist. It either doesn't exist, or you are not its author.. + /// + public static string music_playlist_delete_fail { + get { + return ResourceManager.GetString("music_playlist_delete_fail", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playlist deleted.. + /// + public static string music_playlist_deleted { + get { + return ResourceManager.GetString("music_playlist_deleted", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playlist with that ID doesn't exist.. + /// + public static string music_playlist_id_not_found { + get { + return ResourceManager.GetString("music_playlist_id_not_found", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playlist queue complete.. + /// + public static string music_playlist_queue_complete { + get { + return ResourceManager.GetString("music_playlist_queue_complete", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Playlist Saved. + /// + public static string music_playlist_saved { + get { + return ResourceManager.GetString("music_playlist_saved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to `#{0}` - **{1}** by *{2}* ({3} songs). + /// + public static string music_playlists { + get { + return ResourceManager.GetString("music_playlists", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Page {0} of Saved Playlists. + /// + public static string music_playlists_page { + get { + return ResourceManager.GetString("music_playlists_page", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Queue. + /// + public static string music_queue { + get { + return ResourceManager.GetString("music_queue", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Music queue cleared.. + /// + public static string music_queue_cleared { + get { + return ResourceManager.GetString("music_queue_cleared", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Queue is full at {0}/{0}.. + /// + public static string music_queue_full { + get { + return ResourceManager.GetString("music_queue_full", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Queued Song. + /// + public static string music_queued_song { + get { + return ResourceManager.GetString("music_queued_song", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Removed song. + /// + public static string music_removed_song { + get { + return ResourceManager.GetString("music_removed_song", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repeating Current Song. + /// + public static string music_repeating_cur_song { + get { + return ResourceManager.GetString("music_repeating_cur_song", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repeating Playlist. + /// + public static string music_repeating_playlist { + get { + return ResourceManager.GetString("music_repeating_playlist", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repeating Track. + /// + public static string music_repeating_track { + get { + return ResourceManager.GetString("music_repeating_track", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Current track repeat stopped.. + /// + public static string music_repeating_track_stopped { + get { + return ResourceManager.GetString("music_repeating_track_stopped", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Music playback resumed.. + /// + public static string music_resumed { + get { + return ResourceManager.GetString("music_resumed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repeat playlist disabled.. + /// + public static string music_rpl_disabled { + get { + return ResourceManager.GetString("music_rpl_disabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Repeat playlist enabled.. + /// + public static string music_rpl_enabled { + get { + return ResourceManager.GetString("music_rpl_enabled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to I will now output playing, finished, paused and removed songs in this channel.. + /// + public static string music_set_music_channel { + get { + return ResourceManager.GetString("music_set_music_channel", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Skipped to `{0}:{1}`. + /// + public static string music_skipped_to { + get { + return ResourceManager.GetString("music_skipped_to", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Song Moved. + /// + public static string music_song_moved { + get { + return ResourceManager.GetString("music_song_moved", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Songs shuffled.. + /// + public static string music_songs_shuffled { + get { + return ResourceManager.GetString("music_songs_shuffled", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}h {1}m {2}s. + /// + public static string music_time_format { + get { + return ResourceManager.GetString("music_time_format", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to To position. + /// + public static string music_to_position { + get { + return ResourceManager.GetString("music_to_position", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to unlimited. + /// + public static string music_unlimited { + get { + return ResourceManager.GetString("music_unlimited", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Volume must be between 0 and 100. + /// + public static string music_volume_input_invalid { + get { + return ResourceManager.GetString("music_volume_input_invalid", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Volume set to {0}%. + /// + public static string music_volume_set { + get { + return ResourceManager.GetString("music_volume_set", resourceCulture); + } + } + /// /// Looks up a localized string similar to Autohentai started. Reposting every {0}s with one of the following tags: ///{1}. diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 5030e3bd..8598ae3e 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1332,6 +1332,166 @@ Don't forget to leave your discord name or id in the message. {0} vs {1} + + Attempting to queue {0} songs... + + + Autoplay disabled. + + + Autoplay enabled. + + + Default volume set to {0}% + + + Directory queue complete. + + + Finished Song + + + Fair play disabled. + + + Fair play enabled. + + + From position + + + Id + + + Invalid input. + + + Max playtime has no limit now. + + + Max playtime set to {0} second(s). + + + Max music queue size set to unlimited. + + + Max music queue size set to {0} track(s). + + + You need to be in the voice channel on this server. + + + Name + + + Now Playing + + + No active music player. + + + No search results. + + + Music playback paused. + + + No music player active. + + + Player Queue - Page {0}/{1} + + + Playing Song + + + `#{0}` - **{1}** by *{2}* ({3} songs) + + + Page {0} of Saved Playlists + + + Playlist deleted. + + + Failed to delete that playlist. It either doesn't exist, or you are not its author. + + + Playlist with that ID doesn't exist. + + + Playlist queue complete. + + + Playlist Saved + + + {0}s limit + + + Queue + + + Queued Song + + + Music queue cleared. + + + Queue is full at {0}/{0}. + + + Removed song + context: "removed song #5" + + + Repeating Current Song + + + Repeating Playlist + + + Repeating Track + + + Current track repeat stopped. + + + Music playback resumed. + + + Repeat playlist disabled. + + + Repeat playlist enabled. + + + I will now output playing, finished, paused and removed songs in this channel. + + + Skipped to `{0}:{1}` + + + Songs shuffled. + + + Song Moved + + + {0}h {1}m {2}s + + + To position + + + unlimited + + + Volume must be between 0 and 100 + + + Volume set to {0}% + Disabled usage of ALL MODULES on {0} channel. diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index f7f39985..f3058f97 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -14,9 +14,9 @@ namespace NadekoBot.Services.Impl public class StatsService : IStatsService { private readonly DiscordShardedClient _client; - private DateTime _started; + private readonly DateTime _started; - public const string BotVersion = "1.1.8-alpha"; + public const string BotVersion = "1.2-beta"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net"; From 778dbcb1d02d56412b7c4fad5340a2c3e2f6ae20 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 03:43:30 +0100 Subject: [PATCH 202/256] Fixed 3 response strings. Fixed ~pokeab rare bug thanks to infy --- .../Games/Commands/PlantAndPickCommands.cs | 2 +- src/NadekoBot/Modules/Music/Music.cs | 4 ++-- .../Searches/Commands/Models/SearchPokemon.cs | 17 +++++++++-------- .../Searches/Commands/PokemonSearchCommands.cs | 7 +++++-- .../Resources/ResponseStrings.Designer.cs | 9 +++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 3 +++ 6 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs index 4778e37c..42fea1a4 100644 --- a/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs +++ b/src/NadekoBot/Modules/Games/Commands/PlantAndPickCommands.cs @@ -82,7 +82,7 @@ namespace NadekoBot.Modules.Games var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name]; var toSend = dropAmount == 1 ? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign, prefix) - : GetLocalText(channel, "curgen_pl", NadekoBot.BotConfig.CurrencySign, prefix); + : GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign, prefix); var file = GetRandomCurrencyImage(); using (var fileStream = file.Value.ToStream()) { diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 5d685bbc..d1734a2d 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -915,9 +915,9 @@ namespace NadekoBot.Modules.Music { IUserMessage msg; if (paused) - msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("music_paused")).ConfigureAwait(false); + msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("paused")).ConfigureAwait(false); else - msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("music_resumed")).ConfigureAwait(false); + msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("resumed")).ConfigureAwait(false); msg?.DeleteAfter(10); } diff --git a/src/NadekoBot/Modules/Searches/Commands/Models/SearchPokemon.cs b/src/NadekoBot/Modules/Searches/Commands/Models/SearchPokemon.cs index b1e24ad5..ed758f7f 100644 --- a/src/NadekoBot/Modules/Searches/Commands/Models/SearchPokemon.cs +++ b/src/NadekoBot/Modules/Searches/Commands/Models/SearchPokemon.cs @@ -33,22 +33,23 @@ namespace NadekoBot.Modules.Searches.Models public string[] Evos { get; set; } public string[] EggGroups { get; set; } - public override string ToString() => $@"`Name:` {Species} -`Types:` {string.Join(", ", Types)} -`Stats:` {BaseStats} -`Height:` {HeightM,4}m `Weight:` {WeightKg}kg -`Abilities:` {string.Join(", ", Abilities.Values)}"; +// public override string ToString() => $@"`Name:` {Species} +//`Types:` {string.Join(", ", Types)} +//`Stats:` {BaseStats} +//`Height:` {HeightM,4}m `Weight:` {WeightKg}kg +//`Abilities:` {string.Join(", ", Abilities.Values)}"; } public class SearchPokemonAbility { public string Desc { get; set; } + public string ShortDesc { get; set; } public string Name { get; set; } public float Rating { get; set; } - public override string ToString() => $@"`Name:` : {Name} -`Rating:` {Rating} -`Description:` {Desc}"; +// public override string ToString() => $@"`Name:` : {Name} +//`Rating:` {Rating} +//`Description:` {Desc}"; } } diff --git a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs index b04769fa..bc77cda3 100644 --- a/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/PokemonSearchCommands.cs @@ -77,8 +77,11 @@ namespace NadekoBot.Modules.Searches { await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() .WithTitle(kvp.Value.Name) - .WithDescription(kvp.Value.Desc) - .AddField(efb => efb.WithName(GetText("rating")).WithValue(kvp.Value.Rating.ToString(_cultureInfo)).WithIsInline(true)) + .WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc) + ? kvp.Value.ShortDesc + : kvp.Value.Desc) + .AddField(efb => efb.WithName(GetText("rating")) + .WithValue(kvp.Value.Rating.ToString(_cultureInfo)).WithIsInline(true)) ).ConfigureAwait(false); return; } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 1d11bac1..e785af5a 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2081,6 +2081,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to Animal Race is already running.. + /// + public static string gambling_animal_race_already_started { + get { + return ResourceManager.GetString("gambling_animal_race_already_started", resourceCulture); + } + } + /// /// Looks up a localized string similar to Failed to start since there was not enough participants.. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 8598ae3e..79eb3601 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1218,6 +1218,9 @@ Don't forget to leave your discord name or id in the message. Submissions Closed + + Animal Race is already running. + Total: {0} Average: {1} From 68ac62d4e88140b7d29aaf5f40e8ec319bc00356 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 03:52:27 +0100 Subject: [PATCH 203/256] fairplay key was missing --- src/NadekoBot/Resources/ResponseStrings.Designer.cs | 9 +++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 3 +++ 2 files changed, 12 insertions(+) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index e785af5a..839a9927 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -3442,6 +3442,15 @@ namespace NadekoBot.Resources { } } + /// + /// Looks up a localized string similar to fairplay. + /// + public static string music_fairplay { + get { + return ResourceManager.GetString("music_fairplay", resourceCulture); + } + } + /// /// Looks up a localized string similar to Finished Song. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 79eb3601..8dd225d1 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1350,6 +1350,9 @@ Don't forget to leave your discord name or id in the message. Directory queue complete. + + fairplay + Finished Song From 628504b5fd1916e5039b07e8a5393e5a9c584441 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 03:57:59 +0100 Subject: [PATCH 204/256] videocall fixed --- src/NadekoBot/Modules/Searches/Searches.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Searches/Searches.cs b/src/NadekoBot/Modules/Searches/Searches.cs index 28fb15bb..7ed4dae9 100644 --- a/src/NadekoBot/Modules/Searches/Searches.cs +++ b/src/NadekoBot/Modules/Searches/Searches.cs @@ -611,7 +611,7 @@ namespace NadekoBot.Modules.Searches } [NadekoCommand, Usage, Description, Aliases] - public async Task Videocall([Remainder] params IUser[] users) + public async Task Videocall(params IUser[] users) { var allUsrs = users.Append(Context.User); var allUsrsArray = allUsrs.ToArray(); From 7c00b9fe868dbb89428d0ab4368fb7a779c1b779 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 04:02:11 +0100 Subject: [PATCH 205/256] ~trans now properly throws an error if language is invalid --- .../Modules/Searches/Commands/GoogleTranslator.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs b/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs index 5604fdd1..9cd0645c 100644 --- a/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs +++ b/src/NadekoBot/Modules/Searches/Commands/GoogleTranslator.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json.Linq; +using System; +using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Linq; using System.Net; @@ -155,6 +156,11 @@ namespace NadekoBot.Modules.Searches { string text; + if(!_languageDictionary.ContainsKey(sourceLanguage) || + !_languageDictionary.ContainsKey(targetLanguage)) + throw new ArgumentException(); + + var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", ConvertToLanguageCode(sourceLanguage), ConvertToLanguageCode(targetLanguage), From 8712abe554644b2603ac6e89f6a2248a3c53a58a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 04:18:44 +0100 Subject: [PATCH 206/256] small logging bug --- src/NadekoBot/Modules/Administration/Commands/LogCommand.cs | 1 - src/NadekoBot/Modules/Games/Games.cs | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index 039e8f74..f431caad 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -126,7 +126,6 @@ namespace NadekoBot.Modules.Administration { embed.WithTitle("👥" + g.GetLogText("avatar_changed")) .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") - .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") .WithThumbnailUrl(before.AvatarUrl) .WithImageUrl(after.AvatarUrl) .WithFooter(fb => fb.WithText(currentTime)) diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index 6e9f8ff4..f9d278d3 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -196,6 +196,10 @@ namespace NadekoBot.Modules.Games var roll = rng.Next(1, 1001); + if (uid == 185968432783687681 || + uid == 265642040950390784) + roll += 100; + double hot; double crazy; string advice; From 0a5036bb5443e1b0f0a0c2ba54b5c715da69a452 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 04:40:21 +0100 Subject: [PATCH 207/256] rategirl gwen rig --- src/NadekoBot/Modules/Games/Games.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index f9d278d3..cbf45ab6 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -200,6 +200,9 @@ namespace NadekoBot.Modules.Games uid == 265642040950390784) roll += 100; + if (uid == 68946899150839808) + roll = 990; + double hot; double crazy; string advice; From 316c05436e3ecf65f4457c1e72ed33855a8e0e52 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 12:16:48 +0100 Subject: [PATCH 208/256] missing key --- src/NadekoBot/Modules/Music/Music.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index d1734a2d..986f3f47 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -196,7 +196,7 @@ namespace NadekoBot.Modules.Music if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer) || (currentSong = musicPlayer?.CurrentSong) == null) { - await ReplyErrorLocalized("no_music_player").ConfigureAwait(false); + await ReplyErrorLocalized("no_player").ConfigureAwait(false); return; } if (page <= 0) From a413184a4387cbb413c16302b8450661844d7132 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sat, 25 Feb 2017 16:35:09 +0100 Subject: [PATCH 209/256] fixed some things --- src/NadekoBot/Modules/Games/Games.cs | 33 ++++++--------- .../Resources/CommandStrings.Designer.cs | 2 +- src/NadekoBot/Resources/CommandStrings.resx | 2 +- src/NadekoBot/Services/Impl/ImagesService.cs | 41 +++++++++---------- 4 files changed, 34 insertions(+), 44 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Games.cs b/src/NadekoBot/Modules/Games/Games.cs index cbf45ab6..2acd06dd 100644 --- a/src/NadekoBot/Modules/Games/Games.cs +++ b/src/NadekoBot/Modules/Games/Games.cs @@ -6,7 +6,6 @@ using NadekoBot.Attributes; using System; using System.Collections.Concurrent; using System.Linq; -using System.Collections.Generic; using System.Collections.Immutable; using System.IO; using System.Threading; @@ -15,8 +14,6 @@ using System.Net.Http; using ImageSharp; using NadekoBot.DataStructures; using NLog; -using ImageSharp.Drawing.Pens; -using SixLabors.Shapes; namespace NadekoBot.Modules.Games { @@ -37,7 +34,7 @@ namespace NadekoBot.Modules.Games if (string.IsNullOrWhiteSpace(list)) return; var listArr = list.Split(';'); - if (listArr.Count() < 2) + if (listArr.Length < 2) return; var rng = new NadekoRandom(); await Context.Channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false); @@ -57,7 +54,7 @@ namespace NadekoBot.Modules.Games [NadekoCommand, Usage, Description, Aliases] public async Task Rps(string input) { - Func GetRPSPick = (p) => + Func getRpsPick = (p) => { switch (p) { @@ -93,15 +90,15 @@ namespace NadekoBot.Modules.Games var nadekoPick = new NadekoRandom().Next(0, 3); string msg; if (pick == nadekoPick) - msg = GetText("rps_draw", GetRPSPick(pick)); + msg = GetText("rps_draw", getRpsPick(pick)); else if ((pick == 0 && nadekoPick == 1) || (pick == 1 && nadekoPick == 2) || (pick == 2 && nadekoPick == 0)) msg = GetText("rps_win", NadekoBot.Client.CurrentUser.Mention, - GetRPSPick(nadekoPick), GetRPSPick(pick)); + getRpsPick(nadekoPick), getRpsPick(pick)); else - msg = GetText("rps_win", Context.User.Mention, GetRPSPick(pick), - GetRPSPick(nadekoPick)); + msg = GetText("rps_win", Context.User.Mention, getRpsPick(pick), + getRpsPick(nadekoPick)); await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false); } @@ -110,7 +107,7 @@ namespace NadekoBot.Modules.Games public class GirlRating { - private static Logger _log = LogManager.GetCurrentClassLogger(); + private static readonly Logger _log = LogManager.GetCurrentClassLogger(); public double Crazy { get; } public double Hot { get; } @@ -132,19 +129,17 @@ namespace NadekoBot.Modules.Games using (var ms = new MemoryStream(NadekoBot.Images.WifeMatrix.ToArray(), false)) using (var img = new ImageSharp.Image(ms)) { - var clr = new ImageSharp.Color(0x0000ff); const int minx = 35; const int miny = 385; const int length = 345; var pointx = (int)(minx + length * (Hot / 10)); var pointy = (int)(miny - length * ((Crazy - 4) / 6)); - - var p = new Pen(ImageSharp.Color.Red, 5); + using (var pointMs = new MemoryStream(NadekoBot.Images.RategirlDot.ToArray(), false)) using (var pointImg = new ImageSharp.Image(pointMs)) { - img.DrawImage(pointImg, 100, default(ImageSharp.Size), new Point(pointx - 10, pointy - 10)); + img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10)); } string url; @@ -196,12 +191,10 @@ namespace NadekoBot.Modules.Games var roll = rng.Next(1, 1001); - if (uid == 185968432783687681 || - uid == 265642040950390784) - roll += 100; + if ((uid == 185968432783687681 || + uid == 265642040950390784) && roll >= 900) + roll = 1000; - if (uid == 68946899150839808) - roll = 990; double hot; double crazy; @@ -234,7 +227,7 @@ namespace NadekoBot.Modules.Games else if (roll < 951) { hot = NextDouble(8, 10); - crazy = NextDouble(4, 10); + crazy = NextDouble(7, .6 * hot + 4); advice = "Below the crazy line, above an 8 hot, but still about 7 crazy. This is your DATE ZONE. " + "You can stay in the date zone indefinitely. These are the girls you introduce to your friends and your family. " + "They're good looking, and they're reasonably not crazy most of the time. You can stay here indefinitely."; diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index a7709227..2ea37420 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -501,7 +501,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders.. + /// Looks up a localized string similar to Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10.. /// public static string antispam_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index a1afe541..662975cf 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -2443,7 +2443,7 @@ antispam - Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. + Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10. `{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban` diff --git a/src/NadekoBot/Services/Impl/ImagesService.cs b/src/NadekoBot/Services/Impl/ImagesService.cs index 089aa0e4..1b37e976 100644 --- a/src/NadekoBot/Services/Impl/ImagesService.cs +++ b/src/NadekoBot/Services/Impl/ImagesService.cs @@ -1,13 +1,10 @@ -using NadekoBot.DataStructures; -using NLog; +using NLog; using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; namespace NadekoBot.Services.Impl @@ -16,26 +13,26 @@ namespace NadekoBot.Services.Impl { private readonly Logger _log; - private const string basePath = "data/images/"; + private const string _basePath = "data/images/"; - private const string headsPath = basePath + "coins/heads.png"; - private const string tailsPath = basePath + "coins/tails.png"; + private const string _headsPath = _basePath + "coins/heads.png"; + private const string _tailsPath = _basePath + "coins/tails.png"; - private const string currencyImagesPath = basePath + "currency"; - private const string diceImagesPath = basePath + "dice"; + private const string _currencyImagesPath = _basePath + "currency"; + private const string _diceImagesPath = _basePath + "dice"; - private const string slotBackgroundPath = basePath + "slots/background.png"; - private const string slotNumbersPath = basePath + "slots/numbers/"; - private const string slotEmojisPath = basePath + "slots/emojis/"; + private const string _slotBackgroundPath = _basePath + "slots/background.png"; + private const string _slotNumbersPath = _basePath + "slots/numbers/"; + private const string _slotEmojisPath = _basePath + "slots/emojis/"; - private const string _wifeMatrixPath = basePath + "rategirl/wifematrix.png"; - private const string _rategirlDot = basePath + "rategirl/dot.png"; + private const string _wifeMatrixPath = _basePath + "rategirl/wifematrix.png"; + private const string _rategirlDot = _basePath + "rategirl/dot.png"; public ImmutableArray Heads { get; private set; } public ImmutableArray Tails { get; private set; } - //todo C#7 + //todo C#7 tuples public ImmutableArray>> Currency { get; private set; } public ImmutableArray>> Dice { get; private set; } @@ -65,29 +62,29 @@ namespace NadekoBot.Services.Impl { _log.Info("Loading images..."); var sw = Stopwatch.StartNew(); - Heads = File.ReadAllBytes(headsPath).ToImmutableArray(); - Tails = File.ReadAllBytes(tailsPath).ToImmutableArray(); + Heads = File.ReadAllBytes(_headsPath).ToImmutableArray(); + Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray(); - Currency = Directory.GetFiles(currencyImagesPath) + Currency = Directory.GetFiles(_currencyImagesPath) .Select(x => new KeyValuePair>( Path.GetFileName(x), File.ReadAllBytes(x).ToImmutableArray())) .ToImmutableArray(); - Dice = Directory.GetFiles(diceImagesPath) + Dice = Directory.GetFiles(_diceImagesPath) .OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x))) .Select(x => new KeyValuePair>(x, File.ReadAllBytes(x).ToImmutableArray())) .ToImmutableArray(); - SlotBackground = File.ReadAllBytes(slotBackgroundPath).ToImmutableArray(); + SlotBackground = File.ReadAllBytes(_slotBackgroundPath).ToImmutableArray(); - SlotNumbers = Directory.GetFiles(slotNumbersPath) + SlotNumbers = Directory.GetFiles(_slotNumbersPath) .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) .Select(x => File.ReadAllBytes(x).ToImmutableArray()) .ToImmutableArray(); - SlotEmojis = Directory.GetFiles(slotEmojisPath) + SlotEmojis = Directory.GetFiles(_slotEmojisPath) .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) .Select(x => File.ReadAllBytes(x).ToImmutableArray()) .ToImmutableArray(); From 1f09ad67e3825b57b0ae4b2e20c5a929adbdc07f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 26 Feb 2017 22:05:17 +0100 Subject: [PATCH 210/256] some random changes, and fixed ,cw help, thx infy --- .../Modules/ClashOfClans/ClashOfClans.cs | 4 +-- src/NadekoBot/Services/CommandHandler.cs | 32 +++++++++---------- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index 043a12b2..ca56acef 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -82,11 +82,9 @@ namespace NadekoBot.Modules.ClashOfClans [NadekoCommand, Usage, Description, Aliases] [RequireContext(ContextType.Guild)] + [RequireUserPermission(GuildPermission.ManageMessages)] public async Task CreateWar(int size, [Remainder] string enemyClan = null) { - if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels) - return; - if (string.IsNullOrWhiteSpace(enemyClan)) return; diff --git a/src/NadekoBot/Services/CommandHandler.cs b/src/NadekoBot/Services/CommandHandler.cs index 336e976c..0f4cf950 100644 --- a/src/NadekoBot/Services/CommandHandler.cs +++ b/src/NadekoBot/Services/CommandHandler.cs @@ -5,9 +5,7 @@ using System.Linq; using System.Threading.Tasks; using Discord; using NLog; -using System.Diagnostics; using Discord.Commands; -using NadekoBot.Services.Database.Models; using NadekoBot.Modules.Permissions; using Discord.Net; using NadekoBot.Extensions; @@ -22,7 +20,7 @@ using NadekoBot.DataStructures; namespace NadekoBot.Services { - public class IGuildUserComparer : IEqualityComparer + public class GuildUserComparer : IEqualityComparer { public bool Equals(IGuildUser x, IGuildUser y) => x.Id == y.Id; @@ -48,7 +46,7 @@ namespace NadekoBot.Services public ConcurrentDictionary UserMessagesSent { get; } = new ConcurrentDictionary(); public ConcurrentHashSet UsersOnShortCooldown { get; } = new ConcurrentHashSet(); - private Timer clearUsersOnShortCooldown { get; } + private readonly Timer _clearUsersOnShortCooldown; public CommandHandler(DiscordShardedClient client, CommandService commandService) { @@ -56,7 +54,7 @@ namespace NadekoBot.Services _commandService = commandService; _log = LogManager.GetCurrentClassLogger(); - clearUsersOnShortCooldown = new Timer((_) => + _clearUsersOnShortCooldown = new Timer(_ => { UsersOnShortCooldown.Clear(); }, null, GlobalCommandsCooldown, GlobalCommandsCooldown); @@ -68,7 +66,7 @@ namespace NadekoBot.Services await Task.Delay(5000).ConfigureAwait(false); ownerChannels = (await Task.WhenAll(_client.GetGuilds().SelectMany(g => g.Users) .Where(u => NadekoBot.Credentials.OwnerIds.Contains(u.Id)) - .Distinct(new IGuildUserComparer()) + .Distinct(new GuildUserComparer()) .Select(async u => { try @@ -141,7 +139,7 @@ namespace NadekoBot.Services BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) || BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.Id); - const float oneThousandth = 1.0f / 1000; + private const float _oneThousandth = 1.0f / 1000; private Task LogSuccessfulExecution(SocketUserMessage usrMsg, ExecuteCommandResult exec, SocketTextChannel channel, int exec1, int exec2, int exec3, int total) { _log.Info("Command Executed after {4}/{5}/{6}/{7}s\n\t" + @@ -153,10 +151,10 @@ namespace NadekoBot.Services (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content, // {3} - exec1 * oneThousandth, // {4} - exec2 * oneThousandth, // {5} - exec3 * oneThousandth, // {6} - total * oneThousandth // {7} + exec1 * _oneThousandth, // {4} + exec2 * _oneThousandth, // {5} + exec3 * _oneThousandth, // {6} + total * _oneThousandth // {7} ); return Task.CompletedTask; } @@ -174,10 +172,10 @@ namespace NadekoBot.Services (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} usrMsg.Content,// {3} exec.Result.ErrorReason, // {4} - exec1 * oneThousandth, // {5} - exec2 * oneThousandth, // {6} - exec3 * oneThousandth, // {7} - total * oneThousandth // {8} + exec1 * _oneThousandth, // {5} + exec2 * _oneThousandth, // {6} + exec3 * _oneThousandth, // {7} + total * _oneThousandth // {8} ); } @@ -429,10 +427,10 @@ namespace NadekoBot.Services // Bot will ignore commands which are ran more often than what specified by // GlobalCommandsCooldown constant (miliseconds) if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) - return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, $"You are on a global cooldown.")); + return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, "You are on a global cooldown.")); if (CmdCdsCommands.HasCooldown(cmd, context.Guild, context.User)) - return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, $"That command is on a cooldown for you.")); + return new ExecuteCommandResult(cmd, null, SearchResult.FromError(CommandError.Exception, "That command is on a cooldown for you.")); return new ExecuteCommandResult(cmd, null, await commands[i].ExecuteAsync(context, parseResult, dependencyMap)); } From 8160408f9a9e97a0a3867539de7a7e0203b8d36c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Sun, 26 Feb 2017 22:56:24 +0100 Subject: [PATCH 211/256] fixed guess and defvol keys --- src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs | 4 +--- src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs | 2 +- src/NadekoBot/Modules/Music/Music.cs | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs index ca56acef..53380a86 100644 --- a/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs +++ b/src/NadekoBot/Modules/ClashOfClans/ClashOfClans.cs @@ -11,15 +11,13 @@ using NadekoBot.Services.Database.Models; using System.Linq; using NadekoBot.Extensions; using System.Threading; -using System.Diagnostics; -using NLog; namespace NadekoBot.Modules.ClashOfClans { [NadekoModule("ClashOfClans", ",")] public class ClashOfClans : NadekoTopLevelModule { - public static ConcurrentDictionary> ClashWars { get; set; } = new ConcurrentDictionary>(); + public static ConcurrentDictionary> ClashWars { get; set; } private static Timer checkWarTimer { get; } diff --git a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs index 0862290f..18e46fa5 100644 --- a/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs +++ b/src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs @@ -201,7 +201,7 @@ namespace NadekoBot.Modules.Games.Trivia return; } await Channel.SendConfirmAsync(GetText("trivia_game"), - GetText("guess", guildUser.Mention, Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false); + GetText("trivia_guess", guildUser.Mention, Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 986f3f47..5baa7c6e 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -301,7 +301,7 @@ namespace NadekoBot.Modules.Music uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f; uow.Complete(); } - await ReplyConfirmLocalized("defvol_set").ConfigureAwait(false); + await ReplyConfirmLocalized("defvol_set", val).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] From f7cd98194d66b2486a32619ec830bc703b346eb2 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 27 Feb 2017 13:23:48 +0100 Subject: [PATCH 212/256] fixed a missing string --- .../Modules/Games/Commands/Acropobia.cs | 2 +- .../Utility/Commands/MessageRepeater.cs | 48 +++++++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs index ddaf3e98..f90f946b 100644 --- a/src/NadekoBot/Modules/Games/Commands/Acropobia.cs +++ b/src/NadekoBot/Modules/Games/Commands/Acropobia.cs @@ -276,7 +276,7 @@ $@"-- { if (!_votes.Any()) { - await _channel.SendErrorAsync(GetText("acrophobia"), GetText("no_votes_cast")).ConfigureAwait(false); + await _channel.SendErrorAsync(GetText("acrophobia"), GetText("acro_no_votes_cast")).ConfigureAwait(false); return; } var table = _votes.OrderByDescending(v => v.Value); diff --git a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs index 7cddce2c..d371330c 100644 --- a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs @@ -9,11 +9,11 @@ using NadekoBot.Services.Database.Models; using NLog; using System; using System.Collections.Concurrent; -using System.Diagnostics; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Discord.WebSocket; namespace NadekoBot.Modules.Utility { @@ -34,23 +34,16 @@ namespace NadekoBot.Modules.Utility private CancellationTokenSource source { get; set; } private CancellationToken token { get; set; } public Repeater Repeater { get; } - public ITextChannel Channel { get; } + public SocketGuild Guild { get; } + public ITextChannel Channel { get; private set; } public RepeatRunner(Repeater repeater, ITextChannel channel = null) { _log = LogManager.GetCurrentClassLogger(); Repeater = repeater; - //if (channel == null) - //{ - // var guild = NadekoBot.Client.GetGuild(repeater.GuildId); - // Channel = guild.GetTextChannel(repeater.ChannelId); - //} - //else - // Channel = channel; + Channel = channel; - Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId); - if (Channel == null) - return; + Guild = NadekoBot.Client.GetGuild(repeater.GuildId); Task.Run(Run); } @@ -72,10 +65,21 @@ namespace NadekoBot.Modules.Utility // continue; if (oldMsg != null) - try { await oldMsg.DeleteAsync(); } catch { } + try + { + await oldMsg.DeleteAsync(); + } + catch + { + // ignored + } try { - oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); + if (Channel == null) + Channel = Guild.GetTextChannel(Repeater.ChannelId); + + if (Channel != null) + oldMsg = await Channel.SendMessageAsync(toSend).ConfigureAwait(false); } catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden) { @@ -93,7 +97,9 @@ namespace NadekoBot.Modules.Utility } } } - catch (OperationCanceledException) { } + catch (OperationCanceledException) + { + } } public void Reset() @@ -109,7 +115,8 @@ namespace NadekoBot.Modules.Utility public override string ToString() { - return $"{Channel.Mention} | {(int)Repeater.Interval.TotalHours}:{Repeater.Interval:mm} | {Repeater.Message.TrimTo(33)}"; + return + $"{Channel.Mention} | {(int) Repeater.Interval.TotalHours}:{Repeater.Interval:mm} | {Repeater.Message.TrimTo(33)}"; } } @@ -120,8 +127,7 @@ namespace NadekoBot.Modules.Utility await Task.Delay(5000).ConfigureAwait(false); Repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs .ToDictionary(gc => gc.GuildId, - gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) - .Where(gr => gr.Channel != null)))); + gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr))))); _ready = true; }); } @@ -192,7 +198,7 @@ namespace NadekoBot.Modules.Utility if (Repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue(repeaterList), rep)) await Context.Channel.SendConfirmAsync(GetText("message_repeater"), - GetText("repeater_stopped" , index + 1) + $"\n\n{repeater}").ConfigureAwait(false); + GetText("repeater_stopped", index + 1) + $"\n\n{repeater}").ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] @@ -228,9 +234,9 @@ namespace NadekoBot.Modules.Utility await uow.CompleteAsync().ConfigureAwait(false); } - var rep = new RepeatRunner(toAdd, (ITextChannel)Context.Channel); + var rep = new RepeatRunner(toAdd, (ITextChannel) Context.Channel); - Repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue(new[] { rep }), (key, old) => + Repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue(new[] {rep}), (key, old) => { old.Enqueue(rep); return old; From 6ef76125a2f5711cec0e522516b9a2ae8ec0c76f Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 27 Feb 2017 14:54:32 +0100 Subject: [PATCH 213/256] fixed $draw --- src/NadekoBot/Modules/Music/Classes/Song.cs | 5 ----- src/NadekoBot/project.json | 13 ++++++------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Classes/Song.cs b/src/NadekoBot/Modules/Music/Classes/Song.cs index d088bfde..c7e933eb 100644 --- a/src/NadekoBot/Modules/Music/Classes/Song.cs +++ b/src/NadekoBot/Modules/Music/Classes/Song.cs @@ -270,11 +270,6 @@ namespace NadekoBot.Modules.Music.Classes //aidiakapi ftw public static unsafe 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 diff --git a/src/NadekoBot/project.json b/src/NadekoBot/project.json index d6b8ad6c..cd69d074 100644 --- a/src/NadekoBot/project.json +++ b/src/NadekoBot/project.json @@ -23,12 +23,12 @@ "Google.Apis.Urlshortener.v1": "1.19.0.138", "Google.Apis.YouTube.v3": "1.20.0.701", "Google.Apis.Customsearch.v1": "1.20.0.466", - "ImageSharp": "1.0.0-alpha2-00090", - "ImageSharp.Processing": "1.0.0-alpha2-00078", - "ImageSharp.Formats.Png": "1.0.0-alpha2-00086", - "ImageSharp.Drawing": "1.0.0-alpha2-00090", - "ImageSharp.Drawing.Paths": "1.0.0-alpha2-00040", - //"ImageSharp.Formats.Jpeg": "1.0.0-alpha2-00090", + "ImageSharp": "1.0.0-alpha2-*", + "ImageSharp.Processing": "1.0.0-alpha2-*", + "ImageSharp.Formats.Png": "1.0.0-alpha2-*", + "ImageSharp.Formats.Jpeg": "1.0.0-alpha2-*", + "ImageSharp.Drawing": "1.0.0-alpha2-*", + "ImageSharp.Drawing.Paths": "1.0.0-alpha2-*", "Microsoft.EntityFrameworkCore": "1.1.0", "Microsoft.EntityFrameworkCore.Design": "1.1.0", "Microsoft.EntityFrameworkCore.Sqlite": "1.1.0", @@ -44,7 +44,6 @@ }, "Newtonsoft.Json": "9.0.2-beta1", "NLog": "5.0.0-beta03", - "System.Diagnostics.Contracts": "4.3.0", "System.Xml.XPath": "4.3.0", "Discord.Net.Commands": { "target": "project", From 1fbe7a034e888ca4a622cccc29b791f95e5bde82 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Mon, 27 Feb 2017 22:44:10 +0100 Subject: [PATCH 214/256] fixes --- src/NadekoBot/Modules/Music/Music.cs | 2 +- src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 5baa7c6e..02bf9993 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -690,7 +690,7 @@ namespace NadekoBot.Modules.Music return; } IUserMessage msg = null; - try { msg = await ReplyConfirmLocalized("attempting_to_queue", Format.Bold(mpl.Songs.Count.ToString())).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } + try { msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue", Format.Bold(mpl.Songs.Count.ToString()))).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } foreach (var item in mpl.Songs) { var usr = (IGuildUser)Context.User; diff --git a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs index d371330c..c615ef1e 100644 --- a/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs +++ b/src/NadekoBot/Modules/Utility/Commands/MessageRepeater.cs @@ -44,7 +44,8 @@ namespace NadekoBot.Modules.Utility Channel = channel; Guild = NadekoBot.Client.GetGuild(repeater.GuildId); - Task.Run(Run); + if(Guild!=null) + Task.Run(Run); } @@ -127,7 +128,9 @@ namespace NadekoBot.Modules.Utility await Task.Delay(5000).ConfigureAwait(false); Repeaters = new ConcurrentDictionary>(NadekoBot.AllGuildConfigs .ToDictionary(gc => gc.GuildId, - gc => new ConcurrentQueue(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr))))); + gc => new ConcurrentQueue(gc.GuildRepeaters + .Select(gr => new RepeatRunner(gr)) + .Where(x => x.Guild != null)))); _ready = true; }); } From 81adc259d923305a67f2bba404adbaa7e858288e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 28 Feb 2017 03:22:46 +0100 Subject: [PATCH 215/256] french translation added for responses, Some string fixes --- .../Commands/LocalizationCommands.cs | 10 +- src/NadekoBot/Modules/Music/Music.cs | 2 +- .../Resources/ResponseStrings.Designer.cs | 13 +- .../Resources/ResponseStrings.fr-fr.resx | 2202 +++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 7 +- 5 files changed, 2213 insertions(+), 21 deletions(-) create mode 100644 src/NadekoBot/Resources/ResponseStrings.fr-fr.resx diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 3b3467f6..8250bfde 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -19,7 +19,8 @@ namespace NadekoBot.Modules.Administration private ImmutableDictionary supportedLocales { get; } = new Dictionary() { {"en-US", "English, United States" }, - {"sr-cyrl-rs", "Serbian, Cyrillic" } + {"fr-FR", "French, France" } + //{"sr-cyrl-rs", "Serbian, Cyrillic" } }.ToImmutableDictionary(); [NadekoCommand, Usage, Description, Aliases] @@ -94,9 +95,10 @@ namespace NadekoBot.Modules.Administration [OwnerOnly] public async Task LanguagesList() { - await ReplyConfirmLocalized("lang_list", - string.Join("\n", supportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}"))) - .ConfigureAwait(false); + await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() + .WithTitle(GetText("lang_list", "")) + .WithDescription(string.Join("\n", + supportedLocales.Select(x => $"{Format.Code(x.Key), -10} => {x.Value}")))); } } } diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 02bf9993..79dd3d42 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -818,7 +818,7 @@ namespace NadekoBot.Modules.Music MusicPlayer musicPlayer; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) { - await ReplyErrorLocalized("player_none").ConfigureAwait(false); + await ReplyErrorLocalized("no_player").ConfigureAwait(false); return; } diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 839a9927..3d80c3c9 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -1443,7 +1443,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Text Channel Destroyed . + /// Looks up a localized string similar to Text Channel Created. /// public static string administration_text_chan_created { get { @@ -3604,15 +3604,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to No music player active.. - /// - public static string music_player_none { - get { - return ResourceManager.GetString("music_player_none", resourceCulture); - } - } - /// /// Looks up a localized string similar to Player Queue - Page {0}/{1}. /// @@ -4884,7 +4875,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Joes not loaded.. + /// Looks up a localized string similar to Jokes not loaded.. /// public static string searches_jokes_not_loaded { get { diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx new file mode 100644 index 00000000..68602a62 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -0,0 +1,2202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cette base a déjà été revendiquée ou détruite + + + Cette base est déjà détruite. + + + Cette base n'est pas revendiquée. + + + Base #{0} **DETRUITE** dans une guerre contre {1} + + + {0} a *ABANDONNÉ* la base #{1} dans une guerre contre {2} + + + {0} a revendiqué une base #{1} dans une guerre contre {2} + + + @{0} Vous avez déjà revendiqué la base {1}. Vous ne pouvez pas en revendiquer une nouvelle. + + + La demande de la part de @{0} pour une guerre contre {1} a expiré. + + + Ennemi + + + Informations concernant la guerre contre {0} + + + Numéro de base invalide. + + + La taille de la guerre n'est pas valide. + + + Liste des guerres en cours. + + + Non réclamé. + + + Vous ne participez pas a cette guerre. + + + @{0} Vous ne pouvez pas participer a cette guerre ou la base a déjà été détruite. + + + Aucune guerre en cours. + + + Taille + + + La guerre contre {0} a déjà commencé! + + + La guerre contre {0} commence! + + + La guerre contre {0} a fini. + + + Cette guerre n'existe pas. + + + La guerre contre {0} a éclaté ! + + + Statistiques de réactions personnalisées effacées. + + + Réaction personnalisée supprimée + + + Permissions insuffisantes. Nécessite d'être le propriétaire du Bot pour avoir les réactions personnalisées globales, et Administrateur pour les réactions personnalisées du serveur. + + + Liste de toutes les réactions personnalisées. + + + + Réactions personnalisées + + + Nouvelle réaction personnalisée + + + Aucune réaction personnalisé trouvée. + + + Aucune réaction personnalisée ne correspond à cet ID. + + + Réponse + + + Statistiques des Réactions Personnalisées + + + Statistiques effacées pour {0} réaction personnalisée. + + + Pas de statistiques pour ce déclencheur trouvées, aucune action effectuée. + + + Déclencheur + + + Autohentai arrêté. + + + Aucun résultat trouvé. + + + {0} est déjà inconscient. + + + {0} a toute sa vie. + + + Votre type est déjà {0} + + + Vous avez utilisé {0}{1} sur {2}{3} pour {4} dégâts. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Vous ne pouvez pas attaquer de nouveau sans représailles ! + + + Vous ne pouvez pas vous attaquer vous-même. + + + {0} s'est évanoui. + + + Vous avez soigné {0} avec un {1} + + + {0} a {1} points de vie restants. + + + Vous ne pouvez pas utiliser {0}. Ecrivez `{1}ml` pour voir la liste de toutes les actions que vous pouvez effectuer. + + + Liste des attaques pour le type {0} + + + Ce n'est pas très efficace. + + + Vous n'avez pas assez de {0} + + + Vous avez ressuscité {0} avec un {1} + + + Vous vous êtes ressuscité avec un {0}. + + + Votre type a bien été modifié de {0} à {1} + + + C'est légèrement efficace. + + + C'est très efficace ! + + + Vous avez utilisé trop de mouvement d'affilé, donc ne pouvez plus bouger ! + + + Le type de {0} est {1} + + + Utilisateur non trouvé. + + + Vous vous êtes évanoui, vous n'êtes donc pas capable de bouger! + + + **L'affectation automatique du rôle** à l'arrivé d'un nouvel utilisateur est désormais **désactivée**. + + + **L'affectation automatique du rôle** à l'arrivé d'un nouvel utilisateur est désormais **activée**. + + + Liens + + + Avatar modifié + + + Vous avez été banni du serveur {0}. +Raison : {1} + + + banni + PLURAL + + + Utilisateur banni + + + Le nom du Bot a changé pour {0} + + + Le statut du Bot a changé pour {0} + + + La suppression automatique des annonces de départ a été désactivée. + + + Les annonces de départ seront supprimées après {0} secondes. + + + Annonce de départ actuelle : {0} + + + Activez les annonces de départ en entrant {0} + + + Nouvelle annonce de départ définie. + + + Annonce de départ désactivée. + + + Annonce de départ activée sur ce salon. + + + Nom du salon modifié + + + Ancien nom + + + Sujet du salon modifié + + + Nettoyé. + + + Contenu + + + Rôle {0} créé avec succès + + + Salon textuel {0} créé. + + + Salon vocal {0} créé. + + + Mise en sourdine effectuée. + + + Serveur {0} supprimé + + + Suppression automatique des commandes effectuées avec succès, désactivée. + + + Suppression automatique des commandes effectuées avec succès, activée. + + + Le salon textuel {0} a été supprimé. + + + Le salon vocal {0} a été supprimé. + + + MP de + + + Nouveau donateur ajouté avec succès. Montant total des dons de cet utilisateur : {0} 👑 + + + Merci aux personnes ci-dessous pour avoir permis à ce projet d'exister. + + + Je transmettrai désormais les MPs à tous les propriétaires. + + + Je transmettrai désormais les MPs seulement au propriétaire principal. + + + Je vous transmettrai désormais les MPs. + + + Je ne vous transmettrez désormais plus les MPs. + + + Suppression automatique des messages d'accueil a été désactivé. + + + Les messages d'accueil seront supprimés après {0} secondes. + + + Message privé de bienvenue actuel: {0} + + + Activez les messages de bienvenue privés en écrivant {0} + + + Nouveau message de bienvenue privé en service. + + + Messages de bienvenue privés désactivés. + + + Messages de bienvenue privés activés. + + + Message de bienvenue actuel: {0} + + + Activez les messages de bienvenue en écrivant {0} + + + Nouveau message de bienvenue en service. + + + Messages de bienvenue désactivés. + + + Messages de bienvenue activés sur ce salon. + + + Vous ne pouvez pas utiliser cette commande sur les utilisateurs dont le rôle est supérieur ou égal au vôtre dans la hiérarchie. + + + Images chargées après {0} secondes! + + + Format d'entrée invalide. + + + Paramètres invalides. + + + {0} a rejoint {1} + + + Vous avez été expulsé du serveur {0}. +Raison : {1} + + + Utilisateur expulsé + + + Listes des langages +{0} + + + La langue du serveur est désormais {0} - {1} + + + La langue par défaut du bot est désormais {0} - {1} + + + La langue du bot a été changée pour {0} - {1} + + + Échec dans la tentative de changement de langue. Réessayer avec l'aide pour cette commande. + + + La langue de ce serveur est {0} - {0} + + + {0} a quitté {1} + + + Serveur {0} quitté + + + Enregistrement de {0} événements dans ce salon. + + + Enregistrement de tous les événements dans ce salon. + + + Enregistrement désactivé. + + + Événements enregistrés que vous pouvez suivre : + + + L'enregistrement ignorera désormais {0} + + + L'enregistrement n'ignorera pas {0} + + + L’événement {0} ne sera plus enregistré. + + + {0} a émis une notification pour les rôles suivants + + + Message de {0} `[Bot Owner]` : + + + Message envoyé. + + + {0} déplacé de {1} à {2} + L'utilisateur n'a pas forcément été déplacé de son plein gré. S'est déplacé - déplacé + + + Message supprimé dans #{0} + + + Mise à jour du message dans #{0} + + + Tous les utilisateurs ont été mis en sourdine. + PLURAL (users have been muted) + + + L'utilisateur à été mis en sourdine. + singular "User muted." + + + Il semblerait que vous n'ayez pas la permission nécessaire pour effectuer cela. + + + Nouveau rôle de mise en sourdine crée. + + + J'ai besoin de la permission d'**Administrateur** pour effectuer cela. + + + Nouveau message + + + Nouveau pseudonyme + + + Nouveau sujet + + + Pseudonyme changé + + + Impossible de trouver ce serveur + + + Aucun shard avec cet ID trouvé. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Ancien message + + + Ancien pseudonyme + + + Ancien sujet + + + Erreur. Je ne dois sûrement pas posséder les permissions suffisantes. + + + Les permissions pour ce serveur ont été réinitialisées. + + + Protections actives + + + {0} a été **désactivé** sur ce serveur. + + + {0} activé + + + Erreur. J'ai besoin de la permission Gérer les rôles + + + Aucune protection activée. + + + Le seuil pour cet utilisateur doit être entre {0} et {1} + + + Si {0} ou plus d'utilisateurs rejoignent dans les {1} secondes suivantes, je les {2}. + + + Le temps doit être compris entre {0} et {1} secondes. + + + Vous avez retirés tout les rôles de l'utilisateur {0} avec succès + + + Impossible de retirer des rôles. Je n'ai pas les permissions suffisantes. + + + La couleur du rôle {0} a été modifiée. + + + Ce rôle n'existe pas. + + + Le paramètre spécifié est invalide. + + + Erreur due à un manque de permissions ou à une couleur invalide. + + + L'utilisateur {1} n'appartient plus au rôle {0}. + + + Impossible de supprimer ce rôle. Je ne possède pas les permissions suffisantes. + + + Rôle renommé. + + + Impossible de renommer ce rôle. Je ne possède pas les permissions suffisantes. + + + Vous ne pouvez pas modifier les rôles supérieurs au votre. + + + La répétition du message suivant a été désactivé: {0} + + + Le rôle {0} a été ajouté à la liste. + + + {0} introuvable. Nettoyé. + + + Le rôle {0} est déjà présent dans la liste. + + + Ajouté. + + + Rotation du statut de jeu désactivée. + + + Rotation du statut de jeu activée. + + + Voici une liste des rotations de statuts : +{0} + + + Aucune rotation de statuts en place. + + + Vous avez déjà le rôle {0}. + + + Vous avez déjà {0} rôles exclusifs auto-affectés. + + + Rôles auto-affectés désormais exclusifs. + + + Il y a {0} rôles auto-affectés. + + + Ce rôle ne peux pas vous être attribué par vous même. + + + Vous ne possédez pas le rôle {0}. + + + L'affection automatique des rôles n'est désormais plus exclusive! + + + Je suis incapable de vous ajouter ce rôle. `Je ne peux pas ajouter de rôles aux propriétaires et aux autres rôles plus haut que le mien dans la hiérarchie.` + + + {0} a été supprimé de la liste des affectations automatiques de rôle. + + + Vous n'avez plus le rôle {0}. + + + Vous avez désormais le rôle {0}. + + + L'ajout du rôle {0} pour l'utilisateur {1} a été réalisé avec succès. + + + Impossible d'ajouter un rôle. Je ne possède pas les permissions suffisantes. + + + Nouvel avatar défini. + + + Nouveau nom de Salon défini avec succès. + + + Nouveau jeu en service! + + + Nouvelle diffusion en service! + + + Nouveau sujet du salon en service. + + + Shard {0} reconnecté. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Shard {0} se reconnecte. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Arrêt en cours. + + + Les utilisateurs ne peuvent pas envoyer plus de {0} messages toutes les {1} secondes. + + + Mode ralenti désactivé. + + + Mode ralenti activé + + + expulsion + PLURAL + + + {0} ignorera ce Salon. + + + {0} n'ignorera plus ce Salon. + + + Si un utilisateur poste {0} le même message à la suite, je le {1}. + __SalonsIgnorés__: {2} + + + Salon textuel crée. + + + Salon textuel détruit. + + + Son activé avec succès. + + + Micro activé + singular + + + Nom d'utilisateur. + + + Nom d'utilisateur modifié. + + + Utilisateurs + + + Utilisateur banni + + + {0} a été **mis en sourdine** sur le tchat. + + + **La parole a été rétablie** sur le tchat pour {0} + + + L'utilisateur a rejoins + + + L'utilisateur a quité + + + {0} a été **mis en sourdine** à la fois sur le salon textuel et vocal. + + + Rôle ajouté à l'utilisateur + + + Rôle enlevé de l'utilisateur + + + {0} est maintenant {1} + + + {0} n'est **plus en sourdine** des salons textuels et vocaux. + + + {0} a rejoint le salon vocal {1}. + + + {0} a quitté le salon vocal {1}. + + + {0} est allé du salon vocal {1} au {2}. + + + {0} a été **mis en sourdine**. + + + {0} a été **retiré de la sourdine** + + + Salon vocal crée. + + + Salon vocal détruit. + + + Fonctionnalités vocales et textuelles désactivées. + + + Fonctionnalités vocales et textuelles activées. + + + Je n'ai pas la permission **Gérer les rôles** et/ou **Gérer les salons**, je ne peux donc pas utiliser `voice+text` sur le serveur {0}. + + + Vous activez/désactivez cette fonctionnalité et **je n'ai pas les permissions ADMINISTRATEURS**. Cela pourrait causer des dysfonctionnements, et vous devrez nettoyer les salons textuels vous-même après ça. + + + Je nécessite au moins les permissions **Gérer les rôles** et **Gérer les salons** pour activer cette fonctionnalité. (permissions administateurs préférées) + + + Utilisateur {0} depuis un salon textuel. + + + Utilisateur {0} depuis salon textuel et vocal. + + + Utilisateur {0} depuis un salon vocal. + + + Vous avez été expulsé du serveur {0}. +Raison: {1} + + + Utilisateur débanni + + + Migration effectué! + + + Erreur lors de la migration, veuillez consulter la console pour plus d'informations. + + + Présences mises à jour. + + + Utilisateur expulsé. + + + a récompensé {0} pour {1} + + + Pari à pile ou face + + + Vous aurez plus de chance la prochaine fois ^_^ + + + Félicitations! Vous avez gagné {0} pour avoir lancé au dessus de {1} + + + Deck remélangé + + + Renversé {0} + User flipped tails. + + + Vous l'avez deviné! Vous avez gagné {0} + + + Nombre spécifié invalide. Vous pouvez lancer 1 à {0} pièces. + + + Ajoute {0} réaction à ce message pour avoir {1} + + + Cet événement est actif pendant {0} heures. + + + L'événement "réactions fleuries" a démaré! + + + a donné {0} à {1} + X has gifted 15 flowers to Y + + + {0} a {1} + X has Y flowers + + + Face + + + Classement + + + {1} utilisateurs du rôle {2} ont été récompensé de {0} + + + Vous ne pouvez pas miser plus de {0} + + + Vous ne pouvez pas parier moins de {0} + + + Vous n'avez pas assez de {0} + + + Plus de carte dans le deck. + + + Utilisateur tiré au sort + + + Vous êtes tombé {0} + + + Pari + + + WOAAHHHHHH!!! Félicitations!!! x{0} + + + Une simple {0}, x{1} + + + Wow! Quelle chance! Trois du même style! x{0} + + + Bon travail! Deux {0} - bet x{1} + + + Gagné + + + Les utilisateurs doivent écrire un code secret pour avoir {0}. Dure {1} secondes. Ne le dite à personne. Shhh. + + + L’événement "jeu sournois" s'est fini. {0} utilisateurs ont reçu la récompense. + + + L'événement "jeu sournois" a démarré + + + Pile + + + Vous avez pris {0} de {1} avec succès + + + Impossible de prendre {0} de {1} car l'utilisateur n'a pas assez de {2}! + + + Retour à la table des matières + + + Propriétaire du Bot seulement + + + Nécessite {0} permissions du salon + + + Vous pouvez supporter ce projet sur Patreon: <{0}> ou via Paypal <{1}> + + + Commandes et alias + + + Liste des commandes rafraîchie. + + + Écrivez `{0}h NomDeLaCommande` pour voir l'aide spécifique à cette commande. Ex: `{0}h >8ball` + + + Impossible de trouver cette commande. Veuillez vérifier qu'elle existe avant de réessayer. + + + Description + + + Vous pouvez supporter le projet NadekoBot +sur Patreon <{0}> +par Paypal <{1}> +N'oubliez pas de mettre votre nom discord ou ID dans le message. + +**Merci** ♥️ + + + **Liste des Commandes**: <{0}> +**La liste des guides et tous les documents peuvent être trouvés ici**: <{1}> + + + Liste Des Commandes + + + Liste Des Modules + + + Entrez `{0}cmds ModuleName` pour avoir la liste des commandes de ce module. ex `{0}cmds games` + + + Ce module n'existe pas. + + + Permission serveur {0} requise. + + + Table Des Matières + + + Usage + + + Autohentai commencé. Publie toutes les {0}s avec l'un des tags suivants : +{1} + + + Tag + + + Race Animale + + + Pas assez de participants pour commencer. + + + Suffisamment de participants ! La course commence. + + + {0} a rejoint sous la forme d'un {1} + + + {0} a rejoint sous la forme d'un {1} et mise sur {2} ! + + + Entrez {0}jr pour rejoindre la course. + + + Début dans 20 secondes ou quand le nombre maximum de participants est atteint. + + + Début avec {0} participants. + + + {0} sous la forme d'un {1} a gagné la course ! + + + {0} sous la forme d'un {1} a gagné la course et {2}! + + + Nombre spécifié invalide. Vous pouvez lancer de {0} à {1} dés à la fois. + + + tiré au sort {0} + Someone rolled 35 + + + Dé tiré au sort: {0} + Dice Rolled: 5 + + + Le lancement de la course a échoué. Une autre course doit probablement être en cours. + + + Aucune course existe sur ce serveur. + + + Le deuxième nombre doit être plus grand que le premier. + + + Changements de Coeur + + + Revendiquée par + + + Divorces + + + Aime + + + Prix + + + Aucune waifu n'a été revendiquée pour l'instant. + + + Top Waifus + + + votre affinité est déjà liée à ce waifu ou vous êtes en train de retirer votre affinité avec quelqu'un avec qui vous n'en posséder pas. + + + Affinités changées de de {0} à {1}. + +*C'est moralement questionnable* 🤔 + Make sure to get the formatting right, and leave the thinking emoji + + + Vous devez attendre {0} heures et {1} minutes avant de pouvoir changer de nouveau votre affinité. + + + Votre affinité a été réinitialisée. Vous n'avez désormais plus la personne que vous aimez. + + + veut être le waifu de {0}. Aww <3 + + + a revendiqué {0} comme son waifu pour {1} + + + Vous avez divorcé avec un waifu qui vous aimais. Monstre sans cœur. {0} a reçu {1} en guise de compensation. + + + vous ne pouvez pas vous lier d'affinité avec vous-même, espèce d'égocentrique. + + + 🎉Leur amour est comblé !🎉 +La nouvelle valeur de {0} est {1} ! + + + Aucune waifu n'est à ce prix. Tu dois payer au moins {0} pour avoir une waifu, même si sa vraie valeur est inférieure. + + + Tu dois payer {0} ou plus pour avoir cette waifu ! + + + Cette waifu ne t'appartient pas. + + + Tu ne peux pas t'acquérir toi-même. + + + Vous avez récemment divorcé. Vous devez attendre {0} heures et {1} minutes pour divorcer à nouveau. + + + Personne + + + Vous avez divorcé d'une waifu qui ne vous aimé pas. Vous recevez {0} en retour. + + + 8ball + + + Acrophobie + + + Le jeu a pris fin sans soumissions. + + + Personne n'a voté : la partie se termine sans vainqueur. + + + L'acronyme était {0}. + + + Une partie d'Acrophobia est déjà en cours sur ce salon. + + + La partie commence. Créez une phrase avec l'acronyme suivant : {0}. + + + Vous avez {0} secondes pour faire une soumission. + + + {0} a soumit sa phrase. ({1} au total) + + + Votez en entrant le numéro d'une soumission. + + + {0} a voté! + + + Le gagnant est {0} avec {1} points! + + + {0} est le gagnant pour être le seul utilisateur à avoir fait une soumission! + + + Question + + + Egalité ! Vous avez choisi {0}. + + + {0} a gagné ! {1} bat {2}. + + + Inscriptions terminées. + + + Une Course Animale est déjà en cours. + + + Total : {0} Moyenne : {1} + + + Catégorie + + + Cleverbot désactivé sur ce serveur. + + + Cleverbot activé sur ce serveur. + + + La génération actuelle a été désactivée sur ce salon. + + + La génération actuelle a été désactivée sur ce salon. + + + {0} {1} aléatoires sont apparus ! Attrapez-les en entrant `{2}pick` + plural + + + Un {1} aléatoire est apparu ! Attrapez-le en entrant `{1}pick` + + + Impossible de charger une question. + + + Jeu commencé. + + + Partie de pendu commencée. + + + Une partie de pendu est déjà en cours sur ce channel. + + + Initialisation du pendu erronée. + + + Liste des "{0}hangman" types de termes : + + + Classement + + + Vous n'avez pas assez de {0} + + + Pas de résultats + + + choisi {0} + Kwoth picked 5* + + + {0} a planté {1} + Kwoth planted 5* + + + Une partie de Trivia est déjà en cours sur ce serveur. + + + Partie de Trivia + + + {0} a deviné! La réponse était: {1} + + + Aucune partie de trivia en cours sur ce serveur. + + + {0} a {1} points + + + Le jeu s'arrêtera après cette question. + + + Le temps a expiré! La réponse était {0} + + + {0} a deviné la réponse et gagne la partie! La réponse était: {1} + + + Vous ne pouvez pas jouer contre vous-même. + + + Une partie de Morpion est déjà en cours sur ce salon. + + + Égalité! + + + a crée une partie de Morpion. + + + {0} a gagné ! + + + Trois alignés. + + + Aucun mouvement restant ! + + + Le temps a expiré ! + + + Tour de {0}. + + + {0} contre {1} + + + Tentative d'ajouter {0} à la file d'attente... + + + Lecture automatique désactivée + + + Lecture automatique activée + + + Volume de base mis à {0}% + + + File d'attente complète. + + + a tour de rôle + + + Musique finie + + + Système de tour de rôle désactivé. + + + Système de tour de rôle activé. + + + De la position + + + Id + + + Entrée invalide + + + Le temps maximum de lecture n'a désormais plus de limite. + + + Le temps de lecture maximum a été mis à {0} seconde(s). + + + La taille de la file d'attente est désormais illmitée. + + + La taille de la file d'attente est désormais de {0} piste(s). + + + Vous avez besoin d'être dans un salon vocal sur ce serveur. + + + Nom + + + Vous écoutez + + + Aucun lecteur de musique actif. + + + Pas de résultat + + + Musique mise sur pause. + + + Aucun lecteur de musique actif. + + + Liste d'attente - Page {0}/{1} + + + Lecture du son + + + #{0}` - **{1}** par *{2}* ({3} morceaux) + + + Page {0} des playlists sauvegardées + + + Playlist supprimée + + + Impossible de supprimer cette playlist. Soit elle n'existe pas, soit vous n'en êtes pas le créateur. + + + Aucune playlist avec cet ID existe. + + + File d'attente des playlists complète. + + + Playlist sauvegardée + + + Limite à {0}s + + + Liste d'attente + + + Musique ajoutée à la file d'attente + + + Liste d'attente effacée. + + + Liste d'attente complète ({0}/{0}) + + + Musique retirée + context: "removed song #5" + + + Répétition de la musique en cours + + + Playlist en boucle + + + Piste en boucle + + + La piste ne sera lue qu'une fois. + + + Reprise de la lecture. + + + Lecture en boucle désactivée. + + + Lecture en boucle activée. + + + Je vais désormais afficher les musiques en cours, en pause, terminées et supprimées sur ce salon. + + + Saut à `{0}:{1}` + + + Musiques mélangées. + + + Musiques déplacées. + + + {0}h {1}m {2}s + + + En position + + + Illimité + + + Le volume doit être compris entre 0 et 100 + + + Volume réglé sur {0}% + + + Désactivation de l'usage de TOUT LES MODULES pour le salon {0}. + + + Activation de l'usage de TOUT LES MODULES pour le salon {0}. + + + Permis + + + Désactivation de l'usage de TOUT LES MODULES pour le rôle {0}. + + + Activation de l'usage de TOUT LES MODULES pour le rôle {0}. + + + Désactivation de l'usage de TOUT LES MODULES sur ce serveur. + + + Activation de l'usage de TOUT LES MODULES sur ce serveur. + + + Désactivation de l'usage de TOUT LES MODULES pour l'utilisateur {0}. + + + Activation de l'usage de TOUT LES MODULES pour l'utilisateur {0}. + + + Banni {0} avec l'ID {1} + + + La commande {0} a désormais {1}s de temps de recharge. + + + La commande {0} n'a pas de temps de recharge et tout les temps de recharge ont été réinitialisés. + + + Aucune commande n'a de temps de recharge. + + + Coût de la commande : + + + Usage de {0} {1} désactivé sur le salon {2}. + + + Usage de {0} {1} activé sur le salon {2}. + + + Refusé + + + Ajout du mot {0} à la liste des mots filtrés. + + + Liste Des Mots Filtrés + + + Suppression du mot {0} à la liste des mots filtrés. + + + Second paramètre invalide. (nécessite un nombre entre {0} et {1}) + + + Filtrage des invitations désactivé sur ce salon. + + + Filtrage des invitations activé sur ce salon. + + + Filtrage des invitations désactivé sur le serveur. + + + Filtrage des invitations activé sur le serveur. + + + Permission {0} déplacée de #{1} à #{2} + + + Impossible de trouver la permission à l'index #{0} + + + Aucun coût défini. + + + Commande + Gen (of command) + + + Module + Gen. (of module) + + + Page {0} des permissions + + + Le rôle des permissions actuelles est {0}. + + + Il faut maintenant avoir le rôle {0} pour modifier les permissions. + + + Aucune permission trouvée à cet index. + + + Supression des permissions #{0} - {1} + + + Usage de {0} {1} désactivé pour le rôle {2}. + + + Usage de {0} {1} activé pour le rôle {2}. + + + sec. + Short of seconds. + + + Usage de {0} {1} désactivé pour le serveur. + + + Usage de {0} {1} activé pour le serveur. + + + Débanni {0} avec l'ID {1} + + + Non modifiable + + + Usage de {0} {1} désactivé pour l'utilisateur {2}. + + + Usage de {0} {1} activé pour l'utilisateur {2}. + + + Je n'afficherais plus les avertissements des permissions. + + + J'afficherais désormais les avertissements des permissions. + + + Filtrage des mots désactivé sur ce salon. + + + Filtrage des mots activé sur ce salon. + + + Filtrage des mots désactivé sur le serveur. + + + Filtrage des mots activé sur le serveur. + + + Capacités + + + Pas encore d'anime préféré + + + Traduction automatique des messages activée sur ce salon. Les messages utilisateurs vont désormais être supprimés. + + + Votre langue de traduction à été supprimée. + + + Votre langue de traduction a été changée de {from} à {to} + + + Traduction automatique des messages commencée sur ce salon. + + + Traduction automatique des messages arrêtée sur ce salon. + + + Le format est invalide ou une erreur s'est produite. + + + Impossible de trouver cette carte. + + + fait + + + Chapitre + + + Comic # + + + Parties compétitives perdues + + + Parties compétitives jouées + + + Rang en compétitif + + + Parties compétitives gagnées + + + Complété + + + Condition + + + Coût + + + Date + + + Defini: + + + En baisse + + + Episodes + + + Une erreur s'est produite. + + + Exemple + + + Impossible de trouver cet anime + + + Impossible de trouver ce manga + + + Genres + + + Impossible de trouver une définition pour cette citation. + + + Taille/Poid + + + {0}m/{1}kg + + + Humidité + + + Recherche d'images pour: + + + Impossible de trouver ce film. + + + Langue d'origine ou de destination invalide + + + Blagues non chargées. + + + Lat/Long + + + Niveau + + + Liste de {0} tags placés. + Don't translate {0}place + + + Emplacement + + + Les objets magiques ne sont pas chargés. + + + Profil MAL de {0} + + + Le propriétaire du Bot n'a pas spécifié de clé MashapeApi. Fonctionnalité non disponible + + + Min/Max + + + Aucun salon trouvé. + + + Aucun résultat trouvé. + + + En attente + + + Url original + + + Une clé osu!API est nécessaire + + + Impossible de récupérer la signature osu. + + + Trouvé dans {0} images. Affichage de {0} aléatoires. + + + Utilisateur non trouvé! Veuillez vérifier la région and le BattleTag avant de réessayer. + + + Prévision de lecture + + + Plateforme + + + Attaque non trouvée + + + Pokémon non trouvé. + + + Lien du profil : + + + Qualité + + + Durée en Jeux Rapides + + + Victoires Rapides + + + Évaluation + + + Score: + + + Chercher pour: + + + Impossible de réduire cette Url + + + Url réduite + + + Une erreur s'est produite. + + + Veuillez spécifier les paramètres de recherche. + + + Statut + + + Url stockée + + + Le streamer {0} est hors ligne. + + + Le streamer {0} est en ligne avec {1} spectateurs. + + + Vous suivez {0} streams sur ce serveur. + + + Vous ne suivez aucun stream sur ce serveur. + + + Aucune diffusion de ce nom. + + + Cette diffusion n'existe probablement pas. + + + Diffusion de {0} ({1}) retirée des notifications. + + + Je préviendrais ce salon lors d'un changement de statut. + + + Aube + + + Crépuscule + + + Température + + + Titre: + + + Top 3 anime favoris + + + Traduction: + + + Types + + + Impossible de trouver une définition pour ce terme. + + + Url + + + Spectateurs + + + Regardant + + + Impossible de trouver ce terme sur le wikia spécifié. + + + Entrez un wikia cible, suivi d'une requête de recherche + + + Page non trouvée + + + Vitesse du vent + + + Les {0} champions les plus bannis + + + Impossible de yodifier votre phrase. + + + Rejoint + + + `{0}.` {1} [{2:F2}/s] - {3} total + /s and total need to be localized to fit the context - +`1.` + + + Page d'Activité #{0} + + + {0} utilisateurs en total. + + + Créateur + + + ID du Bot + + + Liste des fonctions pour la commande {0}calc + + + {0} de ce salon est {1} + + + Sujet du salon + + + Commandes éxécutées + + + {0} {1} est équivalent à {2} {3} + + + Unités pouvant être converties : + + + Impossible de convertir {0} en {1}: unités non trouvées + + + Impossible de convertire {0} en {1} : les types des unités ne sont pas compatibles. + + + Créé le + + + Salon inter-serveur rejoint. + + + Salon inter-serveur quitté. + + + Voici votre jeton CSC + + + Emojis personnalisées + + + Erreur + + + Fonctionnalités + + + ID + + + Index hors limites. + + + Voici une liste des utilisateurs dans ces rôles : + + + Vous ne pouvez pas utiliser cette commande sur un rôle avec beaucoup d'utilisateurs afin d'éviter les abus. + + + Valeur {0} invalide. + Invalid months value/ Invalid hours value + + + Discord rejoint + + + Serveur rejoint + + + ID: {0} +Membres: {1} +OwnerID: {2} + + + Aucun serveur trouvée sur cette page. + + + Listes des rappels + + + Membres + + + Mémoire + + + Messages + + + Message de rappel + + + Nom + + + Pseudonyme + + + Personne ne joue à ce jeu. + + + Aucun rappel actif. + + + Aucun rôle sur cette page. + + + Aucun shard sur cette page. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Aucun sujet choisi. + + + Propriétaire + + + ID des propriétaires + + + Présence + + + {0} Serveurs +{1} Salons Textuels +{2} Salons Vocaux + + + Toutes les citations possédant le mot-clé {0} ont été supprimées + + + Page {0} des citations + + + Aucune citation sur cette page. + + + Aucune citation que vous puissiez supprimer n'a été trouvé. + + + Citation ajoutée + + + Une citation aléatoire a été supprimée. + + + Région + + + Inscrit sur + + + Je vais vous rappeler {0} pour {1} dans {2} `({3:d.M.yyyy} à {4:HH:mm})` + + + Format de date non valide. Regardez la liste des commandes. + + + Nouveau modèle de rappel en service. + + + Répétition de {0} chaque {1} jour(s), {2} heure(s) et {3} minute(s) + + + Liste des Rappels + + + Aucun rappel actif sur ce serveur. + + + #{0} arrêté. + + + Pas de message de rappel trouvé sur ce serveur. + + + Résultat + + + Rôles + + + Page #{0} de tout les rôles sur ce serveur. + + + Page #{0} des rôles pour {1} + + + Aucunes couleurs sont dans le format correct. Utilisez `#00ff00` par exemple. + + + Couleurs alternées pour le rôle {0} activées. + + + Couleurs alternées pour le rôle {0} arrêtées + + + {0} de ce serveur est {1} + + + Info du serveur + + + Shard + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Statistique de Shard + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Le shard **#{0}** est en {1} état avec {2} serveurs + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + **Nom:** {0} **Lien:** {1} + + + Pas d'emojis spéciaux trouvés. + + + Joue actuellement {0} musiques, {1} en attente. + + + Salons textuels + + + Voici le lien pour votre chambre: + + + Durée de fonctionnement + + + {0} de l'utilisateur {1} est {2} + Id of the user kwoth#1234 is 123123123123 + + + Utilisateurs + + + Salons vocaux + + + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 8dd225d1..5485e9ef 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -755,7 +755,7 @@ Reason: {1} __IgnoredChannels__: {2} - Text Channel Destroyed + Text Channel Created Text Channel Destroyed @@ -1401,9 +1401,6 @@ Don't forget to leave your discord name or id in the message. Music playback paused. - - No music player active. - Player Queue - Page {0}/{1} @@ -1757,7 +1754,7 @@ Don't forget to leave your discord name or id in the message. Invalid source or target language. - Joes not loaded. + Jokes not loaded. Lat/Long From 500e128d425a80b99062c1224d4ec16eaed8fc04 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 28 Feb 2017 10:35:36 +0100 Subject: [PATCH 216/256] voicepresence fix --- src/NadekoBot/Modules/Administration/Commands/LogCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs index f431caad..f061f045 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LogCommand.cs @@ -529,7 +529,7 @@ namespace NadekoBot.Modules.Administration else if (afterVch == null) { str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft", - "👤" + Format.Code(prettyCurrentTime), "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), + "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator), Format.Bold(beforeVch.Name ?? "")); } if (str != null) From 868c5e648732cec23145d8a8377d1a92c0538194 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 28 Feb 2017 14:13:50 +0100 Subject: [PATCH 217/256] Update ResponseStrings.fr-fr.resx (POEditor.com) --- .../Resources/ResponseStrings.fr-fr.resx | 407 +++++++++--------- 1 file changed, 202 insertions(+), 205 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx index 68602a62..5f7a188e 100644 --- a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -1,121 +1,121 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Cette base a déjà été revendiquée ou détruite @@ -136,7 +136,7 @@ {0} a revendiqué une base #{1} dans une guerre contre {2} - @{0} Vous avez déjà revendiqué la base {1}. Vous ne pouvez pas en revendiquer une nouvelle. + @{0} Vous avez déjà revendiqué la base #{1}. Vous ne pouvez pas en revendiquer une nouvelle. La demande de la part de @{0} pour une guerre contre {1} a expiré. @@ -178,7 +178,7 @@ La guerre contre {0} commence! - La guerre contre {0} a fini. + La guerre contre {0} est terminée. Cette guerre n'existe pas. @@ -252,7 +252,7 @@ Vous ne pouvez pas vous attaquer vous-même. - {0} s'est évanoui. + {0} s'est évanoui! Vous avez soigné {0} avec un {1} @@ -261,7 +261,7 @@ {0} a {1} points de vie restants. - Vous ne pouvez pas utiliser {0}. Ecrivez `{1}ml` pour voir la liste de toutes les actions que vous pouvez effectuer. + Vous ne pouvez pas utiliser {0}. Ecrivez `{1}ml` pour voir la liste des actions que vous pouvez effectuer. Liste des attaques pour le type {0} @@ -313,7 +313,7 @@ Vous avez été banni du serveur {0}. -Raison : {1} +Raison: {1} banni @@ -398,7 +398,7 @@ Raison : {1} Nouveau donateur ajouté avec succès. Montant total des dons de cet utilisateur : {0} 👑 - Merci aux personnes ci-dessous pour avoir permis à ce projet d'exister. + Merci aux personnes ci-dessous pour avoir permis à ce projet d'exister! Je transmettrai désormais les MPs à tous les propriétaires. @@ -407,31 +407,31 @@ Raison : {1} Je transmettrai désormais les MPs seulement au propriétaire principal. - Je vous transmettrai désormais les MPs. + Je transmettrai désormais les MPs. - Je ne vous transmettrez désormais plus les MPs. + Je ne transmettrai désormais plus les MPs. - Suppression automatique des messages d'accueil a été désactivé. + La suppression automatique des messages d'accueil a été désactivé. Les messages d'accueil seront supprimés après {0} secondes. - Message privé de bienvenue actuel: {0} + MP de bienvenue actuel: {0} - Activez les messages de bienvenue privés en écrivant {0} + Activez les MPs de bienvenue en écrivant {0} - Nouveau message de bienvenue privé en service. + Nouveau MP de bienvenue en service. - Messages de bienvenue privés désactivés. + MPs de bienvenue désactivés. - Messages de bienvenue privés activés. + MPs de bienvenue activés. Message de bienvenue actuel: {0} @@ -544,7 +544,7 @@ Raison : {1} singular "User muted." - Il semblerait que vous n'ayez pas la permission nécessaire pour effectuer cela. + Il semblerait que je n'ai pas la permission nécessaire pour effectuer cela. Nouveau rôle de mise en sourdine crée. @@ -611,7 +611,7 @@ Raison : {1} Le temps doit être compris entre {0} et {1} secondes. - Vous avez retirés tout les rôles de l'utilisateur {0} avec succès + Vous avez retirés tous les rôles de l'utilisateur {0} avec succès Impossible de retirer des rôles. Je n'ai pas les permissions suffisantes. @@ -690,7 +690,7 @@ Raison : {1} Vous ne possédez pas le rôle {0}. - L'affection automatique des rôles n'est désormais plus exclusive! + L'affectation automatique des rôles n'est désormais plus exclusive! Je suis incapable de vous ajouter ce rôle. `Je ne peux pas ajouter de rôles aux propriétaires et aux autres rôles plus haut que le mien dans la hiérarchie.` @@ -711,7 +711,7 @@ Raison : {1} Impossible d'ajouter un rôle. Je ne possède pas les permissions suffisantes. - Nouvel avatar défini. + Nouvel avatar défini! Nouveau nom de Salon défini avec succès. @@ -746,7 +746,7 @@ Raison : {1} Mode ralenti activé - expulsion + expulsés (kick) PLURAL @@ -785,25 +785,25 @@ Raison : {1} Utilisateur banni - {0} a été **mis en sourdine** sur le tchat. + {0} a été **mis en sourdine** sur le chat. - **La parole a été rétablie** sur le tchat pour {0} + **La parole a été rétablie** sur le chat pour {0} - L'utilisateur a rejoins + L'utilisateur a rejoint - L'utilisateur a quité + L'utilisateur a quitté - {0} a été **mis en sourdine** à la fois sur le salon textuel et vocal. + {0} a été **mis en sourdine** à la fois sur le salon textuel et vocal. Rôle ajouté à l'utilisateur - Rôle enlevé de l'utilisateur + Rôle retiré de l'utilisateur {0} est maintenant {1} @@ -876,13 +876,13 @@ Raison: {1} Utilisateur expulsé. - a récompensé {0} pour {1} + a récompensé {0} à {1} Pari à pile ou face - Vous aurez plus de chance la prochaine fois ^_^ + Meilleure chance la prochaine fois ^_^ Félicitations! Vous avez gagné {0} pour avoir lancé au dessus de {1} @@ -891,7 +891,7 @@ Raison: {1} Deck remélangé - Renversé {0} + Lancé {0} User flipped tails. @@ -942,10 +942,10 @@ Raison: {1} Utilisateur tiré au sort - Vous êtes tombé {0} + Vous avez roulé un {0} - Pari + Mise WOAAHHHHHH!!! Félicitations!!! x{0} @@ -954,10 +954,10 @@ Raison: {1} Une simple {0}, x{1} - Wow! Quelle chance! Trois du même style! x{0} + Wow! Quelle chance! Trois du même genre! x{0} - Bon travail! Deux {0} - bet x{1} + Bon travail! Deux {0} - mise x{1} Gagné @@ -1026,7 +1026,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Liste Des Modules - Entrez `{0}cmds ModuleName` pour avoir la liste des commandes de ce module. ex `{0}cmds games` + Entrez `{0}cmds NomDuModule` pour avoir la liste des commandes de ce module. ex `{0}cmds games` Ce module n'existe pas. @@ -1048,7 +1048,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Tag - Race Animale + Course d'animaux Pas assez de participants pour commencer. @@ -1092,7 +1092,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Le lancement de la course a échoué. Une autre course doit probablement être en cours. - Aucune course existe sur ce serveur. + Aucune course n'existe sur ce serveur. Le deuxième nombre doit être plus grand que le premier. @@ -1119,7 +1119,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Top Waifus - votre affinité est déjà liée à ce waifu ou vous êtes en train de retirer votre affinité avec quelqu'un avec qui vous n'en posséder pas. + votre affinité est déjà liée à cette waifu ou vous êtes en train de retirer votre affinité avec quelqu'un avec qui vous n'en posséder pas. Affinités changées de de {0} à {1}. @@ -1134,13 +1134,13 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Votre affinité a été réinitialisée. Vous n'avez désormais plus la personne que vous aimez. - veut être le waifu de {0}. Aww <3 + veut être la waifu de {0}. Aww <3 - a revendiqué {0} comme son waifu pour {1} + a revendiqué {0} comme sa waifu pour {1} - Vous avez divorcé avec un waifu qui vous aimais. Monstre sans cœur. {0} a reçu {1} en guise de compensation. + Vous avez divorcé avec une waifu qui vous aimais. Monstre sans cœur. {0} a reçu {1} en guise de compensation. vous ne pouvez pas vous lier d'affinité avec vous-même, espèce d'égocentrique. @@ -1168,7 +1168,7 @@ La nouvelle valeur de {0} est {1} ! Personne - Vous avez divorcé d'une waifu qui ne vous aimé pas. Vous recevez {0} en retour. + Vous avez divorcé d'une waifu qui ne vous aimait pas. Vous recevez {0} en retour. 8ball @@ -1222,7 +1222,7 @@ La nouvelle valeur de {0} est {1} ! Inscriptions terminées. - Une Course Animale est déjà en cours. + Une Course d'animaux est déjà en cours. Total : {0} Moyenne : {1} @@ -1259,7 +1259,7 @@ La nouvelle valeur de {0} est {1} ! Partie de pendu commencée. - Une partie de pendu est déjà en cours sur ce channel. + Une partie de pendu est déjà en cours sur ce canal Initialisation du pendu erronée. @@ -1348,16 +1348,16 @@ La nouvelle valeur de {0} est {1} ! Lecture automatique activée - Volume de base mis à {0}% + Volume de base défini à {0}% File d'attente complète. - a tour de rôle + à tour de rôle - Musique finie + Musique terminée Système de tour de rôle désactivé. @@ -1404,9 +1404,6 @@ La nouvelle valeur de {0} est {1} ! Musique mise sur pause. - - Aucun lecteur de musique actif. - Liste d'attente - Page {0}/{1} @@ -1417,22 +1414,22 @@ La nouvelle valeur de {0} est {1} ! #{0}` - **{1}** par *{2}* ({3} morceaux) - Page {0} des playlists sauvegardées + Page {0} des listes de lecture sauvegardées - Playlist supprimée + Liste de lecture supprimée - Impossible de supprimer cette playlist. Soit elle n'existe pas, soit vous n'en êtes pas le créateur. + Impossible de supprimer cette liste de lecture. Soit elle n'existe pas, soit vous n'en êtes pas le créateur. - Aucune playlist avec cet ID existe. + Aucune liste de lecture avec cet ID existe. - File d'attente des playlists complète. + File d'attente de la liste complétée. - Playlist sauvegardée + Liste de lecture sauvegardée Limite à {0}s @@ -1457,7 +1454,7 @@ La nouvelle valeur de {0} est {1} ! Répétition de la musique en cours - Playlist en boucle + Liste de lecture en boucle Piste en boucle @@ -1484,7 +1481,7 @@ La nouvelle valeur de {0} est {1} ! Musiques mélangées. - Musiques déplacées. + Musique déplacée. {0}h {1}m {2}s @@ -1502,31 +1499,31 @@ La nouvelle valeur de {0} est {1} ! Volume réglé sur {0}% - Désactivation de l'usage de TOUT LES MODULES pour le salon {0}. + Désactivation de l'usage de TOUS LES MODULES pour le salon {0}. - Activation de l'usage de TOUT LES MODULES pour le salon {0}. + Activation de l'usage de TOUS LES MODULES pour le salon {0}. Permis - Désactivation de l'usage de TOUT LES MODULES pour le rôle {0}. + Désactivation de l'usage de TOUS LES MODULES pour le rôle {0}. - Activation de l'usage de TOUT LES MODULES pour le rôle {0}. + Activation de l'usage de TOUS LES MODULES pour le rôle {0}. - Désactivation de l'usage de TOUT LES MODULES sur ce serveur. + Désactivation de l'usage de TOUS LES MODULES sur ce serveur. - Activation de l'usage de TOUT LES MODULES sur ce serveur. + Activation de l'usage de TOUS LES MODULES sur ce serveur. - Désactivation de l'usage de TOUT LES MODULES pour l'utilisateur {0}. + Désactivation de l'usage de TOUS LES MODULES pour l'utilisateur {0}. - Activation de l'usage de TOUT LES MODULES pour l'utilisateur {0}. + Activation de l'usage de TOUS LES MODULES pour l'utilisateur {0}. Banni {0} avec l'ID {1} @@ -1559,7 +1556,7 @@ La nouvelle valeur de {0} est {1} ! Liste Des Mots Filtrés - Suppression du mot {0} à la liste des mots filtrés. + Suppression du mot {0} de la liste des mots filtrés. Second paramètre invalide. (nécessite un nombre entre {0} et {1}) @@ -1637,10 +1634,10 @@ La nouvelle valeur de {0} est {1} ! Usage de {0} {1} activé pour l'utilisateur {2}. - Je n'afficherais plus les avertissements des permissions. + Je n'afficherai plus les avertissements des permissions. - J'afficherais désormais les avertissements des permissions. + J'afficherai désormais les avertissements des permissions. Filtrage des mots désactivé sur ce salon. @@ -1685,10 +1682,10 @@ La nouvelle valeur de {0} est {1} ! fait - Chapitre + Chapitres - Comic # + Bande dessinée # Parties compétitives perdues @@ -1715,7 +1712,7 @@ La nouvelle valeur de {0} est {1} ! Date - Defini: + Définis: En baisse @@ -1739,7 +1736,7 @@ La nouvelle valeur de {0} est {1} ! Genres - Impossible de trouver une définition pour cette citation. + Impossible de trouver une définition pour ce hashtag. Taille/Poid @@ -1797,7 +1794,7 @@ La nouvelle valeur de {0} est {1} ! En attente - Url original + Url originale Une clé osu!API est nécessaire @@ -1809,7 +1806,7 @@ La nouvelle valeur de {0} est {1} ! Trouvé dans {0} images. Affichage de {0} aléatoires. - Utilisateur non trouvé! Veuillez vérifier la région and le BattleTag avant de réessayer. + Utilisateur non trouvé! Veuillez vérifier la région ainsi que le BattleTag avant de réessayer. Prévision de lecture @@ -1875,13 +1872,13 @@ La nouvelle valeur de {0} est {1} ! Vous ne suivez aucun stream sur ce serveur. - Aucune diffusion de ce nom. + Aucun stream de ce nom. - Cette diffusion n'existe probablement pas. + Ce stream n'existe probablement pas. - Diffusion de {0} ({1}) retirée des notifications. + Stream de {0} ({1}) retirée des notifications. Je préviendrais ce salon lors d'un changement de statut. @@ -1967,7 +1964,7 @@ La nouvelle valeur de {0} est {1} ! Sujet du salon - Commandes éxécutées + Commandes exécutées {0} {1} est équivalent à {2} {3} @@ -1979,7 +1976,7 @@ La nouvelle valeur de {0} est {1} ! Impossible de convertir {0} en {1}: unités non trouvées - Impossible de convertire {0} en {1} : les types des unités ne sont pas compatibles. + Impossible de convertir {0} en {1} : les types des unités ne sont pas compatibles. Créé le @@ -2000,7 +1997,7 @@ La nouvelle valeur de {0} est {1} ! Erreur - Fonctionnalités + Fonctionnalités ID @@ -2012,7 +2009,7 @@ La nouvelle valeur de {0} est {1} ! Voici une liste des utilisateurs dans ces rôles : - Vous ne pouvez pas utiliser cette commande sur un rôle avec beaucoup d'utilisateurs afin d'éviter les abus. + Vous ne pouvez pas utiliser cette commande sur un rôle incluant beaucoup d'utilisateurs afin d'éviter les abus. Valeur {0} invalide. @@ -2033,7 +2030,7 @@ OwnerID: {2} Aucun serveur trouvée sur cette page. - Listes des rappels + Liste des messages répétés Membres @@ -2045,7 +2042,7 @@ OwnerID: {2} Messages - Message de rappel + Répéteur de messages Nom @@ -2057,7 +2054,7 @@ OwnerID: {2} Personne ne joue à ce jeu. - Aucun rappel actif. + Aucune répétition active. Aucun rôle sur cette page. @@ -2120,16 +2117,16 @@ OwnerID: {2} Répétition de {0} chaque {1} jour(s), {2} heure(s) et {3} minute(s) - Liste des Rappels + Liste des répétitions - Aucun rappel actif sur ce serveur. + Aucune répétition active sur ce serveur. #{0} arrêté. - Pas de message de rappel trouvé sur ce serveur. + Pas de message répété trouvé sur ce serveur. Résultat @@ -2144,7 +2141,7 @@ OwnerID: {2} Page #{0} des rôles pour {1} - Aucunes couleurs sont dans le format correct. Utilisez `#00ff00` par exemple. + Aucunes couleurs ne sont dans le format correct. Utilisez `#00ff00` par exemple. Couleurs alternées pour le rôle {0} activées. @@ -2183,7 +2180,7 @@ OwnerID: {2} Salons textuels - Voici le lien pour votre chambre: + Voici le lien pour votre salon: Durée de fonctionnement From 98484b84e5cb79cc716da9e47756aba5daffb54f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 28 Feb 2017 14:25:57 +0100 Subject: [PATCH 218/256] Update ResponseStrings.fr-fr.resx (POEditor.com) From d58ebd100f8b74802ee3cf6db292dde86c0c5067 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 28 Feb 2017 14:42:26 +0100 Subject: [PATCH 219/256] Fixed ~ani and ~mang, searches might be off for some time --- .../Searches/Commands/AnimeSearchCommands.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs index 9fdf07f0..3fcfb654 100644 --- a/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs +++ b/src/NadekoBot/Modules/Searches/Commands/AnimeSearchCommands.cs @@ -30,17 +30,19 @@ namespace NadekoBot.Modules.Searches { try { - var headers = new Dictionary { - {"grant_type", "client_credentials"}, - {"client_id", "kwoth-w0ki9"}, - {"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"}, - }; + var headers = new Dictionary + { + {"grant_type", "client_credentials"}, + {"client_id", "kwoth-w0ki9"}, + {"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"}, + }; using (var http = new HttpClient()) { - http.AddFakeHeaders(); + //http.AddFakeHeaders(); + http.DefaultRequestHeaders.Clear(); var formContent = new FormUrlEncodedContent(headers); - var response = await http.PostAsync("http://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false); + var response = await http.PostAsync("https://anilist.co/api/auth/access_token", formContent).ConfigureAwait(false); var stringContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false); anilistToken = JObject.Parse(stringContent)["access_token"].ToString(); } From f3e019ca6cac9eeddbab23c413adde012ff23e06 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 28 Feb 2017 21:43:56 +0100 Subject: [PATCH 220/256] Added german, fixed some strings --- .../Resources/ResponseStrings.Designer.cs | 14 +- .../Resources/ResponseStrings.de-DE.resx | 2190 +++++++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 14 +- 3 files changed, 2204 insertions(+), 14 deletions(-) create mode 100644 src/NadekoBot/Resources/ResponseStrings.de-DE.resx diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 3d80c3c9..49072329 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -594,7 +594,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Bot's language is set to {0} - {0}. + /// Looks up a localized string similar to Bot's language is set to {0} - {1}. /// public static string administration_lang_set_bot_show { get { @@ -612,7 +612,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to This server's language is set to {0} - {0}. + /// Looks up a localized string similar to This server's language is set to {0} - {1}. /// public static string administration_lang_set_show { get { @@ -1650,7 +1650,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Voice Channel Destroyed. + /// Looks up a localized string similar to Voice Channel Created. /// public static string administration_voice_chan_created { get { @@ -2235,7 +2235,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Invalid number specified. You can roll up to {0}-{1} dice at a time.. + /// Looks up a localized string similar to Invalid number specified. You can roll {0}-{1} dice at once.. /// public static string gambling_dice_invalid_number { get { @@ -4902,7 +4902,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Lsit of {0}place tags. + /// Looks up a localized string similar to List of {0}place tags. /// public static string searches_list_of_place_tags { get { @@ -5001,7 +5001,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Failed retreiving osu signature.. + /// Looks up a localized string similar to Failed retrieving osu! signature.. /// public static string searches_osu_failed { get { @@ -5995,7 +5995,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Page #{0} of roels for {1}. + /// Looks up a localized string similar to Page #{0} of roles for {1}. /// public static string utility_roles_page { get { diff --git a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx new file mode 100644 index 00000000..9aac2f85 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx @@ -0,0 +1,2190 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Diese Basis wurde bereits beansprucht oder zerstört. + + + Diese Basis ist bereits zertört. + + + Diese Basis ist nicht beansprucht. + + + Basis #{0} im Krieg gegen {1} **ZERSTÖRT** + + + {0} hat die Basis #{1} **UNBEANSPRUCHT** im Krieg gegen {2} + + + {0} hat die Basis #{1} beansprucht im Krieg gegen {2} + + + @{0} sie haben die Basis #{1} bereits beansprucht. Sie können keine weitere beanspruchen. + + + Einnahme von @{0} für den Krieg gegen {1} ist abgelaufen. + + + Gegner + + + informationen über den Krieg mit {0} + + + Ungültige Basisnummer. + + + Keine gültige Kriegsgröße. + + + Liste der aktiven Kriege + + + nicht beansprucht + + + Sie nehmen nicht an diesem Krieg teil. + + + @{0} Sie nehmen nicht an diesem Krieg teil, oder diese Basis ist bereits zerstört. + + + Keine aktiven Kriege. + + + Größe + + + Krieg gegen {0} wurde schon gestartet. + + + Krieg gegen {0} erstellt. + + + Krieg gegen {0} ist beendet. + + + Dieser Krieg existiert nicht. + + + Krieg gegen {0} gestartet! + + + Reaktionsstatistiken gelöscht. + + + Benutzerdefinierte Reaktion gelöscht. + + + Unzureichende Rechte. Dies erfordert das sie den Bot besitzen für globale Reaktionen, und Administratorrechte für serverseitige Reaktionen. + + + Liste aller benutzerdefinierten Reaktionen. + + + Benutzerdefinierte Reaktionen + + + Neue benutzerdefinierte Reaktion + + + Keine benutzerdefinierten Reaktionen gefunden. + + + Keine benutzerdefinierte Reaktion mit dieser ID gefunden. + + + Antwort + + + Reaktionsstatistiken + + + Statistiken für die Reaktion {0} gelöscht. + + + Keine Statistiken für diesen Auslöser gefunden, keine Aktion unternommen. + + + Auslöser + + + Autohentai angehalten. + + + Keine Ergebnisse gefunden. + + + {0} ist bereits ohnmächtig. + + + {0} hat bereits volle LP. + + + Dein Typ ist bereits {0} + + + benutzt {0}{1} auf {2}{3} für {4} Schaden. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Sie können ohne Gegenangriff nicht erneut angreifen! + + + Sie können sich nicht selber angreifen. + + + {0} wurde ohnmächtig! + + + heilte {0} mit einem {0} + + + {0} hat {1} LP übrig. + + + Sie können {0} nicht benutzen. Schreibe `{1}ml` um eine Liste deiner verfügbaren Angriffe zu sehen. + + + Angriffsliste für den Typ {0} + + + Das ist nicht sehr effektiv. + + + Sie haben nicht genug {0} + + + belebte {0} wieder mit einem {1} + + + Sie haben sich selbst wiederbelebt mit einem {0} + + + Dein Typ wurde verändert von {0} mit einem {1} + + + Das ist effektiv. + + + Das ist sehr effektiv! + + + Sie haben zu viele Angriffe hintereinander eingesetzt, sodass sie sich nicht bewegen können! + + + Typ von {0} ist {1} + + + Benutzer nicht gefunden. + + + Sie sind ohnmächtig, daher können sie sich nicht bewegen! + + + **Automatische Rollenzuteilung** wenn ein Benutzer beitritt ist nun **deaktiviert**. + + + **Automatische Rollenzuteilung** wenn ein Benutzer beitritt ist nun **aktiviert**. + + + Anhänge + + + Avatar varändert + + + Sie wurden vom Server {0} gebannt. +Grund: {1} + + + gebannt + PLURAL + + + Benutzer gebannt + + + Name des Bots geändert zu {0} + + + Status des Bots geändert zu {0} + + + Automatisches Löschen von Abschiedsnachrichten ist nun deaktiviert. + + + Abschiedsnachrichten werden nun nach {0} Sekunden gelöscht. + + + Aktuelle Abschiedsnachricht: {0} + + + Schalte Abschiedsnachrichten ein durch schreiben von {0} + + + Neues Abschiedsnachrichtenset. + + + Abschiedsnachrichten deaktiviert. + + + Abschiedsnachrichten eingeschaltet in diesem Kanal. + + + Kanalname geändert + + + Alter Name + + + Kanalthema geändert + + + Aufgeräumt. + + + Inhalt + + + Rolle {0} erfolgreich erstellt + + + Textkanal {0} erstellt. + + + Sprachkanal {0} erstellt. + + + Taubschaltung erfolgreich. + + + Server {0} gelöscht + + + Automatisches Löschen von erfolgreichen Befehlsausführungen gestoppt. + + + Automatisches Löschen von erfolgreichen Befehlsausführungen gestartet. + + + Textkanal {0} gelöscht. + + + Sprachkanal {0} gelöscht. + + + DN von + + + Erfolgreich einen neuen Spender hinzugefügt. Gesamte Spendenmenge von diesem Benutzer: {0} 👑 + + + Danke für die untenstehenden Leute die dieses Projekt möglich gemacht haben! + + + Ich werde DNs zu allen Besitzern weiterleiten. + + + Ich werde DNs zum ersten Besitzer weiterleiten. + + + Ich werde nun DNs weiterleiten. + + + Ich werde aufhören DNs weiterzuleiten. + + + Automatisches löschen der Begrüßungsnachrichten wurde deaktiviert. + + + Begrüßungsnachrichten werden gelöscht nach {0} sekunden. + + + Aktuelle DN Begrüßungsnachricht: {0} + + + Aktiviere DN Begrüßungsnachrichten durch schreiben von: {0} + + + Neue DN Begrüßungsnachricht wurde gesetzt. + + + Begrüßungsankündigungen wurden deaktiviert. + + + DN Begrüßungsankündigungen wurden aktiviert. + + + Aktuelle Begrüßungsnachricht: {0} + + + Aktiviere Begrüßungsnachrichten durch schreiben von: {0} + + + Neue Begrüßungsnachricht wurde gesetzt. + + + Begrüßungsankündigungen wurden deaktiviert. + + + Begrüßungsankündigungen wurden für diesen Kanal aktiviert. + + + Sie können diesen befehl nicht an Benutzern mit einer Rolle über oder gleich zu ihrer in der Rangordnung benutzen. + + + Bilder wurden geladen nach {0} sekunden! + + + Ungültiges Eingabeformat. + + + Ungültige Übergabevariable. + + + {0} ist {1} beigetreten + + + Sie haben {0} von dem Server gekickt. +Grund: {1} + + + Benutzer wurde gekickt + + + Liste der Sprachen +{0} + + + Ihr Servers Sprachumgebung ist jetzt {1} - {1} + + + Der Bots standard Sprachumgebung ist jetzt {0} - {1} + + + Die Sprache des Bots ist {0} - {1} + + + Setzen der Sprachumgebung fehlgeschlagen. Greifen sie auf diesen Befehls hilfe erneut auf. + + + Dieser Servers Sprache wurde zu {0} - {1} gesetzt + + + {0} verließ {1} + + + Server {0} wurde verlassen + + + Ereignis {0} wird in diesem Kanal aufgezeichnet. + + + Alle Ereignise wird in diesem Kanal aufgezeichnet. + + + Aufzeichnungen wurden deaktiviert. + + + Aufzeichnungs Ereignise die sie abonnieren können: + + + Aufzeichnungen werden {0} ignorieren + + + Aufzeichnungen werden {0} nicht ignorieren + + + Aufzeichnung von Ereignis {0} gestoppt. + + + {0} hat eine Erwähnung von den folgended Rollen aufgerufen + + + nachricht von {0} `[Besitzer des Bots]`: + + + Nachricht gesendet. + + + {0} wurde von {1} zu {2} verschoben + + + Nachricht in #{0} gelöscht + + + Nachricht in #{0} aktualisiert + + + wurden Stumm geschalten + PLURAL (users have been muted) + + + wurde Stumm geschalten + singular "User muted." + + + Ich habe wahrscheinlich die benötigten Rechte für dies nicht. + + + Neue Stumme Rolle gesetzt. + + + Ich brauche **Administrations** rechte um dies tun zu können. + + + Neue Nachricht + + + Neuer Nickname + + + Neues Thema + + + Nickname wurde geändert + + + Konnte den Server nicht finden + + + Kein Shard mit dieser ID gefunden. + + + Alte Nachricht + + + Alter Nickname + + + Altes Thema + + + Fehler. Ich habe wahrscheinlich nicht ausreichend Rechte. + + + Rechte für diesen Server zurückgesetzt. + + + Aktive Schutzmechanismen + + + {0} wurde auf diesem Server **deaktiviert**. + + + {0} aktiviert + + + Fehler. Ich benötige die Berechtigung RollenVerwalten + + + Keine Schutzmechanismen aktiviert. + + + Benutzerschwelle muss zwischen {0} und {1} sein. + + + Wenn {0} oder mehr Benutzer innerhalb von {1} Sekunden beitreten werde ich sie {2}. + + + Zeit muss zwischen {0} und {1} sekunden sein. + + + Erfolgreich alle Rollen vom Benutzer {0} entfernt + + + Rollen konnten nicht entfernt werden. Ich habe nicht die erforderlichen Rechte. + + + Farbe der Rolle {0} wurde geändert. + + + Diese Rolle existiert nicht. + + + Die angegebenen Parameter sind ungültig. + + + Fehler ist aufgetreten aufgrund von einer ungültigen Farbe oder fehlenden Rechten. + + + Erfolgreich die Rolle {0} vom Benutzer {1} entfernt + + + Entfernen der Rolle fehlgeschlagen. Ich habe nicht die erforderlichen Rechte. + + + Rolle umbenannt. + + + Umbenennen der Rolle fehlgeschlagen. Ich habe nicht die erforderlichen Rechte. + + + Sie kännen keine Rollen bearbeiten die höher als ihre eigenen sind. + + + Die Abspielnachricht wurde entfernt: {0} + + + Die Rolle {0} wurde zur Liste hinzugefügt. + + + {0} nicht gefunden. Aufgeräumt. + + + Die Rolle {0} ist bereits in der Liste. + + + Hinzugefügt. + + + Rotation des Spielstatus deaktiviert. + + + Rotation des Spielstatus aktiviert. + + + Hier ist die Liste der rotierenden Status: +{0} + + + Keine rotierenden Status gesetzt. + + + Sie haben bereits die Rolle {0}. + + + Sie haben bereits die exklusive, selbstzugewiesene Rolle {0}. + + + Selbstzuweisbare Rollen sind nun exklusiv! + + + Es gibt {0} selbstzuweisbare Rollen + + + Diese Rolle ist nicht selbstzuweisbar. + + + Sie haben die Rolle {0} nicht. + + + Selbstzuweisbare Rollen sind nicht länger exklusiv! + + + Ich kann dir diese Rolle nicht zuweisen. `Ich kann keine Rollen zu Besitzern oder andere Rollen die höher als meine in der Rangordnung sind hinzufügen.` + + + {0} wurde von der Liste der selbstzuweisbaren Rollen entfernt. + + + Sie haben nicht länger die Rolle {0}. + + + Sie haben nun die Rolle {0}. + + + Erfolgreich die Rolle {0} zum Benutzer {1} hinzugefügt + + + Fehlgeschlagen die Rolle hinzuzufügen. Ich habe nicht die erforderlichen Rechte. + + + Neuer Avatar gesetzt! + + + Neuer Kanalname gesetzt. + + + Neues Spiel gesetzt! + + + Neuer Stream gesetzt! + + + Neues Kanalthema gesetzt. + + + Verbindung zu Shard {0} wiederhergestellt. + + + Verbindung zu Shard {0} wird wiederhergestellt. + + + Fahre herunter + + + Benutzer können nicht mehr als {0} Nachrichten alle {1} Sekunden senden. + + + Slow mode deaktiviert. + + + Slow mode eingeleitet + + + soft-banned (gekickt) + PLURAL + + + {0} wird diesen Kanal ignorieren. + + + {0} wird diesen Kanal nicht mehr ignorieren. + + + Wenn ein Benutzer {0} gleiche Nachrichten sendet werde ich ihn {1}. +__ignoredChannels__: {2} + + + Textkanal erstellt + + + Textkanal zerstört + + + Taubschaltung aufgehoben. + + + Stummschaltung aufgehoben + singular + + + Benutzername + + + Benutzername geändert + + + Benutzer + + + Benutzer gebannt + + + {0} wurde **stummgeschaltet** im Chat. + + + {0} ist nicht länger **stummgeschaltet** im Chat. + + + Benutzer ist beigetreten + + + Benutzer ist gegangen + + + {0} wurde **stummgeschaltet** im Text- und Sprachchat. + + + Benutzerrolle hinzugefügt + + + Benutzerrolle entfernt + + + {0} ist nun {1} + + + {0} ist nicht länger **stummgeschaltet** im Text- und Sprachchat. + + + {0} ist dem Sprachkanal {1} beigetreten. + + + {0} hat den Sprachkanal {1} verlassen. + + + {0} ging vom Sprachkanal {1} zu {2}. + + + {0} wurde **stummgeschaltet** im Sprachchat. + + + {0} ist nicht länger **stummgeschaltet** im Sprachchat. + + + Sprachkanal erstellt + Should say "Voice Channel Created" + + + Sprachkanal zerstört + + + Text- und Sprachfunktion deaktiviert. + + + Text- und Sprachfunktion aktiviert. + + + Ich habe keine **Rollenmanagement**- und/oder **Kanalmanagement**-Rechte, sodass ich `voice+text` auf dem Server {0} nicht ausführen kann. + + + Sie schalten diese Funktion ein bzw. aus und **ich habe keine ADMINISTRATORRECHTE**. Dies könnte einige Probleme hervorrufen, sodass sie ihre Textkanäle eventuell selber aufräumen musst. + + + Ich benötige zumindest **Rollenmanagement**- und **Kanalmanagement**-Rechte um diese Funktion einzuschalten. (Bevorzugt Administratorrechte) + + + Benutzer {0} vom Textchat + + + Benutzer {0} vom Text- und Sprachchat + + + Benutzer {0} vom Sprachchat + + + Sie wurden vom Server {0} gekickt. +Grund: {1} + + + Benutzer entbannt + + + Migration fertig! + + + Fehler beim migrieren von Daten. Prüfe die Konsole des Bots für mehr Informationen. + + + Anwesenheits Änderungen + + + Nutzer wurde gekickt + + + verleiht {1} {0} + + + Münzwurf-Glücksspiel + + + Hoffentlich haben sie beim nächsten Mal mehr Glück ^_^ + + + Glückwunsch! Sie haben {0} gewonnen, weil sie über {1} gewürfelt haben + + + Deck neu gemischt. + + + Münzwurfergebnis: {0}. + User flipped tails. + + + Sie haben es erraten! Sie haben {0} gewonnen + + + Ungültige Anzahl angegeben. Sie können 1 bis {0} Münzen werfen. + + + Füge die {0} Reaktion zu dieser Nachricht hinzu um {1} zu erhalten + + + Dieses Ereignis ist aktiv für bis zu {0} Stunden. + + + Blumen-Reaktions-Ereignis gestartet + + + hat {0} an {1} verschenkt + X has gifted 15 flowers to Y + + + {0} hat eine {1} + X has Y flowers + + + Kopf + + + Bestenliste + + + {0} zu {1} Benutzern der Rolle {2} verliehen. + + + Sie können nicht mehr als {0} wetten + + + Sie können nicht weniger als {0} wetten + + + Sie haben nicht genug {0} + + + Keine Karten mehr im Deck. + + + Ausgewählter Benutzer + + + Sie haben eine {0} gewürfelt. + + + Wette + + + WOAAHHHHHH!!! Glückwunsch!!! x{0} + + + Eine einzelne {0}, x{1} + + + Wow! Glückspilz! Ein Drilling! x{0} + + + Gut gemacht! Zwei {0} - Einsatz x{1} + + + Gewonnen + + + Benutzer müssen einen geheimen Code schreiben um {0} zu erhalten. Gültig für {1} Sekunden. Erzähls niemanden. Pshhh. + + + Das SneakyGame-Event ist beendet. {0} Nutzer haben die Belohnung erhalten + + + SneakyGameStatus-Event wurde gestartet + + + Zahl + + + hat erfolgreich {0} von {1} genommen + + + war es nicht möglich {0} von {1} zu nehmen, da der Benutzer nicht so viele {2} besitzt! + + + Zurück zu ToC + + + Nur Bot-Besitzer + + + Benötigt Kanalrecht {0}. + + + Sie können das Projekt auf Patreon: <{0}> oder Paypal: <{1}> unterstützen. + + + Befehle und Alias + + + Befehlsliste neu generiert. + + + Gebe `{0}h NameDesBefehls` ein, um die Hilfestellung für diesen Befehl zu sehen. Z.B. `{0}h >8ball` + + + Ich konnte diesen Befehl nicht finden. Bitte stelle sicher das dieser Befehl existiert bevor sie es erneut versuchen. + + + Beschreibung + + + Sie können das NadekoBot-Projekt über +Patreon <{0}> oder +PayPal <{1}> unterstützen. +Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu hinterlassen. + +**Vielen Dank**♥️ + + + **Liste der Befehle**: <{0}> +**Hosting Anleitungen und Dokumentationen können hier gefunden werden**: <{1}> + + + Lister der Befehle + + + Liste der Module + + + Schreibe `{0}cmds ModuleName` um eine Liste aller Befehle dieses Moduls zu erhalten. z.B. `{0}cmds games` + + + Dieses Modul existiert nicht. + + + Benötigt Serverrecht {0}. + + + Inhaltsverzeichnis + + + Nutzung + + + Autohentai wurde gestartet. Es wird alle {0} Sekunden etwas mit einem der folgenden Stichwörtern gepostet: {1} + + + Stichwort + + + Tierrennen + + + Das Rennen konnte nicht gestartet werden, da es nicht genügend Teilnehmer gibt. + + + Rennen ist voll! Startet sofort. + + + {0} tritt als {1} bei + + + {0} tritt als {1} bei und wettete {2}! + + + Schreibe {0}jr um dem Rennen beizutreten. + + + Starte in 20 Sekunden oder wenn der Raum voll ist. + + + Starte mit {0} Teilnehmern. + + + {0} hat als {1} das Rennen gewonnen! + + + {0} hat als {1} das Rennen und {2} gewonnen! + + + Ungültige Anzahl angegeben. Sie können {0}-{1} Würfel gleichzeitig Rollen. + + + würfelte {0} + Someone rolled 35 + + + Würfel gerollt: {0} + Dice Rolled: 5 + + + Das Rennen konnte nicht gestartet werden. Ein anderes Rennen läuft wahrscheinlich. + + + Es gibt kein Rennen auf diesem Server + + + Die zweite Zahl muss größer als die erste sein. + + + Sinneswandel + + + Beansprucht von + + + Scheidungen + + + Positive Bewertungen + + + Preis + + + Keine Waifus wurden bisher beansprucht. + + + Top Waifus + + + Ihre Neigung ist bereits zu dieser Waifu gesetzt oder sie versuchsen ihre Neigung zu entfernen während sie keine gesetzt haben. + + + hat seine Neigung von {0} zu {1} geändert. + +*Das ist moralisch nicht vertretbar.*🤔 + Make sure to get the formatting right, and leave the thinking emoji + + + Sie müssen {0} Stunden und {1} Minuten warten bevor sie ihre Neigung ändern können. + + + Ihre Neigung wurde zurückgesetzt. Sie haben keine Person mehr die sie mögen. + + + will {0}s Waifu sein. Aww <3 + + + hat {0} als seine/ihre Waifu für {1} beansprucht! + + + Sie haben sich von ihrer Waifu die sie mochte scheiden lassen. Du herzloses Monster. {0} hat {1} als Kompensation erhalten. + + + Sie können deine Neigung nicht zu ihnen selbst setzen, sie egomanische Person. + + + 🎉 Ihre Liebe ist besiegelt! 🎉 +{0}'s neuer Wert ist {1}! + + + Keine Waifu ist so billig. Sie müssen wenigstens {0} bezahlen um diese Waifu zu beanspruchen, selbst wenn ihr tatsächlicher Wert geringer ist. + + + Sie müssen {0} oder mehr bezahlen um diese Waifu zu beanspruchen! + + + Diese Waifu gehört nicht dir. + + + Sie können sich nicht selbst beanspruchen. + + + Sie haben sich vor kurzem scheiden lassen. Sie müssen {0} Stunden und {1} Minuten warten bevor sie sich erneut scheiden lassen können. + + + Niemand + + + Sie haben sich von einer Waifu scheiden lassen die sie nicht mochte, Du erhältst {0} zurück. + + + 8ball + + + Acrophobie + + + Spiel wurde beendet ohne Einsendungen. + + + Keine Stimmen abgegeben. Spiel endete ohne Gewinner. + + + Das Acronym war {0}. + + + Akrophobia läuft bereits in diesem Kanal. + + + Spiel gestartet. Erstelle einen Satz aus dem folgenden Akronym. + + + Sie haben {0} Sekunden für ihre Einsendung. + + + {0} hat seinen Satz eingereicht. ({1} insgesamt) + + + Stimme ab indem du die Nummer der Einsendung eingibst + + + {0} hat seine/ihre Stimme abgegeben! + + + Der Gewinner ist {0} mit {1} Punkten. + + + {0} gewinnt, weil dieser Benutzer die einzigste Einsendung hat! + + + Frage + + + Unentschieden! Beide haben {0} gewählt + + + {0} hat gewonnen! {1} schlägt {2} + + + Einsendungen geschlossen + + + Tierrennen läuft bereits. + + + Insgesamt: {0} Durchschnitt: {1} + + + Kategorie + + + Cleverbot auf diesem Server deaktiviert. + + + Cleverbot auf diesem Server aktiviert. + + + Währungsgeneration in diesem Kanal deaktiviert. + + + Währungsgeneration in diesem Kanal aktiviert. + + + {0} zufällige {1} sind erschienen! Sammle sie indem sie `{2}pick` schreiben. + plural + + + Eine zufällige {0} ist erschienen! Sammle sie indem sie `{1}pick` schreiben. + + + Laden einer Frage fehlgeschlagen. + + + Spiel gestartet + + + Hangman-Spiel gestartet + + + Hangman-Spiel läuft bereits in diesem Kanal. + + + Starten von Hangman hat einen Fehler ausgelöst. + + + Liste der "{0}hangman" Worttypen: + + + Bestenliste + + + Sie haben nicht genug {0} + + + Keine Ergebnisse + + + {0} aufgehoben + Kwoth picked 5* + + + {0} pflanzte {1} + Kwoth planted 5* + + + Ein Trivia Spiel läuft schon auf diesem Server + + + Trivia Spiel + + + {0} erriet es! Die Antwort war: {1} + + + Kein aktives Trivia Spiel auf diesem Server. + + + {0} hat {1} punkte + + + Wird beendet after dieser Frage. + + + Die Zeit is um! Die richtige Antwort war {0} + + + {0} erriet es und hat das spiel GEWONNEN! Die Antwort war: {1} + + + Sie können nicht gegen sich selber spielen. + + + Ein TicTacToe Spiel läuft schon in diesem Kanal. + + + Unentschieden! + + + hat ein TicTacToe Spiel erstellt. + + + {0} hat gewonnen! + + + Drei in einer Reihe + + + Keine Züge übrig + + + Zeit abgelaufen + + + {0}'s Zug + + + {0} gegen {1} + + + Versuche {0} Songs einzureihen... + + + Autoplay deaktiviert. + + + Autoplay aktiviert. + + + Standard-Lautstärke auf {0}% gesetzt + + + Ordner-Einreihung fertig. + + + fairer Modus + + + Song beendet + + + Fairer Modus deaktiviert. + + + Fairer Modus aktiviert. + + + Von Position + + + ID + + + Ungültige Eingabe. + + + Maximale Spielzeit hat kein Limit mehr + + + Maximale Spielzeit ist nun {0} Sekunden + + + Maximale Musik-Warteschlangengröße ist nun unbegrenzt. + + + Maximale Musik-Warteschlangengröße ist nun {0} Songs. + + + Sie müssen sich in einem Sprachkanal auf diesem Server befinden. + + + Name + + + Aktueller Song: + + + Kein aktiver Musikspieler. + + + Keine Suchergebnisse. + + + Musikwiedergabe pausiert. + + + Musik-Warteschlange - Seite {0}/{1} + + + Spiele Song + + + `#{0}` - **{1}** by *{2}* ({3} Songs) + + + Seite {0} der gespeicherten Playlists + + + Playlist gelöscht. + + + Playlist konnte nicht gelöscht werden. Entweder sie existiert nicht oder sie gehört nicht dir. + + + Es gibt keine Playlist mit dieser ID. + + + Playlist wurde an die Warteschlange angehängt. + + + Playlist gespeichert + + + {0}'s Limit + + + Warteschlange + + + Eingereihter Song + + + Musik-Warteschlange geleert + + + Warteschlange ist voll bei {0}/{1} + + + Song entfernt + context: "removed song #5" + + + Aktueller Song wird wiederholt + + + Playlist wird wiederholt + + + Song wird wiederholt + + + Aktueller Song wird nicht mehr wiederholt. + + + Musikwiedergabe wiederaufgenommen + + + Playlist-Wiederholung deaktiviert. + + + Playlist-Wiederholung aktiviert. + + + Ich werde nun spielende, beendete, pausierte und entfernte Songs in diesen Channel ausgeben. + + + Gesprungen zu `{0}:{1}` + + + Song gemischt + + + Song bewegt + + + {0}h {1}m {2}s + + + Zu Position + + + unbegrenzt + + + Lautstärke muss zwischen 0 und 100 sein + + + Lautstärke auf {0}% gesetzt + + + Benutzung ALLER MODULE für Kanal {0} verboten. + + + Benutzung ALLER MODULE für Kanal {0} erlaubt. + + + Erlaubt + + + Benutzung ALLER MODULE für die Rolle {0} verboten. + + + Benutzung ALLER MODULE für die Rolle {0} erlaubt. + + + Benutzung ALLER MODULE für diesen Server verboten. + + + Benutzung ALLER MODULE für diesen Server erlaubt. + + + Benutzung ALLER MODULE für den Benutzer {0} verboten. + + + Benutzung ALLER MODULE für den Benutzer {0} erlaubt. + + + {0} mit ID {1} wurde zur Sperrliste hinzugefügt + + + Befehl {0} hat nun {1}s Abklingzeit + + + Befehl {0} hat keine Abklingzeit mehr und alle laufenden Abklingzeiten wurden entfernt. + + + Keine Befehls Abklingzeiten gesetzt. + + + Preis für Befehle + + + Benutzung von {0} {1} wurde für Kanal {2} verboten. + + + Benutzung von {0} {1} wurde für Kanal {2} erlaubt. + + + Verweigert + + + Wort {0} zu der Liste der gefilterten Wörter hinzugefügt. + + + Liste der gefilterten Wörter + + + Wort {0} von der Liste der gefilterten Wörter entfernt. + + + Ungültiger zweiter Parameter. (Muss eine Nummer zwischen {0} und {1} sein) + + + Filterung von Einladungen auf diesem Kanal deaktiviert. + + + Filterung von Einladungen auf diesem Kanal aktiviert. + + + Filterung von Einladungen auf diesem Server deaktiviert. + + + Filterung von Einladungen auf diesem Server aktiviert. + + + Berechtigung {0} von #{1} zu #{2} gesetzt + + + Konnte Berechting an Index #{0} nicht finden + + + Kein Preis gesetzt. + + + Befehl + Gen (of command) + + + Modul + Gen. (of module) + + + Berechtigungen Seite {0} + + + Aktuelle Berechtigungsrolle ist {0}. + + + Benutzer brauchen nun Rolle {0} um die Berechtigungen zu editieren. + + + Keine Berechtigung für diesen Index gefunden + + + Berechtigung #{0} - {1} entfernt + + + Benutzung von {0} {1} wurde für Rolle {2} verboten. + + + Benutzung von {0} {1} wurde für Rolle {2} erlaubt. + + + sek. + Short of seconds. + + + Benutzung von {0} {1} wurde für diesen Server verboten + + + Benutzung von {0} {1} wurde für diesen Server erlaubt. + + + {0} mit ID {1} von Sperrliste entfernt + + + Nicht Bearbeitbar + + + Benutzung von {0} {1} wurde für Benutzer {2} verboten. + + + Benutzung von {0} {1} wurde für Benutzer {2} erlaubt. + + + Ich werde nicht mehr Warnungen für Rechte anzeigen. + + + Ich werde jetzt Warnungen für Rechte anzeigen. + + + Filterung für Wörter in diesem Kanal deaktiviert. + + + Filterung für Wörter in diesem Kanal aktiviert. + + + Filterung für Wörter auf diesem Server deaktiviert. + + + Filterung für Wörter auf diesem Server aktiviert. + + + Fähigkeiten + + + Keine favoritisierten anime + + + Startete die Automatische Übersetzung der Nachrichten auf diesem kanal. Nachrichten von Benutzern werden automatisch gelöscht. + + + Ihre Automatische-Übersetzungs Sprache wurde entfernt. + + + Ihre Automatische-Übersetzungs Sprache wurde zu {from}>{to} gesetzt. + + + Automatische Übersetzung der Nachrichten wurde auf diesem kanal gestartet. + + + Automatische Übersetzung der Nachrichten wurde auf diesem kanal gestoppt. + + + Schlechter Eingabeformat, oder etwas lief schief. + + + Konnte diese Karte nicht finden + + + fakt + + + Kapitel + + + Comic # + + + Verlorene Competitives + + + Gespielte Competitives + + + Competitive Rang + + + Gewonnene Competitives + + + Beendet + + + Kondition + + + Kosten + + + Datum + + + Definiere: + + + Abgebrochen + + + Episoden + + + Ein fehler ist aufgetreten. + + + Beispiel + + + Konnte diesen animu nicht finden. + + + Konnte diesen mango nicht finden. + + + Genres + + + Konnte keine definition für dieses Stichwort finden. + + + Höhe/Gewicht + + + {0}m/{1]kg + + + Feuchtigkeit + + + Bildersuche für: + + + Konnte diesen Film nicht finden. + + + Ungültige Quell- oder Zielsprache, + + + Witze nicht geladen. + + + Lat/long + + + Level + + + Liste der "{0}place" Stichwörtern + Don't translate {0}place + + + Standort + + + Magic Items nicht geladen. + + + {0}s MAL Profil + + + Der Besitzer des Bots hat keinen MashapeApi-Schlüssel angegeben. Sie können diese Funktion nicht benutzen. + + + Min/Max + + + Keine Kanäle gefunden. + + + Keine Ergebnisse gefunden. + + + Pausierte + + + Ursprüngliche Url + + + Ein osu! API-Schlüssel wird benötigt. + + + osu! Signatur konnte nicht geholt werden. + + + Über {0} Bilder gefunden. Zeige zufälliges {0}. + + + Benutzer nicht gefunden! Bitte überprüfe die Region und den BattleTag before erneuten versuchen. + + + Vermerkte + + + Platform + + + Keine Fähigkeit gefunden. + + + Kein pokemon gefunden. + + + Profil Link: + + + Qualität + + + Quick Spielzeit + + + Gewonnene Quicks + + + Bewertung + + + Punktzahl: + + + Suche nach: + + + Url konnte nicht gekürzt werden + + + Kurze Url + + + Etwas lief schief. + + + Bitte spezifizieren sie die Such Parameter. + + + Status + + + Geschäfts Url + + + Streamer {0} ist jetzt offline. + + + Streamer {0} ist online mit {1} Zuschauern. + + + Sie folgen {0} Streamer auf diesem Server. + + + Sie folgen keinem Streamer auf diesem Server. + + + Diesen Stream gibt es nicht. + + + Stream existiert wahrscheinlich nicht. + + + {0}s Stream ({1}) von den Benachrichtigungen entfernt. + + + Ich werde diesen Kanal benachrichtigen wenn der Status sich verändert. + + + Sonnenaufgang + + + Sonnenuntergang + + + Temperature + + + Titel: + + + Top 3 Lieblingsanime: + + + Übersetzung: + + + Typen + + + Die definition für den Begriff konnte nicht gefunden werden. + + + Url + + + Zuschauer + + + Am schauen + + + Der Begriff konnte auf dem spezifizierten wikia nicht gefunden werden. + + + Bitte geben sie ein Ziel wikia ein, gefolgt bei der Suchanfrage. + + + Seite konnte nicht gefunden werden. + + + Wind Geschwindigkeit + + + Die {0} meist gebannten Champions + + + Ihre Nachricht konnte nicht yodifiziert werden. + + + Beigetreten + + + `{0}.` {1} [{2:F2}/s] - {3} total + /s and total need to be localized to fit the context - +`1.` + + + Aktivitäten Liste #{0} + + + {0} totale Benutzer. + + + Autor(in) + + + ID des Bots + + + Liste der Funktionen im {0}calc Befehl + + + {0} dieses Kanals ist {1} + + + Thema des Kanals + + + Befehl ausgeführt + + + {0} {1} ist gleich zu {2} {3} + + + Einhetein die von dem Konvertierer benutzt werden können + + + Kann {0} nicht zu {1} konvertieren: Einheiten nicht gefunden + + + Kann {0} nicht zu {1} konvertieren: Einheiten sind nicht gleich + + + Erstellt am + + + Betritt Multi-Server-Kanal. + + + Verließ Multi-Server-Kanal. + + + Dies ist ihr MSK token + + + Benutzerdefinierte Emojis + + + Fehler + + + Funktionalitäten + + + ID + + + Index außer Reichweite. + + + Hier ist eine Liste mit Nutzern in diesen Rollen: + + + Sie haben keine Berechtigung diesen Befehl auf Rollen mit vielen Nutzern zu benutzen um Missbrauch zu verhindern. + + + Ungültiger {0} Wert. + Invalid months value/ Invalid hours value + + + Discord beigetreten + + + Server beigetreten + + + ID: {0} +Mitglieder: {1} +ID des Besitzers: {2} + + + Keine Server auf dieser Seite gefunden. + + + Liste der Wiederholer + + + Mitglieder + + + Speicher + + + Nachrichten + + + Nachrichten Wiederholer + + + Name + + + Nickname + + + Niemand spielt dieses Spiel. + + + Keine aktiven Wiederholer + + + Keine Rollen auf dieser Seite. + + + Keine Shards auf dieser Seite. + + + Kein Thema gesetzt. + + + Besitzer + + + IDs der Besitzer + + + Anwesenheit + + + {0} Server +{1} Text Kanäle +{2} Sprach Kanäle + + + Alle Zitate mit dem Stichwort {0} wurden gelöscht. + + + Seite {0} der Zitate + + + Keine Zitate auf dieser Seite. + + + Kein Zitat das sie entfernen können gefunden. + + + Zitat hinzugefügt + + + Zufälliger Zitat wurde gelöscht. + + + Region + + + Registriert an + + + Ich werde {0} erinnern {1} in {2} `({3:d.M.yyyy.} um {4:HH:mm})` zu tun. + + + Kein gültiges Zeitformat. Überprüfe die Befehlsliste. + + + Neue Erinnerungs Vorlage wurde gesetzt. + + + {0} wird jede {1} Tag(e), {2}stunde(n) und {3} minute(n) wiederholt. + + + Liste der Wiederholungen + + + Auf diesem Server laufen keine Wiederholer. + + + #{0} wurde gestoppt. + + + Auf diesem Server wurden keine wiederholende Nachrichten gefunden. + + + Resultat + + + Rollen + + + Seite #{0} aller Rollen für diesen Server: + + + Seite #{0} der Rollen für {1} + + + Keine Farben sind in dem Richtigen Format. Benutze zum Beispiel `#00ff00`. + + + Startete die Farbrotation für Rolle {0} + + + Stoppte die Farbrotation für Rolle {0} + + + {0} dieses Servers ist {1} + + + Server Info + + + Shard + + + Shard Statistiken + + + Shard **#{0}** ist im {1} status mit {2} Servern + + + **Name:** {0} **Link:** {1} + + + Keine speziellen emoji gefunden. + + + Wiedergabe von {0} Liedern, {1} in der Musikliste. + + + Text Kanäle + + + Hier ist ihr Raum link + + + Betriebszeit + + + {0} von Benutzer {1} ist {2} + Id of the user kwoth#1234 is 123123123123 + + + Benutzer + + + Sprach Kanäle + + + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 5485e9ef..3dfd8571 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -480,13 +480,13 @@ Reason: {1} Bot's default locale is now {0} - {1} - Bot's language is set to {0} - {0} + Bot's language is set to {0} - {1} Failed setting locale. Revisit this command's help. - This server's language is set to {0} - {0} + This server's language is set to {0} - {1} {0} has left {1} @@ -822,7 +822,7 @@ Reason: {1} {0} has been **voice unmuted**. - Voice Channel Destroyed + Voice Channel Created Voice Channel Destroyed @@ -1074,7 +1074,7 @@ Don't forget to leave your discord name or id in the message. {0} as {1} Won the race and {2}! - Invalid number specified. You can roll up to {0}-{1} dice at a time. + Invalid number specified. You can roll {0}-{1} dice at once. rolled {0} @@ -1763,7 +1763,7 @@ Don't forget to leave your discord name or id in the message. Level - Lsit of {0}place tags + List of {0}place tags Don't translate {0}place @@ -1797,7 +1797,7 @@ Don't forget to leave your discord name or id in the message. An osu! API key is required. - Failed retreiving osu signature. + Failed retrieving osu! signature. Found over {0} images. Showing random {0}. @@ -2134,7 +2134,7 @@ OwnerID: {2} Page #{0} of all roles on this server: - Page #{0} of roels for {1} + Page #{0} of roles for {1} No colors are in the correct format. Use `#00ff00` for example. From 31974baa13dffd9c5ae206d0a343c9db6903de26 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 28 Feb 2017 21:46:06 +0100 Subject: [PATCH 221/256] added russian --- .../Resources/ResponseStrings.ru-RU.resx | 2195 +++++++++++++++++ 1 file changed, 2195 insertions(+) create mode 100644 src/NadekoBot/Resources/ResponseStrings.ru-RU.resx diff --git a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx new file mode 100644 index 00000000..f6729bb5 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx @@ -0,0 +1,2195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + + + + + + + + + + + + + + + + + + + + + Враг + + + Информация о войне против {0} + + + + + + + + + Список активных войн + + + + + + + + + + + + + + + Размер + + + + + + Война против {0} была создана. + Fuzzy + + + Закончилась война против {0}. + + + Эта война не существует. + + + + + + Вся статистика настраиваемых реакций стёрта. + + + Настраиваемая реакция удалена. + Fuzzy + + + Недостаточно прав. Необходимо владеть Бот-ом для глобальных настраиваемых реакций или быть Администратором для реакций по серверу. + Fuzzy + + + Список всех настраиваемых реакций. + Fuzzy + + + Настроить реакции. + Fuzzy + + + Создана новая реакция. + Fuzzy + + + + + + + + + Ответ + + + + + + + + + + + + Активатор + + + Авто-хентай остановлен :( + + + Запрос не найден. + + + {0} уже потерял сознание. + + + {0} уже имеет полное здоровье. + + + Ваш тип уже {0} + + + использовал {0}{1} против {2}{3} и нанёс {4} урона. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Нельзя атаковать два раза подряд. + + + Нельзя атаковать самого себя. + + + {0} потерял сознание! + + + вылечил {0}, использовав {1} + + + У {0} осталось {1} здоровья. + + + Вы не можете использовать {0}. Напишите '{1}ml', чтобы увидеть список доступных Вам приёмов. + + + Список приёмов {0} типа + + + Эта атака не эффективна. + + + У вас не достаточно {0} + + + воскресил {0}, использовав один {1} + + + Вы воскресили себя, использовав один {0} + + + Ваш тип изменён с {0} на {1} + + + Эта атака немного эффективна. + + + Эта атака очень эффективна! + + + Вы использовали слишком много приёмов подряд и не можете двигаться! + + + Тип {0} — {1} + + + Пользователь не найден. + + + Вы не можете использовать приёмы потому-что ваш покемон потерял сознание! + + + ***Авто выдача роли*** каждому новому пользователю **отключена** + + + ***Авто выдача роли*** каждому новому пользователю **включена** + + + Приложения + + + Аватар изменён + + + Вас забанили с сервера {0}. Причина бана: {1}. + + + забанены + PLURAL + + + Пользователь забанен. + + + Имя Бот-а сменено на {0} + + + Статус Бот-а сменён на {0} + + + Автоматическое удаление прощальных сообщений отключено. + + + Прощальные сообщения будут удаляться через {0} секунд. + + + Текущее прощальное сообщение: {0} + + + Чтобы включить прощальные сообщения введите {0} + + + Установлено новое прощальное сообщение. + + + Прощальные сообщения выключены. + + + Прощальные сообщения включены на этом канале. + + + Имя канал изменено. + + + Старое имя. + + + Тема канала сменена. + + + + + + + + + Успешно создана роль {0}. + + + Создан текстовый канал {0}. + + + Создан голосовой канал {0}. + + + Успешное оглушение. + Fuzzy + + + Сервер {0} удален. + + + Отключено автоматическое удаление успешно выполненных команд. + + + Включено автоматическое удаление успешно выполненных команд. + + + Удалён текстовый канал {0}. + + + Удален голосовой канал {0}. + + + ПМ от + + + Успешно добавлен новый донатор. Общее количество пожертвований от этого пользователя: {0} 👑 + Fuzzy + + + Спасибо всем, указанным ниже, что помогли этому проекту! + + + Я буду перенаправлять личные сообщения всем владельцам. + + + Я буду перенаправлять личные сообщения только первому владельцу. + + + Я буду перенаправлять личные сообщения. + + + Я прекращаю перенаправление личных сообщений. + + + Автоматическое удаление приветственных сообщений выключено. + + + Приветственные сообщения будут удаляться через {0} секунд. + + + Приветствие, используемое в настоящий момент: {0} + + + Чтобы включить приветствия, напишите {0} + + + Новое приветствие установлено. + + + Приветствия в личных сообщениях отключены. + + + Приветствия в личных сообщениях включены. + + + Текущее привественное сообщение: {0} + + + Чтобы включить привественные сообщения введите {0} + + + Установлено новое приветствие. + + + Привественные сообщения выключены. + + + Привественные сообщения включены на этом канале. + + + Вы не можете использовать эту команду на пользователях равным или более высоким в иерархии ролей. + + + Картинки загружены за {0} секунд! + + + Неправильный формат ввода. + + + Неправильные параметры. + + + {0} присоединился к {1} + + + Вы были выгнаны с сервера {0}. +По причине: {1} + + + Пользователь выгнан + + + Список языков +{0} + + + Язык вашего сервера теперь {0} - {1} + + + Язык Бот-а по умолчанию теперь {0} - {1} + + + Язык Бот-а теперь установлен как {0} - {1} + + + Не удалось выставить язык. Проверьте справку к этой команде. + + + Язык этого сервера теперь установлен как {00} - {1} + + + {0} покинул {1} + + + Покинул сервер {0} + + + В этом канале регистрируется событие {0}. + + + В этом канале регистрируются все события. + + + Регистрация событий отключена. + + + Регистрируйте события, на которые Вы можете подписаться: + + + Регистрация будет пропускать {0}. + + + Регистрация не будет пропускать {0}. + + + Прекращена регистрация события {0}. + + + {0} вызвал оповещение для следующих ролей + + + Сообщение от {0} '[Владелец бота]': + + + Сообщение отправлено. + + + {0} перемещён из {1} в {2} + + + Сообщение удалено в #{0} + + + Сообщение изменено в #{0} + + + Заглушёны + PLURAL (users have been muted) + + + Заглушён + singular "User muted." + + + Скорее всего, у меня нет необходимых прав. + + + Новая немая роль установлена. + Fuzzy + + + Мне нужно право **Администратор**, чтобы это сделать. + Fuzzy + + + Новое сообщение + + + Новое имя + + + Новый заголовок + + + Имя изменено + + + Сервер не найден + + + Не найдено Shard-а с таким ID. + + + Старое сообщение + + + Старое имя + + + Старый заголовок + + + Ошибка. Скорее всего мне не хватает прав. + + + Права для этого сервера + + + + + + {0} был **отключён** на этом сервере. + + + {0} влючён + + + Ошибка. Требуется право на управление ролями. + + + + + + Порог пользователей должен лежать между {0} и {1}. + + + Если {0} или больше пользователей присоединяются в течение {1} секунд, Я {2} их. + + + Время должно быть между {0} и {1} секунд. + + + Успешно убраны все роли пользователя {0}. + + + Не удалось убрать роли. Отсутвуют требуемые разрешения. + + + Цвет роли {0} был изменён. + + + Данная роль не существует. + + + Заданные параметры недействительны. + + + Возникла ошибка из-за недействительного цвета или недостаточных разрешений. + + + Успешно убрана роль {0} пользователя {1}. + + + Не удалось убрать роль. Отсутвуют требуемые разрешения. + + + Роль переименована. + + + Не получилось переименовать роль. У меня недостаточно прав. + + + Вы не можете редактировать роли, находящиеся выше чем ваша роль. + + + + + + Роль {0} добавлена в лист. + + + + + + Роль {0} уже есть в списке. + + + Добавлено. + + + + + + + + + + + + + + + У вас уже есть роль {0} + + + У Вас уже есть исключающая самоназначенная роль {0}. + + + Самоназначенные роли теперь взаимоисключающие! + + + Существует {0} самоназначенных ролей + + + Эта роль не является самоназначаемой. + + + У вас отсуствует роль {0} + + + Самоназначенные роли теперь не взаимоисключающие! + + + Не удалось добавить Вам эту роль. 'Нельзя добавлять роли владельцам или другим ролям, находящимся выше моей роли в ролевой иерархии' + + + {0} убрана из списка самоназначенных ролей. + + + У вас больше нету роли {0} + + + Теперь у вас есть роль {0} + + + Успешно добавлена роль {0} пользователю {1} + + + Не удалось добавить роль. Нет достаточных разрешений. + + + Новый аватар установлен! + + + Новое имя канала установлено. + + + Новая игра установлена! + + + Новый стрим установлен! + + + Новая тема канала установлена. + + + Shard {0} переподключён. + + + Shard {0} переподключается. + + + Выключение + + + Пользователи не могут посылать более {0} сообщений в {1} секунд. + + + Медленный режим выключен. + + + Медленный режим включен. + + + + PLURAL + + + {0} будет игнорировать данный канал. + + + {0} не будет игнорировать данный канал. + + + Если пользователь пишет {0} одинаковых сообщений подряд, я {} их. __Игнорируемые каналы__: {2} + + + Создан текстовый канал + + + Уничтожен текстовый канал. + + + Отключено заглушение. + + + + singular + + + Имя + + + Имя изменено + + + Пользователи + + + Пользователь заблокирован + + + + + + + + + Пользователь присоединился + + + Пользователь вышел + + + + + + Добавлена роль пользователя + + + Удалена роль пользователя + + + {0} теперь {1} + + + + + + {0} присоединился к голосовому каналу {1}. + + + {0} покинул голосовой канал {1}. + + + {0} переместил {1} в голосовой канал {2}. + + + + + + + + + Голосовой канал удалён + Fuzzy + + + Голосовой канал удалён + + + + + + + + + Нет разрешений **Управление ролями** и/или **Управление каналами**, поэтому нельзя использовать команду 'voice+text' на сервере {0}. + + + Вы пытаетесь включить/отключить это свойство и **отсутвует разрешение АДМИНИСТРАТОР**. Это может вызвать ошибки, и Вам придётся удалять текст в текстовых каналах самостоятельно. + + + Для этого свойства требуются как минимум разрешения **управление ролями** и **управление каналами**. (Рекомендуется разрешение Администратор) + + + Пользователь {0} в текстовом чате + + + Пользователь {0} в текстовом и голосовом чатах + + + Пользовать {0} в голосовом чате + + + Вас забанили на сервере {0}. Причина: {1} + + + Пользователь разбанен. + + + Перемещение закончено! + + + Ошибка при переносе файлов, проверьте консоль бота для получения дальнейшей информации. + + + + + + + + + наградил {0} пользователю {1} + + + + + + В следующий раз повезёт ^_^ + + + Поздравляем! Вы выиграли {0}, так как выбросили больше {1} + + + Колода перетасована. + + + подброшено {0} + User flipped tails. + + + Вы угадали! Вы выиграли {0} + + + Указано неверное число. Вы можете подбросить от 1 до {0} монет. + + + Добавьте {0}, как реакцию к этому сообщению, чтобы получить {1}␣ + + + Это событие активно в течение не более {0} часов. + + + Событие получения цветов началось! + + + подарил {0} {1} + X has gifted 15 flowers to Y + + + У {0} есть {1} + X has Y flowers + + + Орёл + + + Таблица рекордов + + + {1} пользователей c ролью {2} награждены {0}. + + + Вы не можете поставить больше {0} + + + Вы не можете поставить меньше {0} + + + У Вас недостаточно {0} + + + В колоде закончились карты. + + + + + + + + + + + + НИЧЕГО СЕБЕ!!! Поздраляем!!! x{0} + + + Один {0}, x{1} + + + Вот это повезло! Тройка! x{0} + + + Молодец! Две {} - ставка x{1} + + + Выиграл + + + Пользователи могут ввести секретный код, чтобы получить {0}. Длится {1} секунд. Никому не говори! + + + Закончилось событие SneakyGame. {0} пользователей получили награду. + + + Началось событие SneakyGameStatus + + + Решка + + + успешно забрал {0} у {1} + + + не смог забрать {0} у {1}, поскольку у пользователя нет столько {2}! + + + Вернуться к содержанию + + + Только для владельца бота + + + Требуется разрешение канала {0}. + + + Вы можете поддержать проект в patreon: <{0}> или через paypal: <{1}> + + + Команды и альтернативные имена команд + + + Список команд создан. + + + Напишите '{0}h ИмяКоманды', чтобы получить справку для этой команды. Например, '{0}h >8ball' + + + Эта команда не найдена. Пожалуйста, убедитесь, что команда существует. + + + Описание + + + Вы можете поддержать проект NadekoBot в +Patreon <{0}> или +Paypal <{1}> +Не забудьте оставить ваше имя в Discord или id в Вашем сообщении. + +**Спасибо** ♥️ + + + **Список команд**: <{0}> +**Руководства по установке и документы можно найти здесь**: <{1}> + + + Список команд + + + Список модулей + + + Напишите '{0}cmds ИмяМодуля', чтобы получить список команд в этом модуле. Например, '{0}cmds games' + + + Этот модуль не существует + + + Требуются серверное право {0} + + + Содержание + + + Использование + + + + + + Тэг + + + Гонка зверей + + + Не удалось начать гонку, так как не хватает участников. + + + В гонке не осталось мест! Гонка начинается. + + + {0} присоединился в роли {1} + + + {0} присоединился в роли {1} и сделал ставку {2}! + + + Напишите {0}jr, чтобы присоединиться к гонке. + + + Гонка начнётся через 20 секунд или когда все места будут заняты. + + + Гонка началась с {0} участниками. + + + {0} в роли {1} победил в гонке! + + + {0} в роли {1} победил в гонке и получил {2}! + + + Задано неправильное число. Можно бросить {0}-{1} костей одновременно. + + + + Someone rolled 35 + + + Брошено {0} костей. + Dice Rolled: 5 + + + Не удалось начать гонку. Другая гонка уже идёт. + + + На данном сервере не идёт гонка. + + + Второе число должно быть больше первого. + + + + + + + + + Разводы + + + Нравится + + + Цена + + + Ни одной вайфу ещё не забрали + + + Рейтинг вайфу + + + Ваша предрасположенность уже установлена для этой вайфу или Вы пытаесь убрать предрасположенность к вайфу, которой нет. + + + сменил свою предрасположенность с {0} на {1}. + +*Это сомнительно с точки зрения морали* :thinking: + Make sure to get the formatting right, and leave the thinking emoji + + + Вам нужно подождать {0} часов и {1} минут перед тем, как опять менять Вашу предрасположенность. + + + Ваша предрасположенность сброшена. У Вас больше нет человека, который Вам нравится. + + + хочет быть вайфу {0}. Как мило <3 + + + объявил {0} своей вайфу за {1}! + + + Вы развелись с вайфу, которой Вы нравитесь. Вы бессердечный прохиндей! {0} получил {1} в качестве компенсации. + + + Вы не можете установить предрасположенность к самому себе, это чересчур эгоистичсно. + + + 🎉Их любовь нашла взаимность!🎉 +Новое значение {0} — {1}. + + + Не бывает таких дешёвых вайфу. Вам нужно заплатить как минимум {0}, чтобы получить вайфу, даже если их фактическая стоимость ниже этого значения. + + + Вам нужно заплатить {0} или больше, чтобы получить эту вайфу! + + + Эта вайфу — не Ваша. + + + Вы не можете потребовать себя в вайфу. + + + Вы недавно развелись. Нужно подождать {0} часов и {1} минут, если хотите снова развестись. + + + Никто + + + Вы развелись с вайфу, которой Вы не нравились. Вам вернули {0}. + + + + + + Акрофобия. + + + Игра закончилась без ответов. + + + Никто не проголосовал. Игра закончилась без победителся. + + + Акроним был {0}. + + + В этом канале уже идёт игра Акрофобии. + + + Игра начинается. Составьте предложение со следующим акронимом: {0}. + + + У Вас есть {0} секунд, чтобы предложить ответ. + + + {0} предложил своё предложение. ({1} в общей сложности) + + + Чтобы проголосовать, напишите номер ответа. + + + {0} проголосовал! + + + Победитель — {0} с {1} очками. + + + {0} — победитель, так как только он привёл ответ! + + + Вопрос + + + Ничья! Оба игрока выбрали {0} + + + {0} выиграл! {1} побеждает {2} + + + Приём ответов закончен. + + + Гонка зверей уже идёт. + + + Итого: {0} Среднее: {1} + + + Категория + + + На этом сервере отключён cleverbot. + + + На этом сервере включён cleverbot. + + + В этом канале отключено появление валюты. + + + В этом канале включено появление валюты. + + + {0} случайных {1} появились! Напишите '{2}pick', чтобы собрать их. + plural + + + Случайный {0} появился! Напишите '{1}pick', чтобы собрать его. + + + Не удалось загрузить вопрос. + + + Игра началась + + + Игра в Виселицу началась + + + Игра в Виселицу уже идёт в этом канале. + + + Не удалось начать игру в Виселицу. + + + Список типов слов для "{0}hangman": + + + Таблица рекордов + + + У вас не хватает {0} + + + Нет результатов + + + собрал {0} + Kwoth picked 5* + + + {0} посадил {1} + Kwoth planted 5* + + + Викторина уже идёт на этом сервере. + + + Викторина + + + {0} угадал! Ответ: {1} + + + На этом сервере не идёт викторина. + + + У {0} {1} очков. + + + Игра закончится после этого вопроса. + + + Время вышло! Правильный ответ — {0} + + + {0} угадал и ВЫИГРАЛ в игре! Ответ: {0} + + + Вы не можете играть против самого себя. + + + В этом канале уже идёт игра в крестики-нолики. + + + Ничья! + + + создал игру в крестики-нолики. + + + {0} выиграл! + + + Выстроил 3 в ряд + + + Ходов не осталось! + + + Время вышло! + + + Ход {0} + + + {0} против {1} + + + Пытаюсь добавить {0} песен в очередь... + + + Автовоспроизведение отключено. + + + Автовоспроизведение включено. + + + Громкость по умолчанию выставлена на {0}% + + + Папка успешно добавлена в очередь воспроизведения. + + + + + + Песня завершилась. + + + + + + + + + С момента + + + Имя + + + Неправильный ввод. + + + Максимальное время воспроизведения теперь неограничено. + + + Максимальное время воспроизведения установлено на {0} секунд. + + + Максимальный размер очереди воспроизведения теперь неограничен. + + + Максимальный размер очереди воспроизведения установлен на {0} песен. + + + Вам требуется быть в голосовом канале на этом сервере. + + + Название + + + Сейчас играет + + + Нет активного музыкального проигрывателя. + + + Нет результатов поиска. + + + Проигрывание музыки приостановлено. + + + Очередь воспроизведения - Страница {0}/{1} + + + Проигрывается песня + + + '#{0}' - **{1}** *{2}* ({3} песен) + + + Страница {0} сохранённых плейлистов. + + + Плейлист удалён. + + + Не удалось удалить плейлист. Он либо не существует, либо Вы не его автор. + + + Плейлист с таким ID не существует. + + + Добавление плейлиста в очередь завершено. + + + Плейлист сохранён. + + + Ограничение {0}c + + + Очередь воспроизведения + + + Песня добавлена в очередь воспроизведения. + + + Очередь воспроизведения музыки очищена. + + + Очередь воспроизведения полна {0}/{0}. + + + Убрана песня + context: "removed song #5" + + + Повторяется текущая песня. + + + Повторяется плейлист. + + + Повторяется песня. + + + Повтор текущей песни приостановлен. + + + Воспроизведение музыки возобновлено. + + + Отключено повторение плейлиста. + + + Включено повторение плейлиста. + + + Проигрываемые, завершённые, приостановленные и удалённые песни будут выводится в этом канале. + + + Пропускаю до '{0}:{1}' + + + Песни перемешаны. + + + Песня перемещена. + + + {0}ч {1}м {2}с + + + К моменту + + + неограничено + + + Уровень громкости должен быть от 0 до 100 + + + Уровень громкости установлен на {0}% + + + Отключено использование ВСЕХ МОДУЛЕЙ в канале {0}. + + + Включено использование ВСЕХ МОДУЛЕЙ в канале {0}. + + + Разрешено + + + Отключено использование ВСЕХ МОДУЛЕЙ для роли {0}. + + + Включено использование ВСЕХ МОДУЛЕЙ для роли {0}. + + + Отключено использование ВСЕХ МОДУЛЕЙ на этом сервере. + + + Включено использование ВСЕХ МОДУЛЕЙ на этом сервере. + + + Отключено использование ВСЕХ МОДУЛЕЙ для пользователя {0}. + + + Включено использование ВСЕХ МОДУЛЕЙ для пользователя {0}. + + + Добавлено {0} в чёрный список c ID {1} + + + У команды {0} теперь есть время перезарядки {1}c + + + У команды {0} больше нет времени перезарядки и все существующие времена перезадки были сброшены. + + + У команды не установлено время перезарядки. + + + Стоимость команды + + + Отключено использование {0} {1} в канале {2} + + + Включено использование {0} {1} в канале {2} + + + Отказано + + + Слово {0} добавлено в список фильтруемых слов. + + + Список фильтруемых слов + + + Слово {0} убрано из списка фильтруемых слов. + + + Неправильный второй параметр. (Должно быть числом от {0} до {1}) + + + Отключена фильтрация приглашений в этом канале. + + + Включена фильтрация приглашений в этом канале. + + + Отключена фильтрация приглашений в этом сервере. + + + Включена фильтрация приглашений в этом сервере. + + + Передано право {0} с #{1} to #{2} + + + Не найдено право с номером #{0} + + + Стоимостой не установлено + + + команда + Gen (of command) + + + модуль + Gen. (of module) + + + Страница прав {0} + + + Текущая роль прав — {0} + + + Пользователям требуется роль {0} для редактирования прав. + + + Не найдено прав с таким номером. + + + Удалены права #{0} - {1} + + + Отключено использование {0} {1} для роли {2}. + + + Включено использование {0} {1} для роли {2}. + + + сек. + Short of seconds. + + + Отключено использование {0} {1} для данного сервера. + + + Включено использование {0} {1} для данного сервера. + + + {0} с ID {1} убраны из черного списка. + + + нередактируемое + + + Отключено использование {0} {1} для пользователя {2}. + + + Включено использование {0} {1} для пользователя {2}. + + + Оповещения о правах больше не будут показываться в чате. + + + Оповещения о правах будут показываться в чате. + + + В данном канале отключена фильтрация слов. + + + В данном канале включена фильтрация слов. + + + На данном сервере оключена фильтрация слов. + + + На данном сервере включена фильтрация слов. + + + Способности + + + Нет любимого аниме + + + Начинается автоматический перевод сообщений в этом канале. Сообщения пользователей будут автоматически удаляться. + + + Ваш язык автоперевода был удалён. + + + Ваш язык автоперевода изменён с {from} на {to} + + + Начинается автоматический перевод сообщений в этом канале. + + + Остановлен автоматический перевод сообщений в этом канале. + + + Неправильный формат ввода, что-то пошло не так. + + + Эта карта не найдена. + + + факт + + + Главы + + + Комикс # + + + Поражения в соревновательном режиме + + + Матчи в соревновательном режиме + + + Соревновательный ранг + + + Победы в соревновательном режиме + + + Завершено + + + Условие + + + Стоимость + + + Дата + + + Определить: + + + + + + Эпизоды + + + Произошла ошибка + + + Образец + + + Не удалось найти это аниму. + + + Не удалось найти это манго. + + + Жанры + + + Не удалось найти определение для этого тэга. + + + Высота/Вес + + + {0}м/{1}кг + + + Влажность + + + Поиск изображений: + + + Не удалос найти этот фильм. + + + Неправильный источник или целевой язык. + + + Шутки не были загружены. + + + Шир/Долг + + + Уровень + + + Список тэгов для команды {0}place + Don't translate {0}place + + + Местоположение + + + Волшебные предметы не были загружены. + + + Профиль в MAL {0} + + + Владелец бота не задал MashapeApiKey. Вы не можете использовать эту функцию. + + + Мин/Макс + + + Каналов не найдено. + + + Результаты не найдены. + + + Ожидание + + + Оригинальный URL + + + Требуется ключ osu! API. + + + Не удалось получить подпись osu! + + + Найдено больше {0} изображений. Показывается случайные {0} изображений. + + + Пользователь не найден! Проверьте регион и BattleTag и попробуйте ещё раз. + + + Планирует смотреть + + + Платформа + + + Способность не найдена + + + Покемон не найден + + + Ссылка на профиль: + + + Качество: + + + + Is this supposed to be Overwatch Quick Play stats? + + + + + + Рейтинг: + + + Отметка: + + + Искать: + + + Не удалось укоротить эту ссылку. + + + Короткая ссылка + + + Что-то пошло не так. + + + Пожалуйста, задайте параметры поиска. + + + Состояние + + + + + + Стример {0} в оффлане. + + + Стример {0} в онлайне с {1} зрителями. + + + Вы подписаны на {0} стримов на этом сервере. + + + Вы не подписаны ни на один стрим на этом сервере. + + + Такого стрима не существует. + + + Скорее всего, этот стрим не существует. + + + Стрим {0} ({1}) убран из оповещений. + + + + + + Рассвет + + + Закат + + + Температура + + + Название: + + + 3 любимых аниме: + + + Перевод: + + + Типы: + + + Не удалось найти определение для этого запроса. + + + Url + + + Зрители + + + Смотрят + + + Не удалось найти этот термин на указаной вики. + + + Укажите целевую вики и после этого поисковый запрос. + + + Страница не найдена. + + + Скорость ветра. + + + {0} наиболее часто забаненных чемпионов. + + + Предложение, как у Йоды, не получилось сделать. + + + Присоединился + + + '{0}.' {1} [{2:F2}/с] - {3} всего + /s and total need to be localized to fit the context - +`1.` + + + + + + Всего {0} пользователей. + + + Автор + + + ID бота + + + Список функций команды {0}calc + + + {0} этого канала — {1} + + + Тема канала + + + Команд запущено + + + {0} {1} равно {2} {3} + + + Единицы, которые можно использовать в конвертировании + + + Нельзя перевести {0} в {1}: единицы измерения не найдены + + + Нельзя перевести {0} в {1}: единицы измерения не эквивалентны. + + + Создано + + + Присоедился к межсерверному каналу. + + + Покинул межсерверный канал. + + + Ваш CSC токен: + + + Серверные emoji + + + Ошибка + + + Признаки + + + Имя + + + Указатель вышел за пределы диапазона. + + + Список пользователей с этими ролями: + + + Вам запрещено использовать эту комманду в отношении ролей с большим числом пользователей для предотвращения + + + + Invalid months value/ Invalid hours value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {0} Серверов +{1} Текстовых каналов +{2} Голосовых каналов + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Id of the user kwoth#1234 is 123123123123 + + + + + + + + + \ No newline at end of file From 5f8982d4fb643d73b66d1c732c4f0697d02d5a4e Mon Sep 17 00:00:00 2001 From: Kwoth Date: Tue, 28 Feb 2017 21:47:50 +0100 Subject: [PATCH 222/256] Added german and russian to list of supported languages, russian is unfinished atm btw --- .../Modules/Administration/Commands/LocalizationCommands.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 8250bfde..641f90e8 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -18,8 +18,10 @@ namespace NadekoBot.Modules.Administration { private ImmutableDictionary supportedLocales { get; } = new Dictionary() { - {"en-US", "English, United States" }, - {"fr-FR", "French, France" } + {"en-US", "English, United States"}, + {"fr-FR", "French, France"}, + {"ru-RU", "Russian, Russia"}, + {"de-DE", "German, Germany"} //{"sr-cyrl-rs", "Serbian, Cyrillic" } }.ToImmutableDictionary(); From 9485d94a83eb2af501a2bb4008ff34117d90e79e Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 28 Feb 2017 21:50:34 +0100 Subject: [PATCH 223/256] Update ResponseStrings.fr-fr.resx (POEditor.com) --- .../Resources/ResponseStrings.fr-fr.resx | 105 ++++++++++-------- 1 file changed, 56 insertions(+), 49 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx index 5f7a188e..dbce49d8 100644 --- a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -163,7 +163,7 @@ Vous ne participez pas a cette guerre. - @{0} Vous ne pouvez pas participer a cette guerre ou la base a déjà été détruite. + @{0} Vous ne participez pas à cette guerre ou la base a déjà été détruite. Aucune guerre en cours. @@ -206,7 +206,7 @@ Nouvelle réaction personnalisée - Aucune réaction personnalisé trouvée. + Aucune réaction personnalisée trouvée. Aucune réaction personnalisée ne correspond à cet ID. @@ -288,7 +288,7 @@ C'est très efficace ! - Vous avez utilisé trop de mouvement d'affilé, donc ne pouvez plus bouger ! + Vous avez utilisé trop de mouvements d'affilée, vous ne pouvez donc plus bouger! Le type de {0} est {1} @@ -300,10 +300,10 @@ Vous vous êtes évanoui, vous n'êtes donc pas capable de bouger! - **L'affectation automatique du rôle** à l'arrivé d'un nouvel utilisateur est désormais **désactivée**. + **L'affectation automatique de rôle** à l'arrivé d'un nouvel utilisateur est désormais **désactivée**. - **L'affectation automatique du rôle** à l'arrivé d'un nouvel utilisateur est désormais **activée**. + **L'affectation automatique de rôle** à l'arrivé d'un nouvel utilisateur est désormais **activée**. Liens @@ -425,7 +425,7 @@ Raison: {1} Activez les MPs de bienvenue en écrivant {0} - Nouveau MP de bienvenue en service. + Nouveau MP de bienvenue défini. MPs de bienvenue désactivés. @@ -440,7 +440,7 @@ Raison: {1} Activez les messages de bienvenue en écrivant {0} - Nouveau message de bienvenue en service. + Nouveau message de bienvenue défini. Messages de bienvenue désactivés. @@ -471,7 +471,7 @@ Raison : {1} Utilisateur expulsé - Listes des langages + Listes des langues {0} @@ -487,7 +487,7 @@ Raison : {1} Échec dans la tentative de changement de langue. Réessayer avec l'aide pour cette commande. - La langue de ce serveur est {0} - {0} + La langue de ce serveur est {0} - {1} {0} a quitté {1} @@ -568,7 +568,7 @@ Raison : {1} Impossible de trouver ce serveur - Aucun shard avec cet ID trouvé. + Aucune partition pour cet ID trouvée. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -602,7 +602,7 @@ Raison : {1} Aucune protection activée. - Le seuil pour cet utilisateur doit être entre {0} et {1} + Le seuil d'utilisateurs doit être entre {0} et {1} Si {0} ou plus d'utilisateurs rejoignent dans les {1} secondes suivantes, je les {2}. @@ -684,13 +684,14 @@ Raison : {1} Il y a {0} rôles auto-affectés. - Ce rôle ne peux pas vous être attribué par vous même. + Ce rôle ne peux pas vous être attribué par vous-même. Vous ne possédez pas le rôle {0}. - L'affectation automatique des rôles n'est désormais plus exclusive! + Les rôles auto-attribuables ne sont désormais plus exclusifs! + Je ne pense pas que ce soit la bonne traduction.. self-assignable role serait plutôt rôle auto-attribuable Je suis incapable de vous ajouter ce rôle. `Je ne peux pas ajouter de rôles aux propriétaires et aux autres rôles plus haut que le mien dans la hiérarchie.` @@ -717,20 +718,21 @@ Raison : {1} Nouveau nom de Salon défini avec succès. - Nouveau jeu en service! + Nouveau jeu défini! + Pour "set", je pense que défini irait mieux que "en service" - Nouvelle diffusion en service! + Nouveau stream défini! - Nouveau sujet du salon en service. + Nouveau sujet du salon défini. - Shard {0} reconnecté. + Partition {0} reconnectée. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. - Shard {0} se reconnecte. + Partition {0} en reconnection. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -864,7 +866,7 @@ Raison: {1} Utilisateur débanni - Migration effectué! + Migration effectuée! Erreur lors de la migration, veuillez consulter la console pour plus d'informations. @@ -990,7 +992,7 @@ Raison: {1} Nécessite {0} permissions du salon - Vous pouvez supporter ce projet sur Patreon: <{0}> ou via Paypal <{1}> + Vous pouvez supporter ce projet sur Patreon <{0}> ou via Paypal <{1}> Commandes et alias @@ -1119,7 +1121,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Top Waifus - votre affinité est déjà liée à cette waifu ou vous êtes en train de retirer votre affinité avec quelqu'un avec qui vous n'en posséder pas. + votre affinité est déjà liée à cette waifu ou vous êtes en train de retirer votre affinité avec quelqu'un alors que vous n'en possédez pas. Affinités changées de de {0} à {1}. @@ -1237,10 +1239,11 @@ La nouvelle valeur de {0} est {1} ! Cleverbot activé sur ce serveur. - La génération actuelle a été désactivée sur ce salon. + La génération monétaire a été désactivée sur ce salon. + Currency =/= current !!! Il s'agit de monnaie. Par example: Euro is a currency. US Dollar also is. Sur Public Nadeko, il s'agit des fleurs :) - La génération actuelle a été désactivée sur ce salon. + La génération monétaire a été désactivée sur ce salon. {0} {1} aléatoires sont apparus ! Attrapez-les en entrant `{2}pick` @@ -1253,7 +1256,7 @@ La nouvelle valeur de {0} est {1} ! Impossible de charger une question. - Jeu commencé. + La jeu a commencé. Partie de pendu commencée. @@ -1274,7 +1277,7 @@ La nouvelle valeur de {0} est {1} ! Vous n'avez pas assez de {0} - Pas de résultats + Pas de résultat choisi {0} @@ -1357,7 +1360,7 @@ La nouvelle valeur de {0} est {1} ! à tour de rôle - Musique terminée + Lecture terminée Système de tour de rôle désactivé. @@ -1402,13 +1405,13 @@ La nouvelle valeur de {0} est {1} ! Pas de résultat - Musique mise sur pause. + Lecteur mis sur pause. Liste d'attente - Page {0}/{1} - Lecture du son + Lecture en cours: #{0}` - **{1}** par *{2}* ({3} morceaux) @@ -1423,7 +1426,7 @@ La nouvelle valeur de {0} est {1} ! Impossible de supprimer cette liste de lecture. Soit elle n'existe pas, soit vous n'en êtes pas le créateur. - Aucune liste de lecture avec cet ID existe. + Aucune liste de lecture ne correspond a cet ID. File d'attente de la liste complétée. @@ -1438,7 +1441,7 @@ La nouvelle valeur de {0} est {1} ! Liste d'attente - Musique ajoutée à la file d'attente + Son ajouté à la file d'attente Liste d'attente effacée. @@ -1447,7 +1450,7 @@ La nouvelle valeur de {0} est {1} ! Liste d'attente complète ({0}/{0}) - Musique retirée + Son retiré context: "removed song #5" @@ -1478,10 +1481,10 @@ La nouvelle valeur de {0} est {1} ! Saut à `{0}:{1}` - Musiques mélangées. + Lecture aléatoire activée. - Musique déplacée. + Musique déplacée {0}h {1}m {2}s @@ -1527,12 +1530,13 @@ La nouvelle valeur de {0} est {1} ! Banni {0} avec l'ID {1} + Il ne s'agit pas d'un ban mais d'une blacklist interdisant l'utilisateur d'utiliser le bot. La commande {0} a désormais {1}s de temps de recharge. - La commande {0} n'a pas de temps de recharge et tout les temps de recharge ont été réinitialisés. + La commande {0} n'a pas de temps de recharge et tous les temps de recharge ont été réinitialisés. Aucune commande n'a de temps de recharge. @@ -1700,7 +1704,7 @@ La nouvelle valeur de {0} est {1} ! Parties compétitives gagnées - Complété + Complétés Condition @@ -1715,7 +1719,8 @@ La nouvelle valeur de {0} est {1} ! Définis: - En baisse + Abandonnés + droppped as in, "stopped watching" referring to shows/anime Episodes @@ -1779,7 +1784,7 @@ La nouvelle valeur de {0} est {1} ! Profil MAL de {0} - Le propriétaire du Bot n'a pas spécifié de clé MashapeApi. Fonctionnalité non disponible + Le propriétaire du Bot n'a pas spécifié de clé d'API Mashape (MashapeApiKey). Fonctionnalité non disponible Min/Max @@ -1797,10 +1802,10 @@ La nouvelle valeur de {0} est {1} ! Url originale - Une clé osu!API est nécessaire + Une clé d'API osu! est nécessaire - Impossible de récupérer la signature osu. + Impossible de récupérer la signature osu! Trouvé dans {0} images. Affichage de {0} aléatoires. @@ -1810,6 +1815,7 @@ La nouvelle valeur de {0} est {1} ! Prévision de lecture + Je ne pense pas que le sens de la traduction soit le bon. Plateforme @@ -1833,13 +1839,14 @@ La nouvelle valeur de {0} est {1} ! Victoires Rapides - Évaluation + Évaluation Score: Chercher pour: + recherche plutôt non ? Impossible de réduire cette Url @@ -1863,7 +1870,7 @@ La nouvelle valeur de {0} est {1} ! Le streamer {0} est hors ligne. - Le streamer {0} est en ligne avec {1} spectateurs. + Le streamer {0} est en ligne avec {1} viewers. Vous suivez {0} streams sur ce serveur. @@ -1881,7 +1888,7 @@ La nouvelle valeur de {0} est {1} ! Stream de {0} ({1}) retirée des notifications. - Je préviendrais ce salon lors d'un changement de statut. + Je préviendrai ce salon lors d'un changement de statut. Aube @@ -1911,10 +1918,10 @@ La nouvelle valeur de {0} est {1} ! Url - Spectateurs + Viewers - Regardant + En écoute Impossible de trouver ce terme sur le wikia spécifié. @@ -2060,7 +2067,7 @@ OwnerID: {2} Aucun rôle sur cette page. - Aucun shard sur cette page. + Aucune partition sur cette page. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -2111,7 +2118,7 @@ OwnerID: {2} Format de date non valide. Regardez la liste des commandes. - Nouveau modèle de rappel en service. + Nouveau modèle de rappel défini. Répétition de {0} chaque {1} jour(s), {2} heure(s) et {3} minute(s) @@ -2156,15 +2163,15 @@ OwnerID: {2} Info du serveur - Shard + Partition Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. - Statistique de Shard + Statistique des partitions Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. - Le shard **#{0}** est en {1} état avec {2} serveurs + La partition **#{0}** est en état {1} avec {2} serveurs. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. From c4a248889e3e91490e2f96bc414c1820b0b9d322 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 28 Feb 2017 21:50:36 +0100 Subject: [PATCH 224/256] Update ResponseStrings.de-DE.resx (POEditor.com) --- .../Resources/ResponseStrings.de-DE.resx | 234 +++++++++--------- 1 file changed, 117 insertions(+), 117 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx index 9aac2f85..7dd58187 100644 --- a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx +++ b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx @@ -1,121 +1,121 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Diese Basis wurde bereits beansprucht oder zerstört. From 172d401da7da41c356d33d0872f599b8130a2751 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 28 Feb 2017 21:50:39 +0100 Subject: [PATCH 225/256] Update ResponseStrings.ru-RU.resx (POEditor.com) --- .../Resources/ResponseStrings.ru-RU.resx | 243 +++++++++--------- 1 file changed, 122 insertions(+), 121 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx index f6729bb5..bf370873 100644 --- a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx +++ b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx @@ -1,121 +1,121 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 @@ -2014,17 +2014,18 @@ Paypal <{1}> Вам запрещено использовать эту комманду в отношении ролей с большим числом пользователей для предотвращения - + Неправильное значение {0}. Invalid months value/ Invalid hours value - + Присоединился к Discord - + Присоединился к серверу - + Имя: {0} + From b588edeeee88fcd148f85954b795dafa1ad8533a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 01:48:29 +0100 Subject: [PATCH 226/256] added languages in progress to the list too --- .../Commands/LocalizationCommands.cs | 7 +- .../Gambling/Commands/FlipCoinCommand.cs | 2 +- .../Resources/CommandStrings.ja-JP.resx | 3153 +++++++++++++++++ .../Resources/CommandStrings.nl-NL.resx | 3153 +++++++++++++++++ .../Resources/ResponseStrings.Designer.cs | 9 - .../Resources/ResponseStrings.pt-BR.resx | 2184 ++++++++++++ src/NadekoBot/Resources/ResponseStrings.resx | 3 - 7 files changed, 8496 insertions(+), 15 deletions(-) create mode 100644 src/NadekoBot/Resources/CommandStrings.ja-JP.resx create mode 100644 src/NadekoBot/Resources/CommandStrings.nl-NL.resx create mode 100644 src/NadekoBot/Resources/ResponseStrings.pt-BR.resx diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 641f90e8..4200fafb 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -21,8 +21,11 @@ namespace NadekoBot.Modules.Administration {"en-US", "English, United States"}, {"fr-FR", "French, France"}, {"ru-RU", "Russian, Russia"}, - {"de-DE", "German, Germany"} - //{"sr-cyrl-rs", "Serbian, Cyrillic" } + {"de-DE", "German, Germany"}, + {"nl-NL", "Dutch, Netherlands"}, + {"ja-JP", "Japanese, Japan"}, + {"pt-BR", "Portuguese, Brazil"}, + {"sr-cyrl-rs", "Serbian, Serbia - Cyrillic"} }.ToImmutableDictionary(); [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs index 40b8a735..3d58d086 100644 --- a/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs +++ b/src/NadekoBot/Modules/Gambling/Commands/FlipCoinCommand.cs @@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Gambling { var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier); str = Context.User.Mention + " " + GetText("flip_guess", toWin + CurrencySign); - await CurrencyHandler.AddCurrencyAsync(Context.User, GetText("betflip_gamble"), toWin, false).ConfigureAwait(false); + await CurrencyHandler.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false); } else { diff --git a/src/NadekoBot/Resources/CommandStrings.ja-JP.resx b/src/NadekoBot/Resources/CommandStrings.ja-JP.resx new file mode 100644 index 00000000..662975cf --- /dev/null +++ b/src/NadekoBot/Resources/CommandStrings.ja-JP.resx @@ -0,0 +1,3153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + help h + + + Either shows a help for a single command, or DMs you help link if no arguments are specified. + + + `{0}h !!q` or `{0}h` + + + hgit + + + Generates the commandlist.md file. + + + `{0}hgit` + + + donate + + + Instructions for helping the project financially. + + + `{0}donate` + + + modules mdls + + + Lists all bot modules. + + + `{0}modules` + + + commands cmds + + + List all of the bot's commands from a certain module. You can either specify full, or only first few letters of the module name. + + + `{0}commands Administration` or `{0}cmds Admin` + + + greetdel grdel + + + Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. + + + `{0}greetdel 0` or `{0}greetdel 30` + + + greet + + + Toggles anouncements on the current channel when someone joins the server. + + + `{0}greet` + + + greetmsg + + + Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. + + + `{0}greetmsg Welcome, %user%.` + + + bye + + + Toggles anouncements on the current channel when someone leaves the server. + + + `{0}bye` + + + byemsg + + + Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. + + + `{0}byemsg %user% has left.` + + + byedel + + + Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. + + + `{0}byedel 0` or `{0}byedel 30` + + + greetdm + + + Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). + + + `{0}greetdm` + + + logserver + + + Enables or Disables ALL log events. If enabled, all log events will log to this channel. + + + `{0}logserver enable` or `{0}logserver disable` + + + logignore + + + Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. + + + `{0}logignore` + + + userpresence + + + Starts logging to this channel when someone from the server goes online/offline/idle. + + + `{0}userpresence` + + + voicepresence + + + Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. + + + `{0}voicepresence` + + + repeatinvoke repinv + + + Immediately shows the repeat message on a certain index and restarts its timer. + + + `{0}repinv 1` + + + repeat + + + Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. + + + `{0}repeat 5 Hello there` + + + rotateplaying ropl + + + Toggles rotation of playing status of the dynamic strings you previously specified. + + + `{0}ropl` + + + addplaying adpl + + + Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds% + + + `{0}adpl` + + + listplaying lipl + + + Lists all playing statuses with their corresponding number. + + + `{0}lipl` + + + removeplaying rmpl repl + + + Removes a playing string on a given number. + + + `{0}rmpl` + + + slowmode + + + Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. + + + `{0}slowmode 1 5` or `{0}slowmode` + + + cleanvplust cv+t + + + Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. + + + `{0}cleanv+t` + + + voice+text v+t + + + Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. + + + `{0}v+t` + + + scsc + + + Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. + + + `{0}scsc` + + + jcsc + + + Joins current channel to an instance of cross server channel using the token. + + + `{0}jcsc TokenHere` + + + lcsc + + + Leaves Cross server channel instance from this channel. + + + `{0}lcsc` + + + asar + + + Adds a role to the list of self-assignable roles. + + + `{0}asar Gamer` + + + rsar + + + Removes a specified role from the list of self-assignable roles. + + + `{0}rsar` + + + lsar + + + Lists all self-assignable roles. + + + `{0}lsar` + + + togglexclsar tesar + + + Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) + + + `{0}tesar` + + + iam + + + Adds a role to you that you choose. Role must be on a list of self-assignable roles. + + + `{0}iam Gamer` + + + iamnot iamn + + + Removes a role to you that you choose. Role must be on a list of self-assignable roles. + + + `{0}iamn Gamer` + + + addcustreact acr + + + Add a custom reaction with a trigger and a response. Running this command in server requires Administration permission. Running this command in DM is Bot Owner only and adds a new global custom reaction. Guide here: <http://nadekobot.readthedocs.io/en/latest/Custom%20Reactions/> + + + `{0}acr "hello" Hi there %user%` + + + listcustreact lcr + + + Lists global or server custom reactions (20 commands per page). Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. Specifying `all` argument instead of the number will DM you a text file with a list of all custom reactions. + + + `{0}lcr 1` or `{0}lcr all` + + + listcustreactg lcrg + + + Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. + + + `{0}lcrg 1` + + + showcustreact scr + + + Shows a custom reaction's response on a given ID. + + + `{0}scr 1` + + + delcustreact dcr + + + Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. + + + `{0}dcr 5` + + + autoassignrole aar + + + Automaticaly assigns a specified role to every user who joins the server. + + + `{0}aar` to disable, `{0}aar Role Name` to enable + + + leave + + + Makes Nadeko leave the server. Either name or id required. + + + `{0}leave 123123123331` + + + delmsgoncmd + + + Toggles the automatic deletion of user's successful command message to prevent chat flood. + + + `{0}delmsgoncmd` + + + restart + + + Restarts the bot. Might not work. + + + `{0}restart` + + + setrole sr + + + Sets a role for a given user. + + + `{0}sr @User Guest` + + + removerole rr + + + Removes a role from a given user. + + + `{0}rr @User Admin` + + + renamerole renr + + + Renames a role. Roles you are renaming must be lower than bot's highest role. + + + `{0}renr "First role" SecondRole` + + + removeallroles rar + + + Removes all roles from a mentioned user. + + + `{0}rar @User` + + + createrole cr + + + Creates a role with a given name. + + + `{0}cr Awesome Role` + + + rolecolor rc + + + Set a role's color to the hex or 0-255 rgb color value provided. + + + `{0}rc Admin 255 200 100` or `{0}rc Admin ffba55` + + + ban b + + + Bans a user by ID or name with an optional message. + + + `{0}b "@some Guy" Your behaviour is toxic.` + + + softban sb + + + Bans and then unbans a user by ID or name with an optional message. + + + `{0}sb "@some Guy" Your behaviour is toxic.` + + + kick k + + + Kicks a mentioned user. + + + `{0}k "@some Guy" Your behaviour is toxic.` + + + mute + + + Mutes a mentioned user both from speaking and chatting. + + + `{0}mute @Someone` + + + voiceunmute + + + Gives a previously voice-muted user a permission to speak. + + + `{0}voiceunmute @Someguy` + + + deafen deaf + + + Deafens mentioned user or users. + + + `{0}deaf "@Someguy"` or `{0}deaf "@Someguy" "@Someguy"` + + + undeafen undef + + + Undeafens mentioned user or users. + + + `{0}undef "@Someguy"` or `{0}undef "@Someguy" "@Someguy"` + + + delvoichanl dvch + + + Deletes a voice channel with a given name. + + + `{0}dvch VoiceChannelName` + + + creatvoichanl cvch + + + Creates a new voice channel with a given name. + + + `{0}cvch VoiceChannelName` + + + deltxtchanl dtch + + + Deletes a text channel with a given name. + + + `{0}dtch TextChannelName` + + + creatxtchanl ctch + + + Creates a new text channel with a given name. + + + `{0}ctch TextChannelName` + + + settopic st + + + Sets a topic on the current channel. + + + `{0}st My new topic` + + + setchanlname schn + + + Changes the name of the current channel. + + + `{0}schn NewName` + + + prune clr + + + `{0}prune` removes all nadeko's messages in the last 100 messages.`{0}prune X` removes last X messages from the channel (up to 100)`{0}prune @Someone` removes all Someone's messages in the last 100 messages.`{0}prune @Someone X` removes last X 'Someone's' messages in the channel. + + + `{0}prune` or `{0}prune 5` or `{0}prune @Someone` or `{0}prune @Someone X` + + + die + + + Shuts the bot down. + + + `{0}die` + + + setname newnm + + + Gives the bot a new name. + + + `{0}newnm BotName` + + + setavatar setav + + + Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. + + + `{0}setav http://i.imgur.com/xTG3a1I.jpg` + + + setgame + + + Sets the bots game. + + + `{0}setgame with snakes` + + + send + + + Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. + + + `{0}send serverid|c:channelid message` or `{0}send serverid|u:userid message` + + + mentionrole menro + + + Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. + + + `{0}menro RoleName` + + + unstuck + + + Clears the message queue. + + + `{0}unstuck` + + + donators + + + List of lovely people who donated to keep this project alive. + + + `{0}donators` + + + donadd + + + Add a donator to the database. + + + `{0}donadd Donate Amount` + + + announce + + + Sends a message to all servers' general channel bot is connected to. + + + `{0}announce Useless spam` + + + savechat + + + Saves a number of messages to a text file and sends it to you. + + + `{0}savechat 150` + + + remind + + + Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. + + + `{0}remind me 1d5h Do something` or `{0}remind #general 1m Start now!` + + + remindtemplate + + + Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. + + + `{0}remindtemplate %user%, do %message%!` + + + serverinfo sinfo + + + Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. + + + `{0}sinfo Some Server` + + + channelinfo cinfo + + + Shows info about the channel. If no channel is supplied, it defaults to current one. + + + `{0}cinfo #some-channel` + + + userinfo uinfo + + + Shows info about the user. If no user is supplied, it defaults a user running the command. + + + `{0}uinfo @SomeUser` + + + whosplaying whpl + + + Shows a list of users who are playing the specified game. + + + `{0}whpl Overwatch` + + + inrole + + + Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission. + + + `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3` + + + checkmyperms + + + Checks your user-specific permissions on this channel. + + + `{0}checkmyperms` + + + stats + + + Shows some basic stats for Nadeko. + + + `{0}stats` + + + userid uid + + + Shows user ID. + + + `{0}uid` or `{0}uid "@SomeGuy"` + + + channelid cid + + + Shows current channel ID. + + + `{0}cid` + + + serverid sid + + + Shows current server ID. + + + `{0}sid` + + + roles + + + List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. + + + `{0}roles 2` or `{0}roles @Someone` + + + channeltopic ct + + + Sends current channel's topic as a message. + + + `{0}ct` + + + chnlfilterinv cfi + + + Toggles automatic deleting of invites posted in the channel. Does not negate the {0}srvrfilterinv enabled setting. Does not affect Bot Owner. + + + `{0}cfi` + + + srvrfilterinv sfi + + + Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. + + + `{0}sfi` + + + chnlfilterwords cfw + + + Toggles automatic deleting of messages containing banned words on the channel. Does not negate the {0}srvrfilterwords enabled setting. Does not affect bot owner. + + + `{0}cfw` + + + fw + + + Adds or removes (if it exists) a word from the list of filtered words. Use`{0}sfw` or `{0}cfw` to toggle filtering. + + + `{0}fw poop` + + + lstfilterwords lfw + + + Shows a list of filtered words. + + + `{0}lfw` + + + srvrfilterwords sfw + + + Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. + + + `{0}sfw` + + + permrole pr + + + Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. + + + `{0}pr role` + + + verbose v + + + Sets whether to show when a command/module is blocked. + + + `{0}verbose true` + + + srvrmdl sm + + + Sets a module's permission at the server level. + + + `{0}sm ModuleName enable` + + + srvrcmd sc + + + Sets a command's permission at the server level. + + + `{0}sc "command name" disable` + + + rolemdl rm + + + Sets a module's permission at the role level. + + + `{0}rm ModuleName enable MyRole` + + + rolecmd rc + + + Sets a command's permission at the role level. + + + `{0}rc "command name" disable MyRole` + + + chnlmdl cm + + + Sets a module's permission at the channel level. + + + `{0}cm ModuleName enable SomeChannel` + + + chnlcmd cc + + + Sets a command's permission at the channel level. + + + `{0}cc "command name" enable SomeChannel` + + + usrmdl um + + + Sets a module's permission at the user level. + + + `{0}um ModuleName enable SomeUsername` + + + usrcmd uc + + + Sets a command's permission at the user level. + + + `{0}uc "command name" enable SomeUsername` + + + allsrvrmdls asm + + + Enable or disable all modules for your server. + + + `{0}asm [enable/disable]` + + + allchnlmdls acm + + + Enable or disable all modules in a specified channel. + + + `{0}acm enable #SomeChannel` + + + allrolemdls arm + + + Enable or disable all modules for a specific role. + + + `{0}arm [enable/disable] MyRole` + + + ubl + + + Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. + + + `{0}ubl add @SomeUser` or `{0}ubl rem 12312312313` + + + cbl + + + Either [add]s or [rem]oves a channel specified by an ID from a blacklist. + + + `{0}cbl rem 12312312312` + + + sbl + + + Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. + + + `{0}sbl add 12312321312` or `{0}sbl rem SomeTrashServer` + + + cmdcooldown cmdcd + + + Sets a cooldown per user for a command. Set to 0 to remove the cooldown. + + + `{0}cmdcd "some cmd" 5` + + + allcmdcooldowns acmdcds + + + Shows a list of all commands and their respective cooldowns. + + + `{0}acmdcds` + + + . + + + Adds a new quote with the specified name and message. + + + `{0}. sayhi Hi` + + + .. + + + Shows a random quote with a specified name. + + + `{0}.. abc` + + + qsearch + + + Shows a random quote for a keyword that contains any text specified in the search. + + + `{0}qsearch keyword text` + + + deletequote delq + + + Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. + + + `{0}delq abc` + + + draw + + + Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. + + + `{0}draw` or `{0}draw 5` + + + shuffle sh + + + Shuffles the current playlist. + + + `{0}sh` + + + flip + + + Flips coin(s) - heads or tails, and shows an image. + + + `{0}flip` or `{0}flip 3` + + + betflip bf + + + Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner. + + + `{0}bf 5 heads` or `{0}bf 3 t` + + + roll + + + Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. + + + `{0}roll` or `{0}roll 7` or `{0}roll 3d5` or `{0}roll 5dF` + + + rolluo + + + Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. + + + `{0}rolluo` or `{0}rolluo 7` or `{0}rolluo 3d5` + + + nroll + + + Rolls in a given range. + + + `{0}nroll 5` (rolls 0-5) or `{0}nroll 5-15` + + + race + + + Starts a new animal race. + + + `{0}race` + + + joinrace jr + + + Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. + + + `{0}jr` or `{0}jr 5` + + + raffle + + + Prints a name and ID of a random user from the online list from the (optional) role. + + + `{0}raffle` or `{0}raffle RoleName` + + + give + + + Give someone a certain amount of currency. + + + `{0}give 1 "@SomeGuy"` + + + award + + + Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. + + + `{0}award 100 @person` or `{0}award 5 Role Of Gamblers` + + + take + + + Takes a certain amount of currency from someone. + + + `{0}take 1 "@someguy"` + + + betroll br + + + Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10. + + + `{0}br 5` + + + leaderboard lb + + + Displays bot currency leaderboard. + + + `{0}lb` + + + trivia t + + + Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. + + + `{0}t` or `{0}t 5 nohint` + + + tl + + + Shows a current trivia leaderboard. + + + `{0}tl` + + + tq + + + Quits current trivia after current question. + + + `{0}tq` + + + typestart + + + Starts a typing contest. + + + `{0}typestart` + + + typestop + + + Stops a typing contest on the current channel. + + + `{0}typestop` + + + typeadd + + + Adds a new article to the typing contest. + + + `{0}typeadd wordswords` + + + poll + + + Creates a poll which requires users to send the number of the voting option to the bot. + + + `{0}poll Question?;Answer1;Answ 2;A_3` + + + pollend + + + Stops active poll on this server and prints the results in this channel. + + + `{0}pollend` + + + pick + + + Picks the currency planted in this channel. 60 seconds cooldown. + + + `{0}pick` + + + plant + + + Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) + + + `{0}plant` or `{0}plant 5` + + + gencurrency gc + + + Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) + + + `{0}gc` + + + leet + + + Converts a text to leetspeak with 6 (1-6) severity levels + + + `{0}leet 3 Hello` + + + choose + + + Chooses a thing from a list of things + + + `{0}choose Get up;Sleep;Sleep more` + + + 8ball + + + Ask the 8ball a yes/no question. + + + `{0}8ball should I do something` + + + rps + + + Play a game of rocket paperclip scissors with Nadeko. + + + `{0}rps scissors` + + + linux + + + Prints a customizable Linux interjection + + + `{0}linux Spyware Windows` + + + next n + + + Goes to the next song in the queue. You have to be in the same voice channel as the bot. You can skip multiple songs, but in that case songs will not be requeued if {0}rcs or {0}rpl is enabled. + + + `{0}n` or `{0}n 5` + + + stop s + + + Stops the music and clears the playlist. Stays in the channel. + + + `{0}s` + + + destroy d + + + Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) + + + `{0}d` + + + pause p + + + Pauses or Unpauses the song. + + + `{0}p` + + + queue q yq + + + Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. + + + `{0}q Dream Of Venice` + + + soundcloudqueue sq + + + Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. + + + `{0}sq Dream Of Venice` + + + listqueue lq + + + Lists 15 currently queued songs per page. Default page is 1. + + + `{0}lq` or `{0}lq 2` + + + nowplaying np + + + Shows the song currently playing. + + + `{0}np` + + + volume vol + + + Sets the music volume 0-100% + + + `{0}vol 50` + + + defvol dv + + + Sets the default music volume when music playback is started (0-100). Persists through restarts. + + + `{0}dv 80` + + + max + + + Sets the music volume to 100%. + + + `{0}max` + + + half + + + Sets the music volume to 50%. + + + `{0}half` + + + playlist pl + + + Queues up to 500 songs from a youtube playlist specified by a link, or keywords. + + + `{0}pl playlist link or name` + + + soundcloudpl scpl + + + Queue a soundcloud playlist using a link. + + + `{0}scpl soundcloudseturl` + + + localplaylst lopl + + + Queues all songs from a directory. + + + `{0}lopl C:/music/classical` + + + radio ra + + + Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) + + + `{0}ra radio link here` + + + local lo + + + Queues a local file by specifying a full path. + + + `{0}lo C:/music/mysong.mp3` + + + move mv + + + Moves the bot to your voice channel. (works only if music is already playing) + + + `{0}mv` + + + remove rm + + + Remove a song by its # in the queue, or 'all' to remove whole queue. + + + `{0}rm 5` + + + movesong ms + + + Moves a song from one position to another. + + + `{0}ms 5>3` + + + setmaxqueue smq + + + Sets a maximum queue size. Supply 0 or no argument to have no limit. + + + `{0}smq 50` or `{0}smq` + + + cleanup + + + Cleans up hanging voice connections. + + + `{0}cleanup` + + + reptcursong rcs + + + Toggles repeat of current song. + + + `{0}rcs` + + + rpeatplaylst rpl + + + Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). + + + `{0}rpl` + + + save + + + Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. + + + `{0}save classical1` + + + load + + + Loads a saved playlist using it's ID. Use `{0}pls` to list all saved playlists and {0}save to save new ones. + + + `{0}load 5` + + + playlists pls + + + Lists all playlists. Paginated. 20 per page. Default page is 0. + + + `{0}pls 1` + + + deleteplaylist delpls + + + Deletes a saved playlist. Only if you made it or if you are the bot owner. + + + `{0}delpls animu-5` + + + goto + + + Goes to a specific time in seconds in a song. + + + `{0}goto 30` + + + autoplay ap + + + Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) + + + `{0}ap` + + + lolchamp + + + Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. + + + `{0}lolchamp Riven` or `{0}lolchamp Annie sup` + + + lolban + + + Shows top banned champions ordered by ban rate. + + + `{0}lolban` + + + hitbox hb + + + Notifies this channel when a certain user starts streaming. + + + `{0}hitbox SomeStreamer` + + + twitch tw + + + Notifies this channel when a certain user starts streaming. + + + `{0}twitch SomeStreamer` + + + beam bm + + + Notifies this channel when a certain user starts streaming. + + + `{0}beam SomeStreamer` + + + removestream rms + + + Removes notifications of a certain streamer from a certain platform on this channel. + + + `{0}rms Twitch SomeGuy` or `{0}rms Beam SomeOtherGuy` + + + liststreams ls + + + Lists all streams you are following on this server. + + + `{0}ls` + + + convert + + + Convert quantities. Use `{0}convertlist` to see supported dimensions and currencies. + + + `{0}convert m km 1000` + + + convertlist + + + List of the convertible dimensions and currencies. + + + `{0}convertlist` + + + wowjoke + + + Get one of Kwoth's penultimate WoW jokes. + + + `{0}wowjoke` + + + calculate calc + + + Evaluate a mathematical expression. + + + `{0}calc 1+1` + + + osu + + + Shows osu stats for a player. + + + `{0}osu Name` or `{0}osu Name taiko` + + + osub + + + Shows information about an osu beatmap. + + + `{0}osub https://osu.ppy.sh/s/127712` + + + osu5 + + + Displays a user's top 5 plays. + + + `{0}osu5 Name` + + + pokemon poke + + + Searches for a pokemon. + + + `{0}poke Sylveon` + + + pokemonability pokeab + + + Searches for a pokemon ability. + + + `{0}pokeab overgrow` + + + memelist + + + Pulls a list of memes you can use with `{0}memegen` from http://memegen.link/templates/ + + + `{0}memelist` + + + memegen + + + Generates a meme from memelist with top and bottom text. + + + `{0}memegen biw "gets iced coffee" "in the winter"` + + + weather we + + + Shows weather data for a specified city. You can also specify a country after a comma. + + + `{0}we Moscow, RU` + + + youtube yt + + + Searches youtubes and shows the first result + + + `{0}yt query` + + + anime ani aq + + + Queries anilist for an anime and shows the first result. + + + `{0}ani aquarion evol` + + + imdb omdb + + + Queries omdb for movies or series, show first result. + + + `{0}imdb Batman vs Superman` + + + manga mang mq + + + Queries anilist for a manga and shows the first result. + + + `{0}mq Shingeki no kyojin` + + + randomcat meow + + + Shows a random cat image. + + + `{0}meow` + + + randomdog woof + + + Shows a random dog image. + + + `{0}woof` + + + image img + + + Pulls the first image found using a search parameter. Use {0}rimg for different results. + + + `{0}img cute kitten` + + + randomimage rimg + + + Pulls a random image using a search parameter. + + + `{0}rimg cute kitten` + + + lmgtfy + + + Google something for an idiot. + + + `{0}lmgtfy query` + + + google g + + + Get a google search link for some terms. + + + `{0}google query` + + + hearthstone hs + + + Searches for a Hearthstone card and shows its image. Takes a while to complete. + + + `{0}hs Ysera` + + + urbandict ud + + + Searches Urban Dictionary for a word. + + + `{0}ud Pineapple` + + + # + + + Searches Tagdef.com for a hashtag. + + + `{0}# ff` + + + catfact + + + Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> + + + `{0}catfact` + + + yomama ym + + + Shows a random joke from <http://api.yomomma.info/> + + + `{0}ym` + + + randjoke rj + + + Shows a random joke from <http://tambal.azurewebsites.net/joke/random> + + + `{0}rj` + + + chucknorris cn + + + Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> + + + `{0}cn` + + + magicitem mi + + + Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> + + + `{0}mi` + + + revav + + + Returns a google reverse image search for someone's avatar. + + + `{0}revav "@SomeGuy"` + + + revimg + + + Returns a google reverse image search for an image from a link. + + + `{0}revimg Image link` + + + safebooru + + + Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}safebooru yuri+kissing` + + + wikipedia wiki + + + Gives you back a wikipedia link + + + `{0}wiki query` + + + color clr + + + Shows you what color corresponds to that hex. + + + `{0}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. + + + `{0}videocall "@SomeGuy"` + + + avatar av + + + Shows a mentioned person's avatar. + + + `{0}av "@SomeGuy"` + + + hentai + + + Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. + + + `{0}hentai yuri` + + + danbooru + + + Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}danbooru yuri+kissing` + + + atfbooru atf + + + Shows a random hentai image from atfbooru with a given tag. Tag is optional but preferred. + + + `{0}atfbooru yuri+kissing` + + + gelbooru + + + Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}gelbooru yuri+kissing` + + + rule34 + + + Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}rule34 yuri+kissing` + + + e621 + + + Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. + + + `{0}e621 yuri kissing` + + + cp + + + We all know where this will lead you to. + + + `{0}cp` + + + boobs + + + Real adult content. + + + `{0}boobs` + + + butts ass butt + + + Real adult content. + + + `{0}butts` or `{0}ass` + + + createwar cw + + + Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. + + + `{0}cw 15 The Enemy Clan` + + + startwar sw + + + Starts a war with a given number. + + + `{0}sw 15` + + + listwar lw + + + Shows the active war claims by a number. Shows all wars in a short way if no number is specified. + + + `{0}lw [war_number] or {0}lw` + + + claim call c + + + Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. + + + `{0}call [war_number] [base_number] [optional_other_name]` + + + claimfinish cf + + + Finish your claim with 3 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. + + + `{0}cf 1` or `{0}cf 1 5` + + + claimfinish2 cf2 + + + Finish your claim with 2 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. + + + `{0}cf2 1` or `{0}cf2 1 5` + + + claimfinish1 cf1 + + + Finish your claim with 1 star if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. + + + `{0}cf1 1` or `{0}cf1 1 5` + + + unclaim ucall uc + + + Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim + + + `{0}uc [war_number] [optional_other_name]` + + + endwar ew + + + Ends the war with a given index. + + + `{0}ew [war_number]` + + + translate trans + + + Translates from>to text. From the given language to the destination language. + + + `{0}trans en>fr Hello` + + + translangs + + + Lists the valid languages for translation. + + + `{0}translangs` + + + Sends a readme and a guide links to the channel. + + + `{0}readme` or `{0}guide` + + + readme guide + + + Shows all available operations in {0}calc command + + + `{0}calcops` + + + calcops + + + Deletes all quotes on a specified keyword. + + + `{0}delallq kek` + + + delallq daq + + + greetdmmsg + + + `{0}greetdmmsg Welcome to the server, %user%`. + + + Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. + + + Check how much currency a person has. (Defaults to yourself) + + + `{0}$$` or `{0}$$ @SomeGuy` + + + cash $$ + + + Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. + + + `{0}lp` or `{0}lp 3` + + + listperms lp + + + Enable or disable all modules for a specific user. + + + `{0}aum enable @someone` + + + allusrmdls aum + + + Moves permission from one position to another in Permissions list. + + + `{0}mp 2 4` + + + moveperm mp + + + Removes a permission from a given position in Permissions list. + + + `{0}rp 1` + + + removeperm rp + + + Migrate data from old bot configuration + + + `{0}migratedata` + + + migratedata + + + Checks if a user is online on a certain streaming platform. + + + `{0}cs twitch MyFavStreamer` + + + checkstream cs + + + showemojis se + + + Shows a name and a link to every SPECIAL emoji in the message. + + + `{0}se A message full of SPECIAL emojis` + + + shuffle sh + + + Reshuffles all cards back into the deck. + + + `{0}sh` + + + fwmsgs + + + Toggles forwarding of non-command messages sent to bot's DM to the bot owners + + + `{0}fwmsgs` + + + fwtoall + + + Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json + + + `{0}fwtoall` + + + resetperms + + + Resets BOT's permissions module on this server to the default value. + + + `{0}resetperms` + + + antiraid + + + Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) + + + `{0}antiraid 5 20 Kick` + + + antispam + + + Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10. + + + `{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban` + + + chatmute + + + Prevents a mentioned user from chatting in text channels. + + + `{0}chatmute @Someone` + + + voicemute + + + Prevents a mentioned user from speaking in voice channels. + + + `{0}voicemute @Someone` + + + konachan + + + Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. + + + `{0}konachan yuri` + + + setmuterole + + + Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. + + + `{0}setmuterole Silenced` + + + adsarm + + + Toggles the automatic deletion of confirmations for {0}iam and {0}iamn commands. + + + `{0}adsarm` + + + setstream + + + Sets the bots stream. First argument is the twitch link, second argument is stream name. + + + `{0}setstream TWITCHLINK Hello` + + + chatunmute + + + Removes a mute role previously set on a mentioned user with `{0}chatmute` which prevented him from chatting in text channels. + + + `{0}chatunmute @Someone` + + + unmute + + + Unmutes a mentioned user previously muted with `{0}mute` command. + + + `{0}unmute @Someone` + + + xkcd + + + Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. + + + `{0}xkcd` or `{0}xkcd 1400` or `{0}xkcd latest` + + + placelist + + + Shows the list of available tags for the `{0}place` command. + + + `{0}placelist` + + + place + + + Shows a placeholder image of a given tag. Use `{0}placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. + + + `{0}place Cage` or `{0}place steven 500 400` + + + togethertube totube + + + Creates a new room on <https://togethertube.com> and shows the link in the chat. + + + `{0}totube` + + + publicpoll ppoll + + + Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. + + + `{0}ppoll Question?;Answer1;Answ 2;A_3` + + + autotranslang atl + + + Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value. + + + `{0}atl en>fr` + + + autotrans at + + + Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set "del" argument to automatically delete all translated user messages. + + + `{0}at` or `{0}at del` + + + listquotes liqu + + + `{0}liqu` or `{0}liqu 3` + + + Lists all quotes on the server ordered alphabetically. 15 Per page. + + + typedel + + + Deletes a typing article given the ID. + + + `{0}typedel 3` + + + typelist + + + Lists added typing articles with their IDs. 15 per page. + + + `{0}typelist` or `{0}typelist 3` + + + listservers + + + Lists servers the bot is on with some basic info. 15 per page. + + + `{0}listservers 3` + + + hentaibomb + + + Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. + + + `{0}hentaibomb yuri` + + + cleverbot + + + Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. + + + `{0}cleverbot` + + + shorten + + + Attempts to shorten an URL, if it fails, returns the input URL. + + + `{0}shorten https://google.com` + + + minecraftping mcping + + + Pings a minecraft server. + + + `{0}mcping 127.0.0.1:25565` + + + minecraftquery mcq + + + Finds information about a minecraft server. + + + `{0}mcq server:ip` + + + wikia + + + Gives you back a wikia link + + + `{0}wikia mtg Vigilance` or `{0}wikia mlp Dashy` + + + yandere + + + Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}yandere tag1+tag2` + + + magicthegathering mtg + + + Searches for a Magic The Gathering card. + + + `{0}magicthegathering about face` or `{0}mtg about face` + + + yodify yoda + + + Translates your normal sentences into Yoda styled sentences! + + + `{0}yoda my feelings hurt` + + + attack + + + Attacks a target with the given move. Use `{0}movelist` to see a list of moves your type can use. + + + `{0}attack "vine whip" @someguy` + + + heal + + + Heals someone. Revives those who fainted. Costs a NadekoFlower + + + `{0}heal @someone` + + + movelist ml + + + Lists the moves you are able to use + + + `{0}ml` + + + settype + + + Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. + + + `{0}settype fire` or `{0}settype` + + + type + + + Get the poketype of the target. + + + `{0}type @someone` + + + hangmanlist + + + Shows a list of hangman term types. + + + `{0} hangmanlist` + + + hangman + + + Starts a game of hangman in the channel. Use `{0}hangmanlist` to see a list of available term types. Defaults to 'all'. + + + `{0}hangman` or `{0}hangman movies` + + + crstatsclear + + + Resets the counters on `{0}crstats`. You can specify a trigger to clear stats only for that trigger. + + + `{0}crstatsclear` or `{0}crstatsclear rng` + + + crstats + + + Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `{0}crstatsclear` to reset the counters. + + + `{0}crstats` or `{0}crstats 3` + + + overwatch ow + + + Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` + + + `{0}ow us Battletag#1337` or `{0}overwatch eu Battletag#2016` + + + acrophobia acro + + + Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) + + + `{0}acro` or `{0}acro 30` + + + logevents + + + Shows a list of all events you can subscribe to with `{0}log` + + + `{0}logevents` + + + log + + + Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `{0}logevents` to see a list of all events you can subscribe to. + + + `{0}log userpresence` or `{0}log userbanned` + + + fairplay fp + + + Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. + + + `{0}fp` + + + define def + + + Finds a definition of a word. + + + `{0}def heresy` + + + setmaxplaytime smp + + + Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. + + + `{0}smp 0` or `{0}smp 270` + + + activity + + + Checks for spammers. + + + `{0}activity` + + + autohentai + + + Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. + + + `{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai` + + + setstatus + + + Sets the bot's status. (Online/Idle/Dnd/Invisible) + + + `{0}setstatus Idle` + + + rotaterolecolor rrc + + + Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. + + + `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` + + + createinvite crinv + + + Creates a new invite which has infinite max uses and never expires. + + + `{0}crinv` + + + pollstats + + + Shows the poll results without stopping the poll on this server. + + + `{0}pollstats` + + + repeatlist replst + + + Shows currently repeating messages and their indexes. + + + `{0}repeatlist` + + + repeatremove reprm + + + Removes a repeating message on a specified index. Use `{0}repeatlist` to see indexes. + + + `{0}reprm 2` + + + antilist antilst + + + Shows currently enabled protection features. + + + `{0}antilist` + + + antispamignore + + + Toggles whether antispam ignores current channel. Antispam must be enabled. + + + `{0}antispamignore` + + + cmdcosts + + + Shows a list of command costs. Paginated with 9 command per page. + + + `{0}cmdcosts` or `{0}cmdcosts 2` + + + commandcost cmdcost + + + Sets a price for a command. Running that command will take currency from users. Set 0 to remove the price. + + + `{0}cmdcost 0 !!q` or `{0}cmdcost 1 >8ball` + + + startevent + + + Starts one of the events seen on public nadeko. + + + `{0}startevent flowerreaction` + + + slotstats + + + Shows the total stats of the slot command for this bot's session. + + + `{0}slotstats` + + + slottest + + + Tests to see how much slots payout for X number of plays. + + + `{0}slottest 1000` + + + slot + + + Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. + + + `{0}slot 5` + + + affinity + + + Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `{0}claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. + + + `{0}affinity @MyHusband` or `{0}affinity` + + + claimwaifu claim + + + Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `{0}affinity` towards you. + + + `{0}claim 50 @Himesama` + + + waifus waifulb + + + Shows top 9 waifus. + + + `{0}waifus` + + + divorce + + + Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. + + + `{0}divorce @CheatingSloot` + + + waifuinfo waifustats + + + Shows waifu stats for a target person. Defaults to you if no user is provided. + + + `{0}waifuinfo @MyCrush` or `{0}waifuinfo` + + + mal + + + Shows basic info from myanimelist profile. + + + `{0}mal straysocks` + + + setmusicchannel smch + + + Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. + + + `{0}smch` + + + reloadimages + + + Reloads images bot is using. Safe to use even when bot is being used heavily. + + + `{0}reloadimages` + + + shardstats + + + Stats for shards. Paginated with 25 shards per page. + + + `{0}shardstats` or `{0}shardstats 2` + + + connectshard + + + Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors. + + + `{0}connectshard 2` + + + shardid + + + Shows which shard is a certain guild on, by guildid. + + + `{0}shardid 117523346618318850` + + + tictactoe ttt + + + Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. + + + >ttt + + + timezones + + + List of all timezones available on the system to be used with `{0}timezone`. + + + `{0}timezones` + + + timezone + + + Sets this guilds timezone. This affects bot's time output in this server (logs, etc..) + + + `{0}timezone` + + + langsetdefault langsetd + + + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. + + + `{0}langsetd en-US` or `{0}langsetd default` + + + languageset langset + + + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. + + + `{0}langset de-DE ` or `{0}langset default` + + + languageslist langli + + + List of languages for which translation (or part of it) exist atm. + + + `{0}langli` + + + rategirl + + + Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart. + + + `{0}rategirl @SomeGurl` + + \ No newline at end of file diff --git a/src/NadekoBot/Resources/CommandStrings.nl-NL.resx b/src/NadekoBot/Resources/CommandStrings.nl-NL.resx new file mode 100644 index 00000000..662975cf --- /dev/null +++ b/src/NadekoBot/Resources/CommandStrings.nl-NL.resx @@ -0,0 +1,3153 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + help h + + + Either shows a help for a single command, or DMs you help link if no arguments are specified. + + + `{0}h !!q` or `{0}h` + + + hgit + + + Generates the commandlist.md file. + + + `{0}hgit` + + + donate + + + Instructions for helping the project financially. + + + `{0}donate` + + + modules mdls + + + Lists all bot modules. + + + `{0}modules` + + + commands cmds + + + List all of the bot's commands from a certain module. You can either specify full, or only first few letters of the module name. + + + `{0}commands Administration` or `{0}cmds Admin` + + + greetdel grdel + + + Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. + + + `{0}greetdel 0` or `{0}greetdel 30` + + + greet + + + Toggles anouncements on the current channel when someone joins the server. + + + `{0}greet` + + + greetmsg + + + Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. + + + `{0}greetmsg Welcome, %user%.` + + + bye + + + Toggles anouncements on the current channel when someone leaves the server. + + + `{0}bye` + + + byemsg + + + Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. + + + `{0}byemsg %user% has left.` + + + byedel + + + Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. + + + `{0}byedel 0` or `{0}byedel 30` + + + greetdm + + + Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). + + + `{0}greetdm` + + + logserver + + + Enables or Disables ALL log events. If enabled, all log events will log to this channel. + + + `{0}logserver enable` or `{0}logserver disable` + + + logignore + + + Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. + + + `{0}logignore` + + + userpresence + + + Starts logging to this channel when someone from the server goes online/offline/idle. + + + `{0}userpresence` + + + voicepresence + + + Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. + + + `{0}voicepresence` + + + repeatinvoke repinv + + + Immediately shows the repeat message on a certain index and restarts its timer. + + + `{0}repinv 1` + + + repeat + + + Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. + + + `{0}repeat 5 Hello there` + + + rotateplaying ropl + + + Toggles rotation of playing status of the dynamic strings you previously specified. + + + `{0}ropl` + + + addplaying adpl + + + Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds% + + + `{0}adpl` + + + listplaying lipl + + + Lists all playing statuses with their corresponding number. + + + `{0}lipl` + + + removeplaying rmpl repl + + + Removes a playing string on a given number. + + + `{0}rmpl` + + + slowmode + + + Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. + + + `{0}slowmode 1 5` or `{0}slowmode` + + + cleanvplust cv+t + + + Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. + + + `{0}cleanv+t` + + + voice+text v+t + + + Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. + + + `{0}v+t` + + + scsc + + + Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. + + + `{0}scsc` + + + jcsc + + + Joins current channel to an instance of cross server channel using the token. + + + `{0}jcsc TokenHere` + + + lcsc + + + Leaves Cross server channel instance from this channel. + + + `{0}lcsc` + + + asar + + + Adds a role to the list of self-assignable roles. + + + `{0}asar Gamer` + + + rsar + + + Removes a specified role from the list of self-assignable roles. + + + `{0}rsar` + + + lsar + + + Lists all self-assignable roles. + + + `{0}lsar` + + + togglexclsar tesar + + + Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) + + + `{0}tesar` + + + iam + + + Adds a role to you that you choose. Role must be on a list of self-assignable roles. + + + `{0}iam Gamer` + + + iamnot iamn + + + Removes a role to you that you choose. Role must be on a list of self-assignable roles. + + + `{0}iamn Gamer` + + + addcustreact acr + + + Add a custom reaction with a trigger and a response. Running this command in server requires Administration permission. Running this command in DM is Bot Owner only and adds a new global custom reaction. Guide here: <http://nadekobot.readthedocs.io/en/latest/Custom%20Reactions/> + + + `{0}acr "hello" Hi there %user%` + + + listcustreact lcr + + + Lists global or server custom reactions (20 commands per page). Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. Specifying `all` argument instead of the number will DM you a text file with a list of all custom reactions. + + + `{0}lcr 1` or `{0}lcr all` + + + listcustreactg lcrg + + + Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. + + + `{0}lcrg 1` + + + showcustreact scr + + + Shows a custom reaction's response on a given ID. + + + `{0}scr 1` + + + delcustreact dcr + + + Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. + + + `{0}dcr 5` + + + autoassignrole aar + + + Automaticaly assigns a specified role to every user who joins the server. + + + `{0}aar` to disable, `{0}aar Role Name` to enable + + + leave + + + Makes Nadeko leave the server. Either name or id required. + + + `{0}leave 123123123331` + + + delmsgoncmd + + + Toggles the automatic deletion of user's successful command message to prevent chat flood. + + + `{0}delmsgoncmd` + + + restart + + + Restarts the bot. Might not work. + + + `{0}restart` + + + setrole sr + + + Sets a role for a given user. + + + `{0}sr @User Guest` + + + removerole rr + + + Removes a role from a given user. + + + `{0}rr @User Admin` + + + renamerole renr + + + Renames a role. Roles you are renaming must be lower than bot's highest role. + + + `{0}renr "First role" SecondRole` + + + removeallroles rar + + + Removes all roles from a mentioned user. + + + `{0}rar @User` + + + createrole cr + + + Creates a role with a given name. + + + `{0}cr Awesome Role` + + + rolecolor rc + + + Set a role's color to the hex or 0-255 rgb color value provided. + + + `{0}rc Admin 255 200 100` or `{0}rc Admin ffba55` + + + ban b + + + Bans a user by ID or name with an optional message. + + + `{0}b "@some Guy" Your behaviour is toxic.` + + + softban sb + + + Bans and then unbans a user by ID or name with an optional message. + + + `{0}sb "@some Guy" Your behaviour is toxic.` + + + kick k + + + Kicks a mentioned user. + + + `{0}k "@some Guy" Your behaviour is toxic.` + + + mute + + + Mutes a mentioned user both from speaking and chatting. + + + `{0}mute @Someone` + + + voiceunmute + + + Gives a previously voice-muted user a permission to speak. + + + `{0}voiceunmute @Someguy` + + + deafen deaf + + + Deafens mentioned user or users. + + + `{0}deaf "@Someguy"` or `{0}deaf "@Someguy" "@Someguy"` + + + undeafen undef + + + Undeafens mentioned user or users. + + + `{0}undef "@Someguy"` or `{0}undef "@Someguy" "@Someguy"` + + + delvoichanl dvch + + + Deletes a voice channel with a given name. + + + `{0}dvch VoiceChannelName` + + + creatvoichanl cvch + + + Creates a new voice channel with a given name. + + + `{0}cvch VoiceChannelName` + + + deltxtchanl dtch + + + Deletes a text channel with a given name. + + + `{0}dtch TextChannelName` + + + creatxtchanl ctch + + + Creates a new text channel with a given name. + + + `{0}ctch TextChannelName` + + + settopic st + + + Sets a topic on the current channel. + + + `{0}st My new topic` + + + setchanlname schn + + + Changes the name of the current channel. + + + `{0}schn NewName` + + + prune clr + + + `{0}prune` removes all nadeko's messages in the last 100 messages.`{0}prune X` removes last X messages from the channel (up to 100)`{0}prune @Someone` removes all Someone's messages in the last 100 messages.`{0}prune @Someone X` removes last X 'Someone's' messages in the channel. + + + `{0}prune` or `{0}prune 5` or `{0}prune @Someone` or `{0}prune @Someone X` + + + die + + + Shuts the bot down. + + + `{0}die` + + + setname newnm + + + Gives the bot a new name. + + + `{0}newnm BotName` + + + setavatar setav + + + Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. + + + `{0}setav http://i.imgur.com/xTG3a1I.jpg` + + + setgame + + + Sets the bots game. + + + `{0}setgame with snakes` + + + send + + + Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. + + + `{0}send serverid|c:channelid message` or `{0}send serverid|u:userid message` + + + mentionrole menro + + + Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. + + + `{0}menro RoleName` + + + unstuck + + + Clears the message queue. + + + `{0}unstuck` + + + donators + + + List of lovely people who donated to keep this project alive. + + + `{0}donators` + + + donadd + + + Add a donator to the database. + + + `{0}donadd Donate Amount` + + + announce + + + Sends a message to all servers' general channel bot is connected to. + + + `{0}announce Useless spam` + + + savechat + + + Saves a number of messages to a text file and sends it to you. + + + `{0}savechat 150` + + + remind + + + Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. + + + `{0}remind me 1d5h Do something` or `{0}remind #general 1m Start now!` + + + remindtemplate + + + Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. + + + `{0}remindtemplate %user%, do %message%!` + + + serverinfo sinfo + + + Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. + + + `{0}sinfo Some Server` + + + channelinfo cinfo + + + Shows info about the channel. If no channel is supplied, it defaults to current one. + + + `{0}cinfo #some-channel` + + + userinfo uinfo + + + Shows info about the user. If no user is supplied, it defaults a user running the command. + + + `{0}uinfo @SomeUser` + + + whosplaying whpl + + + Shows a list of users who are playing the specified game. + + + `{0}whpl Overwatch` + + + inrole + + + Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission. + + + `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3` + + + checkmyperms + + + Checks your user-specific permissions on this channel. + + + `{0}checkmyperms` + + + stats + + + Shows some basic stats for Nadeko. + + + `{0}stats` + + + userid uid + + + Shows user ID. + + + `{0}uid` or `{0}uid "@SomeGuy"` + + + channelid cid + + + Shows current channel ID. + + + `{0}cid` + + + serverid sid + + + Shows current server ID. + + + `{0}sid` + + + roles + + + List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. + + + `{0}roles 2` or `{0}roles @Someone` + + + channeltopic ct + + + Sends current channel's topic as a message. + + + `{0}ct` + + + chnlfilterinv cfi + + + Toggles automatic deleting of invites posted in the channel. Does not negate the {0}srvrfilterinv enabled setting. Does not affect Bot Owner. + + + `{0}cfi` + + + srvrfilterinv sfi + + + Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. + + + `{0}sfi` + + + chnlfilterwords cfw + + + Toggles automatic deleting of messages containing banned words on the channel. Does not negate the {0}srvrfilterwords enabled setting. Does not affect bot owner. + + + `{0}cfw` + + + fw + + + Adds or removes (if it exists) a word from the list of filtered words. Use`{0}sfw` or `{0}cfw` to toggle filtering. + + + `{0}fw poop` + + + lstfilterwords lfw + + + Shows a list of filtered words. + + + `{0}lfw` + + + srvrfilterwords sfw + + + Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. + + + `{0}sfw` + + + permrole pr + + + Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. + + + `{0}pr role` + + + verbose v + + + Sets whether to show when a command/module is blocked. + + + `{0}verbose true` + + + srvrmdl sm + + + Sets a module's permission at the server level. + + + `{0}sm ModuleName enable` + + + srvrcmd sc + + + Sets a command's permission at the server level. + + + `{0}sc "command name" disable` + + + rolemdl rm + + + Sets a module's permission at the role level. + + + `{0}rm ModuleName enable MyRole` + + + rolecmd rc + + + Sets a command's permission at the role level. + + + `{0}rc "command name" disable MyRole` + + + chnlmdl cm + + + Sets a module's permission at the channel level. + + + `{0}cm ModuleName enable SomeChannel` + + + chnlcmd cc + + + Sets a command's permission at the channel level. + + + `{0}cc "command name" enable SomeChannel` + + + usrmdl um + + + Sets a module's permission at the user level. + + + `{0}um ModuleName enable SomeUsername` + + + usrcmd uc + + + Sets a command's permission at the user level. + + + `{0}uc "command name" enable SomeUsername` + + + allsrvrmdls asm + + + Enable or disable all modules for your server. + + + `{0}asm [enable/disable]` + + + allchnlmdls acm + + + Enable or disable all modules in a specified channel. + + + `{0}acm enable #SomeChannel` + + + allrolemdls arm + + + Enable or disable all modules for a specific role. + + + `{0}arm [enable/disable] MyRole` + + + ubl + + + Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. + + + `{0}ubl add @SomeUser` or `{0}ubl rem 12312312313` + + + cbl + + + Either [add]s or [rem]oves a channel specified by an ID from a blacklist. + + + `{0}cbl rem 12312312312` + + + sbl + + + Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. + + + `{0}sbl add 12312321312` or `{0}sbl rem SomeTrashServer` + + + cmdcooldown cmdcd + + + Sets a cooldown per user for a command. Set to 0 to remove the cooldown. + + + `{0}cmdcd "some cmd" 5` + + + allcmdcooldowns acmdcds + + + Shows a list of all commands and their respective cooldowns. + + + `{0}acmdcds` + + + . + + + Adds a new quote with the specified name and message. + + + `{0}. sayhi Hi` + + + .. + + + Shows a random quote with a specified name. + + + `{0}.. abc` + + + qsearch + + + Shows a random quote for a keyword that contains any text specified in the search. + + + `{0}qsearch keyword text` + + + deletequote delq + + + Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. + + + `{0}delq abc` + + + draw + + + Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. + + + `{0}draw` or `{0}draw 5` + + + shuffle sh + + + Shuffles the current playlist. + + + `{0}sh` + + + flip + + + Flips coin(s) - heads or tails, and shows an image. + + + `{0}flip` or `{0}flip 3` + + + betflip bf + + + Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner. + + + `{0}bf 5 heads` or `{0}bf 3 t` + + + roll + + + Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. + + + `{0}roll` or `{0}roll 7` or `{0}roll 3d5` or `{0}roll 5dF` + + + rolluo + + + Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. + + + `{0}rolluo` or `{0}rolluo 7` or `{0}rolluo 3d5` + + + nroll + + + Rolls in a given range. + + + `{0}nroll 5` (rolls 0-5) or `{0}nroll 5-15` + + + race + + + Starts a new animal race. + + + `{0}race` + + + joinrace jr + + + Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. + + + `{0}jr` or `{0}jr 5` + + + raffle + + + Prints a name and ID of a random user from the online list from the (optional) role. + + + `{0}raffle` or `{0}raffle RoleName` + + + give + + + Give someone a certain amount of currency. + + + `{0}give 1 "@SomeGuy"` + + + award + + + Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. + + + `{0}award 100 @person` or `{0}award 5 Role Of Gamblers` + + + take + + + Takes a certain amount of currency from someone. + + + `{0}take 1 "@someguy"` + + + betroll br + + + Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10. + + + `{0}br 5` + + + leaderboard lb + + + Displays bot currency leaderboard. + + + `{0}lb` + + + trivia t + + + Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. + + + `{0}t` or `{0}t 5 nohint` + + + tl + + + Shows a current trivia leaderboard. + + + `{0}tl` + + + tq + + + Quits current trivia after current question. + + + `{0}tq` + + + typestart + + + Starts a typing contest. + + + `{0}typestart` + + + typestop + + + Stops a typing contest on the current channel. + + + `{0}typestop` + + + typeadd + + + Adds a new article to the typing contest. + + + `{0}typeadd wordswords` + + + poll + + + Creates a poll which requires users to send the number of the voting option to the bot. + + + `{0}poll Question?;Answer1;Answ 2;A_3` + + + pollend + + + Stops active poll on this server and prints the results in this channel. + + + `{0}pollend` + + + pick + + + Picks the currency planted in this channel. 60 seconds cooldown. + + + `{0}pick` + + + plant + + + Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) + + + `{0}plant` or `{0}plant 5` + + + gencurrency gc + + + Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) + + + `{0}gc` + + + leet + + + Converts a text to leetspeak with 6 (1-6) severity levels + + + `{0}leet 3 Hello` + + + choose + + + Chooses a thing from a list of things + + + `{0}choose Get up;Sleep;Sleep more` + + + 8ball + + + Ask the 8ball a yes/no question. + + + `{0}8ball should I do something` + + + rps + + + Play a game of rocket paperclip scissors with Nadeko. + + + `{0}rps scissors` + + + linux + + + Prints a customizable Linux interjection + + + `{0}linux Spyware Windows` + + + next n + + + Goes to the next song in the queue. You have to be in the same voice channel as the bot. You can skip multiple songs, but in that case songs will not be requeued if {0}rcs or {0}rpl is enabled. + + + `{0}n` or `{0}n 5` + + + stop s + + + Stops the music and clears the playlist. Stays in the channel. + + + `{0}s` + + + destroy d + + + Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) + + + `{0}d` + + + pause p + + + Pauses or Unpauses the song. + + + `{0}p` + + + queue q yq + + + Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. + + + `{0}q Dream Of Venice` + + + soundcloudqueue sq + + + Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. + + + `{0}sq Dream Of Venice` + + + listqueue lq + + + Lists 15 currently queued songs per page. Default page is 1. + + + `{0}lq` or `{0}lq 2` + + + nowplaying np + + + Shows the song currently playing. + + + `{0}np` + + + volume vol + + + Sets the music volume 0-100% + + + `{0}vol 50` + + + defvol dv + + + Sets the default music volume when music playback is started (0-100). Persists through restarts. + + + `{0}dv 80` + + + max + + + Sets the music volume to 100%. + + + `{0}max` + + + half + + + Sets the music volume to 50%. + + + `{0}half` + + + playlist pl + + + Queues up to 500 songs from a youtube playlist specified by a link, or keywords. + + + `{0}pl playlist link or name` + + + soundcloudpl scpl + + + Queue a soundcloud playlist using a link. + + + `{0}scpl soundcloudseturl` + + + localplaylst lopl + + + Queues all songs from a directory. + + + `{0}lopl C:/music/classical` + + + radio ra + + + Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) + + + `{0}ra radio link here` + + + local lo + + + Queues a local file by specifying a full path. + + + `{0}lo C:/music/mysong.mp3` + + + move mv + + + Moves the bot to your voice channel. (works only if music is already playing) + + + `{0}mv` + + + remove rm + + + Remove a song by its # in the queue, or 'all' to remove whole queue. + + + `{0}rm 5` + + + movesong ms + + + Moves a song from one position to another. + + + `{0}ms 5>3` + + + setmaxqueue smq + + + Sets a maximum queue size. Supply 0 or no argument to have no limit. + + + `{0}smq 50` or `{0}smq` + + + cleanup + + + Cleans up hanging voice connections. + + + `{0}cleanup` + + + reptcursong rcs + + + Toggles repeat of current song. + + + `{0}rcs` + + + rpeatplaylst rpl + + + Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). + + + `{0}rpl` + + + save + + + Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. + + + `{0}save classical1` + + + load + + + Loads a saved playlist using it's ID. Use `{0}pls` to list all saved playlists and {0}save to save new ones. + + + `{0}load 5` + + + playlists pls + + + Lists all playlists. Paginated. 20 per page. Default page is 0. + + + `{0}pls 1` + + + deleteplaylist delpls + + + Deletes a saved playlist. Only if you made it or if you are the bot owner. + + + `{0}delpls animu-5` + + + goto + + + Goes to a specific time in seconds in a song. + + + `{0}goto 30` + + + autoplay ap + + + Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) + + + `{0}ap` + + + lolchamp + + + Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. + + + `{0}lolchamp Riven` or `{0}lolchamp Annie sup` + + + lolban + + + Shows top banned champions ordered by ban rate. + + + `{0}lolban` + + + hitbox hb + + + Notifies this channel when a certain user starts streaming. + + + `{0}hitbox SomeStreamer` + + + twitch tw + + + Notifies this channel when a certain user starts streaming. + + + `{0}twitch SomeStreamer` + + + beam bm + + + Notifies this channel when a certain user starts streaming. + + + `{0}beam SomeStreamer` + + + removestream rms + + + Removes notifications of a certain streamer from a certain platform on this channel. + + + `{0}rms Twitch SomeGuy` or `{0}rms Beam SomeOtherGuy` + + + liststreams ls + + + Lists all streams you are following on this server. + + + `{0}ls` + + + convert + + + Convert quantities. Use `{0}convertlist` to see supported dimensions and currencies. + + + `{0}convert m km 1000` + + + convertlist + + + List of the convertible dimensions and currencies. + + + `{0}convertlist` + + + wowjoke + + + Get one of Kwoth's penultimate WoW jokes. + + + `{0}wowjoke` + + + calculate calc + + + Evaluate a mathematical expression. + + + `{0}calc 1+1` + + + osu + + + Shows osu stats for a player. + + + `{0}osu Name` or `{0}osu Name taiko` + + + osub + + + Shows information about an osu beatmap. + + + `{0}osub https://osu.ppy.sh/s/127712` + + + osu5 + + + Displays a user's top 5 plays. + + + `{0}osu5 Name` + + + pokemon poke + + + Searches for a pokemon. + + + `{0}poke Sylveon` + + + pokemonability pokeab + + + Searches for a pokemon ability. + + + `{0}pokeab overgrow` + + + memelist + + + Pulls a list of memes you can use with `{0}memegen` from http://memegen.link/templates/ + + + `{0}memelist` + + + memegen + + + Generates a meme from memelist with top and bottom text. + + + `{0}memegen biw "gets iced coffee" "in the winter"` + + + weather we + + + Shows weather data for a specified city. You can also specify a country after a comma. + + + `{0}we Moscow, RU` + + + youtube yt + + + Searches youtubes and shows the first result + + + `{0}yt query` + + + anime ani aq + + + Queries anilist for an anime and shows the first result. + + + `{0}ani aquarion evol` + + + imdb omdb + + + Queries omdb for movies or series, show first result. + + + `{0}imdb Batman vs Superman` + + + manga mang mq + + + Queries anilist for a manga and shows the first result. + + + `{0}mq Shingeki no kyojin` + + + randomcat meow + + + Shows a random cat image. + + + `{0}meow` + + + randomdog woof + + + Shows a random dog image. + + + `{0}woof` + + + image img + + + Pulls the first image found using a search parameter. Use {0}rimg for different results. + + + `{0}img cute kitten` + + + randomimage rimg + + + Pulls a random image using a search parameter. + + + `{0}rimg cute kitten` + + + lmgtfy + + + Google something for an idiot. + + + `{0}lmgtfy query` + + + google g + + + Get a google search link for some terms. + + + `{0}google query` + + + hearthstone hs + + + Searches for a Hearthstone card and shows its image. Takes a while to complete. + + + `{0}hs Ysera` + + + urbandict ud + + + Searches Urban Dictionary for a word. + + + `{0}ud Pineapple` + + + # + + + Searches Tagdef.com for a hashtag. + + + `{0}# ff` + + + catfact + + + Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> + + + `{0}catfact` + + + yomama ym + + + Shows a random joke from <http://api.yomomma.info/> + + + `{0}ym` + + + randjoke rj + + + Shows a random joke from <http://tambal.azurewebsites.net/joke/random> + + + `{0}rj` + + + chucknorris cn + + + Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> + + + `{0}cn` + + + magicitem mi + + + Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> + + + `{0}mi` + + + revav + + + Returns a google reverse image search for someone's avatar. + + + `{0}revav "@SomeGuy"` + + + revimg + + + Returns a google reverse image search for an image from a link. + + + `{0}revimg Image link` + + + safebooru + + + Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}safebooru yuri+kissing` + + + wikipedia wiki + + + Gives you back a wikipedia link + + + `{0}wiki query` + + + color clr + + + Shows you what color corresponds to that hex. + + + `{0}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. + + + `{0}videocall "@SomeGuy"` + + + avatar av + + + Shows a mentioned person's avatar. + + + `{0}av "@SomeGuy"` + + + hentai + + + Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. + + + `{0}hentai yuri` + + + danbooru + + + Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}danbooru yuri+kissing` + + + atfbooru atf + + + Shows a random hentai image from atfbooru with a given tag. Tag is optional but preferred. + + + `{0}atfbooru yuri+kissing` + + + gelbooru + + + Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}gelbooru yuri+kissing` + + + rule34 + + + Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}rule34 yuri+kissing` + + + e621 + + + Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. + + + `{0}e621 yuri kissing` + + + cp + + + We all know where this will lead you to. + + + `{0}cp` + + + boobs + + + Real adult content. + + + `{0}boobs` + + + butts ass butt + + + Real adult content. + + + `{0}butts` or `{0}ass` + + + createwar cw + + + Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. + + + `{0}cw 15 The Enemy Clan` + + + startwar sw + + + Starts a war with a given number. + + + `{0}sw 15` + + + listwar lw + + + Shows the active war claims by a number. Shows all wars in a short way if no number is specified. + + + `{0}lw [war_number] or {0}lw` + + + claim call c + + + Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. + + + `{0}call [war_number] [base_number] [optional_other_name]` + + + claimfinish cf + + + Finish your claim with 3 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. + + + `{0}cf 1` or `{0}cf 1 5` + + + claimfinish2 cf2 + + + Finish your claim with 2 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. + + + `{0}cf2 1` or `{0}cf2 1 5` + + + claimfinish1 cf1 + + + Finish your claim with 1 star if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. + + + `{0}cf1 1` or `{0}cf1 1 5` + + + unclaim ucall uc + + + Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim + + + `{0}uc [war_number] [optional_other_name]` + + + endwar ew + + + Ends the war with a given index. + + + `{0}ew [war_number]` + + + translate trans + + + Translates from>to text. From the given language to the destination language. + + + `{0}trans en>fr Hello` + + + translangs + + + Lists the valid languages for translation. + + + `{0}translangs` + + + Sends a readme and a guide links to the channel. + + + `{0}readme` or `{0}guide` + + + readme guide + + + Shows all available operations in {0}calc command + + + `{0}calcops` + + + calcops + + + Deletes all quotes on a specified keyword. + + + `{0}delallq kek` + + + delallq daq + + + greetdmmsg + + + `{0}greetdmmsg Welcome to the server, %user%`. + + + Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. + + + Check how much currency a person has. (Defaults to yourself) + + + `{0}$$` or `{0}$$ @SomeGuy` + + + cash $$ + + + Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. + + + `{0}lp` or `{0}lp 3` + + + listperms lp + + + Enable or disable all modules for a specific user. + + + `{0}aum enable @someone` + + + allusrmdls aum + + + Moves permission from one position to another in Permissions list. + + + `{0}mp 2 4` + + + moveperm mp + + + Removes a permission from a given position in Permissions list. + + + `{0}rp 1` + + + removeperm rp + + + Migrate data from old bot configuration + + + `{0}migratedata` + + + migratedata + + + Checks if a user is online on a certain streaming platform. + + + `{0}cs twitch MyFavStreamer` + + + checkstream cs + + + showemojis se + + + Shows a name and a link to every SPECIAL emoji in the message. + + + `{0}se A message full of SPECIAL emojis` + + + shuffle sh + + + Reshuffles all cards back into the deck. + + + `{0}sh` + + + fwmsgs + + + Toggles forwarding of non-command messages sent to bot's DM to the bot owners + + + `{0}fwmsgs` + + + fwtoall + + + Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json + + + `{0}fwtoall` + + + resetperms + + + Resets BOT's permissions module on this server to the default value. + + + `{0}resetperms` + + + antiraid + + + Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) + + + `{0}antiraid 5 20 Kick` + + + antispam + + + Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10. + + + `{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban` + + + chatmute + + + Prevents a mentioned user from chatting in text channels. + + + `{0}chatmute @Someone` + + + voicemute + + + Prevents a mentioned user from speaking in voice channels. + + + `{0}voicemute @Someone` + + + konachan + + + Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. + + + `{0}konachan yuri` + + + setmuterole + + + Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. + + + `{0}setmuterole Silenced` + + + adsarm + + + Toggles the automatic deletion of confirmations for {0}iam and {0}iamn commands. + + + `{0}adsarm` + + + setstream + + + Sets the bots stream. First argument is the twitch link, second argument is stream name. + + + `{0}setstream TWITCHLINK Hello` + + + chatunmute + + + Removes a mute role previously set on a mentioned user with `{0}chatmute` which prevented him from chatting in text channels. + + + `{0}chatunmute @Someone` + + + unmute + + + Unmutes a mentioned user previously muted with `{0}mute` command. + + + `{0}unmute @Someone` + + + xkcd + + + Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. + + + `{0}xkcd` or `{0}xkcd 1400` or `{0}xkcd latest` + + + placelist + + + Shows the list of available tags for the `{0}place` command. + + + `{0}placelist` + + + place + + + Shows a placeholder image of a given tag. Use `{0}placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. + + + `{0}place Cage` or `{0}place steven 500 400` + + + togethertube totube + + + Creates a new room on <https://togethertube.com> and shows the link in the chat. + + + `{0}totube` + + + publicpoll ppoll + + + Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. + + + `{0}ppoll Question?;Answer1;Answ 2;A_3` + + + autotranslang atl + + + Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value. + + + `{0}atl en>fr` + + + autotrans at + + + Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set "del" argument to automatically delete all translated user messages. + + + `{0}at` or `{0}at del` + + + listquotes liqu + + + `{0}liqu` or `{0}liqu 3` + + + Lists all quotes on the server ordered alphabetically. 15 Per page. + + + typedel + + + Deletes a typing article given the ID. + + + `{0}typedel 3` + + + typelist + + + Lists added typing articles with their IDs. 15 per page. + + + `{0}typelist` or `{0}typelist 3` + + + listservers + + + Lists servers the bot is on with some basic info. 15 per page. + + + `{0}listservers 3` + + + hentaibomb + + + Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. + + + `{0}hentaibomb yuri` + + + cleverbot + + + Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. + + + `{0}cleverbot` + + + shorten + + + Attempts to shorten an URL, if it fails, returns the input URL. + + + `{0}shorten https://google.com` + + + minecraftping mcping + + + Pings a minecraft server. + + + `{0}mcping 127.0.0.1:25565` + + + minecraftquery mcq + + + Finds information about a minecraft server. + + + `{0}mcq server:ip` + + + wikia + + + Gives you back a wikia link + + + `{0}wikia mtg Vigilance` or `{0}wikia mlp Dashy` + + + yandere + + + Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) + + + `{0}yandere tag1+tag2` + + + magicthegathering mtg + + + Searches for a Magic The Gathering card. + + + `{0}magicthegathering about face` or `{0}mtg about face` + + + yodify yoda + + + Translates your normal sentences into Yoda styled sentences! + + + `{0}yoda my feelings hurt` + + + attack + + + Attacks a target with the given move. Use `{0}movelist` to see a list of moves your type can use. + + + `{0}attack "vine whip" @someguy` + + + heal + + + Heals someone. Revives those who fainted. Costs a NadekoFlower + + + `{0}heal @someone` + + + movelist ml + + + Lists the moves you are able to use + + + `{0}ml` + + + settype + + + Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. + + + `{0}settype fire` or `{0}settype` + + + type + + + Get the poketype of the target. + + + `{0}type @someone` + + + hangmanlist + + + Shows a list of hangman term types. + + + `{0} hangmanlist` + + + hangman + + + Starts a game of hangman in the channel. Use `{0}hangmanlist` to see a list of available term types. Defaults to 'all'. + + + `{0}hangman` or `{0}hangman movies` + + + crstatsclear + + + Resets the counters on `{0}crstats`. You can specify a trigger to clear stats only for that trigger. + + + `{0}crstatsclear` or `{0}crstatsclear rng` + + + crstats + + + Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `{0}crstatsclear` to reset the counters. + + + `{0}crstats` or `{0}crstats 3` + + + overwatch ow + + + Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` + + + `{0}ow us Battletag#1337` or `{0}overwatch eu Battletag#2016` + + + acrophobia acro + + + Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) + + + `{0}acro` or `{0}acro 30` + + + logevents + + + Shows a list of all events you can subscribe to with `{0}log` + + + `{0}logevents` + + + log + + + Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `{0}logevents` to see a list of all events you can subscribe to. + + + `{0}log userpresence` or `{0}log userbanned` + + + fairplay fp + + + Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. + + + `{0}fp` + + + define def + + + Finds a definition of a word. + + + `{0}def heresy` + + + setmaxplaytime smp + + + Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. + + + `{0}smp 0` or `{0}smp 270` + + + activity + + + Checks for spammers. + + + `{0}activity` + + + autohentai + + + Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. + + + `{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai` + + + setstatus + + + Sets the bot's status. (Online/Idle/Dnd/Invisible) + + + `{0}setstatus Idle` + + + rotaterolecolor rrc + + + Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. + + + `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` + + + createinvite crinv + + + Creates a new invite which has infinite max uses and never expires. + + + `{0}crinv` + + + pollstats + + + Shows the poll results without stopping the poll on this server. + + + `{0}pollstats` + + + repeatlist replst + + + Shows currently repeating messages and their indexes. + + + `{0}repeatlist` + + + repeatremove reprm + + + Removes a repeating message on a specified index. Use `{0}repeatlist` to see indexes. + + + `{0}reprm 2` + + + antilist antilst + + + Shows currently enabled protection features. + + + `{0}antilist` + + + antispamignore + + + Toggles whether antispam ignores current channel. Antispam must be enabled. + + + `{0}antispamignore` + + + cmdcosts + + + Shows a list of command costs. Paginated with 9 command per page. + + + `{0}cmdcosts` or `{0}cmdcosts 2` + + + commandcost cmdcost + + + Sets a price for a command. Running that command will take currency from users. Set 0 to remove the price. + + + `{0}cmdcost 0 !!q` or `{0}cmdcost 1 >8ball` + + + startevent + + + Starts one of the events seen on public nadeko. + + + `{0}startevent flowerreaction` + + + slotstats + + + Shows the total stats of the slot command for this bot's session. + + + `{0}slotstats` + + + slottest + + + Tests to see how much slots payout for X number of plays. + + + `{0}slottest 1000` + + + slot + + + Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. + + + `{0}slot 5` + + + affinity + + + Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `{0}claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. + + + `{0}affinity @MyHusband` or `{0}affinity` + + + claimwaifu claim + + + Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `{0}affinity` towards you. + + + `{0}claim 50 @Himesama` + + + waifus waifulb + + + Shows top 9 waifus. + + + `{0}waifus` + + + divorce + + + Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. + + + `{0}divorce @CheatingSloot` + + + waifuinfo waifustats + + + Shows waifu stats for a target person. Defaults to you if no user is provided. + + + `{0}waifuinfo @MyCrush` or `{0}waifuinfo` + + + mal + + + Shows basic info from myanimelist profile. + + + `{0}mal straysocks` + + + setmusicchannel smch + + + Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. + + + `{0}smch` + + + reloadimages + + + Reloads images bot is using. Safe to use even when bot is being used heavily. + + + `{0}reloadimages` + + + shardstats + + + Stats for shards. Paginated with 25 shards per page. + + + `{0}shardstats` or `{0}shardstats 2` + + + connectshard + + + Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors. + + + `{0}connectshard 2` + + + shardid + + + Shows which shard is a certain guild on, by guildid. + + + `{0}shardid 117523346618318850` + + + tictactoe ttt + + + Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. + + + >ttt + + + timezones + + + List of all timezones available on the system to be used with `{0}timezone`. + + + `{0}timezones` + + + timezone + + + Sets this guilds timezone. This affects bot's time output in this server (logs, etc..) + + + `{0}timezone` + + + langsetdefault langsetd + + + Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. + + + `{0}langsetd en-US` or `{0}langsetd default` + + + languageset langset + + + Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. + + + `{0}langset de-DE ` or `{0}langset default` + + + languageslist langli + + + List of languages for which translation (or part of it) exist atm. + + + `{0}langli` + + + rategirl + + + Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart. + + + `{0}rategirl @SomeGurl` + + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index 49072329..a33b43b9 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -2180,15 +2180,6 @@ namespace NadekoBot.Resources { } } - /// - /// Looks up a localized string similar to Betflip Gamble. - /// - public static string gambling_betflip_gamble { - get { - return ResourceManager.GetString("gambling_betflip_gamble", resourceCulture); - } - } - /// /// Looks up a localized string similar to Better luck next time ^_^. /// diff --git a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx new file mode 100644 index 00000000..898269f2 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx @@ -0,0 +1,2184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Esta base já está aclamada ou destruída. + + + Esta base já está destruída. + + + Esta base não está aclamada. + + + + + + + + + + + + @{0} Você já clamou esta base #{1}. Você não pode clamar uma nova. + + + + + + Inimigo + + + + Informações sobre a guerra contra {0} + + + Número de base inválido. + + + Não é um tamanho de guerra válido. + + + Lista de guerras ativas + + + não clamado + + + Você não está participando nesta guerra. + + + @{0} Você não está participando nessa guerra, ou aquela base já está destruída. + + + Nenhuma guerra ativa. + + + Tamanho + + + Guerra contra {0} já começou. + + + Guera contra {0} criada. + + + Guerra contra {0} acabou. + + + Essa guerra não existe. + + + Guerra contra {0} começou! + + + Todos os status de reações personalizadas limpados. + + + Reação personalizada deletada + + + Permissão Insuficiente. Necessita o domínio do bot para reações personalizadas globais, e administrador para reações personalizadas em server. + + + Lista de todas as reações personalizadas + + + Reações personalizadas + + + Nova reação personalizada + + + Nenhuma reação personalizada encontrada. + + + Nenhuma reação personalizada encontrada com este id. + + + Resposta + + + Status de reações customizáveis + + + Status limpado para {0} reação customizável. + + + Nenhum status para aquele comando achado, nenhuma ação foi tomada. + + + Comando + + + Autohentai parou. + + + Nenhum resultado encontrado. + + + {0} já desmaiou. + + + {0} já tem HP cheio. + + + Seu tipo já é {0} + + + usou {0}{1} em {2}{3} para {4} dano. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Você não pode atacar novamente sem retaliação! + + + Você não pode atacar a si mesmo. + + + {0} desmaiou! + + + curou {0} com uma {1} + + + {0} Tem {1} HP restante. + + + Você não pode usar {0}. Digite `{1}ml` para ver uma lista de ataques que você pode usar. + + + Lista de golpes para tipo {0} + + + Não é efetivo. + + + Você não tem {0} o suficiente + + + Reviveu {0} com uma {1} + + + Você reviveu a si mesmo com uma {0} + + + Seu tipo foi mudado de {0} para {1} + + + É mais ou menos efetivo. + + + É super efetivo! + + + Você usou muitos ataques de uma vez, então você não pode se mexer! + + + Tipo de {0} é {1} + + + Usuário não encontrado. + + + Você desmaiou, então você não pode se mexer! + + + **Cargo Automático** para novos usuários **desabilitado**. + + + **Cargo Automático** para novos usuários **habilitado** + + + Anexos + + + Avatar mudado + + + Você foi BANIDO do servidor {0}. +Razão: {1} + + + Banidos + PLURAL + + + Usuário Banido + + + Nome do bot mudado para {0}. + + + Status do bot mudado para {0}. + + + Deleção automática de mensagens de despedida foi desativado. + + + Mensagens de despedida serão deletas após {0} segundos. + + + Mensagem de despedida atual: {0} + + + Ative mensagens de despedidas digitando {0} + + + Nova mensagem de despedida colocada. + + + Mensagens de despedidas desativadas. + + + Mensagens de despedidas foram ativadas neste canal. + + + Nome do canal mudado + + + Nome antigo + + + Tópico do canal mudado + + + Limpo. + + + Conteúdo + + + Cargo {0} criado com sucesso. + + + Canal de texto {0} criado. + + + Canal de voz {0} criado. + + + Ensurdecido com sucesso. + + + Servidor {0} deletado. + + + Parou a eliminação automática de invocações de comandos bem sucedidos. + + + Agora automaticamente deletando invocações de comandos bem sucedidos + + + Canal de texto {0} deletado. + + + Canal de voz {0} deletado. + + + Mensagem direta de + + + Novo doador adicionado com sucesso. Número total doado por este usuário: {0} 👑 + + + Obrigado às pessoas listadas abaixo por fazer este projeto acontecer! + + + Vou enviar mensagens diretas a todos os donos. + + + Vou enviar mensagens diretas apenas ao primeiro dono. + + + Vou enviar mensagens diretas de agora em diante. + + + Vou parar de enviar mensagens diretas de agora em diante. + + + Eliminação automática de mensagens de boas vindas desabilitada. + + + Mensagens de boas vindas serão deletadas após {0} segundos. + + + Mensagem direta de boas vindas atual: {0} + + + Ative mensagens diretas de boas vindas digitando {0} + + + Novas mensagen direta de boas vindas definida. + + + + + + + + + Mensagem de boas vindas atual: {0} + + + Ative mensagens de boas vindas digitando {0} + + + Nova mensagem de boas vindas definida. + + + Anúncios de boas vindas desabilitados. + + + Anúncios de boas vindas habilitados neste canal. + + + Você não pode usar este comando em usuários com um cargo maior ou igual ao seu na hierarquia dos cargos. + + + Imagens carregadas após {0} segundos! + + + + + + Parâmetros inválidos. + + + {0} juntou-se a {1} + + + Você foi banido do servidor {0}. +Razão: {1} + + + Usuário Chutado + + + Lista de linguagens +{0} + + + A região do seu servidor agora é {0} - {1} + + + A região padrão do bot agora é {0} - {1} + + + A linguagem foi definida para {0} - {1} + + + Falha ao definir região. Veja a ajuda desse comando. + + + A língua do servidor está definida para: {0} - {1} + + + {0} saiu de {1} + + + Servidor {0} deixado. + + + + + + + + + + + + + + + + + + + + + + + + + + + Mensagem de {0} `[Bot Owner]` + + + Mensagem enviada. + + + {0} movido de {1} to {2} + + + Mensagem deletada em #{0} + + + Mensagem atualizada em #{0} + + + Mutados + PLURAL (users have been muted) + + + Mutado + singular "User muted." + + + Não tenho a permissão para isso, provavelmente. + + + Novo cargo mudo definido. + + + Eu preciso da permissão de **Administrador** para fazer isso. + + + Nova mensagem + + + Novo Apelido + + + Novo Tópico + + + Apelido Alterado + + + Não posso achar esse servidor + + + Nenhum shard com aquele ID foi encontrado. + + + Mensagem Antiga + + + Apelido Antigo + + + Tópico Antigo + + + Erro. Não tenho permissões suficientes. + + + As permissões para este servidor foram resetadas. + + + Proteções ativadas + + + {0} foi **desativado** neste servidor. + + + {0} Ativado + + + Erro. Preciso da permissão "Gerenciar Cargos". + + + Nenhuma proteção ativa. + + + + + + + + + O tempo deve ser entre {0} e {1} segundos. + + + Todos os cargos foram removidos do usuário {0} com sucesso. + + + Falha ao remover cargos. Eu não possuo permissões suficientes + + + + A cor do cargo {0} foi alterada. + + + Esse cargo não existe. + + + Os parâmetros especificados são inválidos. + + + Um erro ocorreu devido à cor inválida ou permissões insuficientes. + + + Cargo {0} removido do usuário {1} com sucesso. + + + Falha ao remover o cargo. Não possuo permissões suficientes. + + + Cargo renomeado. + + + Falha ao renomear o cargo. Não possuo permissões suficientes. + + + Você não pode editar cargos superiores ao seu cargo mais elevado. + + + + + + O cargo {0} foi adicionado a lista. + + + {0} não encontrado. Limpo. + + + O cargo {0} já está na lista. + + + Adicionado. + + + + + + + + + + + + + + + Você já possui o cargo {0}. + + + + + + Cargos auto-atribuíveis agora são exclusivos! + + + + + + Esse cargo não é auto-atribuível + + + Você não possui o cargo {0}. + + + + + + Não sou capaz de adicionar esse cargo a você. `Não posso adicionar cargos a donos ou outros cargos maiores que o meu cargo na hierarquia dos cargos.` + + + {0} foi removido da lista de cargos auto-aplicáveis. + + + + + + + + + + + + Falha ao adicionar o cargo. Não possuo permissões suficientes. + + + Novo avatar definido! + + + Novo nome do canal definido. + + + Novo jogo definido! + + + Nova stream definida! + + + Novo tópico do canal definido. + + + Shard {0} reconectado. + + + Reconectando shard {0}. + + + Desligando + + + Usuários não podem mandar mais de {0} mensagens a cada {1} segundos + + + Modo lento desativado. + + + Modo lento iniciado. + + + + PLURAL + + + {0} irá ignorar esse canal. + + + {0} irá deixar de ignorar esse canal. + + + Se um usuário postar {0} mensagens iguais em seguida, eu irei {1} eles. +__Canais Ignorados__: {2} + + + Canal de Texto Criado + + + Canal de Texto Destruído + + + + + + Desmutado + singular + + + Nome de usuário + + + Nome de usuário alterado + + + Usuários + + + Usuário Banido + + + {0} foi **mutado** + + + {0} foi **desmutado** + + + Usuário juntou-se + + + Usuário saiu + + + {0} foi **mutado** nos chats de voz e texto. + + + Cargo do usuário adicionado + + + Cargo do usuário removido + + + {0} agora está {1} + + + + + + {0} juntou-se ao canal de voz {1}. + + + {0} deixou o canal de voz {1}. + + + {0} moveu-se do canal de voz {1} para {2}. + + + {0} foi **mutado por voz** + + + {0} foi **desmutado por voz** + + + Canal de voz criado + + + Canal de voz destruído + + + + + + + + + + + + + + + + + + Usuário {0} do chat de texto + + + Usuário {0} dos chats de voz e texto + + + + + + + + + Usuário desbanido + + + Migração concluída! + + + + + + + + + + + + + + + Mais sorte na próxima vez ^_^ + + + Parabéns! Você ganhou {0} por rolar acima {1} + + + Baralho re-embaralhado. + + + + User flipped tails. + + + Você Adivinhou! Você ganhou {0} + + + O número especificado é inválido. Você pode girar de 1 a {0} moedas. + + + + + + Este evento está ativo por até {0} horas. + + + + + + deu {0} de presente para {1} + X has gifted 15 flowers to Y + + + {0} tem {1} + X has Y flowers + + + Cara + + + Placar de Líderes + + + + + + Você não pode apostar mais que {0} + + + Você não pode apostar menos que {0} + + + Você não tem {0} suficientes. + + + Sem cartas no baralho. + + + Usuario sorteado + + + + + + Aposta + + + WOAAHHHHHH!!! Parabéns!!! x{0} + + + + + + Wow! Que sorte! Três de um tipo! x{0} + + + Bom trabalho! Dois {0} - aposta x{1} + + + Ganhou + + + + + + + + + + + + Coroa + + + Tomou {0} de {1} com sucesso + + + Não foi possível tomar {0} de {1} porque o usuário não possuí tanto {2}! + + + + + + Proprietário do bot apenas. + + + Requer a permissão {0} do canal + + + Você pode dar suporte ao projeto no Patreon: <{0}> ou Paypal: <{1}> + + + Comandos e abreviações + + + + + + Digite `{0}h NomeDoComando` para ver a ajuda para o comando especificado. Ex: `{0}h >8ball` + + + Não consigo encontrar esse comando. Por favor, verifique se esse comando existe antes de tentar de novo. + + + Descrição + + + Você pode dar suporte ao projeto da NadekoBot por +Patreon <{0}> ou +Paypal <{1}> +Não esqueça de deixar seu nome ou id do discord na mensagem. +**Obrigado**♥️ + + + **Lista de Comandos**. <{0}> +**Guias de hosteamento e documentos podem ser encontrados aqui**. <{1}> + + + Lista de Comandos + + + Lista de Módulos + + + Digite `{0}cmds NomeDoMódulo` para receber uma lista de comandos deste módulo. Ex: `{0}cmds games` + + + Esse módulo não existe. + + + Requer a permissão {0} do servidor. + + + Tabela de Conteúdo + + + Modo de uso + + + Autohentai iniciado. Repostando a cada {0}s com uma das seguintes tags: +{1} + + + Tag + + + Corrida de Animais + + + Falha ao iniciar já que não tiveram participantes suficientes. + + + Corrida cheia! Começando imediatamente + + + {0} juntou-se como {1} + + + {0} juntou-se como {1} e apostou {2}! + + + Digite {0}jr para juntar-se a corrida. + + + Iniciando em 20 segundos ou quando estiver completa. + + + Iniciando com {0} participantes. + + + {0} como {1} ganhou a corrida! + + + {0} como {1} ganhou a corrida e {2}! + + + + + + + Someone rolled 35 + + + Dados rolados: {0} + Dice Rolled: 5 + + + Falha ao iniciar a corrida. Outra corrida provavelmente está em andamento. + + + Nenhuma raça existe neste servidor. + + + O segundo número deve ser maior que o primeiro. + + + Mudanças no Coração + + + Clamado por + + + Divórcios + + + + + + Preço + + + Nenhuma waifu foi reivindicada ainda. + + + Top Waifus + + + + + + Mudou a afinidade de {0} para {1}. + +*Isto é moralmente questionável.*🤔 + Make sure to get the formatting right, and leave the thinking emoji + + + + + + Sua afinidade foi reiniciada. Você não possui mas alguém que você goste. + + + quer ser a waifu de {0}. Aww <3 + + + clamou {0} como sua waifu por {1}! + + + Você se divorciou de uma waifu que gostava de você. Seu monstro sem coração. +{0} recebeu {1} como compensação. + + + Você não pode colocar sua afinidade em você mesmo, seu egomaníaco. + + + + + + Nenhuma waifu é tão barata. Você deve pagar pelo menos {0} para ter uma waifu, mesmo se o valor dela for menor. + + + Você deve pagar {0} ou mais para reivindicar essa waifu! + + + Essa waifu não é sua. + + + Você não pode reivindicar a si próprio. + + + Você se divorciou recentemente. Aguarde {0} horas e {1} minutos para se divorciar de novo. + + + Ninguém + + + Você se divorciou de uma waifu que não gostava de você. Você recebeu {0} de volta. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Questão + + + É um empate! ambos escolheram {0} + + + + + + + + + A Corrida de Animais já está em andamento + + + Total: {1} Média: {1} + + + Categoria + + + + + + Cleverbot ativado neste servidor. + + + + + + + + + + plural + + + + + + Falha ao carregar a questão + + + Jogo Iniciado + + + + + + + + + + + + + + + Placar de Lideres + + + Você não possui {0} suficiente + + + Sem resultados + + + pegou {0} + Kwoth picked 5* + + + {0} plantou {1} + Kwoth planted 5* + + + Trivia já está em andamento neste servidor. + + + Trivia + + + + + + Nenhuma trivia está em andamento neste servidor. + + + {0} tem {1} pontos + + + Parando após esta questão. + + + Tempo esgotado! A resposta correta era {0} + + + {0} adivinhou e VENCEU o jogo! A resposta era: {1} + + + Você não pode jogar contra si mesmo. + + + Um Jogo da Velha já está em andamento neste canal. + + + Um empate! + + + criou um Jogo da Velha. + + + {0} venceu! + + + Combinou três + + + Nenhum movimento restante! + + + Tempo Esgotado! + + + É a vez de {0} + + + {0} vs {1} + + + + + + Autoplay desabilitado. + + + Autoplay habilitado. + + + Volume padrão definido para {0}% + + + + + + + + + Música concluída. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Você precisa estar em um canal de voz nesse servidor + + + Nome + + + Tocando agora + + + Nenhum player de música ativo. + + + Nenhum resultado para a busca. + + + + + + + + + Tocando Musica + + + + + + Página {0} de Playlists Salvas + + + Playlist deletada. + + + Falha ao deletar essa playlist. Ela não existe ou você não é seu o criador. + + + + + + + + + Playlist Salva + + + + + + Fila + + + Músicas em fila + + + Fila de músicas limpa. + + + A fila está cheia em {0}/{0} + + + Música removida + context: "removed song #5" + + + Repetindo a Música Atual + + + Repetindo Playlist + + + Repetindo Faixa + + + + + + + + + Repetição de playlist desabilitada. + + + Repetição de playlist habilitada. + + + + + + + + + Musicas embaralhadas. + + + Musica movida. + + + + + + + + + + + + Volume deve estar entre 0 e 100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Negado + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Comando + Gen (of command) + + + + Gen. (of module) + + + + + + + + + + + + + + + + + + + + + + + + + Short of seconds. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Habilidades + + + Nenhum anime favorito ainda + + + + + + + + + + + + + + + + + + + + + + + + Fato + + + Capítulos + + + + + + + + + + + + + + + + + + Completado + + + Condição + + + Custo + + + Data + + + Defina + + + + + + Episódios + + + Ocorreu um erro. + + + Exemplo + + + Falha ao encontrar este animu. + + + Falha ao encontrar este mango. + + + Gêneros + + + + + + Altura/Peso + + + + + + + + + + + + Falha ao encontrar este filme. + + + + + + + + + + + + + + + + Don't translate {0}place + + + Localização + + + + + + + + + + + + Min/Max + + + Nenhum canal encontrado + + + Nenhum resultado encontrado + + + + + + Url Original + + + + + + + + + + + + Usuário não encontrado! Por favor cheque a região e a BattleTag antes de tentar de novo. + + + + + + Plataforma + + + Nenhuma habilidade encontrada. + + + Nenhum pokemon encontrado + + + Link do Perfil: + + + Qualidade + + + + + + + + + + + + Pontuação + + + + + + + + + + + + Alguma coisa deu errado + + + + + + Status + + + + + + Streamer {0} está offline + + + Streamer {0} está online com {1} espectadores + + + + + + Você não está seguindo nenhuma stream neste servidor + + + + + + + + + + + + Eu notificarei este canal quando o status mudar + + + Nascer do Sol + + + Pôr do Sol + + + Temperatura + + + Título + + + Top 3 animes favoritos: + + + Tradução: + + + Tipos + + + + + + Url + + + + + + Assistindo + + + + + + + + + Página não encontrada + + + Velocidade do Vento + + + + + + + + + + + + + /s and total need to be localized to fit the context - +`1.` + + + + + + + + + Autor + + + + + + + + + + + + Tópico do Canal + + + Comandos utilizados + + + + + + + + + + + + + + + + + + + + + + + + + + + Emojis Personalizados + + + Erro + + + + + + + + + + + + Aqui está uma lista de usuários nestes cargos + + + + + + + Invalid months value/ Invalid hours value + + + + + + + + + + + + Nenhum servidor encontrado nessa página. + + + Lista de Repetidores + + + Membros + + + Memória + + + Mensagens + + + R + + + Nome + + + Apelido + + + Ninguém está jogando esse jogo. + + + + + + Nenhum cargo nesta página. + + + + + + + + + Dono + + + Dono IDs + + + Presença + + + {0} Servidores +{1} Canais de Texto +{2} Canais de Voz + + + + + + + + + Nenhuma citação nesta página + + + Nenhuma citação que você possa remover foi encontrada + + + Citação adicionada + + + + + + Região + + + + + + + + + + + + + + + + + + Lista de repetidores + + + Nenhum repetidor neste server. + + + #{0} parou. + + + Nenhuma mensagens repetidas encontradas. + + + Resultado + + + Cargos + + + + + + + + + + + + + + + + + + {0} deste servidor é {1} + + + Informações do Servidor + + + Fragmento + + + Status de fragmento + + + + + + **Nome:** {0} **Link:** {1} + + + Nenhum emoji especial encontrado. + + + Tocando {0} canções, {1} no queue. + + + Canais de Texto + + + Aqui está o link do quarto: + + + + + + + Id of the user kwoth#1234 is 123123123123 + + + Usuários + + + Canais de Voz + + + \ No newline at end of file diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index 3dfd8571..efa1fe55 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -873,9 +873,6 @@ Reason: {1} has awarded {0} to {1} - - Betflip Gamble - Better luck next time ^_^ From c48d755c9a8585d1f152572b764022c3ab805159 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:50:52 +0100 Subject: [PATCH 227/256] Update ResponseStrings.fr-fr.resx (POEditor.com) --- .../Resources/ResponseStrings.fr-fr.resx | 134 +++++++++--------- 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx index dbce49d8..3708ba7a 100644 --- a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -118,7 +118,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Cette base a déjà été revendiquée ou détruite + Cette base a déjà été revendiquée ou détruite. Cette base est déjà détruite. @@ -130,7 +130,7 @@ Base #{0} **DETRUITE** dans une guerre contre {1} - {0} a *ABANDONNÉ* la base #{1} dans une guerre contre {2} + {0} a **ABANDONNÉ** la base #{1} dans une guerre contre {2} {0} a revendiqué une base #{1} dans une guerre contre {2} @@ -154,10 +154,10 @@ La taille de la guerre n'est pas valide. - Liste des guerres en cours. + Liste des guerres en cours - Non réclamé. + non réclamé Vous ne participez pas a cette guerre. @@ -196,8 +196,7 @@ Permissions insuffisantes. Nécessite d'être le propriétaire du Bot pour avoir les réactions personnalisées globales, et Administrateur pour les réactions personnalisées du serveur. - Liste de toutes les réactions personnalisées. - + Liste de toutes les réactions personnalisées Réactions personnalisées @@ -236,7 +235,7 @@ {0} est déjà inconscient. - {0} a toute sa vie. + {0} a tous ses PV. Votre type est déjà {0} @@ -276,7 +275,7 @@ Vous avez ressuscité {0} avec un {1} - Vous vous êtes ressuscité avec un {0}. + Vous vous êtes ressuscité avec un {0} Votre type a bien été modifié de {0} à {1} @@ -536,18 +535,18 @@ Raison : {1} Mise à jour du message dans #{0} - Tous les utilisateurs ont été mis en sourdine. + Tous les utilisateurs sont maintenant muets. PLURAL (users have been muted) - L'utilisateur à été mis en sourdine. + L'utilisateur est maintenant muet. singular "User muted." Il semblerait que je n'ai pas la permission nécessaire pour effectuer cela. - Nouveau rôle de mise en sourdine crée. + Nouveau rôle muet créé. J'ai besoin de la permission d'**Administrateur** pour effectuer cela. @@ -568,7 +567,7 @@ Raison : {1} Impossible de trouver ce serveur - Aucune partition pour cet ID trouvée. + Aucun Shard pour cet ID trouvée. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -593,16 +592,16 @@ Raison : {1} {0} a été **désactivé** sur ce serveur. - {0} activé + {0} Activé - Erreur. J'ai besoin de la permission Gérer les rôles + Erreur. J'ai besoin de la permission Gérer les rôles. Aucune protection activée. - Le seuil d'utilisateurs doit être entre {0} et {1} + Le seuil d'utilisateurs doit être entre {0} et {1}. Si {0} ou plus d'utilisateurs rejoignent dans les {1} secondes suivantes, je les {2}. @@ -623,7 +622,7 @@ Raison : {1} Ce rôle n'existe pas. - Le paramètre spécifié est invalide. + Les paramètres spécifiés sont invalides. Erreur due à un manque de permissions ou à une couleur invalide. @@ -675,13 +674,13 @@ Raison : {1} Vous avez déjà le rôle {0}. - Vous avez déjà {0} rôles exclusifs auto-affectés. + Vous avez déjà {0} rôles exclusifs auto-attribués. - Rôles auto-affectés désormais exclusifs. + Rôles auto-attribuables désormais exclusifs. - Il y a {0} rôles auto-affectés. + Il y a {0} rôles auto-attribuables. Ce rôle ne peux pas vous être attribué par vous-même. @@ -697,7 +696,7 @@ Raison : {1} Je suis incapable de vous ajouter ce rôle. `Je ne peux pas ajouter de rôles aux propriétaires et aux autres rôles plus haut que le mien dans la hiérarchie.` - {0} a été supprimé de la liste des affectations automatiques de rôle. + {0} a été supprimé de la liste des rôles auto-attribuables. Vous n'avez plus le rôle {0}. @@ -728,11 +727,11 @@ Raison : {1} Nouveau sujet du salon défini. - Partition {0} reconnectée. + Shard {0} reconnectée. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. - Partition {0} en reconnection. + Shard {0} en reconnection. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -787,10 +786,10 @@ Raison : {1} Utilisateur banni - {0} a été **mis en sourdine** sur le chat. + {0} est maintenant **muet** sur le chat. - **La parole a été rétablie** sur le chat pour {0} + **La parole a été rétablie** sur le chat pour {0}. L'utilisateur a rejoint @@ -799,7 +798,7 @@ Raison : {1} L'utilisateur a quitté - {0} a été **mis en sourdine** à la fois sur le salon textuel et vocal. + {0} est maintenant **muet** à la fois sur le salon textuel et vocal. Rôle ajouté à l'utilisateur @@ -811,7 +810,7 @@ Raison : {1} {0} est maintenant {1} - {0} n'est **plus en sourdine** des salons textuels et vocaux. + {0} n'est maintenant **plus muet** des salons textuels et vocaux. {0} a rejoint le salon vocal {1}. @@ -823,10 +822,10 @@ Raison : {1} {0} est allé du salon vocal {1} au {2}. - {0} a été **mis en sourdine**. + {0} est maintenant **muet**. - {0} a été **retiré de la sourdine** + {0} n'est maintenant **plus muet**. Salon vocal crée. @@ -880,9 +879,6 @@ Raison: {1} a récompensé {0} à {1} - - Pari à pile ou face - Meilleure chance la prochaine fois ^_^ @@ -890,10 +886,10 @@ Raison: {1} Félicitations! Vous avez gagné {0} pour avoir lancé au dessus de {1} - Deck remélangé + Deck remélangé. - Lancé {0} + Lancé {0}. User flipped tails. @@ -903,13 +899,13 @@ Raison: {1} Nombre spécifié invalide. Vous pouvez lancer 1 à {0} pièces. - Ajoute {0} réaction à ce message pour avoir {1} + Ajoute la réaction {0} à ce message pour avoir {1} Cet événement est actif pendant {0} heures. - L'événement "réactions fleuries" a démaré! + L'événement "réactions fleuries" a démarré! a donné {0} à {1} @@ -926,7 +922,7 @@ Raison: {1} Classement - {1} utilisateurs du rôle {2} ont été récompensé de {0} + {1} utilisateurs du rôle {2} ont été récompensé de {0}. Vous ne pouvez pas miser plus de {0} @@ -944,7 +940,7 @@ Raison: {1} Utilisateur tiré au sort - Vous avez roulé un {0} + Vous avez roulé un {0}. Mise @@ -989,7 +985,7 @@ Raison: {1} Propriétaire du Bot seulement - Nécessite {0} permissions du salon + Nécessite {0} permissions du salon. Vous pouvez supporter ce projet sur Patreon <{0}> ou via Paypal <{1}> @@ -1022,10 +1018,10 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. **La liste des guides et tous les documents peuvent être trouvés ici**: <{1}> - Liste Des Commandes + Liste des commandes - Liste Des Modules + Liste des modules Entrez `{0}cmds NomDuModule` pour avoir la liste des commandes de ce module. ex `{0}cmds games` @@ -1037,7 +1033,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Permission serveur {0} requise. - Table Des Matières + Table des matières Usage @@ -1126,7 +1122,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. Affinités changées de de {0} à {1}. -*C'est moralement questionnable* 🤔 +*C'est moralement discutable.* 🤔 Make sure to get the formatting right, and leave the thinking emoji @@ -1142,7 +1138,7 @@ N'oubliez pas de mettre votre nom discord ou ID dans le message. a revendiqué {0} comme sa waifu pour {1} - Vous avez divorcé avec une waifu qui vous aimais. Monstre sans cœur. {0} a reçu {1} en guise de compensation. + Vous avez divorcé avec une waifu qui vous aimais. Monstre sans cœur. {0} a reçu {1} en guise de compensation. vous ne pouvez pas vous lier d'affinité avec vous-même, espèce d'égocentrique. @@ -1224,7 +1220,7 @@ La nouvelle valeur de {0} est {1} ! Inscriptions terminées. - Une Course d'animaux est déjà en cours. + Une course d'animaux est déjà en cours. Total : {0} Moyenne : {1} @@ -1250,7 +1246,7 @@ La nouvelle valeur de {0} est {1} ! plural - Un {1} aléatoire est apparu ! Attrapez-le en entrant `{1}pick` + Un {0} aléatoire est apparu ! Attrapez-le en entrant `{1}pick` Impossible de charger une question. @@ -1262,7 +1258,7 @@ La nouvelle valeur de {0} est {1} ! Partie de pendu commencée. - Une partie de pendu est déjà en cours sur ce canal + Une partie de pendu est déjà en cours sur ce canal. Initialisation du pendu erronée. @@ -1280,7 +1276,7 @@ La nouvelle valeur de {0} est {1} ! Pas de résultat - choisi {0} + a cueilli {0} Kwoth picked 5* @@ -1297,7 +1293,7 @@ La nouvelle valeur de {0} est {1} ! {0} a deviné! La réponse était: {1} - Aucune partie de trivia en cours sur ce serveur. + Aucune partie de Trivia en cours sur ce serveur. {0} a {1} points @@ -1345,10 +1341,10 @@ La nouvelle valeur de {0} est {1} ! Tentative d'ajouter {0} à la file d'attente... - Lecture automatique désactivée + Lecture automatique désactivée. - Lecture automatique activée + Lecture automatique activée. Volume de base défini à {0}% @@ -1375,7 +1371,7 @@ La nouvelle valeur de {0} est {1} ! Id - Entrée invalide + Entrée invalide. Le temps maximum de lecture n'a désormais plus de limite. @@ -1402,7 +1398,7 @@ La nouvelle valeur de {0} est {1} ! Aucun lecteur de musique actif. - Pas de résultat + Pas de résultat. Lecteur mis sur pause. @@ -1420,7 +1416,7 @@ La nouvelle valeur de {0} est {1} ! Page {0} des listes de lecture sauvegardées - Liste de lecture supprimée + Liste de lecture supprimée. Impossible de supprimer cette liste de lecture. Soit elle n'existe pas, soit vous n'en êtes pas le créateur. @@ -1447,7 +1443,7 @@ La nouvelle valeur de {0} est {1} ! Liste d'attente effacée. - Liste d'attente complète ({0}/{0}) + Liste d'attente complète ({0}/{0}). Son retiré @@ -1732,10 +1728,10 @@ La nouvelle valeur de {0} est {1} ! Exemple - Impossible de trouver cet anime + Impossible de trouver cet anime. - Impossible de trouver ce manga + Impossible de trouver ce manga. Genres @@ -1759,7 +1755,7 @@ La nouvelle valeur de {0} est {1} ! Impossible de trouver ce film. - Langue d'origine ou de destination invalide + Langue d'origine ou de destination invalide. Blagues non chargées. @@ -1802,10 +1798,10 @@ La nouvelle valeur de {0} est {1} ! Url originale - Une clé d'API osu! est nécessaire + Une clé d'API osu! est nécessaire. - Impossible de récupérer la signature osu! + Impossible de récupérer la signature osu!. Trouvé dans {0} images. Affichage de {0} aléatoires. @@ -1821,7 +1817,7 @@ La nouvelle valeur de {0} est {1} ! Plateforme - Attaque non trouvée + Attaque non trouvée. Pokémon non trouvé. @@ -1849,7 +1845,7 @@ La nouvelle valeur de {0} est {1} ! recherche plutôt non ? - Impossible de réduire cette Url + Impossible de réduire cette Url. Url réduite @@ -1927,10 +1923,10 @@ La nouvelle valeur de {0} est {1} ! Impossible de trouver ce terme sur le wikia spécifié. - Entrez un wikia cible, suivi d'une requête de recherche + Entrez un wikia cible, suivi d'une requête de recherche. - Page non trouvée + Page non trouvée. Vitesse du vent @@ -2067,7 +2063,7 @@ OwnerID: {2} Aucun rôle sur cette page. - Aucune partition sur cette page. + Aucun shard sur cette page. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -2088,7 +2084,7 @@ OwnerID: {2} {2} Salons Vocaux - Toutes les citations possédant le mot-clé {0} ont été supprimées + Toutes les citations possédant le mot-clé {0} ont été supprimées. Page {0} des citations @@ -2121,7 +2117,7 @@ OwnerID: {2} Nouveau modèle de rappel défini. - Répétition de {0} chaque {1} jour(s), {2} heure(s) et {3} minute(s) + Répétition de {0} chaque {1} jour(s), {2} heure(s) et {3} minute(s). Liste des répétitions @@ -2163,15 +2159,15 @@ OwnerID: {2} Info du serveur - Partition + Shard Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. - Statistique des partitions + Statistique des shards Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. - La partition **#{0}** est en état {1} avec {2} serveurs. + Le shard **#{0}** est en état {1} avec {2} serveurs. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. From 120da4a604065a61cf6df289f020c5968c941f64 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:50:55 +0100 Subject: [PATCH 228/256] Update ResponseStrings.de-DE.resx (POEditor.com) --- .../Resources/ResponseStrings.de-DE.resx | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx index 7dd58187..eb4c7511 100644 --- a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx +++ b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx @@ -874,9 +874,6 @@ Grund: {1} verleiht {1} {0} - - Münzwurf-Glücksspiel - Hoffentlich haben sie beim nächsten Mal mehr Glück ^_^ @@ -1238,11 +1235,11 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Währungsgeneration in diesem Kanal aktiviert. - {0} zufällige {1} sind erschienen! Sammle sie indem sie `{2}pick` schreiben. + {0} zufällige {1} sind erschienen! Sammle sie indem sie `{2}pick` schreiben plural - Eine zufällige {0} ist erschienen! Sammle sie indem sie `{1}pick` schreiben. + Eine zufällige {0} ist erschienen! Sammle sie indem sie `{1}pick` schreiben Laden einer Frage fehlgeschlagen. @@ -1370,10 +1367,10 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Ungültige Eingabe. - Maximale Spielzeit hat kein Limit mehr + Maximale Spielzeit hat kein Limit mehr. - Maximale Spielzeit ist nun {0} Sekunden + Maximale Spielzeit ist nun {0} Sekunden. Maximale Musik-Warteschlangengröße ist nun unbegrenzt. @@ -1436,10 +1433,10 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Eingereihter Song - Musik-Warteschlange geleert + Musik-Warteschlange geleert. - Warteschlange ist voll bei {0}/{1} + Warteschlange ist voll bei {0}/{1}. Song entfernt @@ -1458,7 +1455,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Aktueller Song wird nicht mehr wiederholt. - Musikwiedergabe wiederaufgenommen + Musikwiedergabe wiederaufgenommen. Playlist-Wiederholung deaktiviert. @@ -1473,7 +1470,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Gesprungen zu `{0}:{1}` - Song gemischt + Song gemischt. Song bewegt @@ -1524,7 +1521,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu {0} mit ID {1} wurde zur Sperrliste hinzugefügt - Befehl {0} hat nun {1}s Abklingzeit + Befehl {0} hat nun {1}s Abklingzeit. Befehl {0} hat keine Abklingzeit mehr und alle laufenden Abklingzeiten wurden entfernt. @@ -1595,7 +1592,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Benutzer brauchen nun Rolle {0} um die Berechtigungen zu editieren. - Keine Berechtigung für diesen Index gefunden + Keine Berechtigung für diesen Index gefunden. Berechtigung #{0} - {1} entfernt @@ -1611,7 +1608,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Short of seconds. - Benutzung von {0} {1} wurde für diesen Server verboten + Benutzung von {0} {1} wurde für diesen Server verboten. Benutzung von {0} {1} wurde für diesen Server erlaubt. @@ -1659,7 +1656,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Ihre Automatische-Übersetzungs Sprache wurde entfernt. - Ihre Automatische-Übersetzungs Sprache wurde zu {from}>{to} gesetzt. + Ihre Automatische-Übersetzungs Sprache wurde zu {from}>{to} gesetzt Automatische Übersetzung der Nachrichten wurde auf diesem kanal gestartet. @@ -1671,7 +1668,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Schlechter Eingabeformat, oder etwas lief schief. - Konnte diese Karte nicht finden + Konnte diese Karte nicht finden. fakt @@ -1749,7 +1746,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Konnte diesen Film nicht finden. - Ungültige Quell- oder Zielsprache, + Ungültige Quell- oder Zielsprache. Witze nicht geladen. @@ -2049,7 +2046,7 @@ ID des Besitzers: {2} Niemand spielt dieses Spiel. - Keine aktiven Wiederholer + Keine aktiven Wiederholer. Keine Rollen auf dieser Seite. @@ -2138,7 +2135,7 @@ ID des Besitzers: {2} Keine Farben sind in dem Richtigen Format. Benutze zum Beispiel `#00ff00`. - Startete die Farbrotation für Rolle {0} + Startete die Farbrotation für Rolle {0}. Stoppte die Farbrotation für Rolle {0} From 8be5e0bf61b8d9ed91f46b81d8fcd540d132164b Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:50:57 +0100 Subject: [PATCH 229/256] Update ResponseStrings.ru-RU.resx (POEditor.com) --- .../Resources/ResponseStrings.ru-RU.resx | 279 ++++++++++-------- 1 file changed, 156 insertions(+), 123 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx index bf370873..d2980cc8 100644 --- a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx +++ b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx @@ -118,28 +118,34 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Эта база уже захвачена или разрушена. + Fuzzy - + Эта база уже разрушена - + Эта база не захвачена. + Fuzzy - + **РАЗРУШЕННАЯ** база #{0} ведёт войну против {1}. - + У {0} есть **НЕ ЗАХВАЧЕННАЯ** база #{1}, ведущая войну против {2} + Fuzzy - + {0} захватил базу #{1} после войны с {2} + Fuzzy - + @{0} Вы уже захватили базу #{1}. Вы не можете захватить новую базу. + Fuzzy - + Время действия запроса от @{0} на войну против {1} истёкло. + Fuzzy Враг @@ -148,31 +154,32 @@ Информация о войне против {0} - + Неправильный номер базы. - + Неправильный размер войны. Список активных войн - + не захваченная + Fuzzy - + Вы не участвуете в этой войне. - + @{0} Вы либо не участвуете в этой войне, либо эта база разрушена. - + Нет активных войн. Размер - + Война против {0} уже началась. Война против {0} была создана. @@ -185,10 +192,11 @@ Эта война не существует. - + Война против {0} началась! Вся статистика настраиваемых реакций стёрта. + Fuzzy Настраиваемая реакция удалена. @@ -211,22 +219,27 @@ Fuzzy - + Не найдено настраиваемых реакций. + Fuzzy - + Не найдено настраеваемых реакций с таким номером. + Fuzzy Ответ - + Статистика настраеваемых реакций. + Fuzzy - + Статистика удалена для настраеваемой реакции {0}. + Fuzzy - + Не найдено статистики для этого запроса, никаких действий не применено. + Fuzzy Активатор @@ -267,33 +280,41 @@ Вы не можете использовать {0}. Напишите '{1}ml', чтобы увидеть список доступных Вам приёмов. + Fuzzy Список приёмов {0} типа Эта атака не эффективна. + Fuzzy У вас не достаточно {0} воскресил {0}, использовав один {1} + Fuzzy Вы воскресили себя, использовав один {0} + Fuzzy Ваш тип изменён с {0} на {1} + Fuzzy Эта атака немного эффективна. + Fuzzy Эта атака очень эффективна! + Fuzzy Вы использовали слишком много приёмов подряд и не можете двигаться! + Fuzzy Тип {0} — {1} @@ -363,10 +384,10 @@ Тема канала сменена. - + Чат очищен. - + Содержание Успешно создана роль {0}. @@ -542,11 +563,13 @@ Заглушёны - PLURAL (users have been muted) + PLURAL (users have been muted) +Fuzzy Заглушён - singular "User muted." + singular "User muted." +Fuzzy Скорее всего, у меня нет необходимых прав. @@ -593,7 +616,7 @@ Права для этого сервера - + Активные защиты от рейдов {0} был **отключён** на этом сервере. @@ -605,7 +628,7 @@ Ошибка. Требуется право на управление ролями. - + Нет защит от рейдов Порог пользователей должен лежать между {0} и {1}. @@ -650,13 +673,13 @@ Вы не можете редактировать роли, находящиеся выше чем ваша роль. - + Удалено повторяющееся сообщение: {0} Роль {0} добавлена в лист. - + {0} не найдена. Чат очищен. Роль {0} уже есть в списке. @@ -665,16 +688,17 @@ Добавлено. - + Отключены чередующиеся статусы. - + Чередующиеся статусы отключены. - + Список чередующихся статусов: +{0} - + Чередующиеся статусы не установлены. У вас уже есть роль {0} @@ -749,7 +773,7 @@ Медленный режим включен. - + выгнаны PLURAL @@ -771,8 +795,9 @@ Отключено заглушение. - - singular + Вкл. звук + singular +Fuzzy Имя @@ -787,10 +812,12 @@ Пользователь заблокирован - + {0} получил **запрет** на разговор в чате. + Fuzzy - + {0} потерял **запрет** на разговор в чате. + Fuzzy Пользователь присоединился @@ -799,7 +826,8 @@ Пользователь вышел - + {0} получил **запрет** на разговор в текстовом и голосовом чатах. + Fuzzy Добавлена роль пользователя @@ -811,7 +839,8 @@ {0} теперь {1} - + {0} потерял **запрет** на разговор в текстовом и голосовом чатах. + Fuzzy {0} присоединился к голосовому каналу {1}. @@ -820,26 +849,27 @@ {0} покинул голосовой канал {1}. - {0} переместил {1} в голосовой канал {2}. + {0} переместил из голосового канала {1} в {2}. - + **Выключен микрофон** у {0}. + Fuzzy - + **Включён микрофон** у {0}. + Fuzzy - Голосовой канал удалён - Fuzzy + Голосовой канал создан Голосовой канал удалён - + Отключены голосовые + текстовые функции. - + Включены голосовые + текстовые функции. Нет разрешений **Управление ролями** и/или **Управление каналами**, поэтому нельзя использовать команду 'voice+text' на сервере {0}. @@ -872,17 +902,14 @@ Ошибка при переносе файлов, проверьте консоль бота для получения дальнейшей информации. - + История присутвия пользователей - + Пользователя выгнали наградил {0} пользователю {1} - - - В следующий раз повезёт ^_^ @@ -941,13 +968,13 @@ В колоде закончились карты. - + Победитель лотереи - + Вам выпало {0}. - + Ставка НИЧЕГО СЕБЕ!!! Поздраляем!!! x{0} @@ -1043,7 +1070,8 @@ Paypal <{1}> Использование - + Авто-хентай запущен. Каждые {0}с будут отправляться изображения с одним из следующих тэгов: +{1} Тэг @@ -1082,7 +1110,7 @@ Paypal <{1}> Задано неправильное число. Можно бросить {0}-{1} костей одновременно. - + выпало {0} Someone rolled 35 @@ -1099,10 +1127,11 @@ Paypal <{1}> Второе число должно быть больше первого. - + Смены чувств - + В браке + Fuzzy Разводы @@ -1172,7 +1201,7 @@ Paypal <{1}> Вы развелись с вайфу, которой Вы не нравились. Вам вернули {0}. - + 8ball Акрофобия. @@ -1355,16 +1384,19 @@ Paypal <{1}> Папка успешно добавлена в очередь воспроизведения. - + fairplay + Fuzzy Песня завершилась. - + Отключено справедливое воспроизведение. + Fuzzy - + Включено справедливое воспроизведение. + Fuzzy С момента @@ -1716,7 +1748,7 @@ Paypal <{1}> Определить: - + Брошено Эпизоды @@ -1828,11 +1860,11 @@ Paypal <{1}> Качество: - + Время игры в Быстрой Игре Is this supposed to be Overwatch Quick Play stats? - + Побед в Быстрой Игре Рейтинг: @@ -1859,7 +1891,7 @@ Paypal <{1}> Состояние - + Url Магазина Стример {0} в оффлане. @@ -1883,7 +1915,7 @@ Paypal <{1}> Стрим {0} ({1}) убран из оповещений. - + Этот канал будет оповещён, когда статус стрима изменится. Рассвет @@ -1945,7 +1977,7 @@ Paypal <{1}> `1.` - + Страница списка активности #{0} Всего {0} пользователей. @@ -2025,55 +2057,56 @@ Paypal <{1}> Имя: {0} - +Участники: {1} +IDВладельца: {2} - + На этой странице не найдено серверов. - + Список повторяемых сообщений - + Участники - + Память - + Сообщения - + Повторяемое сообщения - + Имя - + Кличка - + Никто не играет в эту игру - + Нет активных повторяемых сообщений - + На этой странице нет ролей. - + На этой странице нет Shard-ов. - + Тема не задана - + Владелец - + ID владельца - + Присутствие {0} Серверов @@ -2081,116 +2114,116 @@ Paypal <{1}> {2} Голосовых каналов - + Удалены все цитаты с ключевым словом {0}. - + Страница {0} цитат - + На этой странице нет цитат. - + Не найдено цитат, пригодных для удаления. - + Цитата добавлена - + Случайно выбранная цитата удалена. - + Регион - + Зарегистрирован - + Я напомню, чтобы {0} сделал {2} '{3:d.M.yyyy.} в {4:HH:mm}' - + Неправильный формат времени. Проверьте список команд. - + Новый образец для напоминаний задан. - + Повторяю {0} каждые {} дня(ей), {2} час(а) и {3} минут(у). - + Список повторяемых сообщений - + На этом сервере нет повторяемых сообщений. - + #{0} остановлен. - + На этом сервере не найдено повторяемых сообщений - + Результат - + Роли - + Страница #{0} всех ролей на этом сервере: - + Страница #{0} ролей для {1} - + Цвета заданы в неправильном формате. Используйте, например '#00ff00'. - + Начато чередование цветов роли {0}. - + Остановлено чередование цветов роли {0}. - + {0} этого сервера — {1} - + Информация о сервере - + Shard - + Статискика Shard-а - + Shard **#{0}** находится в состоянии {1} с {2} серверами. - + **Имя:** {0} **Link:** {1} - + Серверные emoji не найдены. - + Проигрывается {0} песен, {1} в очереди - + Текстовые каналы - + Ссылка на Вашу комнату: - + Время работы - + {} пользователя {1} — {2} Id of the user kwoth#1234 is 123123123123 - + Пользователи - + Голосовые каналы \ No newline at end of file From 1d38b92757dc2762d73dfc168a93e04d22fb35ea Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:51:00 +0100 Subject: [PATCH 230/256] Update CommandStrings.nl-NL.resx (POEditor.com) --- .../Resources/CommandStrings.nl-NL.resx | 5316 +++++++---------- 1 file changed, 2166 insertions(+), 3150 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.nl-NL.resx b/src/NadekoBot/Resources/CommandStrings.nl-NL.resx index 662975cf..d3b70713 100644 --- a/src/NadekoBot/Resources/CommandStrings.nl-NL.resx +++ b/src/NadekoBot/Resources/CommandStrings.nl-NL.resx @@ -1,3153 +1,2169 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Die basis is al veroverd of vernietigd. + + + Die basis is al vernietigd. + + + Die basis is nog niet veroverd. + + + **VERNIETIGD**basis #{0} in een oorlog tegen {1} + + + {0} heeft **ONOVERWONNEN** basis #{1} in een oorlog tegen {2} + + + {0} heeft basis #{1} overwonnen in een oorlog tegen {2} + + + @{0} Jij hebt al een basis overwonnen #{1}. Je kunt niet nog een nemen. + + + De aanvraag van @{0} voor een oorlog tegen {1} is niet meer geldig. + + + Vijand + + + Informatie over oorlog tegen {0} + + + Ongeldige basis nummer + + + Ongeldige oorlogs formaat. + + + Lijst van voorlopende oorlogen. + + + Niet veroverd. + + + Jij doet niet mee aan die oorlog. + + + @{0} Jij doet niet mee aan die oorlog, or die basis is al vernietigd. + + + Geen voorlopende oorlogen. + + + Grootte. + + + Oorlog tegen {0} is al begonnen. + + + Oorlog tegen {0} gecreëerd + + + De oorlog tegen {0} is beëindigd. + + + Die oorlog bestaat niet. + + + De oorlog tegen {0} is begonnen! + + + Alle speciale reactie statistieken zijn verwijderd. + + + Speciale Reactie verwijderdt. + + + Onvoldoende rechten. Bot Eigendom is nodig voor globale speciale reacties, en Administrator voor speciale server-reacties. + + + Lijst van alle zelf gemaakte reacties + + + Speciale Reacties + + + Nieuwe Speciale Reacties + + + Geen speciale reacties gevonden. + + + Geen speciale reacties gevonden met die ID. + + + Antwoord + + + Speciale Reactie Statistieken + + + Statistieke verwijderd voor {0} speciale reactie. + + + Geen statistieken voor die trekker gevonden, geen actie genomen. + + + Trekker + + + Autohentai gestopt. + + + Geen resultaten gevonden + + + {0} is al flauw gevallen. + + + {0} heeft al vol HP. + + + Jouw element is al {0} + + + heeft {0}{1} op {2}{3} gebruikt voor {4} schade. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Jij zal niet winnen zonder tegenstand! + + + Je kunt je zelf niet aanvallen + + + {0} is verslagen! + + + heeft {0} genezen met een {1} + + + {0} heeft nog {1} HP over + + + Je kan {0} niet gebruiken. Typ `{1}ml` om een lijst te bekijken van de aanvallen die jij kunt gebruiken + + + Aanvallijst voor {0} element + + + Het heeft weinig effect. + + + Je hebt niet genoeg {0} + + + heeft {0} herstelt met een {1} + + + Je kunt jezelf herstellen met een {0} + + + Je element is veranderd van {0} naar {1} + + + Het is een beetje effectief. + + + Het is super effectief! + + + Je hebt te veel aanvallen achter elkaar gemaakt, je kan dus niet bewegen! + + + Element van {0} is {1} + + + Gebruiker niet gevonden. + + + Je bent flauw gevallen, dus je kunt niet bewegen! + + + **Auto aanwijzing van rollen** op gebruiker is nu **uitgeschakeld**. + + + **Auto aanwijzing van rollen** op gebruiker is nu **ingeschakeld**. + + + Bestanden + + + Avatar veranderd + + + Je bent verbannen van {0} server. Reden: {1} + + + Verbannen + PLURAL + + + Gebruiker verbannen + + + Bot naam is veranderd naar {0} + + + Bot status is veranderd naar {0} + + + Automatische verwijdering van de bye berichten is uitgeschakeld. + + + Bye berichten zullen worden verwijderd na {0} seconden. + + + Momenteel is de bye message: {0} + + + Schakel de bye berichten in door {0} te typen. + + + Nieuw bye bericht is geplaatst. + + + Dag aankondigingen uitgeschakeld. + + + Dag aankondigingen ingeschakeld op dit kanaal. + + + Kanaal naam is veranderd. + + + Oude naam + + + Kanaal onderwerp is veranderd + + + Opgeruimd + + + Inhoud + + + Met success een nieuwe rol gecreëerd. + + + Tekst kanaal {0} gecreëerd. + + + Stem kanaal {0} gecreëerd. + + + Dempen succesvol. + + + Server verwijderd {0} + + + Stopt van automatische verwijdering van succesvolle reacties aanroepingen commando. + + + Verwijdert nu automatisch succesvolle commando aanroepingen + + + Tekst kanaal {0} verwijdert. + + + Stem kanaal {0} verwijdert. + + + Privé bericht van + + + Met succes een nieuwe donateur toegevoegt. Totaal gedoneerde bedrag van deze gebruiker {0} + + + Dank aan de mensen hieronder vernoemt voor het waarmaken van dit project! + + + Ik zal alle eigenaren een privé bericht sturen. + + + Ik zal een privé bericht sturen naar de eerste eigenaar + + + Ik zal privé berichten sturen vanaf nu. + + + Ik stop vanaf nu met het sturen van privé berichten. + + + Automatisch verwijderen van groet berichten is uitgeschakeld. + + + Groet berichten zullen worden verwijderd na {0} seconden. + + + Momenteen is het privé groet bericht: {0} + + + Schakel DM begroetings bericht in door {0} in te typen. + + + Nieuwe DM begroetings bericht vastgesteld. + + + DM begroetings aankondiging uitgeschakeld. + + + DM begroetings aankondiging ingeschakeld. + + + Momentele begroetings bericht: {0} + + + Schakel begroetings bericht in door {0} in te typen + + + Nieuwe begroetings message vastgesteld. + + + Begroetings aankondiging uitgeschakeld. + + + Begroetings aankondiging uitschakeld op dit kanaal. + + + Jij kunt geen commando gebruiken op gebruikers met hogere of dezelfde rol als die van jouw in de rollen hiërarchie. + + + Afbeeldingen geplaatst na {0} seconden! + + + Ongelde invoer formaat. + + + Ongeldige parameters. + + + {0} heeft {1} toegetreden. + + + Je bent weggeschopt van de {0} server. +Reden: {1} + + + Gebruiker weggeschopt. + + + Lijst van talen +{0} + + + Jouw server's plaats van handeling is nu {0} - {1} + + + Bot's standaard plaats van handeling is nu {0} - {1} + + + Bot's taal is vastgesteld van {0} - {1} + + + + + + deze server taal is gezet naar: + + + heeft verlaten + + + Heeft de server verlaten + + + + + + + + + Logging uitgeschakeld + + + + + + + + + + + + + + + + + + + + + Bericht verstuurd + + + verplaats van ... naar ... + + + bericht verwijdert in + + + bericht bijgewerkt in + + + gemute + PLURAL (users have been muted) + + + gemute + singular "User muted." + + + + + + nieuwe demp group gezet + + + Ik heb **administrator** rechten nodig voor dat + + + nieuw bericht + + + nieuwe bijnaam + + + Nieuw onderwerp + + + Bijnaam veranderd + + + Kan de server niet vinden + + + + + + Oud bericht + + + Oude bijnaam + + + Oude onderwerp + + + error hoogst waarschijnlijk niet voldoende permissie + + + de rechten op deze server zijn gereset + + + actieve bescherming + + + is ** uitgeschakeld** op deze server + + + aangezet + + + fout. ik heb managergroup rechten nodig + + + geen bescherming aangezet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + group naam veranderd + + + group naam veranderen mislukt. ik heb niet genoeg rechten + + + je kan geen groupen bijwerken die hoger zijn dan jou eigen group + + + Verwijderd van speel bericht + + + group... is toegevoegd aan de lijst + + + niet gevonden. aan het opruimen + + + group is al in de lijst + + + toegevoegd + + + + + + + + + + + + + + + je hebt al .. group + + + + + + + + + + + + deze group is niet zelf + + + + + + + + + + + + + + + je heb niet lang meer de {0} group + + + je hebt nu {0} group + + + met sucess een group {0) toegevoegd aan gebruiker {1} + + + fout bij het toevoegen van een group. je heb onvoldoende rechten + + + Nieuwe avatar gezet! + + + Nieuwe kanaal naam gezet. + + + Nieuwe game gezet! + + + Nieuwe stream gezet! + + + Nieuwe kanaal onderwerp gezet + + + + + + + + + afsluiten + + + + + + langzame mode uitgezet + + + langzame mode gestart + + + zacht-verbannen(kicked) + PLURAL + + + {0} wil deze kanaal dempen + + + + + + + + + + + + + + + + + + + singular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + User flipped tails. + + + + + + + + + + + + + + + + + + + X has gifted 15 flowers to Y + + + + X has Y flowers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Someone rolled 35 + + + + Dice Rolled: 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Make sure to get the formatting right, and leave the thinking emoji + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + plural + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kwoth picked 5* + + + + Kwoth planted 5* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + context: "removed song #5" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gen (of command) + + + + Gen. (of module) + + + + + + + + + + + + + + + + + + + + + + + + + Short of seconds. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Don't translate {0}place + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /s and total need to be localized to fit the context - +`1.` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Invalid months value/ Invalid hours value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Id of the user kwoth#1234 is 123123123123 + + + + + + + - mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array - : using a System.ComponentModel.TypeConverter - : and then encoded with base64 encoding. - --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - help h - - - Either shows a help for a single command, or DMs you help link if no arguments are specified. - - - `{0}h !!q` or `{0}h` - - - hgit - - - Generates the commandlist.md file. - - - `{0}hgit` - - - donate - - - Instructions for helping the project financially. - - - `{0}donate` - - - modules mdls - - - Lists all bot modules. - - - `{0}modules` - - - commands cmds - - - List all of the bot's commands from a certain module. You can either specify full, or only first few letters of the module name. - - - `{0}commands Administration` or `{0}cmds Admin` - - - greetdel grdel - - - Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. - - - `{0}greetdel 0` or `{0}greetdel 30` - - - greet - - - Toggles anouncements on the current channel when someone joins the server. - - - `{0}greet` - - - greetmsg - - - Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. - - - `{0}greetmsg Welcome, %user%.` - - - bye - - - Toggles anouncements on the current channel when someone leaves the server. - - - `{0}bye` - - - byemsg - - - Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. - - - `{0}byemsg %user% has left.` - - - byedel - - - Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. - - - `{0}byedel 0` or `{0}byedel 30` - - - greetdm - - - Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). - - - `{0}greetdm` - - - logserver - - - Enables or Disables ALL log events. If enabled, all log events will log to this channel. - - - `{0}logserver enable` or `{0}logserver disable` - - - logignore - - - Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. - - - `{0}logignore` - - - userpresence - - - Starts logging to this channel when someone from the server goes online/offline/idle. - - - `{0}userpresence` - - - voicepresence - - - Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. - - - `{0}voicepresence` - - - repeatinvoke repinv - - - Immediately shows the repeat message on a certain index and restarts its timer. - - - `{0}repinv 1` - - - repeat - - - Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. - - - `{0}repeat 5 Hello there` - - - rotateplaying ropl - - - Toggles rotation of playing status of the dynamic strings you previously specified. - - - `{0}ropl` - - - addplaying adpl - - - Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds% - - - `{0}adpl` - - - listplaying lipl - - - Lists all playing statuses with their corresponding number. - - - `{0}lipl` - - - removeplaying rmpl repl - - - Removes a playing string on a given number. - - - `{0}rmpl` - - - slowmode - - - Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. - - - `{0}slowmode 1 5` or `{0}slowmode` - - - cleanvplust cv+t - - - Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. - - - `{0}cleanv+t` - - - voice+text v+t - - - Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. - - - `{0}v+t` - - - scsc - - - Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. - - - `{0}scsc` - - - jcsc - - - Joins current channel to an instance of cross server channel using the token. - - - `{0}jcsc TokenHere` - - - lcsc - - - Leaves Cross server channel instance from this channel. - - - `{0}lcsc` - - - asar - - - Adds a role to the list of self-assignable roles. - - - `{0}asar Gamer` - - - rsar - - - Removes a specified role from the list of self-assignable roles. - - - `{0}rsar` - - - lsar - - - Lists all self-assignable roles. - - - `{0}lsar` - - - togglexclsar tesar - - - Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) - - - `{0}tesar` - - - iam - - - Adds a role to you that you choose. Role must be on a list of self-assignable roles. - - - `{0}iam Gamer` - - - iamnot iamn - - - Removes a role to you that you choose. Role must be on a list of self-assignable roles. - - - `{0}iamn Gamer` - - - addcustreact acr - - - Add a custom reaction with a trigger and a response. Running this command in server requires Administration permission. Running this command in DM is Bot Owner only and adds a new global custom reaction. Guide here: <http://nadekobot.readthedocs.io/en/latest/Custom%20Reactions/> - - - `{0}acr "hello" Hi there %user%` - - - listcustreact lcr - - - Lists global or server custom reactions (20 commands per page). Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. Specifying `all` argument instead of the number will DM you a text file with a list of all custom reactions. - - - `{0}lcr 1` or `{0}lcr all` - - - listcustreactg lcrg - - - Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. - - - `{0}lcrg 1` - - - showcustreact scr - - - Shows a custom reaction's response on a given ID. - - - `{0}scr 1` - - - delcustreact dcr - - - Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. - - - `{0}dcr 5` - - - autoassignrole aar - - - Automaticaly assigns a specified role to every user who joins the server. - - - `{0}aar` to disable, `{0}aar Role Name` to enable - - - leave - - - Makes Nadeko leave the server. Either name or id required. - - - `{0}leave 123123123331` - - - delmsgoncmd - - - Toggles the automatic deletion of user's successful command message to prevent chat flood. - - - `{0}delmsgoncmd` - - - restart - - - Restarts the bot. Might not work. - - - `{0}restart` - - - setrole sr - - - Sets a role for a given user. - - - `{0}sr @User Guest` - - - removerole rr - - - Removes a role from a given user. - - - `{0}rr @User Admin` - - - renamerole renr - - - Renames a role. Roles you are renaming must be lower than bot's highest role. - - - `{0}renr "First role" SecondRole` - - - removeallroles rar - - - Removes all roles from a mentioned user. - - - `{0}rar @User` - - - createrole cr - - - Creates a role with a given name. - - - `{0}cr Awesome Role` - - - rolecolor rc - - - Set a role's color to the hex or 0-255 rgb color value provided. - - - `{0}rc Admin 255 200 100` or `{0}rc Admin ffba55` - - - ban b - - - Bans a user by ID or name with an optional message. - - - `{0}b "@some Guy" Your behaviour is toxic.` - - - softban sb - - - Bans and then unbans a user by ID or name with an optional message. - - - `{0}sb "@some Guy" Your behaviour is toxic.` - - - kick k - - - Kicks a mentioned user. - - - `{0}k "@some Guy" Your behaviour is toxic.` - - - mute - - - Mutes a mentioned user both from speaking and chatting. - - - `{0}mute @Someone` - - - voiceunmute - - - Gives a previously voice-muted user a permission to speak. - - - `{0}voiceunmute @Someguy` - - - deafen deaf - - - Deafens mentioned user or users. - - - `{0}deaf "@Someguy"` or `{0}deaf "@Someguy" "@Someguy"` - - - undeafen undef - - - Undeafens mentioned user or users. - - - `{0}undef "@Someguy"` or `{0}undef "@Someguy" "@Someguy"` - - - delvoichanl dvch - - - Deletes a voice channel with a given name. - - - `{0}dvch VoiceChannelName` - - - creatvoichanl cvch - - - Creates a new voice channel with a given name. - - - `{0}cvch VoiceChannelName` - - - deltxtchanl dtch - - - Deletes a text channel with a given name. - - - `{0}dtch TextChannelName` - - - creatxtchanl ctch - - - Creates a new text channel with a given name. - - - `{0}ctch TextChannelName` - - - settopic st - - - Sets a topic on the current channel. - - - `{0}st My new topic` - - - setchanlname schn - - - Changes the name of the current channel. - - - `{0}schn NewName` - - - prune clr - - - `{0}prune` removes all nadeko's messages in the last 100 messages.`{0}prune X` removes last X messages from the channel (up to 100)`{0}prune @Someone` removes all Someone's messages in the last 100 messages.`{0}prune @Someone X` removes last X 'Someone's' messages in the channel. - - - `{0}prune` or `{0}prune 5` or `{0}prune @Someone` or `{0}prune @Someone X` - - - die - - - Shuts the bot down. - - - `{0}die` - - - setname newnm - - - Gives the bot a new name. - - - `{0}newnm BotName` - - - setavatar setav - - - Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. - - - `{0}setav http://i.imgur.com/xTG3a1I.jpg` - - - setgame - - - Sets the bots game. - - - `{0}setgame with snakes` - - - send - - - Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. - - - `{0}send serverid|c:channelid message` or `{0}send serverid|u:userid message` - - - mentionrole menro - - - Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. - - - `{0}menro RoleName` - - - unstuck - - - Clears the message queue. - - - `{0}unstuck` - - - donators - - - List of lovely people who donated to keep this project alive. - - - `{0}donators` - - - donadd - - - Add a donator to the database. - - - `{0}donadd Donate Amount` - - - announce - - - Sends a message to all servers' general channel bot is connected to. - - - `{0}announce Useless spam` - - - savechat - - - Saves a number of messages to a text file and sends it to you. - - - `{0}savechat 150` - - - remind - - - Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. - - - `{0}remind me 1d5h Do something` or `{0}remind #general 1m Start now!` - - - remindtemplate - - - Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. - - - `{0}remindtemplate %user%, do %message%!` - - - serverinfo sinfo - - - Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. - - - `{0}sinfo Some Server` - - - channelinfo cinfo - - - Shows info about the channel. If no channel is supplied, it defaults to current one. - - - `{0}cinfo #some-channel` - - - userinfo uinfo - - - Shows info about the user. If no user is supplied, it defaults a user running the command. - - - `{0}uinfo @SomeUser` - - - whosplaying whpl - - - Shows a list of users who are playing the specified game. - - - `{0}whpl Overwatch` - - - inrole - - - Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission. - - - `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3` - - - checkmyperms - - - Checks your user-specific permissions on this channel. - - - `{0}checkmyperms` - - - stats - - - Shows some basic stats for Nadeko. - - - `{0}stats` - - - userid uid - - - Shows user ID. - - - `{0}uid` or `{0}uid "@SomeGuy"` - - - channelid cid - - - Shows current channel ID. - - - `{0}cid` - - - serverid sid - - - Shows current server ID. - - - `{0}sid` - - - roles - - - List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. - - - `{0}roles 2` or `{0}roles @Someone` - - - channeltopic ct - - - Sends current channel's topic as a message. - - - `{0}ct` - - - chnlfilterinv cfi - - - Toggles automatic deleting of invites posted in the channel. Does not negate the {0}srvrfilterinv enabled setting. Does not affect Bot Owner. - - - `{0}cfi` - - - srvrfilterinv sfi - - - Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. - - - `{0}sfi` - - - chnlfilterwords cfw - - - Toggles automatic deleting of messages containing banned words on the channel. Does not negate the {0}srvrfilterwords enabled setting. Does not affect bot owner. - - - `{0}cfw` - - - fw - - - Adds or removes (if it exists) a word from the list of filtered words. Use`{0}sfw` or `{0}cfw` to toggle filtering. - - - `{0}fw poop` - - - lstfilterwords lfw - - - Shows a list of filtered words. - - - `{0}lfw` - - - srvrfilterwords sfw - - - Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. - - - `{0}sfw` - - - permrole pr - - - Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. - - - `{0}pr role` - - - verbose v - - - Sets whether to show when a command/module is blocked. - - - `{0}verbose true` - - - srvrmdl sm - - - Sets a module's permission at the server level. - - - `{0}sm ModuleName enable` - - - srvrcmd sc - - - Sets a command's permission at the server level. - - - `{0}sc "command name" disable` - - - rolemdl rm - - - Sets a module's permission at the role level. - - - `{0}rm ModuleName enable MyRole` - - - rolecmd rc - - - Sets a command's permission at the role level. - - - `{0}rc "command name" disable MyRole` - - - chnlmdl cm - - - Sets a module's permission at the channel level. - - - `{0}cm ModuleName enable SomeChannel` - - - chnlcmd cc - - - Sets a command's permission at the channel level. - - - `{0}cc "command name" enable SomeChannel` - - - usrmdl um - - - Sets a module's permission at the user level. - - - `{0}um ModuleName enable SomeUsername` - - - usrcmd uc - - - Sets a command's permission at the user level. - - - `{0}uc "command name" enable SomeUsername` - - - allsrvrmdls asm - - - Enable or disable all modules for your server. - - - `{0}asm [enable/disable]` - - - allchnlmdls acm - - - Enable or disable all modules in a specified channel. - - - `{0}acm enable #SomeChannel` - - - allrolemdls arm - - - Enable or disable all modules for a specific role. - - - `{0}arm [enable/disable] MyRole` - - - ubl - - - Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. - - - `{0}ubl add @SomeUser` or `{0}ubl rem 12312312313` - - - cbl - - - Either [add]s or [rem]oves a channel specified by an ID from a blacklist. - - - `{0}cbl rem 12312312312` - - - sbl - - - Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. - - - `{0}sbl add 12312321312` or `{0}sbl rem SomeTrashServer` - - - cmdcooldown cmdcd - - - Sets a cooldown per user for a command. Set to 0 to remove the cooldown. - - - `{0}cmdcd "some cmd" 5` - - - allcmdcooldowns acmdcds - - - Shows a list of all commands and their respective cooldowns. - - - `{0}acmdcds` - - - . - - - Adds a new quote with the specified name and message. - - - `{0}. sayhi Hi` - - - .. - - - Shows a random quote with a specified name. - - - `{0}.. abc` - - - qsearch - - - Shows a random quote for a keyword that contains any text specified in the search. - - - `{0}qsearch keyword text` - - - deletequote delq - - - Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. - - - `{0}delq abc` - - - draw - - - Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. - - - `{0}draw` or `{0}draw 5` - - - shuffle sh - - - Shuffles the current playlist. - - - `{0}sh` - - - flip - - - Flips coin(s) - heads or tails, and shows an image. - - - `{0}flip` or `{0}flip 3` - - - betflip bf - - - Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner. - - - `{0}bf 5 heads` or `{0}bf 3 t` - - - roll - - - Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. - - - `{0}roll` or `{0}roll 7` or `{0}roll 3d5` or `{0}roll 5dF` - - - rolluo - - - Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. - - - `{0}rolluo` or `{0}rolluo 7` or `{0}rolluo 3d5` - - - nroll - - - Rolls in a given range. - - - `{0}nroll 5` (rolls 0-5) or `{0}nroll 5-15` - - - race - - - Starts a new animal race. - - - `{0}race` - - - joinrace jr - - - Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. - - - `{0}jr` or `{0}jr 5` - - - raffle - - - Prints a name and ID of a random user from the online list from the (optional) role. - - - `{0}raffle` or `{0}raffle RoleName` - - - give - - - Give someone a certain amount of currency. - - - `{0}give 1 "@SomeGuy"` - - - award - - - Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. - - - `{0}award 100 @person` or `{0}award 5 Role Of Gamblers` - - - take - - - Takes a certain amount of currency from someone. - - - `{0}take 1 "@someguy"` - - - betroll br - - - Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10. - - - `{0}br 5` - - - leaderboard lb - - - Displays bot currency leaderboard. - - - `{0}lb` - - - trivia t - - - Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. - - - `{0}t` or `{0}t 5 nohint` - - - tl - - - Shows a current trivia leaderboard. - - - `{0}tl` - - - tq - - - Quits current trivia after current question. - - - `{0}tq` - - - typestart - - - Starts a typing contest. - - - `{0}typestart` - - - typestop - - - Stops a typing contest on the current channel. - - - `{0}typestop` - - - typeadd - - - Adds a new article to the typing contest. - - - `{0}typeadd wordswords` - - - poll - - - Creates a poll which requires users to send the number of the voting option to the bot. - - - `{0}poll Question?;Answer1;Answ 2;A_3` - - - pollend - - - Stops active poll on this server and prints the results in this channel. - - - `{0}pollend` - - - pick - - - Picks the currency planted in this channel. 60 seconds cooldown. - - - `{0}pick` - - - plant - - - Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) - - - `{0}plant` or `{0}plant 5` - - - gencurrency gc - - - Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) - - - `{0}gc` - - - leet - - - Converts a text to leetspeak with 6 (1-6) severity levels - - - `{0}leet 3 Hello` - - - choose - - - Chooses a thing from a list of things - - - `{0}choose Get up;Sleep;Sleep more` - - - 8ball - - - Ask the 8ball a yes/no question. - - - `{0}8ball should I do something` - - - rps - - - Play a game of rocket paperclip scissors with Nadeko. - - - `{0}rps scissors` - - - linux - - - Prints a customizable Linux interjection - - - `{0}linux Spyware Windows` - - - next n - - - Goes to the next song in the queue. You have to be in the same voice channel as the bot. You can skip multiple songs, but in that case songs will not be requeued if {0}rcs or {0}rpl is enabled. - - - `{0}n` or `{0}n 5` - - - stop s - - - Stops the music and clears the playlist. Stays in the channel. - - - `{0}s` - - - destroy d - - - Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) - - - `{0}d` - - - pause p - - - Pauses or Unpauses the song. - - - `{0}p` - - - queue q yq - - - Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. - - - `{0}q Dream Of Venice` - - - soundcloudqueue sq - - - Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. - - - `{0}sq Dream Of Venice` - - - listqueue lq - - - Lists 15 currently queued songs per page. Default page is 1. - - - `{0}lq` or `{0}lq 2` - - - nowplaying np - - - Shows the song currently playing. - - - `{0}np` - - - volume vol - - - Sets the music volume 0-100% - - - `{0}vol 50` - - - defvol dv - - - Sets the default music volume when music playback is started (0-100). Persists through restarts. - - - `{0}dv 80` - - - max - - - Sets the music volume to 100%. - - - `{0}max` - - - half - - - Sets the music volume to 50%. - - - `{0}half` - - - playlist pl - - - Queues up to 500 songs from a youtube playlist specified by a link, or keywords. - - - `{0}pl playlist link or name` - - - soundcloudpl scpl - - - Queue a soundcloud playlist using a link. - - - `{0}scpl soundcloudseturl` - - - localplaylst lopl - - - Queues all songs from a directory. - - - `{0}lopl C:/music/classical` - - - radio ra - - - Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) - - - `{0}ra radio link here` - - - local lo - - - Queues a local file by specifying a full path. - - - `{0}lo C:/music/mysong.mp3` - - - move mv - - - Moves the bot to your voice channel. (works only if music is already playing) - - - `{0}mv` - - - remove rm - - - Remove a song by its # in the queue, or 'all' to remove whole queue. - - - `{0}rm 5` - - - movesong ms - - - Moves a song from one position to another. - - - `{0}ms 5>3` - - - setmaxqueue smq - - - Sets a maximum queue size. Supply 0 or no argument to have no limit. - - - `{0}smq 50` or `{0}smq` - - - cleanup - - - Cleans up hanging voice connections. - - - `{0}cleanup` - - - reptcursong rcs - - - Toggles repeat of current song. - - - `{0}rcs` - - - rpeatplaylst rpl - - - Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). - - - `{0}rpl` - - - save - - - Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. - - - `{0}save classical1` - - - load - - - Loads a saved playlist using it's ID. Use `{0}pls` to list all saved playlists and {0}save to save new ones. - - - `{0}load 5` - - - playlists pls - - - Lists all playlists. Paginated. 20 per page. Default page is 0. - - - `{0}pls 1` - - - deleteplaylist delpls - - - Deletes a saved playlist. Only if you made it or if you are the bot owner. - - - `{0}delpls animu-5` - - - goto - - - Goes to a specific time in seconds in a song. - - - `{0}goto 30` - - - autoplay ap - - - Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) - - - `{0}ap` - - - lolchamp - - - Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. - - - `{0}lolchamp Riven` or `{0}lolchamp Annie sup` - - - lolban - - - Shows top banned champions ordered by ban rate. - - - `{0}lolban` - - - hitbox hb - - - Notifies this channel when a certain user starts streaming. - - - `{0}hitbox SomeStreamer` - - - twitch tw - - - Notifies this channel when a certain user starts streaming. - - - `{0}twitch SomeStreamer` - - - beam bm - - - Notifies this channel when a certain user starts streaming. - - - `{0}beam SomeStreamer` - - - removestream rms - - - Removes notifications of a certain streamer from a certain platform on this channel. - - - `{0}rms Twitch SomeGuy` or `{0}rms Beam SomeOtherGuy` - - - liststreams ls - - - Lists all streams you are following on this server. - - - `{0}ls` - - - convert - - - Convert quantities. Use `{0}convertlist` to see supported dimensions and currencies. - - - `{0}convert m km 1000` - - - convertlist - - - List of the convertible dimensions and currencies. - - - `{0}convertlist` - - - wowjoke - - - Get one of Kwoth's penultimate WoW jokes. - - - `{0}wowjoke` - - - calculate calc - - - Evaluate a mathematical expression. - - - `{0}calc 1+1` - - - osu - - - Shows osu stats for a player. - - - `{0}osu Name` or `{0}osu Name taiko` - - - osub - - - Shows information about an osu beatmap. - - - `{0}osub https://osu.ppy.sh/s/127712` - - - osu5 - - - Displays a user's top 5 plays. - - - `{0}osu5 Name` - - - pokemon poke - - - Searches for a pokemon. - - - `{0}poke Sylveon` - - - pokemonability pokeab - - - Searches for a pokemon ability. - - - `{0}pokeab overgrow` - - - memelist - - - Pulls a list of memes you can use with `{0}memegen` from http://memegen.link/templates/ - - - `{0}memelist` - - - memegen - - - Generates a meme from memelist with top and bottom text. - - - `{0}memegen biw "gets iced coffee" "in the winter"` - - - weather we - - - Shows weather data for a specified city. You can also specify a country after a comma. - - - `{0}we Moscow, RU` - - - youtube yt - - - Searches youtubes and shows the first result - - - `{0}yt query` - - - anime ani aq - - - Queries anilist for an anime and shows the first result. - - - `{0}ani aquarion evol` - - - imdb omdb - - - Queries omdb for movies or series, show first result. - - - `{0}imdb Batman vs Superman` - - - manga mang mq - - - Queries anilist for a manga and shows the first result. - - - `{0}mq Shingeki no kyojin` - - - randomcat meow - - - Shows a random cat image. - - - `{0}meow` - - - randomdog woof - - - Shows a random dog image. - - - `{0}woof` - - - image img - - - Pulls the first image found using a search parameter. Use {0}rimg for different results. - - - `{0}img cute kitten` - - - randomimage rimg - - - Pulls a random image using a search parameter. - - - `{0}rimg cute kitten` - - - lmgtfy - - - Google something for an idiot. - - - `{0}lmgtfy query` - - - google g - - - Get a google search link for some terms. - - - `{0}google query` - - - hearthstone hs - - - Searches for a Hearthstone card and shows its image. Takes a while to complete. - - - `{0}hs Ysera` - - - urbandict ud - - - Searches Urban Dictionary for a word. - - - `{0}ud Pineapple` - - - # - - - Searches Tagdef.com for a hashtag. - - - `{0}# ff` - - - catfact - - - Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> - - - `{0}catfact` - - - yomama ym - - - Shows a random joke from <http://api.yomomma.info/> - - - `{0}ym` - - - randjoke rj - - - Shows a random joke from <http://tambal.azurewebsites.net/joke/random> - - - `{0}rj` - - - chucknorris cn - - - Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> - - - `{0}cn` - - - magicitem mi - - - Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> - - - `{0}mi` - - - revav - - - Returns a google reverse image search for someone's avatar. - - - `{0}revav "@SomeGuy"` - - - revimg - - - Returns a google reverse image search for an image from a link. - - - `{0}revimg Image link` - - - safebooru - - - Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}safebooru yuri+kissing` - - - wikipedia wiki - - - Gives you back a wikipedia link - - - `{0}wiki query` - - - color clr - - - Shows you what color corresponds to that hex. - - - `{0}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. - - - `{0}videocall "@SomeGuy"` - - - avatar av - - - Shows a mentioned person's avatar. - - - `{0}av "@SomeGuy"` - - - hentai - - - Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. - - - `{0}hentai yuri` - - - danbooru - - - Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}danbooru yuri+kissing` - - - atfbooru atf - - - Shows a random hentai image from atfbooru with a given tag. Tag is optional but preferred. - - - `{0}atfbooru yuri+kissing` - - - gelbooru - - - Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}gelbooru yuri+kissing` - - - rule34 - - - Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}rule34 yuri+kissing` - - - e621 - - - Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. - - - `{0}e621 yuri kissing` - - - cp - - - We all know where this will lead you to. - - - `{0}cp` - - - boobs - - - Real adult content. - - - `{0}boobs` - - - butts ass butt - - - Real adult content. - - - `{0}butts` or `{0}ass` - - - createwar cw - - - Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. - - - `{0}cw 15 The Enemy Clan` - - - startwar sw - - - Starts a war with a given number. - - - `{0}sw 15` - - - listwar lw - - - Shows the active war claims by a number. Shows all wars in a short way if no number is specified. - - - `{0}lw [war_number] or {0}lw` - - - claim call c - - - Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. - - - `{0}call [war_number] [base_number] [optional_other_name]` - - - claimfinish cf - - - Finish your claim with 3 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. - - - `{0}cf 1` or `{0}cf 1 5` - - - claimfinish2 cf2 - - - Finish your claim with 2 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. - - - `{0}cf2 1` or `{0}cf2 1 5` - - - claimfinish1 cf1 - - - Finish your claim with 1 star if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. - - - `{0}cf1 1` or `{0}cf1 1 5` - - - unclaim ucall uc - - - Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim - - - `{0}uc [war_number] [optional_other_name]` - - - endwar ew - - - Ends the war with a given index. - - - `{0}ew [war_number]` - - - translate trans - - - Translates from>to text. From the given language to the destination language. - - - `{0}trans en>fr Hello` - - - translangs - - - Lists the valid languages for translation. - - - `{0}translangs` - - - Sends a readme and a guide links to the channel. - - - `{0}readme` or `{0}guide` - - - readme guide - - - Shows all available operations in {0}calc command - - - `{0}calcops` - - - calcops - - - Deletes all quotes on a specified keyword. - - - `{0}delallq kek` - - - delallq daq - - - greetdmmsg - - - `{0}greetdmmsg Welcome to the server, %user%`. - - - Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. - - - Check how much currency a person has. (Defaults to yourself) - - - `{0}$$` or `{0}$$ @SomeGuy` - - - cash $$ - - - Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. - - - `{0}lp` or `{0}lp 3` - - - listperms lp - - - Enable or disable all modules for a specific user. - - - `{0}aum enable @someone` - - - allusrmdls aum - - - Moves permission from one position to another in Permissions list. - - - `{0}mp 2 4` - - - moveperm mp - - - Removes a permission from a given position in Permissions list. - - - `{0}rp 1` - - - removeperm rp - - - Migrate data from old bot configuration - - - `{0}migratedata` - - - migratedata - - - Checks if a user is online on a certain streaming platform. - - - `{0}cs twitch MyFavStreamer` - - - checkstream cs - - - showemojis se - - - Shows a name and a link to every SPECIAL emoji in the message. - - - `{0}se A message full of SPECIAL emojis` - - - shuffle sh - - - Reshuffles all cards back into the deck. - - - `{0}sh` - - - fwmsgs - - - Toggles forwarding of non-command messages sent to bot's DM to the bot owners - - - `{0}fwmsgs` - - - fwtoall - - - Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json - - - `{0}fwtoall` - - - resetperms - - - Resets BOT's permissions module on this server to the default value. - - - `{0}resetperms` - - - antiraid - - - Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) - - - `{0}antiraid 5 20 Kick` - - - antispam - - - Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10. - - - `{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban` - - - chatmute - - - Prevents a mentioned user from chatting in text channels. - - - `{0}chatmute @Someone` - - - voicemute - - - Prevents a mentioned user from speaking in voice channels. - - - `{0}voicemute @Someone` - - - konachan - - - Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. - - - `{0}konachan yuri` - - - setmuterole - - - Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. - - - `{0}setmuterole Silenced` - - - adsarm - - - Toggles the automatic deletion of confirmations for {0}iam and {0}iamn commands. - - - `{0}adsarm` - - - setstream - - - Sets the bots stream. First argument is the twitch link, second argument is stream name. - - - `{0}setstream TWITCHLINK Hello` - - - chatunmute - - - Removes a mute role previously set on a mentioned user with `{0}chatmute` which prevented him from chatting in text channels. - - - `{0}chatunmute @Someone` - - - unmute - - - Unmutes a mentioned user previously muted with `{0}mute` command. - - - `{0}unmute @Someone` - - - xkcd - - - Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. - - - `{0}xkcd` or `{0}xkcd 1400` or `{0}xkcd latest` - - - placelist - - - Shows the list of available tags for the `{0}place` command. - - - `{0}placelist` - - - place - - - Shows a placeholder image of a given tag. Use `{0}placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. - - - `{0}place Cage` or `{0}place steven 500 400` - - - togethertube totube - - - Creates a new room on <https://togethertube.com> and shows the link in the chat. - - - `{0}totube` - - - publicpoll ppoll - - - Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. - - - `{0}ppoll Question?;Answer1;Answ 2;A_3` - - - autotranslang atl - - - Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value. - - - `{0}atl en>fr` - - - autotrans at - - - Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set "del" argument to automatically delete all translated user messages. - - - `{0}at` or `{0}at del` - - - listquotes liqu - - - `{0}liqu` or `{0}liqu 3` - - - Lists all quotes on the server ordered alphabetically. 15 Per page. - - - typedel - - - Deletes a typing article given the ID. - - - `{0}typedel 3` - - - typelist - - - Lists added typing articles with their IDs. 15 per page. - - - `{0}typelist` or `{0}typelist 3` - - - listservers - - - Lists servers the bot is on with some basic info. 15 per page. - - - `{0}listservers 3` - - - hentaibomb - - - Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. - - - `{0}hentaibomb yuri` - - - cleverbot - - - Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. - - - `{0}cleverbot` - - - shorten - - - Attempts to shorten an URL, if it fails, returns the input URL. - - - `{0}shorten https://google.com` - - - minecraftping mcping - - - Pings a minecraft server. - - - `{0}mcping 127.0.0.1:25565` - - - minecraftquery mcq - - - Finds information about a minecraft server. - - - `{0}mcq server:ip` - - - wikia - - - Gives you back a wikia link - - - `{0}wikia mtg Vigilance` or `{0}wikia mlp Dashy` - - - yandere - - - Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}yandere tag1+tag2` - - - magicthegathering mtg - - - Searches for a Magic The Gathering card. - - - `{0}magicthegathering about face` or `{0}mtg about face` - - - yodify yoda - - - Translates your normal sentences into Yoda styled sentences! - - - `{0}yoda my feelings hurt` - - - attack - - - Attacks a target with the given move. Use `{0}movelist` to see a list of moves your type can use. - - - `{0}attack "vine whip" @someguy` - - - heal - - - Heals someone. Revives those who fainted. Costs a NadekoFlower - - - `{0}heal @someone` - - - movelist ml - - - Lists the moves you are able to use - - - `{0}ml` - - - settype - - - Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. - - - `{0}settype fire` or `{0}settype` - - - type - - - Get the poketype of the target. - - - `{0}type @someone` - - - hangmanlist - - - Shows a list of hangman term types. - - - `{0} hangmanlist` - - - hangman - - - Starts a game of hangman in the channel. Use `{0}hangmanlist` to see a list of available term types. Defaults to 'all'. - - - `{0}hangman` or `{0}hangman movies` - - - crstatsclear - - - Resets the counters on `{0}crstats`. You can specify a trigger to clear stats only for that trigger. - - - `{0}crstatsclear` or `{0}crstatsclear rng` - - - crstats - - - Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `{0}crstatsclear` to reset the counters. - - - `{0}crstats` or `{0}crstats 3` - - - overwatch ow - - - Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` - - - `{0}ow us Battletag#1337` or `{0}overwatch eu Battletag#2016` - - - acrophobia acro - - - Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) - - - `{0}acro` or `{0}acro 30` - - - logevents - - - Shows a list of all events you can subscribe to with `{0}log` - - - `{0}logevents` - - - log - - - Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `{0}logevents` to see a list of all events you can subscribe to. - - - `{0}log userpresence` or `{0}log userbanned` - - - fairplay fp - - - Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. - - - `{0}fp` - - - define def - - - Finds a definition of a word. - - - `{0}def heresy` - - - setmaxplaytime smp - - - Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. - - - `{0}smp 0` or `{0}smp 270` - - - activity - - - Checks for spammers. - - - `{0}activity` - - - autohentai - - - Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. - - - `{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai` - - - setstatus - - - Sets the bot's status. (Online/Idle/Dnd/Invisible) - - - `{0}setstatus Idle` - - - rotaterolecolor rrc - - - Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. - - - `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` - - - createinvite crinv - - - Creates a new invite which has infinite max uses and never expires. - - - `{0}crinv` - - - pollstats - - - Shows the poll results without stopping the poll on this server. - - - `{0}pollstats` - - - repeatlist replst - - - Shows currently repeating messages and their indexes. - - - `{0}repeatlist` - - - repeatremove reprm - - - Removes a repeating message on a specified index. Use `{0}repeatlist` to see indexes. - - - `{0}reprm 2` - - - antilist antilst - - - Shows currently enabled protection features. - - - `{0}antilist` - - - antispamignore - - - Toggles whether antispam ignores current channel. Antispam must be enabled. - - - `{0}antispamignore` - - - cmdcosts - - - Shows a list of command costs. Paginated with 9 command per page. - - - `{0}cmdcosts` or `{0}cmdcosts 2` - - - commandcost cmdcost - - - Sets a price for a command. Running that command will take currency from users. Set 0 to remove the price. - - - `{0}cmdcost 0 !!q` or `{0}cmdcost 1 >8ball` - - - startevent - - - Starts one of the events seen on public nadeko. - - - `{0}startevent flowerreaction` - - - slotstats - - - Shows the total stats of the slot command for this bot's session. - - - `{0}slotstats` - - - slottest - - - Tests to see how much slots payout for X number of plays. - - - `{0}slottest 1000` - - - slot - - - Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. - - - `{0}slot 5` - - - affinity - - - Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `{0}claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. - - - `{0}affinity @MyHusband` or `{0}affinity` - - - claimwaifu claim - - - Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `{0}affinity` towards you. - - - `{0}claim 50 @Himesama` - - - waifus waifulb - - - Shows top 9 waifus. - - - `{0}waifus` - - - divorce - - - Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. - - - `{0}divorce @CheatingSloot` - - - waifuinfo waifustats - - - Shows waifu stats for a target person. Defaults to you if no user is provided. - - - `{0}waifuinfo @MyCrush` or `{0}waifuinfo` - - - mal - - - Shows basic info from myanimelist profile. - - - `{0}mal straysocks` - - - setmusicchannel smch - - - Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. - - - `{0}smch` - - - reloadimages - - - Reloads images bot is using. Safe to use even when bot is being used heavily. - - - `{0}reloadimages` - - - shardstats - - - Stats for shards. Paginated with 25 shards per page. - - - `{0}shardstats` or `{0}shardstats 2` - - - connectshard - - - Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors. - - - `{0}connectshard 2` - - - shardid - - - Shows which shard is a certain guild on, by guildid. - - - `{0}shardid 117523346618318850` - - - tictactoe ttt - - - Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. - - - >ttt - - - timezones - - - List of all timezones available on the system to be used with `{0}timezone`. - - - `{0}timezones` - - - timezone - - - Sets this guilds timezone. This affects bot's time output in this server (logs, etc..) - - - `{0}timezone` - - - langsetdefault langsetd - - - Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. - - - `{0}langsetd en-US` or `{0}langsetd default` - - - languageset langset - - - Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. - - - `{0}langset de-DE ` or `{0}langset default` - - - languageslist langli - - - List of languages for which translation (or part of it) exist atm. - - - `{0}langli` - - - rategirl - - - Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart. - - - `{0}rategirl @SomeGurl` - \ No newline at end of file From 5dcab616415eb135f254e8639786ad5521c09c3f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:51:02 +0100 Subject: [PATCH 231/256] Update CommandStrings.ja-JP.resx (POEditor.com) --- .../Resources/CommandStrings.ja-JP.resx | 5314 +++++++---------- 1 file changed, 2164 insertions(+), 3150 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.ja-JP.resx b/src/NadekoBot/Resources/CommandStrings.ja-JP.resx index 662975cf..17febe51 100644 --- a/src/NadekoBot/Resources/CommandStrings.ja-JP.resx +++ b/src/NadekoBot/Resources/CommandStrings.ja-JP.resx @@ -1,3153 +1,2167 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PLURAL + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PLURAL (users have been muted) + + + + singular "User muted." + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PLURAL + + + + + + + + + + + + + + + + + + + + + + singular + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + User flipped tails. + + + + + + + + + + + + + + + + + + + X has gifted 15 flowers to Y + + + + X has Y flowers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Someone rolled 35 + + + + Dice Rolled: 5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Make sure to get the formatting right, and leave the thinking emoji + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + plural + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Kwoth picked 5* + + + + Kwoth planted 5* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + context: "removed song #5" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gen (of command) + + + + Gen. (of module) + + + + + + + + + + + + + + + + + + + + + + + + + Short of seconds. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Don't translate {0}place + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /s and total need to be localized to fit the context - +`1.` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Invalid months value/ Invalid hours value + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Id of the user kwoth#1234 is 123123123123 + + + + + + + - mimetype: application/x-microsoft.net.object.bytearray.base64 - value : The object must be serialized into a byte array - : using a System.ComponentModel.TypeConverter - : and then encoded with base64 encoding. - --> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - help h - - - Either shows a help for a single command, or DMs you help link if no arguments are specified. - - - `{0}h !!q` or `{0}h` - - - hgit - - - Generates the commandlist.md file. - - - `{0}hgit` - - - donate - - - Instructions for helping the project financially. - - - `{0}donate` - - - modules mdls - - - Lists all bot modules. - - - `{0}modules` - - - commands cmds - - - List all of the bot's commands from a certain module. You can either specify full, or only first few letters of the module name. - - - `{0}commands Administration` or `{0}cmds Admin` - - - greetdel grdel - - - Sets the time it takes (in seconds) for greet messages to be auto-deleted. Set 0 to disable automatic deletion. - - - `{0}greetdel 0` or `{0}greetdel 30` - - - greet - - - Toggles anouncements on the current channel when someone joins the server. - - - `{0}greet` - - - greetmsg - - - Sets a new join announcement message which will be shown in the server's channel. Type %user% if you want to mention the new member. Using it with no message will show the current greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. - - - `{0}greetmsg Welcome, %user%.` - - - bye - - - Toggles anouncements on the current channel when someone leaves the server. - - - `{0}bye` - - - byemsg - - - Sets a new leave announcement message. Type %user% if you want to show the name the user who left. Type %id% to show id. Using this command with no message will show the current bye message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. - - - `{0}byemsg %user% has left.` - - - byedel - - - Sets the time it takes (in seconds) for bye messages to be auto-deleted. Set 0 to disable automatic deletion. - - - `{0}byedel 0` or `{0}byedel 30` - - - greetdm - - - Toggles whether the greet messages will be sent in a DM (This is separate from greet - you can have both, any or neither enabled). - - - `{0}greetdm` - - - logserver - - - Enables or Disables ALL log events. If enabled, all log events will log to this channel. - - - `{0}logserver enable` or `{0}logserver disable` - - - logignore - - - Toggles whether the .logserver command ignores this channel. Useful if you have hidden admin channel and public log channel. - - - `{0}logignore` - - - userpresence - - - Starts logging to this channel when someone from the server goes online/offline/idle. - - - `{0}userpresence` - - - voicepresence - - - Toggles logging to this channel whenever someone joins or leaves a voice channel you are currently in. - - - `{0}voicepresence` - - - repeatinvoke repinv - - - Immediately shows the repeat message on a certain index and restarts its timer. - - - `{0}repinv 1` - - - repeat - - - Repeat a message every X minutes in the current channel. You can have up to 5 repeating messages on the server in total. - - - `{0}repeat 5 Hello there` - - - rotateplaying ropl - - - Toggles rotation of playing status of the dynamic strings you previously specified. - - - `{0}ropl` - - - addplaying adpl - - - Adds a specified string to the list of playing strings to rotate. Supported placeholders: %servers%, %users%, %playing%, %queued%, %time%,%shardid%,%shardcount%, %shardguilds% - - - `{0}adpl` - - - listplaying lipl - - - Lists all playing statuses with their corresponding number. - - - `{0}lipl` - - - removeplaying rmpl repl - - - Removes a playing string on a given number. - - - `{0}rmpl` - - - slowmode - - - Toggles slowmode. Disable by specifying no parameters. To enable, specify a number of messages each user can send, and an interval in seconds. For example 1 message every 5 seconds. - - - `{0}slowmode 1 5` or `{0}slowmode` - - - cleanvplust cv+t - - - Deletes all text channels ending in `-voice` for which voicechannels are not found. Use at your own risk. - - - `{0}cleanv+t` - - - voice+text v+t - - - Creates a text channel for each voice channel only users in that voice channel can see.If you are server owner, keep in mind you will see them all the time regardless. - - - `{0}v+t` - - - scsc - - - Starts an instance of cross server channel. You will get a token as a DM that other people will use to tune in to the same instance. - - - `{0}scsc` - - - jcsc - - - Joins current channel to an instance of cross server channel using the token. - - - `{0}jcsc TokenHere` - - - lcsc - - - Leaves Cross server channel instance from this channel. - - - `{0}lcsc` - - - asar - - - Adds a role to the list of self-assignable roles. - - - `{0}asar Gamer` - - - rsar - - - Removes a specified role from the list of self-assignable roles. - - - `{0}rsar` - - - lsar - - - Lists all self-assignable roles. - - - `{0}lsar` - - - togglexclsar tesar - - - Toggles whether the self-assigned roles are exclusive. (So that any person can have only one of the self assignable roles) - - - `{0}tesar` - - - iam - - - Adds a role to you that you choose. Role must be on a list of self-assignable roles. - - - `{0}iam Gamer` - - - iamnot iamn - - - Removes a role to you that you choose. Role must be on a list of self-assignable roles. - - - `{0}iamn Gamer` - - - addcustreact acr - - - Add a custom reaction with a trigger and a response. Running this command in server requires Administration permission. Running this command in DM is Bot Owner only and adds a new global custom reaction. Guide here: <http://nadekobot.readthedocs.io/en/latest/Custom%20Reactions/> - - - `{0}acr "hello" Hi there %user%` - - - listcustreact lcr - - - Lists global or server custom reactions (20 commands per page). Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. Specifying `all` argument instead of the number will DM you a text file with a list of all custom reactions. - - - `{0}lcr 1` or `{0}lcr all` - - - listcustreactg lcrg - - - Lists global or server custom reactions (20 commands per page) grouped by trigger, and show a number of responses for each. Running the command in DM will list global custom reactions, while running it in server will list that server's custom reactions. - - - `{0}lcrg 1` - - - showcustreact scr - - - Shows a custom reaction's response on a given ID. - - - `{0}scr 1` - - - delcustreact dcr - - - Deletes a custom reaction on a specific index. If ran in DM, it is bot owner only and deletes a global custom reaction. If ran in a server, it requires Administration priviledges and removes server custom reaction. - - - `{0}dcr 5` - - - autoassignrole aar - - - Automaticaly assigns a specified role to every user who joins the server. - - - `{0}aar` to disable, `{0}aar Role Name` to enable - - - leave - - - Makes Nadeko leave the server. Either name or id required. - - - `{0}leave 123123123331` - - - delmsgoncmd - - - Toggles the automatic deletion of user's successful command message to prevent chat flood. - - - `{0}delmsgoncmd` - - - restart - - - Restarts the bot. Might not work. - - - `{0}restart` - - - setrole sr - - - Sets a role for a given user. - - - `{0}sr @User Guest` - - - removerole rr - - - Removes a role from a given user. - - - `{0}rr @User Admin` - - - renamerole renr - - - Renames a role. Roles you are renaming must be lower than bot's highest role. - - - `{0}renr "First role" SecondRole` - - - removeallroles rar - - - Removes all roles from a mentioned user. - - - `{0}rar @User` - - - createrole cr - - - Creates a role with a given name. - - - `{0}cr Awesome Role` - - - rolecolor rc - - - Set a role's color to the hex or 0-255 rgb color value provided. - - - `{0}rc Admin 255 200 100` or `{0}rc Admin ffba55` - - - ban b - - - Bans a user by ID or name with an optional message. - - - `{0}b "@some Guy" Your behaviour is toxic.` - - - softban sb - - - Bans and then unbans a user by ID or name with an optional message. - - - `{0}sb "@some Guy" Your behaviour is toxic.` - - - kick k - - - Kicks a mentioned user. - - - `{0}k "@some Guy" Your behaviour is toxic.` - - - mute - - - Mutes a mentioned user both from speaking and chatting. - - - `{0}mute @Someone` - - - voiceunmute - - - Gives a previously voice-muted user a permission to speak. - - - `{0}voiceunmute @Someguy` - - - deafen deaf - - - Deafens mentioned user or users. - - - `{0}deaf "@Someguy"` or `{0}deaf "@Someguy" "@Someguy"` - - - undeafen undef - - - Undeafens mentioned user or users. - - - `{0}undef "@Someguy"` or `{0}undef "@Someguy" "@Someguy"` - - - delvoichanl dvch - - - Deletes a voice channel with a given name. - - - `{0}dvch VoiceChannelName` - - - creatvoichanl cvch - - - Creates a new voice channel with a given name. - - - `{0}cvch VoiceChannelName` - - - deltxtchanl dtch - - - Deletes a text channel with a given name. - - - `{0}dtch TextChannelName` - - - creatxtchanl ctch - - - Creates a new text channel with a given name. - - - `{0}ctch TextChannelName` - - - settopic st - - - Sets a topic on the current channel. - - - `{0}st My new topic` - - - setchanlname schn - - - Changes the name of the current channel. - - - `{0}schn NewName` - - - prune clr - - - `{0}prune` removes all nadeko's messages in the last 100 messages.`{0}prune X` removes last X messages from the channel (up to 100)`{0}prune @Someone` removes all Someone's messages in the last 100 messages.`{0}prune @Someone X` removes last X 'Someone's' messages in the channel. - - - `{0}prune` or `{0}prune 5` or `{0}prune @Someone` or `{0}prune @Someone X` - - - die - - - Shuts the bot down. - - - `{0}die` - - - setname newnm - - - Gives the bot a new name. - - - `{0}newnm BotName` - - - setavatar setav - - - Sets a new avatar image for the NadekoBot. Argument is a direct link to an image. - - - `{0}setav http://i.imgur.com/xTG3a1I.jpg` - - - setgame - - - Sets the bots game. - - - `{0}setgame with snakes` - - - send - - - Sends a message to someone on a different server through the bot. Separate server and channel/user ids with `|` and prepend channel id with `c:` and user id with `u:`. - - - `{0}send serverid|c:channelid message` or `{0}send serverid|u:userid message` - - - mentionrole menro - - - Mentions every person from the provided role or roles (separated by a ',') on this server. Requires you to have mention everyone permission. - - - `{0}menro RoleName` - - - unstuck - - - Clears the message queue. - - - `{0}unstuck` - - - donators - - - List of lovely people who donated to keep this project alive. - - - `{0}donators` - - - donadd - - - Add a donator to the database. - - - `{0}donadd Donate Amount` - - - announce - - - Sends a message to all servers' general channel bot is connected to. - - - `{0}announce Useless spam` - - - savechat - - - Saves a number of messages to a text file and sends it to you. - - - `{0}savechat 150` - - - remind - - - Sends a message to you or a channel after certain amount of time. First argument is me/here/'channelname'. Second argument is time in a descending order (mo>w>d>h>m) example: 1w5d3h10m. Third argument is a (multiword)message. - - - `{0}remind me 1d5h Do something` or `{0}remind #general 1m Start now!` - - - remindtemplate - - - Sets message for when the remind is triggered. Available placeholders are %user% - user who ran the command, %message% - Message specified in the remind, %target% - target channel of the remind. - - - `{0}remindtemplate %user%, do %message%!` - - - serverinfo sinfo - - - Shows info about the server the bot is on. If no channel is supplied, it defaults to current one. - - - `{0}sinfo Some Server` - - - channelinfo cinfo - - - Shows info about the channel. If no channel is supplied, it defaults to current one. - - - `{0}cinfo #some-channel` - - - userinfo uinfo - - - Shows info about the user. If no user is supplied, it defaults a user running the command. - - - `{0}uinfo @SomeUser` - - - whosplaying whpl - - - Shows a list of users who are playing the specified game. - - - `{0}whpl Overwatch` - - - inrole - - - Lists every person from the provided role or roles, separated with space, on this server. You can use role IDs, role names (in quotes if it has multiple words), or role mention If the list is too long for 1 message, you must have Manage Messages permission. - - - `{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3` - - - checkmyperms - - - Checks your user-specific permissions on this channel. - - - `{0}checkmyperms` - - - stats - - - Shows some basic stats for Nadeko. - - - `{0}stats` - - - userid uid - - - Shows user ID. - - - `{0}uid` or `{0}uid "@SomeGuy"` - - - channelid cid - - - Shows current channel ID. - - - `{0}cid` - - - serverid sid - - - Shows current server ID. - - - `{0}sid` - - - roles - - - List roles on this server or a roles of a specific user if specified. Paginated. 20 roles per page. - - - `{0}roles 2` or `{0}roles @Someone` - - - channeltopic ct - - - Sends current channel's topic as a message. - - - `{0}ct` - - - chnlfilterinv cfi - - - Toggles automatic deleting of invites posted in the channel. Does not negate the {0}srvrfilterinv enabled setting. Does not affect Bot Owner. - - - `{0}cfi` - - - srvrfilterinv sfi - - - Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. - - - `{0}sfi` - - - chnlfilterwords cfw - - - Toggles automatic deleting of messages containing banned words on the channel. Does not negate the {0}srvrfilterwords enabled setting. Does not affect bot owner. - - - `{0}cfw` - - - fw - - - Adds or removes (if it exists) a word from the list of filtered words. Use`{0}sfw` or `{0}cfw` to toggle filtering. - - - `{0}fw poop` - - - lstfilterwords lfw - - - Shows a list of filtered words. - - - `{0}lfw` - - - srvrfilterwords sfw - - - Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. - - - `{0}sfw` - - - permrole pr - - - Sets a role which can change permissions. Or supply no parameters to find out the current one. Default one is 'Nadeko'. - - - `{0}pr role` - - - verbose v - - - Sets whether to show when a command/module is blocked. - - - `{0}verbose true` - - - srvrmdl sm - - - Sets a module's permission at the server level. - - - `{0}sm ModuleName enable` - - - srvrcmd sc - - - Sets a command's permission at the server level. - - - `{0}sc "command name" disable` - - - rolemdl rm - - - Sets a module's permission at the role level. - - - `{0}rm ModuleName enable MyRole` - - - rolecmd rc - - - Sets a command's permission at the role level. - - - `{0}rc "command name" disable MyRole` - - - chnlmdl cm - - - Sets a module's permission at the channel level. - - - `{0}cm ModuleName enable SomeChannel` - - - chnlcmd cc - - - Sets a command's permission at the channel level. - - - `{0}cc "command name" enable SomeChannel` - - - usrmdl um - - - Sets a module's permission at the user level. - - - `{0}um ModuleName enable SomeUsername` - - - usrcmd uc - - - Sets a command's permission at the user level. - - - `{0}uc "command name" enable SomeUsername` - - - allsrvrmdls asm - - - Enable or disable all modules for your server. - - - `{0}asm [enable/disable]` - - - allchnlmdls acm - - - Enable or disable all modules in a specified channel. - - - `{0}acm enable #SomeChannel` - - - allrolemdls arm - - - Enable or disable all modules for a specific role. - - - `{0}arm [enable/disable] MyRole` - - - ubl - - - Either [add]s or [rem]oves a user specified by a mention or ID from a blacklist. - - - `{0}ubl add @SomeUser` or `{0}ubl rem 12312312313` - - - cbl - - - Either [add]s or [rem]oves a channel specified by an ID from a blacklist. - - - `{0}cbl rem 12312312312` - - - sbl - - - Either [add]s or [rem]oves a server specified by a Name or ID from a blacklist. - - - `{0}sbl add 12312321312` or `{0}sbl rem SomeTrashServer` - - - cmdcooldown cmdcd - - - Sets a cooldown per user for a command. Set to 0 to remove the cooldown. - - - `{0}cmdcd "some cmd" 5` - - - allcmdcooldowns acmdcds - - - Shows a list of all commands and their respective cooldowns. - - - `{0}acmdcds` - - - . - - - Adds a new quote with the specified name and message. - - - `{0}. sayhi Hi` - - - .. - - - Shows a random quote with a specified name. - - - `{0}.. abc` - - - qsearch - - - Shows a random quote for a keyword that contains any text specified in the search. - - - `{0}qsearch keyword text` - - - deletequote delq - - - Deletes a random quote with the specified keyword. You have to either be server Administrator or the creator of the quote to delete it. - - - `{0}delq abc` - - - draw - - - Draws a card from the deck.If you supply number X, she draws up to 5 cards from the deck. - - - `{0}draw` or `{0}draw 5` - - - shuffle sh - - - Shuffles the current playlist. - - - `{0}sh` - - - flip - - - Flips coin(s) - heads or tails, and shows an image. - - - `{0}flip` or `{0}flip 3` - - - betflip bf - - - Bet to guess will the result be heads or tails. Guessing awards you 1.95x the currency you've bet (rounded up). Multiplier can be changed by the bot owner. - - - `{0}bf 5 heads` or `{0}bf 3 t` - - - roll - - - Rolls 0-100. If you supply a number [x] it rolls up to 30 normal dice. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. Y can be a letter 'F' if you want to roll fate dice instead of dnd. - - - `{0}roll` or `{0}roll 7` or `{0}roll 3d5` or `{0}roll 5dF` - - - rolluo - - - Rolls X normal dice (up to 30) unordered. If you split 2 numbers with letter d (xdy) it will roll x dice from 1 to y. - - - `{0}rolluo` or `{0}rolluo 7` or `{0}rolluo 3d5` - - - nroll - - - Rolls in a given range. - - - `{0}nroll 5` (rolls 0-5) or `{0}nroll 5-15` - - - race - - - Starts a new animal race. - - - `{0}race` - - - joinrace jr - - - Joins a new race. You can specify an amount of currency for betting (optional). You will get YourBet*(participants-1) back if you win. - - - `{0}jr` or `{0}jr 5` - - - raffle - - - Prints a name and ID of a random user from the online list from the (optional) role. - - - `{0}raffle` or `{0}raffle RoleName` - - - give - - - Give someone a certain amount of currency. - - - `{0}give 1 "@SomeGuy"` - - - award - - - Awards someone a certain amount of currency. You can also specify a role name to award currency to all users in a role. - - - `{0}award 100 @person` or `{0}award 5 Role Of Gamblers` - - - take - - - Takes a certain amount of currency from someone. - - - `{0}take 1 "@someguy"` - - - betroll br - - - Bets a certain amount of currency and rolls a dice. Rolling over 66 yields x2 of your currency, over 90 - x4 and 100 x10. - - - `{0}br 5` - - - leaderboard lb - - - Displays bot currency leaderboard. - - - `{0}lb` - - - trivia t - - - Starts a game of trivia. You can add nohint to prevent hints.First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question. - - - `{0}t` or `{0}t 5 nohint` - - - tl - - - Shows a current trivia leaderboard. - - - `{0}tl` - - - tq - - - Quits current trivia after current question. - - - `{0}tq` - - - typestart - - - Starts a typing contest. - - - `{0}typestart` - - - typestop - - - Stops a typing contest on the current channel. - - - `{0}typestop` - - - typeadd - - - Adds a new article to the typing contest. - - - `{0}typeadd wordswords` - - - poll - - - Creates a poll which requires users to send the number of the voting option to the bot. - - - `{0}poll Question?;Answer1;Answ 2;A_3` - - - pollend - - - Stops active poll on this server and prints the results in this channel. - - - `{0}pollend` - - - pick - - - Picks the currency planted in this channel. 60 seconds cooldown. - - - `{0}pick` - - - plant - - - Spend an amount of currency to plant it in this channel. Default is 1. (If bot is restarted or crashes, the currency will be lost) - - - `{0}plant` or `{0}plant 5` - - - gencurrency gc - - - Toggles currency generation on this channel. Every posted message will have chance to spawn currency. Chance is specified by the Bot Owner. (default is 2%) - - - `{0}gc` - - - leet - - - Converts a text to leetspeak with 6 (1-6) severity levels - - - `{0}leet 3 Hello` - - - choose - - - Chooses a thing from a list of things - - - `{0}choose Get up;Sleep;Sleep more` - - - 8ball - - - Ask the 8ball a yes/no question. - - - `{0}8ball should I do something` - - - rps - - - Play a game of rocket paperclip scissors with Nadeko. - - - `{0}rps scissors` - - - linux - - - Prints a customizable Linux interjection - - - `{0}linux Spyware Windows` - - - next n - - - Goes to the next song in the queue. You have to be in the same voice channel as the bot. You can skip multiple songs, but in that case songs will not be requeued if {0}rcs or {0}rpl is enabled. - - - `{0}n` or `{0}n 5` - - - stop s - - - Stops the music and clears the playlist. Stays in the channel. - - - `{0}s` - - - destroy d - - - Completely stops the music and unbinds the bot from the channel. (may cause weird behaviour) - - - `{0}d` - - - pause p - - - Pauses or Unpauses the song. - - - `{0}p` - - - queue q yq - - - Queue a song using keywords or a link. Bot will join your voice channel.**You must be in a voice channel**. - - - `{0}q Dream Of Venice` - - - soundcloudqueue sq - - - Queue a soundcloud song using keywords. Bot will join your voice channel.**You must be in a voice channel**. - - - `{0}sq Dream Of Venice` - - - listqueue lq - - - Lists 15 currently queued songs per page. Default page is 1. - - - `{0}lq` or `{0}lq 2` - - - nowplaying np - - - Shows the song currently playing. - - - `{0}np` - - - volume vol - - - Sets the music volume 0-100% - - - `{0}vol 50` - - - defvol dv - - - Sets the default music volume when music playback is started (0-100). Persists through restarts. - - - `{0}dv 80` - - - max - - - Sets the music volume to 100%. - - - `{0}max` - - - half - - - Sets the music volume to 50%. - - - `{0}half` - - - playlist pl - - - Queues up to 500 songs from a youtube playlist specified by a link, or keywords. - - - `{0}pl playlist link or name` - - - soundcloudpl scpl - - - Queue a soundcloud playlist using a link. - - - `{0}scpl soundcloudseturl` - - - localplaylst lopl - - - Queues all songs from a directory. - - - `{0}lopl C:/music/classical` - - - radio ra - - - Queues a radio stream from a link. It can be a direct mp3 radio stream, .m3u, .pls .asx or .xspf (Usage Video: <https://streamable.com/al54>) - - - `{0}ra radio link here` - - - local lo - - - Queues a local file by specifying a full path. - - - `{0}lo C:/music/mysong.mp3` - - - move mv - - - Moves the bot to your voice channel. (works only if music is already playing) - - - `{0}mv` - - - remove rm - - - Remove a song by its # in the queue, or 'all' to remove whole queue. - - - `{0}rm 5` - - - movesong ms - - - Moves a song from one position to another. - - - `{0}ms 5>3` - - - setmaxqueue smq - - - Sets a maximum queue size. Supply 0 or no argument to have no limit. - - - `{0}smq 50` or `{0}smq` - - - cleanup - - - Cleans up hanging voice connections. - - - `{0}cleanup` - - - reptcursong rcs - - - Toggles repeat of current song. - - - `{0}rcs` - - - rpeatplaylst rpl - - - Toggles repeat of all songs in the queue (every song that finishes is added to the end of the queue). - - - `{0}rpl` - - - save - - - Saves a playlist under a certain name. Name must be no longer than 20 characters and mustn't contain dashes. - - - `{0}save classical1` - - - load - - - Loads a saved playlist using it's ID. Use `{0}pls` to list all saved playlists and {0}save to save new ones. - - - `{0}load 5` - - - playlists pls - - - Lists all playlists. Paginated. 20 per page. Default page is 0. - - - `{0}pls 1` - - - deleteplaylist delpls - - - Deletes a saved playlist. Only if you made it or if you are the bot owner. - - - `{0}delpls animu-5` - - - goto - - - Goes to a specific time in seconds in a song. - - - `{0}goto 30` - - - autoplay ap - - - Toggles autoplay - When the song is finished, automatically queue a related youtube song. (Works only for youtube songs and when queue is empty) - - - `{0}ap` - - - lolchamp - - - Shows League Of Legends champion statistics. If there are spaces/apostrophes or in the name - omit them. Optional second parameter is a role. - - - `{0}lolchamp Riven` or `{0}lolchamp Annie sup` - - - lolban - - - Shows top banned champions ordered by ban rate. - - - `{0}lolban` - - - hitbox hb - - - Notifies this channel when a certain user starts streaming. - - - `{0}hitbox SomeStreamer` - - - twitch tw - - - Notifies this channel when a certain user starts streaming. - - - `{0}twitch SomeStreamer` - - - beam bm - - - Notifies this channel when a certain user starts streaming. - - - `{0}beam SomeStreamer` - - - removestream rms - - - Removes notifications of a certain streamer from a certain platform on this channel. - - - `{0}rms Twitch SomeGuy` or `{0}rms Beam SomeOtherGuy` - - - liststreams ls - - - Lists all streams you are following on this server. - - - `{0}ls` - - - convert - - - Convert quantities. Use `{0}convertlist` to see supported dimensions and currencies. - - - `{0}convert m km 1000` - - - convertlist - - - List of the convertible dimensions and currencies. - - - `{0}convertlist` - - - wowjoke - - - Get one of Kwoth's penultimate WoW jokes. - - - `{0}wowjoke` - - - calculate calc - - - Evaluate a mathematical expression. - - - `{0}calc 1+1` - - - osu - - - Shows osu stats for a player. - - - `{0}osu Name` or `{0}osu Name taiko` - - - osub - - - Shows information about an osu beatmap. - - - `{0}osub https://osu.ppy.sh/s/127712` - - - osu5 - - - Displays a user's top 5 plays. - - - `{0}osu5 Name` - - - pokemon poke - - - Searches for a pokemon. - - - `{0}poke Sylveon` - - - pokemonability pokeab - - - Searches for a pokemon ability. - - - `{0}pokeab overgrow` - - - memelist - - - Pulls a list of memes you can use with `{0}memegen` from http://memegen.link/templates/ - - - `{0}memelist` - - - memegen - - - Generates a meme from memelist with top and bottom text. - - - `{0}memegen biw "gets iced coffee" "in the winter"` - - - weather we - - - Shows weather data for a specified city. You can also specify a country after a comma. - - - `{0}we Moscow, RU` - - - youtube yt - - - Searches youtubes and shows the first result - - - `{0}yt query` - - - anime ani aq - - - Queries anilist for an anime and shows the first result. - - - `{0}ani aquarion evol` - - - imdb omdb - - - Queries omdb for movies or series, show first result. - - - `{0}imdb Batman vs Superman` - - - manga mang mq - - - Queries anilist for a manga and shows the first result. - - - `{0}mq Shingeki no kyojin` - - - randomcat meow - - - Shows a random cat image. - - - `{0}meow` - - - randomdog woof - - - Shows a random dog image. - - - `{0}woof` - - - image img - - - Pulls the first image found using a search parameter. Use {0}rimg for different results. - - - `{0}img cute kitten` - - - randomimage rimg - - - Pulls a random image using a search parameter. - - - `{0}rimg cute kitten` - - - lmgtfy - - - Google something for an idiot. - - - `{0}lmgtfy query` - - - google g - - - Get a google search link for some terms. - - - `{0}google query` - - - hearthstone hs - - - Searches for a Hearthstone card and shows its image. Takes a while to complete. - - - `{0}hs Ysera` - - - urbandict ud - - - Searches Urban Dictionary for a word. - - - `{0}ud Pineapple` - - - # - - - Searches Tagdef.com for a hashtag. - - - `{0}# ff` - - - catfact - - - Shows a random catfact from <http://catfacts-api.appspot.com/api/facts> - - - `{0}catfact` - - - yomama ym - - - Shows a random joke from <http://api.yomomma.info/> - - - `{0}ym` - - - randjoke rj - - - Shows a random joke from <http://tambal.azurewebsites.net/joke/random> - - - `{0}rj` - - - chucknorris cn - - - Shows a random chucknorris joke from <http://tambal.azurewebsites.net/joke/random> - - - `{0}cn` - - - magicitem mi - - - Shows a random magicitem from <https://1d4chan.org/wiki/List_of_/tg/%27s_magic_items> - - - `{0}mi` - - - revav - - - Returns a google reverse image search for someone's avatar. - - - `{0}revav "@SomeGuy"` - - - revimg - - - Returns a google reverse image search for an image from a link. - - - `{0}revimg Image link` - - - safebooru - - - Shows a random image from safebooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}safebooru yuri+kissing` - - - wikipedia wiki - - - Gives you back a wikipedia link - - - `{0}wiki query` - - - color clr - - - Shows you what color corresponds to that hex. - - - `{0}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. - - - `{0}videocall "@SomeGuy"` - - - avatar av - - - Shows a mentioned person's avatar. - - - `{0}av "@SomeGuy"` - - - hentai - - - Shows a hentai image from a random website (gelbooru or danbooru or konachan or atfbooru or yandere) with a given tag. Tag is optional but preferred. Only 1 tag allowed. - - - `{0}hentai yuri` - - - danbooru - - - Shows a random hentai image from danbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}danbooru yuri+kissing` - - - atfbooru atf - - - Shows a random hentai image from atfbooru with a given tag. Tag is optional but preferred. - - - `{0}atfbooru yuri+kissing` - - - gelbooru - - - Shows a random hentai image from gelbooru with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}gelbooru yuri+kissing` - - - rule34 - - - Shows a random image from rule34.xx with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}rule34 yuri+kissing` - - - e621 - - - Shows a random hentai image from e621.net with a given tag. Tag is optional but preferred. Use spaces for multiple tags. - - - `{0}e621 yuri kissing` - - - cp - - - We all know where this will lead you to. - - - `{0}cp` - - - boobs - - - Real adult content. - - - `{0}boobs` - - - butts ass butt - - - Real adult content. - - - `{0}butts` or `{0}ass` - - - createwar cw - - - Creates a new war by specifying a size (>10 and multiple of 5) and enemy clan name. - - - `{0}cw 15 The Enemy Clan` - - - startwar sw - - - Starts a war with a given number. - - - `{0}sw 15` - - - listwar lw - - - Shows the active war claims by a number. Shows all wars in a short way if no number is specified. - - - `{0}lw [war_number] or {0}lw` - - - claim call c - - - Claims a certain base from a certain war. You can supply a name in the third optional argument to claim in someone else's place. - - - `{0}call [war_number] [base_number] [optional_other_name]` - - - claimfinish cf - - - Finish your claim with 3 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. - - - `{0}cf 1` or `{0}cf 1 5` - - - claimfinish2 cf2 - - - Finish your claim with 2 stars if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. - - - `{0}cf2 1` or `{0}cf2 1 5` - - - claimfinish1 cf1 - - - Finish your claim with 1 star if you destroyed a base. First argument is the war number, optional second argument is a base number if you want to finish for someone else. - - - `{0}cf1 1` or `{0}cf1 1 5` - - - unclaim ucall uc - - - Removes your claim from a certain war. Optional second argument denotes a person in whose place to unclaim - - - `{0}uc [war_number] [optional_other_name]` - - - endwar ew - - - Ends the war with a given index. - - - `{0}ew [war_number]` - - - translate trans - - - Translates from>to text. From the given language to the destination language. - - - `{0}trans en>fr Hello` - - - translangs - - - Lists the valid languages for translation. - - - `{0}translangs` - - - Sends a readme and a guide links to the channel. - - - `{0}readme` or `{0}guide` - - - readme guide - - - Shows all available operations in {0}calc command - - - `{0}calcops` - - - calcops - - - Deletes all quotes on a specified keyword. - - - `{0}delallq kek` - - - delallq daq - - - greetdmmsg - - - `{0}greetdmmsg Welcome to the server, %user%`. - - - Sets a new join announcement message which will be sent to the user who joined. Type %user% if you want to mention the new member. Using it with no message will show the current DM greet message. You can use embed json from <http://nadekobot.xyz/embedbuilder/> instead of a regular text, if you want the message to be embedded. - - - Check how much currency a person has. (Defaults to yourself) - - - `{0}$$` or `{0}$$ @SomeGuy` - - - cash $$ - - - Lists whole permission chain with their indexes. You can specify an optional page number if there are a lot of permissions. - - - `{0}lp` or `{0}lp 3` - - - listperms lp - - - Enable or disable all modules for a specific user. - - - `{0}aum enable @someone` - - - allusrmdls aum - - - Moves permission from one position to another in Permissions list. - - - `{0}mp 2 4` - - - moveperm mp - - - Removes a permission from a given position in Permissions list. - - - `{0}rp 1` - - - removeperm rp - - - Migrate data from old bot configuration - - - `{0}migratedata` - - - migratedata - - - Checks if a user is online on a certain streaming platform. - - - `{0}cs twitch MyFavStreamer` - - - checkstream cs - - - showemojis se - - - Shows a name and a link to every SPECIAL emoji in the message. - - - `{0}se A message full of SPECIAL emojis` - - - shuffle sh - - - Reshuffles all cards back into the deck. - - - `{0}sh` - - - fwmsgs - - - Toggles forwarding of non-command messages sent to bot's DM to the bot owners - - - `{0}fwmsgs` - - - fwtoall - - - Toggles whether messages will be forwarded to all bot owners or only to the first one specified in the credentials.json - - - `{0}fwtoall` - - - resetperms - - - Resets BOT's permissions module on this server to the default value. - - - `{0}resetperms` - - - antiraid - - - Sets an anti-raid protection on the server. First argument is number of people which will trigger the protection. Second one is a time interval in which that number of people needs to join in order to trigger the protection, and third argument is punishment for those people (Kick, Ban, Mute) - - - `{0}antiraid 5 20 Kick` - - - antispam - - - Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders. Max message count is 10. - - - `{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban` - - - chatmute - - - Prevents a mentioned user from chatting in text channels. - - - `{0}chatmute @Someone` - - - voicemute - - - Prevents a mentioned user from speaking in voice channels. - - - `{0}voicemute @Someone` - - - konachan - - - Shows a random hentai image from konachan with a given tag. Tag is optional but preferred. - - - `{0}konachan yuri` - - - setmuterole - - - Sets a name of the role which will be assigned to people who should be muted. Default is nadeko-mute. - - - `{0}setmuterole Silenced` - - - adsarm - - - Toggles the automatic deletion of confirmations for {0}iam and {0}iamn commands. - - - `{0}adsarm` - - - setstream - - - Sets the bots stream. First argument is the twitch link, second argument is stream name. - - - `{0}setstream TWITCHLINK Hello` - - - chatunmute - - - Removes a mute role previously set on a mentioned user with `{0}chatmute` which prevented him from chatting in text channels. - - - `{0}chatunmute @Someone` - - - unmute - - - Unmutes a mentioned user previously muted with `{0}mute` command. - - - `{0}unmute @Someone` - - - xkcd - - - Shows a XKCD comic. No arguments will retrieve random one. Number argument will retrieve a specific comic, and "latest" will get the latest one. - - - `{0}xkcd` or `{0}xkcd 1400` or `{0}xkcd latest` - - - placelist - - - Shows the list of available tags for the `{0}place` command. - - - `{0}placelist` - - - place - - - Shows a placeholder image of a given tag. Use `{0}placelist` to see all available tags. You can specify the width and height of the image as the last two optional arguments. - - - `{0}place Cage` or `{0}place steven 500 400` - - - togethertube totube - - - Creates a new room on <https://togethertube.com> and shows the link in the chat. - - - `{0}totube` - - - publicpoll ppoll - - - Creates a public poll which requires users to type a number of the voting option in the channel command is ran in. - - - `{0}ppoll Question?;Answer1;Answ 2;A_3` - - - autotranslang atl - - - Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value. - - - `{0}atl en>fr` - - - autotrans at - - - Starts automatic translation of all messages by users who set their `{0}atl` in this channel. You can set "del" argument to automatically delete all translated user messages. - - - `{0}at` or `{0}at del` - - - listquotes liqu - - - `{0}liqu` or `{0}liqu 3` - - - Lists all quotes on the server ordered alphabetically. 15 Per page. - - - typedel - - - Deletes a typing article given the ID. - - - `{0}typedel 3` - - - typelist - - - Lists added typing articles with their IDs. 15 per page. - - - `{0}typelist` or `{0}typelist 3` - - - listservers - - - Lists servers the bot is on with some basic info. 15 per page. - - - `{0}listservers 3` - - - hentaibomb - - - Shows a total 5 images (from gelbooru, danbooru, konachan, yandere and atfbooru). Tag is optional but preferred. - - - `{0}hentaibomb yuri` - - - cleverbot - - - Toggles cleverbot session. When enabled, the bot will reply to messages starting with bot mention in the server. Custom reactions starting with %mention% won't work if cleverbot is enabled. - - - `{0}cleverbot` - - - shorten - - - Attempts to shorten an URL, if it fails, returns the input URL. - - - `{0}shorten https://google.com` - - - minecraftping mcping - - - Pings a minecraft server. - - - `{0}mcping 127.0.0.1:25565` - - - minecraftquery mcq - - - Finds information about a minecraft server. - - - `{0}mcq server:ip` - - - wikia - - - Gives you back a wikia link - - - `{0}wikia mtg Vigilance` or `{0}wikia mlp Dashy` - - - yandere - - - Shows a random image from yandere with a given tag. Tag is optional but preferred. (multiple tags are appended with +) - - - `{0}yandere tag1+tag2` - - - magicthegathering mtg - - - Searches for a Magic The Gathering card. - - - `{0}magicthegathering about face` or `{0}mtg about face` - - - yodify yoda - - - Translates your normal sentences into Yoda styled sentences! - - - `{0}yoda my feelings hurt` - - - attack - - - Attacks a target with the given move. Use `{0}movelist` to see a list of moves your type can use. - - - `{0}attack "vine whip" @someguy` - - - heal - - - Heals someone. Revives those who fainted. Costs a NadekoFlower - - - `{0}heal @someone` - - - movelist ml - - - Lists the moves you are able to use - - - `{0}ml` - - - settype - - - Set your poketype. Costs a NadekoFlower. Provide no arguments to see a list of available types. - - - `{0}settype fire` or `{0}settype` - - - type - - - Get the poketype of the target. - - - `{0}type @someone` - - - hangmanlist - - - Shows a list of hangman term types. - - - `{0} hangmanlist` - - - hangman - - - Starts a game of hangman in the channel. Use `{0}hangmanlist` to see a list of available term types. Defaults to 'all'. - - - `{0}hangman` or `{0}hangman movies` - - - crstatsclear - - - Resets the counters on `{0}crstats`. You can specify a trigger to clear stats only for that trigger. - - - `{0}crstatsclear` or `{0}crstatsclear rng` - - - crstats - - - Shows a list of custom reactions and the number of times they have been executed. Paginated with 10 per page. Use `{0}crstatsclear` to reset the counters. - - - `{0}crstats` or `{0}crstats 3` - - - overwatch ow - - - Show's basic stats on a player (competitive rank, playtime, level etc) Region codes are: `eu` `us` `cn` `kr` - - - `{0}ow us Battletag#1337` or `{0}overwatch eu Battletag#2016` - - - acrophobia acro - - - Starts an Acrophobia game. Second argment is optional round length in seconds. (default is 60) - - - `{0}acro` or `{0}acro 30` - - - logevents - - - Shows a list of all events you can subscribe to with `{0}log` - - - `{0}logevents` - - - log - - - Toggles logging event. Disables it if it's active anywhere on the server. Enables if it's not active. Use `{0}logevents` to see a list of all events you can subscribe to. - - - `{0}log userpresence` or `{0}log userbanned` - - - fairplay fp - - - Toggles fairplay. While enabled, music player will prioritize songs from users who didn't have their song recently played instead of the song's position in the queue. - - - `{0}fp` - - - define def - - - Finds a definition of a word. - - - `{0}def heresy` - - - setmaxplaytime smp - - - Sets a maximum number of seconds (>14) a song can run before being skipped automatically. Set 0 to have no limit. - - - `{0}smp 0` or `{0}smp 270` - - - activity - - - Checks for spammers. - - - `{0}activity` - - - autohentai - - - Posts a hentai every X seconds with a random tag from the provided tags. Use `|` to separate tags. 20 seconds minimum. Provide no arguments to disable. - - - `{0}autohentai 30 yuri|tail|long_hair` or `{0}autohentai` - - - setstatus - - - Sets the bot's status. (Online/Idle/Dnd/Invisible) - - - `{0}setstatus Idle` - - - rotaterolecolor rrc - - - Rotates a roles color on an interval with a list of supplied colors. First argument is interval in seconds (Minimum 60). Second argument is a role, followed by a space-separated list of colors in hex. Provide a rolename with a 0 interval to disable. - - - `{0}rrc 60 MyLsdRole #ff0000 #00ff00 #0000ff` or `{0}rrc 0 MyLsdRole` - - - createinvite crinv - - - Creates a new invite which has infinite max uses and never expires. - - - `{0}crinv` - - - pollstats - - - Shows the poll results without stopping the poll on this server. - - - `{0}pollstats` - - - repeatlist replst - - - Shows currently repeating messages and their indexes. - - - `{0}repeatlist` - - - repeatremove reprm - - - Removes a repeating message on a specified index. Use `{0}repeatlist` to see indexes. - - - `{0}reprm 2` - - - antilist antilst - - - Shows currently enabled protection features. - - - `{0}antilist` - - - antispamignore - - - Toggles whether antispam ignores current channel. Antispam must be enabled. - - - `{0}antispamignore` - - - cmdcosts - - - Shows a list of command costs. Paginated with 9 command per page. - - - `{0}cmdcosts` or `{0}cmdcosts 2` - - - commandcost cmdcost - - - Sets a price for a command. Running that command will take currency from users. Set 0 to remove the price. - - - `{0}cmdcost 0 !!q` or `{0}cmdcost 1 >8ball` - - - startevent - - - Starts one of the events seen on public nadeko. - - - `{0}startevent flowerreaction` - - - slotstats - - - Shows the total stats of the slot command for this bot's session. - - - `{0}slotstats` - - - slottest - - - Tests to see how much slots payout for X number of plays. - - - `{0}slottest 1000` - - - slot - - - Play Nadeko slots. Max bet is 999. 3 seconds cooldown per user. - - - `{0}slot 5` - - - affinity - - - Sets your affinity towards someone you want to be claimed by. Setting affinity will reduce their `{0}claim` on you by 20%. You can leave second argument empty to clear your affinity. 30 minutes cooldown. - - - `{0}affinity @MyHusband` or `{0}affinity` - - - claimwaifu claim - - - Claim a waifu for yourself by spending currency. You must spend atleast 10% more than her current value unless she set `{0}affinity` towards you. - - - `{0}claim 50 @Himesama` - - - waifus waifulb - - - Shows top 9 waifus. - - - `{0}waifus` - - - divorce - - - Releases your claim on a specific waifu. You will get some of the money you've spent back unless that waifu has an affinity towards you. 6 hours cooldown. - - - `{0}divorce @CheatingSloot` - - - waifuinfo waifustats - - - Shows waifu stats for a target person. Defaults to you if no user is provided. - - - `{0}waifuinfo @MyCrush` or `{0}waifuinfo` - - - mal - - - Shows basic info from myanimelist profile. - - - `{0}mal straysocks` - - - setmusicchannel smch - - - Sets the current channel as the default music output channel. This will output playing, finished, paused and removed songs to that channel instead of the channel where the first song was queued in. - - - `{0}smch` - - - reloadimages - - - Reloads images bot is using. Safe to use even when bot is being used heavily. - - - `{0}reloadimages` - - - shardstats - - - Stats for shards. Paginated with 25 shards per page. - - - `{0}shardstats` or `{0}shardstats 2` - - - connectshard - - - Try (re)connecting a shard with a certain shardid when it dies. No one knows will it work. Keep an eye on the console for errors. - - - `{0}connectshard 2` - - - shardid - - - Shows which shard is a certain guild on, by guildid. - - - `{0}shardid 117523346618318850` - - - tictactoe ttt - - - Starts a game of tic tac toe. Another user must run the command in the same channel in order to accept the challenge. Use numbers 1-9 to play. 15 seconds per move. - - - >ttt - - - timezones - - - List of all timezones available on the system to be used with `{0}timezone`. - - - `{0}timezones` - - - timezone - - - Sets this guilds timezone. This affects bot's time output in this server (logs, etc..) - - - `{0}timezone` - - - langsetdefault langsetd - - - Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language. - - - `{0}langsetd en-US` or `{0}langsetd default` - - - languageset langset - - - Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language. - - - `{0}langset de-DE ` or `{0}langset default` - - - languageslist langli - - - List of languages for which translation (or part of it) exist atm. - - - `{0}langli` - - - rategirl - - - Use the universal hot-crazy wife zone matrix to determine the girl's worth. It is everything young men need to know about women. At any moment in time, any woman you have previously located on this chart can vanish from that location and appear anywhere else on the chart. - - - `{0}rategirl @SomeGurl` - \ No newline at end of file From 9a8c77a4b7ab38d2195e5b39dc12a3985b4e9612 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:51:05 +0100 Subject: [PATCH 232/256] Update ResponseStrings.pt-BR.resx (POEditor.com) --- .../Resources/ResponseStrings.pt-BR.resx | 280 +++++++++--------- 1 file changed, 140 insertions(+), 140 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx index 898269f2..30bb7b31 100644 --- a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx +++ b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx @@ -1,121 +1,121 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Esta base já está aclamada ou destruída. @@ -1207,7 +1207,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. É um empate! ambos escolheram {0} - + {0} ganhou! {1} vence {2} @@ -1222,10 +1222,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Categoria - + Cleverbot desabilitado neste servidor - Cleverbot ativado neste servidor. + Cleverbot habilitado neste servidor. @@ -1241,7 +1241,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - Falha ao carregar a questão + Falha ao carregar a questão. Jogo Iniciado @@ -1487,7 +1487,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Volume deve estar entre 0 e 100 - + Volume ajustado para {0}% @@ -1582,10 +1582,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Gen. (of module) - + Pagina {0} de permissões - + Atual cargo de permissões é {0} @@ -1594,29 +1594,29 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Permissões removidas #{0} - {1} - + Desabilitado o uso de {0} {1} para o cargo {2}. - + Habilitado o uso de {0} {1} para o cargo {2}. Short of seconds. - + Desabilitado o uso de {0} {1} nesse servidor. - + Habilitado o uso de {0} {1} nesse servidor. - + Não editavel @@ -1625,22 +1625,22 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Não vou mas mostrar avisos de permissões. - + Vou passar a mostrar avisos de permissões. - + Filtragem de palavras desabilitada nesse canal. - + Filtragem de palavras habilitada nesse canal. - + Filtragem de palavras desabilitada nesse servidor. - + Filtragem de palavras habilitada nesse servidor. Habilidades @@ -1679,16 +1679,16 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Derrotas Competitivas - + Partidas Competitivas jogadas - + Rank Competitivo - + Vitorias Competitivas Completado From 79681b4958b755c2f42a56a1a42a8918982187a3 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 01:51:07 +0100 Subject: [PATCH 233/256] Update ResponseStrings.sr-cyrl-rs.resx (POEditor.com) --- .../Resources/ResponseStrings.sr-cyrl-rs.resx | 1705 ++++++++++++++--- 1 file changed, 1424 insertions(+), 281 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx index 8d58263b..ea90f4b6 100644 --- a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx +++ b/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.resx @@ -1,189 +1,122 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - {0} је већ онесвешћен. - - - {0} је већ потпуно здрав. - - - Твој тип већ јесте {0} - - - је искористио {0}{1} на {2}{3} и нанео {4} штете. - Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. - - - Не можеш напасти пре узвратног ударца. - - - Не можеш напасти себе. - - - {0} се онесвестио. - - - је излечио {0} са једним {1} - - - {0} има {1} преосталог здравља. - - - Не можеш искористити {0}. Укуцај `{1}ml` да би видео листу удараца које можеш користити. - - - Листа покрета за {0} тип - - - Није ефективно. - - - Немаш довољно {0} - - - је оживео {0} са једним {1} - - - Оживео си себе са једним {0} - - - Твој тип је промењен на {0} за један {1} - - - Има неког ефекта! - - - Супер ефективно! - - - Користио си превише напада узастопно, не можеш да се крећеш! - - - {0}-ов тип је {1} - - - Корисник није нађен. - - - Онесвешћен си, не можеш да се крећеш. - Та база је већ под захетвом или је уништена. @@ -193,6 +126,9 @@ Та база није под захтевом. + + **DESTROYED** base #{0} in a war against {1} + {0} је **ПОНИШТИО ЗАХТЕВ** за базу #{1} у рату против {2} @@ -289,82 +225,79 @@ Окидач - - Назад на Садржај - - - Само Власник Бота - - - Захтева {0} дозволу на каналу. - - - Можете подржати пројекат на Пејтриону: <{0}> или Пејпалу: <{1}> - - - Команде и псеудоними - - - Листа команди је обновљена. - - - Укуцај `{0}h ИмеКоманде` да видиш помоћ за ту команду. нпр. `{0}h >8ball` - - - Не могу да нађем ту команду. Проверите да ли команда постоји, па покушајте поново. - - - Опис - - - Можете подржати НадекоБот пројекат на -Пејтриону <{0}> или -Пејпалу <{1}> -Не заборавите да оставите своје корисничко име или ИД. - -**Хвала** ♥️ - - - **List of Commands**: <{0}> -**Hosting Guides and docs can be found here**: <{1}> - - - Листа Команди - - - Листа Модула - - - Укуцај `{0}cmds ИмеМодула` да видиш листу свих команди у том модулу. нпр `{0}cmds games` - - - Тај модул не постоји. - - - Захтева {0} дозволу на серверу. - - - Садржај - - - Упутство - - - Аутохентаи започет. Постоваћу сваких {0} сек. користећи један од следећих тагова: -{1} - АутоХентаи заустављен. - - Таг - - - **DESTROYED** base #{0} in a war against {1} - No results found. + + {0} је већ онесвешћен. + + + {0} је већ потпуно здрав. + + + Твој тип већ јесте {0} + + + је искористио {0}{1} на {2}{3} и нанео {4} штете. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Не можеш напасти пре узвратног ударца. + + + Не можеш напасти себе. + + + {0} се онесвестио. + + + је излечио {0} са једним {1} + + + {0} има {1} преосталог здравља. + + + Не можеш искористити {0}. Укуцај `{1}ml` да би видео листу удараца које можеш користити. + + + Листа покрета за {0} тип + + + Није ефективно. + + + Немаш довољно {0} + + + је оживео {0} са једним {1} + + + Оживео си себе са једним {0} + + + Твој тип је промењен на {0} за један {1} + + + Има неког ефекта! + + + Супер ефективно! + + + Користио си превише напада узастопно, не можеш да се крећеш! + + + {0}-ов тип је {1} + + + Корисник није нађен. + + + Онесвешћен си, не можеш да се крећеш. + **Auto assign role** on user join is now **disabled**. @@ -515,7 +448,7 @@ Reason: {1} Greet announcements enabled on this channel. - You can't use this command on users with a role higher or equal to yours in the role hierarchy. + You can't use this command on users with a role higher or equal to yours in the role hierarchy. Images loaded after {0} seconds! @@ -541,19 +474,21 @@ Reason: {1} {0} - Your server's locale is now {0} - {1} + Your server's locale is now {0} - {1} - Bot's default locale is now {0} - {1} + Bot's default locale is now {0} - {1} - Bot's language is set to {0} - {0} + Bot's language is set to {0} - {0} + Fuzzy - Failed setting locale. Revisit this command's help. + Failed setting locale. Revisit this command's help. - This server's language is set to {0} - {0} + This server's language is set to {0} - {0} + Fuzzy {0} has left {1} @@ -606,10 +541,10 @@ Reason: {1} Muted - singular "User muted." + singular "User muted." - I don't have the permission necessary for that most likely. + I don't have the permission necessary for that most likely. New mute role set. @@ -630,7 +565,7 @@ Reason: {1} Nickname Changed - Can't find that server + Can't find that server No shard with that ID found. @@ -645,7 +580,7 @@ Reason: {1} Old Topic - Error. Most likely I don't have sufficient permissions. + Error. Most likely I don't have sufficient permissions. Permissions for this server are reset. @@ -705,7 +640,7 @@ Reason: {1} Failed to rename role. I have insufficient permissions. - You can't edit roles higher than your highest role. + You can't edit roles higher than your highest role. Removed the playing message: {0} @@ -751,13 +686,13 @@ Reason: {1} That role is not self-assignable. - You don't have {0} role. + You don't have {0} role. Self assigned roles are now not exclusive! - 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.` + 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.` {0} has been removed from the list of self-assignable roles. @@ -799,7 +734,7 @@ Reason: {1} Shutting down - Users can't send more than {0} messages every {1} seconds. + Users can't send more than {0} messages every {1} seconds. Slow mode disabled. @@ -862,10 +797,10 @@ Reason: {1} {0} has been **muted** from text and voice chat. - User's Role Added + User's Role Added - User's Role Removed + User's Role Removed {0} is now {1} @@ -901,7 +836,7 @@ Reason: {1} Enabled voice + text feature. - I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server. + I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server. You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. This may cause some issues, and you will have to clean up text channels yourself afterwards. @@ -929,7 +864,7 @@ Reason: {1} Migration done! - Error while migrating, check bot's console for more information. + Error while migrating, check bot's console for more information. Presence Updates @@ -940,9 +875,6 @@ Reason: {1} has awarded {0} to {1} - - Betflip Gamble - Better luck next time ^_^ @@ -989,13 +921,13 @@ Reason: {1} Awarded {0} to {1} users from {2} role. - You can't bet more than {0} + You can't bet more than {0} - You can't bet less than {0} + You can't bet less than {0} - You don't have enough {0} + You don't have enough {0} No more cards in the deck. @@ -1026,7 +958,7 @@ Reason: {1} Users must type a secret code to get {0}. -Lasts {1} seconds. Don't tell anyone. Shhh. +Lasts {1} seconds. Don't tell anyone. Shhh. SneakyGame event ended. {0} users received the reward. @@ -1041,6 +973,1217 @@ Lasts {1} seconds. Don't tell anyone. Shhh. successfully took {0} from {1} - was unable to take {0} from{1} because the user doesn't have that much {2}! + was unable to take {0} from{1} because the user doesn't have that much {2}! + + Назад на Садржај + + + Само Власник Бота + + + Захтева {0} дозволу на каналу. + + + Можете подржати пројекат на Пејтриону: <{0}> или Пејпалу: <{1}> + + + Команде и псеудоними + + + Листа команди је обновљена. + + + Укуцај `{0}h ИмеКоманде` да видиш помоћ за ту команду. нпр. `{0}h >8ball` + + + Не могу да нађем ту команду. Проверите да ли команда постоји, па покушајте поново. + + + Опис + + + Можете подржати НадекоБот пројекат на +Пејтриону <{0}> или +Пејпалу <{1}> +Не заборавите да оставите своје корисничко име или ИД. + +**Хвала** ♥️ + + + **List of Commands**: <{0}> +**Hosting Guides and docs can be found here**: <{1}> + + + Листа Команди + + + Листа Модула + + + Укуцај `{0}cmds ИмеМодула` да видиш листу свих команди у том модулу. нпр `{0}cmds games` + + + Тај модул не постоји. + + + Захтева {0} дозволу на серверу. + + + Садржај + + + Упутство + + + Аутохентаи започет. Постоваћу сваких {0} сек. користећи један од следећих тагова: +{1} + + + Таг + + + Трка Животиња + + + Започињање трке није успело јер нема довољно учесника. + + + Трка је пуна! Почиње одмах. + + + {0} се прикључио као {1} + + + {0} се прикључио као {1} и уложио {2}! + + + Укуцај {0}jr да би се прикључио трци. + + + Почиње за 20 секунди или када је соба пуна. + + + Почиње са {0} учесника. + + + {0} као {1} је победио! + + + {0} као {1} је победио трку и освојио {2}! + + + Неисправан број. Можете бацити {0}-{1} коцкица у исто време. + Fuzzy + + + је добио {0} + Someone rolled 35 + + + Коцкица бачена: {0} + Dice Rolled: 5 + + + Неуспешно започињање партије. Друга трка је вероватно у току. + + + Не постоји трка на овом серверу. + + + Други број мора бити већи од првог. + + + Промена Осећања + + + Присвојена од стране + + + Развода + + + Свиђа јој се + + + Цена + + + Ниједна waifu још није присвојена. + + + Најбоље Waifu + + + твој афинитет је већ постављен на ту waifu или покушаваш да обришеш афинитет који не постоји. + + + + Make sure to get the formatting right, and leave the thinking emoji + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + plural + + + + + + + + + + + + + + + + + + + + + + + + Ранг листа + + + + Немаш довољно {0} + + + + + + + Kwoth picked 5* + + + + Kwoth planted 5* + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + context: "removed song #5" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Gen (of command) + + + + Gen. (of module) + + + + + + + + + + + + + + + + + + + + + + + + + Short of seconds. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Don't translate {0}place + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + /s and total need to be localized to fit the context - +`1.` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Емотикони по Мери + + + Грешка + + + Додатак + + + ИД + + + Индекс је ван опсега. + + + Ево је листа корисника у тим ролама: + + + није ти дозвољено да користиш ову команду на ролама са пуно корисника да би се спречила злоупотреба. + + + Нетачна вредност {0}. + Invalid months value/ Invalid hours value + + + Регистровао се + + + Ушао на сервер + + + ИД: {0} +Чланова; {1} +Ид Власника; {2} + + + Нема сервера на овој страници. + + + Листа Понављача + + + Чланова + + + Меморија + + + Поруке + + + Понављач Порука + + + Име + + + Надимак + + + Нико не игра ту игру. + + + Нема активних понављача. + + + Нема рола на овој страни. + + + Нема шардова на овој страни. + + + Нема теме. + + + Бласник + + + ИДеви Власника + + + Присуство + + + {0} Сервера +{1} Текстуалних Канала +{2} Говорних Канала + + + Обрисани су сви цитати са кључном речи {0}. + + + Страна {0} цитата. + + + Нема цитата на овој страни. + + + Није нађен цитат који можеш да обришеш. + + + Цитат додат + + + Обрисана насумишна + + + Регион + + + Регистрован + + + + + + Није исправан формат времена. Проверите листу команди. + + + Нови темплејт за подсетник је постаљен. + + + + + + Листа понављача + + + Нема понављача на овом серверу. + + + + + + Нису пронађене понављајуће поруке на овом серверу. + + + Резултат + + + Роле + + + + + + + + + + + + + + + + + + + + + Инфо Сервера + + + Шард + + + Статови Шардова + + + + + + + + + Нема специјалних емотикона. + + + + + + Текст Канали + + + Ево га твој линк ка соби: + + + Време Рада + + + + Id of the user kwoth#1234 is 123123123123 + + + Корисници + + + Говорни канал + + \ No newline at end of file From 139bd79778bfa478e5e2d91a75766195acbd079f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:27 +0100 Subject: [PATCH 234/256] Update CommandStrings.nl-NL.resx (POEditor.com) From 972726e6afd55d90e56c872ce283c10c8e113c36 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:32 +0100 Subject: [PATCH 235/256] Update ResponseStrings.fr-fr.resx (POEditor.com) --- src/NadekoBot/Resources/ResponseStrings.fr-fr.resx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx index 3708ba7a..72a2957f 100644 --- a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -1410,7 +1410,7 @@ La nouvelle valeur de {0} est {1} ! Lecture en cours: - #{0}` - **{1}** par *{2}* ({3} morceaux) + `#{0}` - **{1}** par *{2}* ({3} morceaux) Page {0} des listes de lecture sauvegardées @@ -1525,7 +1525,7 @@ La nouvelle valeur de {0} est {1} ! Activation de l'usage de TOUS LES MODULES pour l'utilisateur {0}. - Banni {0} avec l'ID {1} + {0} sur liste noire avec l'ID {1} Il ne s'agit pas d'un ban mais d'une blacklist interdisant l'utilisateur d'utiliser le bot. @@ -1767,7 +1767,7 @@ La nouvelle valeur de {0} est {1} ! Niveau - Liste de {0} tags placés. + Liste de tags pour {0}place. Don't translate {0}place @@ -1810,7 +1810,7 @@ La nouvelle valeur de {0} est {1} ! Utilisateur non trouvé! Veuillez vérifier la région ainsi que le BattleTag avant de réessayer. - Prévision de lecture + Prévus de regarder Je ne pense pas que le sens de la traduction soit le bon. @@ -1946,7 +1946,7 @@ La nouvelle valeur de {0} est {1} ! `1.` - Page d'Activité #{0} + Page d'activité #{0} {0} utilisateurs en total. From f025ee646161a55da82ba7376c3c5ea7c2168701 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:35 +0100 Subject: [PATCH 236/256] Update ResponseStrings.de-DE.resx (POEditor.com) --- src/NadekoBot/Resources/ResponseStrings.de-DE.resx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx index eb4c7511..8d8866c5 100644 --- a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx +++ b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx @@ -1956,7 +1956,10 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Thema des Kanals - Befehl ausgeführt + Befehle ausgeführt + Commands ran +9 +^used like that in .stats {0} {1} ist gleich zu {2} {3} From 98a0f97bbe33e43120a7c36f6cbac49e91196e9f Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:37 +0100 Subject: [PATCH 237/256] Update CommandStrings.ja-JP.resx (POEditor.com) --- .../Resources/CommandStrings.ja-JP.resx | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/NadekoBot/Resources/CommandStrings.ja-JP.resx b/src/NadekoBot/Resources/CommandStrings.ja-JP.resx index 17febe51..76de6cb2 100644 --- a/src/NadekoBot/Resources/CommandStrings.ja-JP.resx +++ b/src/NadekoBot/Resources/CommandStrings.ja-JP.resx @@ -118,16 +118,19 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + その基盤はすでに主張されている。または破壊されました。 + - + ベースが破壊されました + - + その基地は主張されていない。 + - + {1}との戦争中の整数{0}を破壊しました @@ -169,7 +172,7 @@ - + サイズ @@ -211,7 +214,7 @@ - + 反応 @@ -223,7 +226,7 @@ - + トリガー @@ -305,7 +308,7 @@ - + 接続 @@ -314,11 +317,11 @@ - + BANNED PLURAL - + ユーザーはBANNED @@ -351,7 +354,7 @@ - + 古称 @@ -360,7 +363,7 @@ - + コンテント @@ -390,7 +393,7 @@ - + タイレクトメッセージガ @@ -531,11 +534,12 @@ - - PLURAL (users have been muted) + ミュート + PLURAL (users have been muted) +Fuzzy - + ミュート singular "User muted." From 71f542081907fcefd3a904d6de61fa0a95414c29 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:40 +0100 Subject: [PATCH 238/256] Update ResponseStrings.pt-BR.resx (POEditor.com) --- .../Resources/ResponseStrings.pt-BR.resx | 405 +++++++++--------- 1 file changed, 205 insertions(+), 200 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx index 30bb7b31..634142d7 100644 --- a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx +++ b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx @@ -118,13 +118,13 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Esta base já está aclamada ou destruída. + Esta base já foi reivindicada ou destruída. Esta base já está destruída. - Esta base não está aclamada. + Esta base não foi reivindicada. @@ -133,10 +133,10 @@ - + {0} clamou a base #{1} durante a guerra contra {2} - @{0} Você já clamou esta base #{1}. Você não pode clamar uma nova. + @{0} Você já reivindicou esta base #{1}. Você não pode reivindicar uma nova. @@ -158,7 +158,7 @@ Lista de guerras ativas - não clamado + não reivindicado Você não está participando nesta guerra. @@ -188,13 +188,13 @@ Guerra contra {0} começou! - Todos os status de reações personalizadas limpados. + Todos os status de reações personalizadas limpos. Reação personalizada deletada - Permissão Insuficiente. Necessita o domínio do bot para reações personalizadas globais, e administrador para reações personalizadas em server. + Permissão Insuficiente. Precisa ser dono do bot para reações personalizadas globais, e permissão de administrador para reações personalizadas no servidor. Lista de todas as reações personalizadas @@ -215,13 +215,13 @@ Resposta - Status de reações customizáveis + Status de reações personalizadas Status limpado para {0} reação customizável. - Nenhum status para aquele comando achado, nenhuma ação foi tomada. + Nenhum status para aquele comando encontrado, nenhuma ação foi tomada. Comando @@ -258,7 +258,7 @@ curou {0} com uma {1} - {0} Tem {1} HP restante. + {0} tem {1} HP restante. Você não pode usar {0}. Digite `{1}ml` para ver uma lista de ataques que você pode usar. @@ -291,7 +291,7 @@ Você usou muitos ataques de uma vez, então você não pode se mexer! - Tipo de {0} é {1} + O tipo de {0} é {1} Usuário não encontrado. @@ -300,20 +300,20 @@ Você desmaiou, então você não pode se mexer! - **Cargo Automático** para novos usuários **desabilitado**. + **Atribuir cargo automaticamente** a novos usuários **desabilitado**. - **Cargo Automático** para novos usuários **habilitado** + **Atribuir cargo automaticamente** a novos usuários **habilitado**. Anexos - Avatar mudado + Avatar alterado Você foi BANIDO do servidor {0}. -Razão: {1} +Motivo: {1} Banidos @@ -323,16 +323,16 @@ Razão: {1} Usuário Banido - Nome do bot mudado para {0}. + Nome do bot alterado para {0}. - Status do bot mudado para {0}. + Status do bot alterado para {0}. Deleção automática de mensagens de despedida foi desativado. - Mensagens de despedida serão deletas após {0} segundos. + Mensagens de despedida serão deletadas após {0} segundos. Mensagem de despedida atual: {0} @@ -341,7 +341,7 @@ Razão: {1} Ative mensagens de despedidas digitando {0} - Nova mensagem de despedida colocada. + Nova mensagem de despedida definida. Mensagens de despedidas desativadas. @@ -350,13 +350,13 @@ Razão: {1} Mensagens de despedidas foram ativadas neste canal. - Nome do canal mudado + Nome do canal alterado Nome antigo - Tópico do canal mudado + Tópico do canal alterado Limpo. @@ -517,7 +517,7 @@ Razão: {1} - + {0} invocou uma menção nos seguintes cargos Mensagem de {0} `[Bot Owner]` @@ -603,7 +603,7 @@ Razão: {1} - + Se {0} ou mais usuários entrarem em menos de {1} segundos, {2}. O tempo deve ser entre {0} e {1} segundos. @@ -643,7 +643,7 @@ Razão: {1} Você não pode editar cargos superiores ao seu cargo mais elevado. - + Removida a mensagem "jogando": {0} O cargo {0} foi adicionado a lista. @@ -658,13 +658,14 @@ Razão: {1} Adicionado. - + Rotação de status "jogando" desativada. - + Rotação de status "jogando" ativada. - + Aqui está uma lista dos status rotacionados: +{0} @@ -688,7 +689,7 @@ Razão: {1} Você não possui o cargo {0}. - + Cargos auto-atribuíveis agora são exclusivos! Não sou capaz de adicionar esse cargo a você. `Não posso adicionar cargos a donos ou outros cargos maiores que o meu cargo na hierarquia dos cargos.` @@ -697,13 +698,13 @@ Razão: {1} {0} foi removido da lista de cargos auto-aplicáveis. - + Voce nao possui mas o cargo {0}. - + Voce agora possui o cargo {0}. - + Cargo {0} adicionado à {1} com sucesso. Falha ao adicionar o cargo. Não possuo permissões suficientes. @@ -829,19 +830,19 @@ __Canais Ignorados__: {2} Canal de voz destruído - + Atributo voz + texto desabilitado. - + Atributo voz + texto habilitado. - + Eu não possuo a permissão de **gerenciar cargos** e/ou **gerenciar canais**, então não posso executar `voz+texto` no servidor {0}. - + Você está habilitando/desabilitando este atributo e **Eu não tenho permissão de ADMINISTRADOR**. Isso pode causar alguns problemas e você terá que limpar os canais de texto você mesmo depois. - + Eu preciso pelo menos das permissões de **gerenciar cargos** e **gerenciar canais** para habilitar este atributo. (Preferencialmente, permissão de Administrador) Usuário {0} do chat de texto @@ -850,10 +851,11 @@ __Canais Ignorados__: {2} Usuário {0} dos chats de voz e texto - + Usuário {0} do chat de voz - + Você foi banido temporariamente do servidor {0}. +Motivo: {1} Usuário desbanido @@ -862,16 +864,16 @@ __Canais Ignorados__: {2} Migração concluída! - + Erro enquanto migrava, verifique o console do bot para mais informações. - + Atualizações de Presença - + Usuário Banido Temporariamente - + concedeu {0} para {1} Mais sorte na próxima vez ^_^ @@ -883,7 +885,7 @@ __Canais Ignorados__: {2} Baralho re-embaralhado. - + girou {0}. User flipped tails. @@ -893,7 +895,7 @@ __Canais Ignorados__: {2} O número especificado é inválido. Você pode girar de 1 a {0} moedas. - + Adicione {0} reação Este evento está ativo por até {0} horas. @@ -934,7 +936,7 @@ __Canais Ignorados__: {2} Usuario sorteado - + Você rolou {0} Aposta @@ -955,7 +957,8 @@ __Canais Ignorados__: {2} Ganhou - + Usuários devem digitar um código secreto pra ganhar {0}. +Dura {1} segundos. Não diga a ninguém. Shhh. @@ -1183,19 +1186,19 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Você tem {0} segundos para fazer uma submissão. - + {0} submeteram suas frases. ({1} total) - + Vote digitando um número da submissão - + {0} lançam seus votos! - + Vence {0} com {1} pontos. @@ -1234,11 +1237,11 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + {0} {1} aleatórios aparecem! Capture-os digitando `{2}pick` plural - + Um {0} aleatório apareceu! Capture-o digitando `{1}pick` Falha ao carregar a questão. @@ -1247,16 +1250,16 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Jogo Iniciado - + Jogo da Forca iniciado - + Já existe um Jogo da Forca em andamento neste canal. - + Erro ao iniciar o Jogo da Forca. - + Lista dos tipos de termo do "{0}hangman" Placar de Lideres @@ -1282,7 +1285,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Trivia - + {0} acertou! A resposta era: {1} Nenhuma trivia está em andamento neste servidor. @@ -1330,7 +1333,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. {0} vs {1} - + Tentando adicionar {0} músicas à fila... Autoplay desabilitado. @@ -1360,10 +1363,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Id - + Entrada inválida. @@ -1372,13 +1375,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Tamanho máximo da fila de música definido para ilimitado. - + Tamanho máximo da fila de música definido para {0} faixa(s). - Você precisa estar em um canal de voz nesse servidor + Você precisa estar em um canal de voz nesse servidor. Nome @@ -1402,7 +1405,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Tocando Musica - + `#{0}` - **{1}** by *{2}* ({3} músicas) Página {0} de Playlists Salvas @@ -1414,7 +1417,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Falha ao deletar essa playlist. Ela não existe ou você não é seu o criador. - + Não existe uma playlist com esse ID. @@ -1451,7 +1454,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Repetindo Faixa - + A repetição da faixa atual parou. @@ -1463,25 +1466,25 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Repetição de playlist habilitada. - + Eu irei mostrar as músicas em andamento, concluídas, pausadas e removidas neste canal. - + Pulando para `{0}:{1}` - Musicas embaralhadas. + Músicas embaralhadas. - Musica movida. + Música movida. - + {0}h {1}m {2}s - + para a posição - + ilimitada Volume deve estar entre 0 e 100 @@ -1490,67 +1493,67 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Volume ajustado para {0}% - + O uso de TODOS OS MÓDULOS foi desabilitado no canal {0}. - + O uso de TODOS OS MÓDULOS foi habilitado no canal {0}. - + Permitido - + O uso de TODOS OS MÓDULOS foi desabilitado para o cargo {0}. - + O uso de TODOS OS MÓDULOS foi habilitado para o cargo {0}. - + O uso de TODOS OS MÓDULOS foi desabilitado neste servidor. - + O uso de TODOS OS MÓDULOS foi habilitado neste servidor. - + O uso de TODOS OS MÓDULOS foi desabilitado para o usuário {0}. - + O uso de TODOS OS MÓDULOS foi habilitado para o usuário {0}. - + O comando {0} agora possui um cooldown de {1}s. - + O comando {0} não possui nenhum cooldown agora e todos os cooldowns existentes foram limpos. - + Nenhum cooldown de comando definido. - + Desabilitado o uso de {0} {1} no canal {2}. - + Desabilitado o uso de {0} {1} no canal {2}. Negado - + A palavra {0} foi adicionada a lista de palavras filtradas. - + Lista de Palavras Filtradas - + A palavra {0} foi removida da lista de palavras filtradas. - + Segundo parâmetro inválido. (Deve ser um número entre {0} e {1}) @@ -1565,24 +1568,24 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Permissão {0} movida de #{1} para #{2} - + Nenhum custo definido. - Comando + comando Gen (of command) - + módulo Gen. (of module) - Pagina {0} de permissões + Página {0} de Permissões Atual cargo de permissões é {0} @@ -1603,7 +1606,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Habilitado o uso de {0} {1} para o cargo {2}. - + sec. Short of seconds. @@ -1616,7 +1619,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - Não editavel + Não editável @@ -1625,7 +1628,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - Não vou mas mostrar avisos de permissões. + Não vou mais mostrar avisos de permissões. Vou passar a mostrar avisos de permissões. @@ -1649,25 +1652,25 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Nenhum anime favorito ainda - + A tradução automática de mensagens nesse canal foi iniciada. As mensagens do usuário serão deletadas automaticamente. - + A linguagem de tradução automática foi removida. - + A linguagem de tradução automática foi definida de {from}>{to} - + A tradução automática de mensagens foi ativada neste canal. - + A tradução automática de mensagens neste canal parou. - + Entrada com má formatação ou algo deu errado. - + Não consegui encontrar essa carta. Fato @@ -1676,7 +1679,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Capítulos - + HQ # Derrotas Competitivas @@ -1688,7 +1691,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Rank Competitivo - Vitorias Competitivas + Vitórias Competitivas Completado @@ -1703,10 +1706,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Data - Defina + Defina: - + Dropado Episódios @@ -1727,80 +1730,80 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Gêneros - + Falha ao encontrar uma definição para essa tag. Altura/Peso - + {0}m/{1}kg - + Humidade - + Busca de Imagens para: Falha ao encontrar este filme. - + Linguagem ou fonte inválida. - + Piadas não carregadas. - + Nível - + lista de tags {0}place Don't translate {0}place Localização - + Itens mágicos não carregados. - + Perfil MAL de {0} - + O proprietário do bot não especificou a MashapeApiKey. Você não pode usar essa funcionalidade. Min/Max - Nenhum canal encontrado + Nenhum canal encontrado. - Nenhum resultado encontrado + Nenhum resultado encontrado. - + Em espera Url Original - + Requer uma API key de osu! - + Falha ao obter a assinatura osu! - + Cerca de {0} imagens encontradas. Mostrando aleatória {0}. Usuário não encontrado! Por favor cheque a região e a BattleTag antes de tentar de novo. - + Planeja assistir Plataforma @@ -1809,13 +1812,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Nenhuma habilidade encontrada. - Nenhum pokemon encontrado + Nenhum pokemon encontrado. Link do Perfil: - Qualidade + Qualidade: @@ -1824,25 +1827,25 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + Avaliação - Pontuação + Pontuação: - + Busca Por: - + Falha ao encurtar esse url. - Alguma coisa deu errado + Alguma coisa deu errado. - + Por favor, especifique os parâmetros de busca. Status @@ -1851,28 +1854,28 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - Streamer {0} está offline + Streamer {0} está offline. - Streamer {0} está online com {1} espectadores + Streamer {0} está online com {1} espectadores. - + Você esta seguindo {0} streams nesse servidor. - Você não está seguindo nenhuma stream neste servidor + Você não está seguindo nenhuma stream neste servidor. - + Nenhuma stream. - + Stream provavelmente não existe. - + Stream de {0} ({1}) removida das notificações. - Eu notificarei este canal quando o status mudar + Eu notificarei este canal quando o status mudar. Nascer do Sol @@ -1884,7 +1887,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Temperatura - Título + Título: Top 3 animes favoritos: @@ -1896,37 +1899,37 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Tipos - + Falha ao encontrar a definição para esse termo. Url - + Espectadores Assistindo - + Falha ao tentar encontrar esse termo na wikia especificada. - + Por favor, insira a wikia alvo, seguida do que deve ser pesquisado. - Página não encontrada + Página não encontrada. Velocidade do Vento - + Os {0} campeões mais banidos - + Falha ao yodificar sua frase. - + Juntou-se @@ -1937,16 +1940,16 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + {0} usuários no total. Autor - + Bot ID - + Lista de funções no comando {0}calc @@ -1955,22 +1958,22 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Tópico do Canal - Comandos utilizados + Comandos utilizados - + {0} {1} é igual a {2} {3} - + Unidades que podem ser utilizadas pelo conversor - + Não foi possível converter {0} para {1}: unidades não encontradas - + Não foi possível converter {0} para {1}: as unidades não são do mesmo tipo - + Criado em @@ -1991,29 +1994,31 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. - + ID - + Índice fora de alcance. - Aqui está uma lista de usuários nestes cargos + Aqui está uma lista de usuários nestes cargos: - + você não tem permissão de usar esse comando em cargos com muitos usuários neles para prevenir abuso. - + Valor {0} inválido. Invalid months value/ Invalid hours value - + Juntou-se ao Discord - + Juntou-se ao Servidor - + ID: {0} +Membros: {1} +OwnerID: {2} Nenhum servidor encontrado nessa página. @@ -2031,7 +2036,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Mensagens - R + Repetidor de Mensagem Nome @@ -2043,22 +2048,22 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Ninguém está jogando esse jogo. - + Nenhum repetidor ativo. Nenhum cargo nesta página. - + Nenhum Shard nesta página. - + Nenhum tópico definido. Dono - Dono IDs + IDs do Dono Presença @@ -2069,52 +2074,52 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. {2} Canais de Voz - + Todas as citações com a palavra-chave {0} foram deletadas. - + Página {0} de citações - Nenhuma citação nesta página + Nenhuma citação nesta página. - Nenhuma citação que você possa remover foi encontrada + Nenhuma citação que você possa remover foi encontrada. Citação adicionada - + Uma citação aleatória foi removida. Região - + Registrado em - + Eu lembrarei {0} de {1} em {2} `({3:d.M.yyyy.} at {4:HH:mm})` - + Formato de data inválido. Verifique a lista de comandos. - + Novo modelo de lembrete definido. - + Repetindo {0} a cada {1} dia(s), {2} hora(s) e {3} minuto(s). - Lista de repetidores + Lista de Repetidores - Nenhum repetidor neste server. + Nenhum repetidor neste servidor. #{0} parou. - Nenhuma mensagens repetidas encontradas. + Nenhuma mensagem repetindo neste servidor. Resultado @@ -2123,19 +2128,19 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Cargos - + Página #{0} de todos os cargos neste servidor: - + Página #{0} de cargos para {1} - + Nenhuma cor no formato correto. Use `#00ff00`, por exemplo. - + Começando a rotacionar cores do cargo {0}. - + Parando de rotacionar cores do cargo {0} {0} deste servidor é {1} @@ -2144,13 +2149,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Informações do Servidor - Fragmento + Shard - Status de fragmento + Status do Shard - + Shard **#{0}** está no estado {1} com {2} servidores **Nome:** {0} **Link:** {1} @@ -2159,7 +2164,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Nenhum emoji especial encontrado. - Tocando {0} canções, {1} no queue. + Tocando {0} músicas, {1} na fila. Canais de Texto @@ -2168,10 +2173,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Aqui está o link do quarto: - + Tempo de Atividade - + {0} do usuário {1} é {2} Id of the user kwoth#1234 is 123123123123 From 130bdd767a4df965ba0f0f7af646ec2a1cdaabae Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:43 +0100 Subject: [PATCH 239/256] Update ResponseStrings.ru-RU.resx (POEditor.com) From ca4a798d5fcdf36f4ce63db1239d4c44cb7d585b Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 10:36:45 +0100 Subject: [PATCH 240/256] Update ResponseStrings.sr-cyrl-rs.resx (POEditor.com) From d17055f36430e1adab73a50983b26adb80cf1253 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 12:36:28 +0100 Subject: [PATCH 241/256] Fixed .prune X --- src/NadekoBot/Modules/Administration/Administration.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NadekoBot/Modules/Administration/Administration.cs b/src/NadekoBot/Modules/Administration/Administration.cs index 405485dd..f779a751 100644 --- a/src/NadekoBot/Modules/Administration/Administration.cs +++ b/src/NadekoBot/Modules/Administration/Administration.cs @@ -452,7 +452,6 @@ namespace NadekoBot.Modules.Administration { if (count < 1) return; - count += 1; await Context.Message.DeleteAsync().ConfigureAwait(false); int limit = (count < 100) ? count : 100; var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten().ConfigureAwait(false)); From ca98cc51edb1e8f208612b8b1cfca3aeb2224917 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 16:24:39 +0100 Subject: [PATCH 242/256] Update ResponseStrings.pt-BR.resx (POEditor.com) --- .../Resources/ResponseStrings.pt-BR.resx | 273 +++++++++--------- 1 file changed, 137 insertions(+), 136 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx index 634142d7..27351b00 100644 --- a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx +++ b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx @@ -127,26 +127,26 @@ Esta base não foi reivindicada. - + Base #{0} **DESTRUÍDA** na guerra contra {1} - + {0} **RENUNCIOU** a base #{1} na guerra contra {2} - {0} clamou a base #{1} durante a guerra contra {2} + {0} reivindicou a base #{1} durante a guerra contra {2} - @{0} Você já reivindicou esta base #{1}. Você não pode reivindicar uma nova. + @{0} Você já reivindicou a base #{1}. Você não pode reivindicar uma nova. - + O pedido de guerra de @{0} contra {1} expirou. Inimigo - Informações sobre a guerra contra {0} + Informações sobre a guerra contra {0} Número de base inválido. @@ -164,7 +164,7 @@ Você não está participando nesta guerra. - @{0} Você não está participando nessa guerra, ou aquela base já está destruída. + @{0} Você não está participando nessa guerra, ou essa base já está destruída. Nenhuma guerra ativa. @@ -185,7 +185,7 @@ Essa guerra não existe. - Guerra contra {0} começou! + Guerra contra {0} iniciada! Todos os status de reações personalizadas limpos. @@ -200,10 +200,10 @@ Lista de todas as reações personalizadas - Reações personalizadas + Reações Personalizadas - Nova reação personalizada + Nova Reação Personalizada Nenhuma reação personalizada encontrada. @@ -215,10 +215,10 @@ Resposta - Status de reações personalizadas + Status de Reação Personalizada - Status limpado para {0} reação customizável. + Status da reação customizável {0} limpo. Nenhum status para aquele comando encontrado, nenhuma ação foi tomada. @@ -270,7 +270,7 @@ Não é efetivo. - Você não tem {0} o suficiente + Você não tem {0} suficiente Reviveu {0} com uma {1} @@ -323,13 +323,13 @@ Motivo: {1} Usuário Banido - Nome do bot alterado para {0}. + Nome do bot alterado para {0} - Status do bot alterado para {0}. + Status do bot alterado para {0} - Deleção automática de mensagens de despedida foi desativado. + Remoção automática de mensagens de despedida foi desativada. Mensagens de despedida serão deletadas após {0} segundos. @@ -365,7 +365,7 @@ Motivo: {1} Conteúdo - Cargo {0} criado com sucesso. + Cargo {0} criado com sucesso Canal de texto {0} criado. @@ -383,7 +383,7 @@ Motivo: {1} Parou a eliminação automática de invocações de comandos bem sucedidos. - Agora automaticamente deletando invocações de comandos bem sucedidos + Agora automaticamente deletando invocações de comandos bem sucedidos. Canal de texto {0} deletado. @@ -395,7 +395,7 @@ Motivo: {1} Mensagem direta de - Novo doador adicionado com sucesso. Número total doado por este usuário: {0} 👑 + Novo doador adicionado com sucesso. Valor total doado por este usuário: {0} 👑 Obrigado às pessoas listadas abaixo por fazer este projeto acontecer! @@ -413,7 +413,7 @@ Motivo: {1} Vou parar de enviar mensagens diretas de agora em diante. - Eliminação automática de mensagens de boas vindas desabilitada. + Remoção automática de mensagens de boas vindas desabilitada. Mensagens de boas vindas serão deletadas após {0} segundos. @@ -428,10 +428,10 @@ Motivo: {1} Novas mensagen direta de boas vindas definida. - + Mensagens diretas de boas vindas desabilitadas. - + Mensagens diretas de boas vindas habilitadas. Mensagem de boas vindas atual: {0} @@ -443,10 +443,10 @@ Motivo: {1} Nova mensagem de boas vindas definida. - Anúncios de boas vindas desabilitados. + Mensagens de boas vindas desabilitadas. - Anúncios de boas vindas habilitados neste canal. + Mensagens de boas vindas habilitadas neste canal. Você não pode usar este comando em usuários com um cargo maior ou igual ao seu na hierarquia dos cargos. @@ -455,7 +455,7 @@ Motivo: {1} Imagens carregadas após {0} segundos! - + Formato de entrada inválido. Parâmetros inválidos. @@ -464,14 +464,14 @@ Motivo: {1} {0} juntou-se a {1} - Você foi banido do servidor {0}. + Você foi kickado do servidor {0}. Razão: {1} - Usuário Chutado + Usuário Kickado - Lista de linguagens + Lista de Linguagens {0} @@ -496,25 +496,25 @@ Razão: {1} Servidor {0} deixado. - + Logando o evento {0} neste canal. - + Logando todos os eventos neste canal. - + Log desativado. - + Eventos Log em que você pode se inscrever: - + O log vai ignorar {0} - + O log vai deixar de ignorar {0} - + Parando de logar o evento {0}. {0} invocou uma menção nos seguintes cargos @@ -564,7 +564,7 @@ Razão: {1} Apelido Alterado - Não posso achar esse servidor + Não posso encontrar esse servidor Nenhum shard com aquele ID foi encontrado. @@ -600,7 +600,7 @@ Razão: {1} Nenhuma proteção ativa. - + O limite de usuários deve ser entre {0} e {1}. Se {0} ou mais usuários entrarem em menos de {1} segundos, {2}. @@ -609,10 +609,10 @@ Razão: {1} O tempo deve ser entre {0} e {1} segundos. - Todos os cargos foram removidos do usuário {0} com sucesso. + Todos os cargos foram removidos do usuário {0} com sucesso - Falha ao remover cargos. Eu não possuo permissões suficientes + Falha ao remover cargos. Eu não possuo permissões suficientes. @@ -628,7 +628,7 @@ Razão: {1} Um erro ocorreu devido à cor inválida ou permissões insuficientes. - Cargo {0} removido do usuário {1} com sucesso. + Cargo {0} removido do usuário {1} com sucesso Falha ao remover o cargo. Não possuo permissões suficientes. @@ -668,22 +668,22 @@ Razão: {1} {0} - + Nenhum status de rotação "jogando" definido. Você já possui o cargo {0}. - + Você já possui o cargo auto-atribuível exclusivo {0}. Cargos auto-atribuíveis agora são exclusivos! - + Existem {0} cargos auto-atribuíveis - Esse cargo não é auto-atribuível + Esse cargo não é auto-atribuível. Você não possui o cargo {0}. @@ -695,16 +695,16 @@ Razão: {1} Não sou capaz de adicionar esse cargo a você. `Não posso adicionar cargos a donos ou outros cargos maiores que o meu cargo na hierarquia dos cargos.` - {0} foi removido da lista de cargos auto-aplicáveis. + {0} foi removido da lista de cargos auto-atribuíveis. - Voce nao possui mas o cargo {0}. + Você não possui mais o cargo {0}. - Voce agora possui o cargo {0}. + Você agora possui o cargo {0}. - Cargo {0} adicionado à {1} com sucesso. + Cargo {0} adicionado ao usuário {1} com sucesso. Falha ao adicionar o cargo. Não possuo permissões suficientes. @@ -734,7 +734,7 @@ Razão: {1} Desligando - Usuários não podem mandar mais de {0} mensagens a cada {1} segundos + Usuários não podem mandar mais de {0} mensagens a cada {1} segundos. Modo lento desativado. @@ -743,7 +743,7 @@ Razão: {1} Modo lento iniciado. - + Banidos temporariamente (kickados) PLURAL @@ -753,7 +753,7 @@ Razão: {1} {0} irá deixar de ignorar esse canal. - Se um usuário postar {0} mensagens iguais em seguida, eu irei {1} eles. + Se um usuário postar {0} mensagens iguais em seguida, {1}. __Canais Ignorados__: {2} @@ -763,7 +763,7 @@ __Canais Ignorados__: {2} Canal de Texto Destruído - + Desensurdecido com sucesso. Desmutado @@ -806,7 +806,7 @@ __Canais Ignorados__: {2} {0} agora está {1} - + {0} foi **desmutado** nos chats de voz e texto. {0} juntou-se ao canal de voz {1}. @@ -818,10 +818,10 @@ __Canais Ignorados__: {2} {0} moveu-se do canal de voz {1} para {2}. - {0} foi **mutado por voz** + {0} foi **mutado por voz**. - {0} foi **desmutado por voz** + {0} foi **desmutado por voz**. Canal de voz criado @@ -879,7 +879,7 @@ Motivo: {1} Mais sorte na próxima vez ^_^ - Parabéns! Você ganhou {0} por rolar acima {1} + Parabéns! Você ganhou {0} por rolar acima de {1} Baralho re-embaralhado. @@ -895,13 +895,13 @@ Motivo: {1} O número especificado é inválido. Você pode girar de 1 a {0} moedas. - Adicione {0} reação + Adicione {0} reação a esta mensagem para ganhar {1} Este evento está ativo por até {0} horas. - + Evento de Reação da Flor iniciado! deu {0} de presente para {1} @@ -918,7 +918,7 @@ Motivo: {1} Placar de Líderes - + Concedeu {0} a {1} usuários do cargo {2}. Você não pode apostar mais que {0} @@ -933,10 +933,10 @@ Motivo: {1} Sem cartas no baralho. - Usuario sorteado + Usuário sorteado - Você rolou {0} + Você rolou {0}. Aposta @@ -945,7 +945,7 @@ Motivo: {1} WOAAHHHHHH!!! Parabéns!!! x{0} - + Um único {0}, x{1} Wow! Que sorte! Três de um tipo! x{0} @@ -961,10 +961,10 @@ Motivo: {1} Dura {1} segundos. Não diga a ninguém. Shhh. - + O evento "Jogo Sorrateiro" terminou. {0} usuários receberam o prêmio. - + O evento "Jogo Sorrateiro" começou Coroa @@ -976,13 +976,13 @@ Dura {1} segundos. Não diga a ninguém. Shhh. Não foi possível tomar {0} de {1} porque o usuário não possuí tanto {2}! - + Voltar à Tabela de Conteúdos Proprietário do bot apenas. - Requer a permissão {0} do canal + Requer a permissão {0} do canal. Você pode dar suporte ao projeto no Patreon: <{0}> ou Paypal: <{1}> @@ -991,7 +991,7 @@ Dura {1} segundos. Não diga a ninguém. Shhh. Comandos e abreviações - + Lista de Comandos Regenerada. Digite `{0}h NomeDoComando` para ver a ajuda para o comando especificado. Ex: `{0}h >8ball` @@ -1045,10 +1045,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Corrida de Animais - Falha ao iniciar já que não tiveram participantes suficientes. + Falha ao iniciar, não houve participantes suficientes. - Corrida cheia! Começando imediatamente + Corrida cheia! Começando imediatamente. {0} juntou-se como {1} @@ -1072,10 +1072,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. {0} como {1} ganhou a corrida e {2}! - + Número especificado inválido. Você pode rolar {0}-{1} dados de uma vez. - + rolou {0} Someone rolled 35 @@ -1095,13 +1095,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Mudanças no Coração - Clamado por + Reivindicado por Divórcios - + Gosta de Preço @@ -1113,7 +1113,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Top Waifus - + Sua afinidade já foi definida para essa waifu ou você está tentando remover sua afinidade sem ter uma. Mudou a afinidade de {0} para {1}. @@ -1122,7 +1122,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Make sure to get the formatting right, and leave the thinking emoji - + Você deve esperar {0} horas e {1} minutos para mudar sua afinidade de novo. Sua afinidade foi reiniciada. Você não possui mas alguém que você goste. @@ -1141,7 +1141,8 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Você não pode colocar sua afinidade em você mesmo, seu egomaníaco. - + 🎉 O amor deles está realizado! 🎉 +O novo valor de {0} é {1}! Nenhuma waifu é tão barata. Você deve pagar pelo menos {0} para ter uma waifu, mesmo se o valor dela for menor. @@ -1165,31 +1166,31 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Você se divorciou de uma waifu que não gostava de você. Você recebeu {0} de volta. - + 8ball - + Acrofobia - + O jogo terminou sem submissões. - + Nenhum voto recebido. O jogo terminou sem vencedores. - + O acrônimo era {0}. - + Acrofobia já está em andamento neste canal. - + Jogo iniciado. Crie uma frase com o seguinte acrônimo: {0}. Você tem {0} segundos para fazer uma submissão. - {0} submeteram suas frases. ({1} total) + {0} submeteram suas frases. ({1} no total) Vote digitando um número da submissão @@ -1201,7 +1202,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Vence {0} com {1} pontos. - + {0} venceu por ser o único usuário a fazer uma submissão! Questão @@ -1213,7 +1214,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. {0} ganhou! {1} vence {2} - + Submissões Encerradas A Corrida de Animais já está em andamento @@ -1231,10 +1232,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Cleverbot habilitado neste servidor. - + Geração de moeda desabilitada neste canal. - + Geração de moeda habilitada neste canal. {0} {1} aleatórios aparecem! Capture-os digitando `{2}pick` @@ -1259,7 +1260,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Erro ao iniciar o Jogo da Forca. - Lista dos tipos de termo do "{0}hangman" + Lista dos tipos de termos do "{0}hangman" Placar de Lideres @@ -1345,22 +1346,22 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Volume padrão definido para {0}% - + Diretório adicionado à fila. - + fairplay Música concluída. - + Fair play desativado. - + Fair play ativado. - + Da posição Id @@ -1369,10 +1370,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Entrada inválida. - + Tempo de atividade máximo agora não tem limite. - + Tempo de atividade máximo definido para {0} segundo(s). Tamanho máximo da fila de música definido para ilimitado. @@ -1396,10 +1397,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Nenhum resultado para a busca. - + Música pausada. - + Fila de Músicas - Página {0}/{1} Tocando Musica @@ -1420,13 +1421,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Não existe uma playlist com esse ID. - + Playlist adicionada à fila. Playlist Salva - + Limite de {0}s Fila @@ -1441,7 +1442,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. A fila está cheia em {0}/{0} - Música removida + Música removida: context: "removed song #5" @@ -1457,7 +1458,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. A repetição da faixa atual parou. - + Música retomada. Repetição de playlist desabilitada. @@ -1475,7 +1476,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Músicas embaralhadas. - Música movida. + Música movida {0}h {1}m {2}s @@ -1520,7 +1521,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. O uso de TODOS OS MÓDULOS foi habilitado para o usuário {0}. - + {0} entrou na Lista Negra com o ID {1} O comando {0} agora possui um cooldown de {1}s. @@ -1532,13 +1533,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Nenhum cooldown de comando definido. - + Custos de Comando Desabilitado o uso de {0} {1} no canal {2}. - Desabilitado o uso de {0} {1} no canal {2}. + Habilitado o uso de {0} {1} no canal {2}. Negado @@ -1556,22 +1557,22 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Segundo parâmetro inválido. (Deve ser um número entre {0} e {1}) - + Filtro de convite desabilitado neste canal. - + Filtro de convite habilitado neste canal. - + Filtro de convite desabilitado neste servidor. - + Filtro de convite habilitado neste servidor. Permissão {0} movida de #{1} para #{2} - + Não consigo encontrar a permissão no índice #{0} Nenhum custo definido. @@ -1588,13 +1589,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Página {0} de Permissões - Atual cargo de permissões é {0} + Cargo atual de permissões é {0}. - + Usuários agora precisam do cargo {0} para editar permissões. - + Nenhuma permissão encontrada nesse índice. Permissões removidas #{0} - {1} @@ -1616,16 +1617,16 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Habilitado o uso de {0} {1} nesse servidor. - + {0} saiu da Lista Negra com o ID {1} - Não editável + não editável - + Desabilitado o uso de {0} {1} para o usuário {2}. - + Habilitado o uso de {0} {1} para o usuário {2}. Não vou mais mostrar avisos de permissões. @@ -1652,7 +1653,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Nenhum anime favorito ainda - A tradução automática de mensagens nesse canal foi iniciada. As mensagens do usuário serão deletadas automaticamente. + Iniciada a tradução automática de mensagens nesse canal. As mensagens do usuário serão deletadas automaticamente. A linguagem de tradução automática foi removida. @@ -1673,7 +1674,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Não consegui encontrar essa carta. - Fato + fato Capítulos @@ -1694,7 +1695,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Vitórias Competitivas - Completado + Concluída Condição @@ -1754,7 +1755,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Piadas não carregadas. - + Latitude/Longitude Nível @@ -1821,10 +1822,10 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Qualidade: - + Tempo em Partida Rápida - + Vitórias em Partida Rápida Avaliação @@ -1839,7 +1840,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Falha ao encurtar esse url. - + Url Curta Alguma coisa deu errado. @@ -1851,7 +1852,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Status - + Url da Loja Streamer {0} está offline. @@ -1932,12 +1933,12 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Juntou-se - + `{0}.` {1} [{2:F2}/s] - {3} total /s and total need to be localized to fit the context - `1.` - + Página de Atividade #{0} {0} usuários no total. @@ -1952,13 +1953,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Lista de funções no comando {0}calc - + {0} deste canal é {1} Tópico do Canal - Comandos utilizados + Comandos Utilizados {0} {1} é igual a {2} {3} @@ -1976,13 +1977,13 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Criado em - + Juntou-se ao canal de servidor cruzado. - + Deixou o canal de servidor cruzado. - + Este é seu token de Canal de Servidor Cruzado Emojis Personalizados @@ -1991,7 +1992,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Erro - + Atributos ID @@ -2003,7 +2004,7 @@ Não esqueça de deixar seu nome ou id do discord na mensagem. Aqui está uma lista de usuários nestes cargos: - você não tem permissão de usar esse comando em cargos com muitos usuários neles para prevenir abuso. + você não tem permissão de usar esse comando em cargos com muitos usuários para prevenir abuso. Valor {0} inválido. @@ -2170,7 +2171,7 @@ OwnerID: {2} Canais de Texto - Aqui está o link do quarto: + Aqui está o link da sala: Tempo de Atividade From a199891612d7f2c18e4c4b2279d0171054c6708c Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 17:12:38 +0100 Subject: [PATCH 243/256] fixed some strings, thx xnaas, closes #1093 --- src/NadekoBot/Modules/Utility/Utility.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Utility/Utility.cs b/src/NadekoBot/Modules/Utility/Utility.cs index f69aef7d..0452dab6 100644 --- a/src/NadekoBot/Modules/Utility/Utility.cs +++ b/src/NadekoBot/Modules/Utility/Utility.cs @@ -273,7 +273,7 @@ namespace NadekoBot.Modules.Utility [NadekoCommand, Usage, Description, Aliases] public async Task ChannelId() { - await ReplyConfirmLocalized("channelidd", "🆔", Format.Code(Context.Channel.Id.ToString())) + await ReplyConfirmLocalized("channelid", "🆔", Format.Code(Context.Channel.Id.ToString())) .ConfigureAwait(false); } @@ -439,7 +439,7 @@ namespace NadekoBot.Modules.Utility var result = string.Join("\n", tags.Select(m => GetText("showemojis", m, m.Url))); if (string.IsNullOrWhiteSpace(result)) - await ReplyErrorLocalized("emojis_none").ConfigureAwait(false); + await ReplyErrorLocalized("showemojis_none").ConfigureAwait(false); else await Context.Channel.SendMessageAsync(result).ConfigureAwait(false); } From c65398482a1776f9564d238b32a5737a01a7f0dc Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 18:10:58 +0100 Subject: [PATCH 244/256] Update ResponseStrings.fr-fr.resx (POEditor.com) --- src/NadekoBot/Resources/ResponseStrings.fr-fr.resx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx index 72a2957f..ac95afb6 100644 --- a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -483,7 +483,7 @@ Raison : {1} La langue du bot a été changée pour {0} - {1} - Échec dans la tentative de changement de langue. Réessayer avec l'aide pour cette commande. + Échec dans la tentative de changement de langue. Veuillez consulter l'aide pour cette commande. La langue de ce serveur est {0} - {1} @@ -1664,7 +1664,8 @@ La nouvelle valeur de {0} est {1} ! Votre langue de traduction à été supprimée. - Votre langue de traduction a été changée de {from} à {to} + Votre langue de traduction a été changée de {0} à {1} + Fuzzy Traduction automatique des messages commencée sur ce salon. @@ -2111,7 +2112,7 @@ OwnerID: {2} Je vais vous rappeler {0} pour {1} dans {2} `({3:d.M.yyyy} à {4:HH:mm})` - Format de date non valide. Regardez la liste des commandes. + Format de date non valide. Vérifiez la liste des commandes. Nouveau modèle de rappel défini. From 78c2eb4430eca2216e62084707713e7c5c7cce08 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 18:11:01 +0100 Subject: [PATCH 245/256] Update ResponseStrings.de-DE.resx (POEditor.com) --- .../Resources/ResponseStrings.de-DE.resx | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx index 8d8866c5..04611d86 100644 --- a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx +++ b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx @@ -872,7 +872,7 @@ Grund: {1} Nutzer wurde gekickt - verleiht {1} {0} + vegab {0} zu {1} Hoffentlich haben sie beim nächsten Mal mehr Glück ^_^ @@ -907,7 +907,7 @@ Grund: {1} X has gifted 15 flowers to Y - {0} hat eine {1} + {0} hat {1} X has Y flowers @@ -1031,7 +1031,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Inhaltsverzeichnis - Nutzung + Benutzweise Autohentai wurde gestartet. Es wird alle {0} Sekunden etwas mit einem der folgenden Stichwörtern gepostet: {1} @@ -1099,7 +1099,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Scheidungen - Positive Bewertungen + Mag Preis @@ -1202,7 +1202,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu {0} gewinnt, weil dieser Benutzer die einzigste Einsendung hat! - Frage + Gestellte Frage Unentschieden! Beide haben {0} gewählt @@ -1292,7 +1292,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu {0} hat {1} punkte - Wird beendet after dieser Frage. + Wird beendet nach dieser Frage. Die Zeit is um! Die richtige Antwort war {0} @@ -1331,7 +1331,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu {0} gegen {1} - Versuche {0} Songs einzureihen... + Versuche {0} Lieder einzureihen... Autoplay deaktiviert. @@ -1349,7 +1349,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu fairer Modus - Song beendet + Lied beendet Fairer Modus deaktiviert. @@ -1376,7 +1376,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Maximale Musik-Warteschlangengröße ist nun unbegrenzt. - Maximale Musik-Warteschlangengröße ist nun {0} Songs. + Maximale Musik-Warteschlangengröße ist nun {0} Lieder. Sie müssen sich in einem Sprachkanal auf diesem Server befinden. @@ -1385,7 +1385,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Name - Aktueller Song: + Aktuelles Lied: Kein aktiver Musikspieler. @@ -1400,10 +1400,10 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Musik-Warteschlange - Seite {0}/{1} - Spiele Song + Spiele Lied - `#{0}` - **{1}** by *{2}* ({3} Songs) + `#{0}` - **{1}** by *{2}* ({3} Lieder) Seite {0} der gespeicherten Playlists @@ -1430,7 +1430,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Warteschlange - Eingereihter Song + Eingereihtes Lied Musik-Warteschlange geleert. @@ -1439,20 +1439,20 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Warteschlange ist voll bei {0}/{1}. - Song entfernt + Lied entfernt context: "removed song #5" - Aktueller Song wird wiederholt + Aktuelle Lied wird wiederholt Playlist wird wiederholt - Song wird wiederholt + Lied wird wiederholt - Aktueller Song wird nicht mehr wiederholt. + Aktuelles Lied wird nicht mehr wiederholt. Musikwiedergabe wiederaufgenommen. @@ -1464,16 +1464,16 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Playlist-Wiederholung aktiviert. - Ich werde nun spielende, beendete, pausierte und entfernte Songs in diesen Channel ausgeben. + Ich werde nun spielende, beendete, pausierte und entfernte Lieder in diesem Channel ausgeben. Gesprungen zu `{0}:{1}` - Song gemischt. + Lieder gemischt. - Song bewegt + Lieder bewegt {0}h {1}m {2}s @@ -1656,7 +1656,8 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Ihre Automatische-Übersetzungs Sprache wurde entfernt. - Ihre Automatische-Übersetzungs Sprache wurde zu {from}>{to} gesetzt + Ihre Automatische-Übersetzungs Sprache wurde zu {0}>{1} gesetzt + Fuzzy Automatische Übersetzung der Nachrichten wurde auf diesem kanal gestartet. From ded86b51b02a0127a5dc338cc9482c24c7388109 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 18:11:04 +0100 Subject: [PATCH 246/256] Update ResponseStrings.pt-BR.resx (POEditor.com) --- src/NadekoBot/Resources/ResponseStrings.pt-BR.resx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx index 27351b00..bac83942 100644 --- a/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx +++ b/src/NadekoBot/Resources/ResponseStrings.pt-BR.resx @@ -1659,7 +1659,8 @@ O novo valor de {0} é {1}! A linguagem de tradução automática foi removida. - A linguagem de tradução automática foi definida de {from}>{to} + A linguagem de tradução automática foi definida de {0}>{1} + Fuzzy A tradução automática de mensagens foi ativada neste canal. From b93c212c687ddcad38e9f1766e4269c1bbb019f9 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 18:11:07 +0100 Subject: [PATCH 247/256] Update ResponseStrings.ru-RU.resx (POEditor.com) --- .../Resources/ResponseStrings.ru-RU.resx | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx index d2980cc8..21b36460 100644 --- a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx +++ b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx @@ -119,14 +119,12 @@ Эта база уже захвачена или разрушена. - Fuzzy Эта база уже разрушена Эта база не захвачена. - Fuzzy **РАЗРУШЕННАЯ** база #{0} ведёт войну против {1}. @@ -136,7 +134,7 @@ Fuzzy - {0} захватил базу #{1} после войны с {2} + {0} захватил базу #{1} ведя войну против {2} Fuzzy @@ -163,7 +161,7 @@ Список активных войн - не захваченная + не захваченна Fuzzy @@ -211,11 +209,11 @@ Fuzzy - Настроить реакции. + Настроить ответы. Fuzzy - Создана новая реакция. + Новый ответ. Fuzzy @@ -223,7 +221,7 @@ Fuzzy - Не найдено настраеваемых реакций с таким номером. + Не найдено настраиваемых реакций с таким номером. Fuzzy @@ -1010,7 +1008,7 @@ Fuzzy не смог забрать {0} у {1}, поскольку у пользователя нет столько {2}! - Вернуться к содержанию + Вернуться к оглавлению Только для владельца бота @@ -1064,7 +1062,7 @@ Paypal <{1}> Требуются серверное право {0} - Содержание + Оглавление Использование @@ -1384,7 +1382,7 @@ Paypal <{1}> Папка успешно добавлена в очередь воспроизведения. - fairplay + справедливое воспроизведение Fuzzy @@ -1697,7 +1695,7 @@ Paypal <{1}> Ваш язык автоперевода был удалён. - Ваш язык автоперевода изменён с {from} на {to} + Ваш язык автоперевода изменён с {0} на {1} Начинается автоматический перевод сообщений в этом канале. From f294b9997b0793bbea78a9534cef3e518c3dfaa6 Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 18:14:22 +0100 Subject: [PATCH 248/256] fixed string --- src/NadekoBot/Resources/ResponseStrings.Designer.cs | 2 +- src/NadekoBot/Resources/ResponseStrings.resx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.Designer.cs index a33b43b9..c3d3a842 100644 --- a/src/NadekoBot/Resources/ResponseStrings.Designer.cs +++ b/src/NadekoBot/Resources/ResponseStrings.Designer.cs @@ -4587,7 +4587,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Your auto-translate language has been set to {from}>{to}. + /// Looks up a localized string similar to Your auto-translate language has been set to {0}>{1}. /// public static string searches_atl_set { get { diff --git a/src/NadekoBot/Resources/ResponseStrings.resx b/src/NadekoBot/Resources/ResponseStrings.resx index efa1fe55..c89a5084 100644 --- a/src/NadekoBot/Resources/ResponseStrings.resx +++ b/src/NadekoBot/Resources/ResponseStrings.resx @@ -1658,7 +1658,7 @@ Don't forget to leave your discord name or id in the message. your auto-translate language has been removed. - Your auto-translate language has been set to {from}>{to} + Your auto-translate language has been set to {0}>{1} Started automatic translation of messages on this channel. From 50553bd8b01c4e976e2ec455fda8e5190b7636cc Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 20:40:09 +0100 Subject: [PATCH 249/256] Fixed resource filenames --- .../Modules/Administration/Commands/LocalizationCommands.cs | 2 +- .../{ResponseStrings.fr-fr.resx => ResponseStrings.fr-FR.resx} | 0 ...rl-rs.Designer.cs => ResponseStrings.sr-Cyrl-RS.Designer.cs} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/NadekoBot/Resources/{ResponseStrings.fr-fr.resx => ResponseStrings.fr-FR.resx} (100%) rename src/NadekoBot/Resources/{ResponseStrings.sr-cyrl-rs.Designer.cs => ResponseStrings.sr-Cyrl-RS.Designer.cs} (100%) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 4200fafb..135a1da7 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Administration {"nl-NL", "Dutch, Netherlands"}, {"ja-JP", "Japanese, Japan"}, {"pt-BR", "Portuguese, Brazil"}, - {"sr-cyrl-rs", "Serbian, Serbia - Cyrillic"} + {"sr-Cyrl-RS", "Serbian, Serbia - Cyrillic"} }.ToImmutableDictionary(); [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-FR.resx similarity index 100% rename from src/NadekoBot/Resources/ResponseStrings.fr-fr.resx rename to src/NadekoBot/Resources/ResponseStrings.fr-FR.resx diff --git a/src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.Designer.cs b/src/NadekoBot/Resources/ResponseStrings.sr-Cyrl-RS.Designer.cs similarity index 100% rename from src/NadekoBot/Resources/ResponseStrings.sr-cyrl-rs.Designer.cs rename to src/NadekoBot/Resources/ResponseStrings.sr-Cyrl-RS.Designer.cs From c04163e4c1e41794441a7ae65654366d12222896 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 21:36:58 +0100 Subject: [PATCH 250/256] Create ResponseStrings.fr-fr.resx (POEditor.com) --- .../Resources/ResponseStrings.fr-fr.resx | 2203 +++++++++++++++++ 1 file changed, 2203 insertions(+) create mode 100644 src/NadekoBot/Resources/ResponseStrings.fr-fr.resx diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx new file mode 100644 index 00000000..1c802a95 --- /dev/null +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -0,0 +1,2203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cette base a déjà été revendiquée ou détruite. + + + Cette base est déjà détruite. + + + Cette base n'est pas revendiquée. + + + Base #{0} **DETRUITE** dans une guerre contre {1} + + + {0} a **ABANDONNÉ** la base #{1} dans une guerre contre {2} + + + {0} a revendiqué une base #{1} dans une guerre contre {2} + + + @{0} Vous avez déjà revendiqué la base #{1}. Vous ne pouvez pas en revendiquer une nouvelle. + + + La demande de la part de @{0} pour une guerre contre {1} a expiré. + + + Ennemi + + + Informations concernant la guerre contre {0} + + + Numéro de base invalide. + + + La taille de la guerre n'est pas valide. + + + Liste des guerres en cours + + + non réclamé + + + Vous ne participez pas a cette guerre. + + + @{0} Vous ne participez pas à cette guerre ou la base a déjà été détruite. + + + Aucune guerre en cours. + + + Taille + + + La guerre contre {0} a déjà commencé! + + + La guerre contre {0} commence! + + + La guerre contre {0} est terminée. + + + Cette guerre n'existe pas. + + + La guerre contre {0} a éclaté ! + + + Statistiques de réactions personnalisées effacées. + + + Réaction personnalisée supprimée + + + Permissions insuffisantes. Nécessite d'être le propriétaire du Bot pour avoir les réactions personnalisées globales, et Administrateur pour les réactions personnalisées du serveur. + + + Liste de toutes les réactions personnalisées + + + Réactions personnalisées + + + Nouvelle réaction personnalisée + + + Aucune réaction personnalisée trouvée. + + + Aucune réaction personnalisée ne correspond à cet ID. + + + Réponse + + + Statistiques des Réactions Personnalisées + + + Statistiques effacées pour {0} réaction personnalisée. + + + Pas de statistiques pour ce déclencheur trouvées, aucune action effectuée. + + + Déclencheur + + + Autohentai arrêté. + + + Aucun résultat trouvé. + + + {0} est déjà inconscient. + + + {0} a tous ses PV. + + + Votre type est déjà {0} + + + Vous avez utilisé {0}{1} sur {2}{3} pour {4} dégâts. + Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. + + + Vous ne pouvez pas attaquer de nouveau sans représailles ! + + + Vous ne pouvez pas vous attaquer vous-même. + + + {0} s'est évanoui! + + + Vous avez soigné {0} avec un {1} + + + {0} a {1} points de vie restants. + + + Vous ne pouvez pas utiliser {0}. Ecrivez `{1}ml` pour voir la liste des actions que vous pouvez effectuer. + + + Liste des attaques pour le type {0} + + + Ce n'est pas très efficace. + + + Vous n'avez pas assez de {0} + + + Vous avez ressuscité {0} avec un {1} + + + Vous vous êtes ressuscité avec un {0} + + + Votre type a bien été modifié de {0} à {1} + + + C'est légèrement efficace. + + + C'est très efficace ! + + + Vous avez utilisé trop de mouvements d'affilée, vous ne pouvez donc plus bouger! + + + Le type de {0} est {1} + + + Utilisateur non trouvé. + + + Vous vous êtes évanoui, vous n'êtes donc pas capable de bouger! + + + **L'affectation automatique de rôle** à l'arrivé d'un nouvel utilisateur est désormais **désactivée**. + + + **L'affectation automatique de rôle** à l'arrivé d'un nouvel utilisateur est désormais **activée**. + + + Liens + + + Avatar modifié + + + Vous avez été banni du serveur {0}. +Raison: {1} + + + banni + PLURAL + + + Utilisateur banni + + + Le nom du Bot a changé pour {0} + + + Le statut du Bot a changé pour {0} + + + La suppression automatique des annonces de départ a été désactivée. + + + Les annonces de départ seront supprimées après {0} secondes. + + + Annonce de départ actuelle : {0} + + + Activez les annonces de départ en entrant {0} + + + Nouvelle annonce de départ définie. + + + Annonce de départ désactivée. + + + Annonce de départ activée sur ce salon. + + + Nom du salon modifié + + + Ancien nom + + + Sujet du salon modifié + + + Nettoyé. + + + Contenu + + + Rôle {0} créé avec succès + + + Salon textuel {0} créé. + + + Salon vocal {0} créé. + + + Mise en sourdine effectuée. + + + Serveur {0} supprimé + + + Suppression automatique des commandes effectuées avec succès, désactivée. + + + Suppression automatique des commandes effectuées avec succès, activée. + + + Le salon textuel {0} a été supprimé. + + + Le salon vocal {0} a été supprimé. + + + MP de + + + Nouveau donateur ajouté avec succès. Montant total des dons de cet utilisateur : {0} 👑 + + + Merci aux personnes ci-dessous pour avoir permis à ce projet d'exister! + + + Je transmettrai désormais les MPs à tous les propriétaires. + + + Je transmettrai désormais les MPs seulement au propriétaire principal. + + + Je transmettrai désormais les MPs. + + + Je ne transmettrai désormais plus les MPs. + + + La suppression automatique des messages d'accueil a été désactivé. + + + Les messages d'accueil seront supprimés après {0} secondes. + + + MP de bienvenue actuel: {0} + + + Activez les MPs de bienvenue en écrivant {0} + + + Nouveau MP de bienvenue défini. + + + MPs de bienvenue désactivés. + + + MPs de bienvenue activés. + + + Message de bienvenue actuel: {0} + + + Activez les messages de bienvenue en écrivant {0} + + + Nouveau message de bienvenue défini. + + + Messages de bienvenue désactivés. + + + Messages de bienvenue activés sur ce salon. + + + Vous ne pouvez pas utiliser cette commande sur les utilisateurs dont le rôle est supérieur ou égal au vôtre dans la hiérarchie. + + + Images chargées après {0} secondes! + + + Format d'entrée invalide. + + + Paramètres invalides. + + + {0} a rejoint {1} + + + Vous avez été expulsé du serveur {0}. +Raison : {1} + + + Utilisateur expulsé + + + Listes des langues +{0} + + + La langue du serveur est désormais {0} - {1} + + + La langue par défaut du bot est désormais {0} - {1} + + + La langue du bot a été changée pour {0} - {1} + + + Échec dans la tentative de changement de langue. Veuillez consulter l'aide pour cette commande. + + + La langue de ce serveur est {0} - {1} + + + {0} a quitté {1} + + + Serveur {0} quitté + + + Enregistrement de {0} événements dans ce salon. + + + Enregistrement de tous les événements dans ce salon. + + + Enregistrement désactivé. + + + Événements enregistrés que vous pouvez suivre : + + + L'enregistrement ignorera désormais {0} + + + L'enregistrement n'ignorera pas {0} + + + L’événement {0} ne sera plus enregistré. + + + {0} a émis une notification pour les rôles suivants + + + Message de {0} `[Bot Owner]` : + + + Message envoyé. + + + {0} déplacé de {1} à {2} + L'utilisateur n'a pas forcément été déplacé de son plein gré. S'est déplacé - déplacé + + + Message supprimé dans #{0} + + + Mise à jour du message dans #{0} + + + Tous les utilisateurs sont maintenant muets. + PLURAL (users have been muted) + + + L'utilisateur est maintenant muet. + singular "User muted." + + + Il semblerait que je n'ai pas la permission nécessaire pour effectuer cela. + + + Nouveau rôle muet créé. + + + J'ai besoin de la permission d'**Administrateur** pour effectuer cela. + + + Nouveau message + + + Nouveau pseudonyme + + + Nouveau sujet + + + Pseudonyme changé + + + Impossible de trouver ce serveur + + + Aucun Shard pour cet ID trouvée. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Ancien message + + + Ancien pseudonyme + + + Ancien sujet + + + Erreur. Je ne dois sûrement pas posséder les permissions suffisantes. + + + Les permissions pour ce serveur ont été réinitialisées. + + + Protections actives + + + {0} a été **désactivé** sur ce serveur. + + + {0} Activé + + + Erreur. J'ai besoin de la permission Gérer les rôles. + + + Aucune protection activée. + + + Le seuil d'utilisateurs doit être entre {0} et {1}. + + + Si {0} ou plus d'utilisateurs rejoignent dans les {1} secondes suivantes, je les {2}. + + + Le temps doit être compris entre {0} et {1} secondes. + + + Vous avez retirés tous les rôles de l'utilisateur {0} avec succès + + + Impossible de retirer des rôles. Je n'ai pas les permissions suffisantes. + + + La couleur du rôle {0} a été modifiée. + + + Ce rôle n'existe pas. + + + Les paramètres spécifiés sont invalides. + + + Erreur due à un manque de permissions ou à une couleur invalide. + + + L'utilisateur {1} n'a plus le rôle {0}. + + + Impossible de supprimer ce rôle. Je ne possède pas les permissions suffisantes. + + + Rôle renommé. + + + Impossible de renommer ce rôle. Je ne possède pas les permissions suffisantes. + + + Vous ne pouvez pas modifier les rôles supérieurs au votre. + + + La répétition du message suivant a été désactivé: {0} + + + Le rôle {0} a été ajouté à la liste. + + + {0} introuvable. Nettoyé. + + + Le rôle {0} est déjà présent dans la liste. + + + Ajouté. + + + Rotation du statut de jeu désactivée. + + + Rotation du statut de jeu activée. + + + Voici une liste des rotations de statuts : +{0} + + + Aucune rotation de statuts en place. + + + Vous avez déjà le rôle {0}. + + + Vous avez déjà {0} rôles exclusifs auto-attribués. + + + Rôles auto-attribuables désormais exclusifs. + + + Il y a {0} rôles auto-attribuables. + + + Ce rôle ne peux pas vous être attribué par vous-même. + + + Vous ne possédez pas le rôle {0}. + + + Les rôles auto-attribuables ne sont désormais plus exclusifs! + Je ne pense pas que ce soit la bonne traduction.. self-assignable role serait plutôt rôle auto-attribuable + + + Je suis incapable de vous ajouter ce rôle. `Je ne peux pas ajouter de rôles aux propriétaires et aux autres rôles plus haut que le mien dans la hiérarchie.` + + + {0} a été supprimé de la liste des rôles auto-attribuables. + + + Vous n'avez plus le rôle {0}. + + + Vous avez désormais le rôle {0}. + + + L'utilisateur {1} a désormais le rôle {0}. + + + Impossible d'ajouter un rôle. Je ne possède pas les permissions suffisantes. + + + Nouvel avatar défini! + + + Nouveau nom de Salon défini avec succès. + + + Nouveau jeu défini! + Pour "set", je pense que défini irait mieux que "en service" + + + Nouveau stream défini! + + + Nouveau sujet du salon défini. + + + Shard {0} reconnecté. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Shard {0} en reconnection. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Arrêt en cours. + + + Les utilisateurs ne peuvent pas envoyer plus de {0} messages toutes les {1} secondes. + + + Mode ralenti désactivé. + + + Mode ralenti activé + + + expulsés (kick) + PLURAL + + + {0} ignorera ce Salon. + + + {0} n'ignorera plus ce Salon. + + + Si un utilisateur poste {0} le même message à la suite, je le {1}. + __SalonsIgnorés__: {2} + + + Salon textuel crée. + + + Salon textuel supprimé. + + + Son activé avec succès. + + + Micro activé + singular + + + Nom d'utilisateur + + + Nom d'utilisateur modifié. + + + Utilisateurs + + + Utilisateur banni + + + {0} est maintenant **muet** sur le chat. + + + **La parole a été rétablie** sur le chat pour {0}. + + + L'utilisateur a rejoint + + + L'utilisateur a quitté + + + {0} est maintenant **muet** à la fois sur les salons textuels et vocaux. + + + Rôle ajouté à l'utilisateur + + + Rôle retiré de l'utilisateur + + + {0} est maintenant {1} + + + {0} n'est maintenant **plus muet** des salons textuels et vocaux. + + + {0} a rejoint le salon vocal {1}. + + + {0} a quitté le salon vocal {1}. + + + {0} est allé du salon vocal {1} à {2}. + + + {0} est maintenant **muet**. + + + {0} n'est maintenant **plus muet**. + + + Salon vocal crée. + + + Salon vocal supprimé. + + + Fonctionnalités vocales et textuelles désactivées. + + + Fonctionnalités vocales et textuelles activées. + + + Je n'ai pas la permission **Gérer les rôles** et/ou **Gérer les salons**, je ne peux donc pas utiliser `voice+text` sur le serveur {0}. + + + Vous activez/désactivez cette fonctionnalité et **je n'ai pas la permission ADMINISTRATEUR**. Cela pourrait causer des dysfonctionnements, et vous devrez nettoyer les salons textuels vous-même après ça. + + + Je nécessite au moins les permissions **Gérer les rôles** et **Gérer les salons** pour activer cette fonctionnalité. (Permission Administrateur de préférence) + + + Utilisateur {0} depuis un salon textuel. + + + Utilisateur {0} depuis salon textuel et vocal. + + + Utilisateur {0} depuis un salon vocal. + + + Vous avez été expulsé du serveur {0}. +Raison: {1} + + + Utilisateur débanni + + + Migration effectuée! + + + Erreur lors de la migration, veuillez consulter la console pour plus d'informations. + + + Présences mises à jour. + + + Utilisateur expulsé. + + + a récompensé {0} à {1} + + + Plus de chance la prochaine fois ^_^ + C'est vraiment québecois ca.. On ne le dit pas comme ca ici xD + + + Félicitations! Vous avez gagné {0} pour avoir lancé au dessus de {1}. + + + Deck remélangé. + + + Lancé {0}. + User flipped tails. + + + Vous avez deviné! Vous avez gagné {0} + + + Nombre spécifié invalide. Vous pouvez lancer 1 à {0} pièces. + + + Ajoute la réaction {0} à ce message pour avoir {1} + + + Cet événement est actif pendant {0} heures. + + + L'événement "réactions fleuries" a démarré! + + + a donné {0} à {1} + X has gifted 15 flowers to Y + + + {0} a {1} + X has Y flowers + + + Face + + + Classement + + + {1} utilisateurs du rôle {2} ont été récompensé de {0}. + + + Vous ne pouvez pas miser plus de {0} + + + Vous ne pouvez pas parier moins de {0} + + + Vous n'avez pas assez de {0} + + + Plus de carte dans le deck. + + + Utilisateur tiré au sort + + + Vous avez roulé un {0}. + + + Mise + + + WOAAHHHHHH!!! Félicitations!!! x{0} + + + Une simple {0}, x{1} + + + Wow! Quelle chance! Trois du même genre! x{0} + + + Bon travail! Deux {0} - mise x{1} + + + Gagné + + + Les utilisateurs doivent écrire un code secret pour avoir {0}. Dure {1} secondes. Ne le dite à personne. Shhh. + + + L’événement "jeu sournois" s'est fini. {0} utilisateurs ont reçu la récompense. + + + L'événement "jeu sournois" a démarré + + + Pile + + + Vous avez pris {0} de {1} avec succès + + + Impossible de prendre {0} de {1} car l'utilisateur n'a pas assez de {2}! + + + Retour à la table des matières + + + Propriétaire du Bot seulement + + + Nécessite {0} permissions du salon. + + + Vous pouvez supporter ce projet sur Patreon <{0}> ou via Paypal <{1}> + + + Commandes et alias + + + Liste des commandes rafraîchie. + + + Écrivez `{0}h NomDeLaCommande` pour voir l'aide spécifique à cette commande. Ex: `{0}h >8ball` + + + Impossible de trouver cette commande. Veuillez vérifier qu'elle existe avant de réessayer. + + + Description + + + Vous pouvez supporter le projet NadekoBot +sur Patreon <{0}> +par Paypal <{1}> +N'oubliez pas de mettre votre nom discord ou ID dans le message. + +**Merci** ♥️ + + + **Liste des Commandes**: <{0}> +**La liste des guides et tous les documents peuvent être trouvés ici**: <{1}> + + + Liste des commandes + + + Liste des modules + + + Entrez `{0}cmds NomDuModule` pour avoir la liste des commandes de ce module. ex `{0}cmds games` + + + Ce module n'existe pas. + + + Permission serveur {0} requise. + + + Table des matières + + + Usage + + + Autohentai commencé. Publie toutes les {0}s avec l'un des tags suivants : +{1} + + + Tag + + + Course d'animaux + + + Pas assez de participants pour commencer. + + + Suffisamment de participants ! La course commence. + + + {0} a rejoint sous la forme d'un {1} + + + {0} a rejoint sous la forme d'un {1} et mise sur {2} ! + + + Entrez {0}jr pour rejoindre la course. + + + Début dans 20 secondes ou quand le nombre maximum de participants est atteint. + + + Début avec {0} participants. + + + {0} sous la forme d'un {1} a gagné la course ! + + + {0} sous la forme d'un {1} a gagné la course et {2}! + + + Nombre spécifié invalide. Vous pouvez lancer de {0} à {1} dés à la fois. + + + tiré au sort {0} + Someone rolled 35 + + + Dé tiré au sort: {0} + Dice Rolled: 5 + + + Le lancement de la course a échoué. Une autre course doit probablement être en cours. + + + Aucune course n'existe sur ce serveur. + + + Le deuxième nombre doit être plus grand que le premier. + + + Changements de Coeur + + + Revendiquée par + + + Divorces + + + Aime + + + Prix + + + Aucune waifu n'a été revendiquée pour l'instant. + + + Top Waifus + + + votre affinité est déjà liée à cette waifu ou vous êtes en train de retirer votre affinité avec quelqu'un alors que vous n'en possédez pas. + + + Affinités changées de de {0} à {1}. + +*C'est moralement discutable.* 🤔 + Make sure to get the formatting right, and leave the thinking emoji + + + Vous devez attendre {0} heures et {1} minutes avant de pouvoir changer de nouveau votre affinité. + + + Votre affinité a été réinitialisée. Vous n'avez désormais plus la personne que vous aimez. + + + veut être la waifu de {0}. Aww <3 + + + a revendiqué {0} comme sa waifu pour {1} + + + Vous avez divorcé avec une waifu qui vous aimais. Monstre sans cœur. {0} a reçu {1} en guise de compensation. + + + vous ne pouvez pas vous lier d'affinité avec vous-même, espèce d'égocentrique. + + + 🎉Leur amour est comblé !🎉 +La nouvelle valeur de {0} est {1} ! + + + Aucune waifu n'est à ce prix. Tu dois payer au moins {0} pour avoir une waifu, même si sa vraie valeur est inférieure. + + + Tu dois payer {0} ou plus pour avoir cette waifu ! + + + Cette waifu ne t'appartient pas. + + + Tu ne peux pas t'acquérir toi-même. + + + Vous avez récemment divorcé. Vous devez attendre {0} heures et {1} minutes pour divorcer à nouveau. + + + Personne + + + Vous avez divorcé d'une waifu qui ne vous aimait pas. Vous recevez {0} en retour. + + + 8ball + + + Acrophobie + + + Le jeu a pris fin sans soumissions. + + + Personne n'a voté : la partie se termine sans vainqueur. + + + L'acronyme était {0}. + + + Une partie d'Acrophobia est déjà en cours sur ce salon. + + + La partie commence. Créez une phrase avec l'acronyme suivant : {0}. + + + Vous avez {0} secondes pour faire une soumission. + + + {0} a soumit sa phrase. ({1} au total) + + + Votez en entrant le numéro d'une soumission. + + + {0} a voté! + + + Le gagnant est {0} avec {1} points! + + + {0} est le gagnant pour être le seul utilisateur à avoir fait une soumission! + + + Question + + + Egalité ! Vous avez choisi {0}. + + + {0} a gagné ! {1} bat {2}. + + + Inscriptions terminées. + + + Une course d'animaux est déjà en cours. + + + Total : {0} Moyenne : {1} + + + Catégorie + + + Cleverbot désactivé sur ce serveur. + + + Cleverbot activé sur ce serveur. + + + La génération monétaire a été désactivée sur ce salon. + Currency =/= current !!! Il s'agit de monnaie. Par example: Euro is a currency. US Dollar also is. Sur Public Nadeko, il s'agit des fleurs :) + + + La génération monétaire a été désactivée sur ce salon. + + + {0} {1} aléatoires sont apparus ! Attrapez-les en entrant `{2}pick` + plural + + + Un {0} aléatoire est apparu ! Attrapez-le en entrant `{1}pick` + + + Impossible de charger une question. + + + La jeu a commencé. + + + Partie de pendu commencée. + + + Une partie de pendu est déjà en cours sur ce canal. + + + Initialisation du pendu erronée. + + + Liste des "{0}hangman" types de termes : + + + Classement + + + Vous n'avez pas assez de {0} + + + Pas de résultat + + + a cueilli {0} + Kwoth picked 5* + + + {0} a planté {1} + Kwoth planted 5* + + + Une partie de Trivia est déjà en cours sur ce serveur. + + + Partie de Trivia + + + {0} a deviné! La réponse était: {1} + + + Aucune partie de Trivia en cours sur ce serveur. + + + {0} a {1} points + + + Le jeu s'arrêtera après cette question. + + + Le temps a expiré! La réponse était {0} + + + {0} a deviné la réponse et gagne la partie! La réponse était: {1} + + + Vous ne pouvez pas jouer contre vous-même. + + + Une partie de Morpion est déjà en cours sur ce salon. + + + Égalité! + + + a crée une partie de Morpion. + + + {0} a gagné ! + + + Trois alignés. + + + Aucun mouvement restant ! + + + Le temps a expiré ! + + + Tour de {0}. + + + {0} contre {1} + + + Tentative d'ajouter {0} musiques à la file d'attente... + + + Lecture automatique désactivée. + + + Lecture automatique activée. + + + Volume par défaut défini à {0}% + + + File d'attente complète. + + + à tour de rôle + + + Lecture terminée + + + Système de tour de rôle désactivé. + + + Système de tour de rôle activé. + + + De la position + + + Id + + + Entrée invalide. + + + Le temps maximum de lecture est désormais illimité. + + + Temps maximum de lecture défini à {0} seconde(s). + + + La taille de la file d'attente est désormais illmitée. + + + La taille de la file d'attente est désormais de {0} piste(s). + + + Vous avez besoin d'être dans un salon vocal sur ce serveur. + + + Nom + + + Vous écoutez + + + Aucun lecteur de musique actif. + + + Pas de résultat. + + + Lecteur mis sur pause. + + + Liste d'attente - Page {0}/{1} + + + Lecture en cours: + + + `#{0}` - **{1}** par *{2}* ({3} morceaux) + + + Page {0} des listes de lecture sauvegardées + + + Liste de lecture supprimée. + + + Impossible de supprimer cette liste de lecture. Soit elle n'existe pas, soit vous n'en êtes pas le créateur. + + + Aucune liste de lecture ne correspond a cet ID. + + + File d'attente de la liste complétée. + + + Liste de lecture sauvegardée + + + Limite à {0}s + + + Liste d'attente + + + Musique ajoutée à la file d'attente + + + Liste d'attente effacée. + + + Liste d'attente complète ({0}/{0}). + + + Musique retirée + context: "removed song #5" + + + Répétition de la musique en cours + + + Liste de lecture en boucle + + + Piste en boucle + + + La piste ne sera lue qu'une fois. + + + Reprise de la lecture. + + + Lecture en boucle désactivée. + + + Lecture en boucle activée. + + + Je vais désormais afficher les musiques en cours, en pause, terminées et supprimées sur ce salon. + + + Saut à `{0}:{1}` + + + Lecture aléatoire activée. + + + Musique déplacée + + + {0}h {1}m {2}s + + + En position + + + Illimité + + + Le volume doit être compris entre 0 et 100 + + + Volume réglé sur {0}% + + + Désactivation de l'usage de TOUS LES MODULES pour le salon {0}. + + + Activation de l'usage de TOUS LES MODULES pour le salon {0}. + + + Permis + + + Désactivation de l'usage de TOUS LES MODULES pour le rôle {0}. + + + Activation de l'usage de TOUS LES MODULES pour le rôle {0}. + + + Désactivation de l'usage de TOUS LES MODULES sur ce serveur. + + + Activation de l'usage de TOUS LES MODULES sur ce serveur. + + + Désactivation de l'usage de TOUS LES MODULES pour l'utilisateur {0}. + + + Activation de l'usage de TOUS LES MODULES pour l'utilisateur {0}. + + + {0} sur liste noire avec l'ID {1} + Il ne s'agit pas d'un ban mais d'une blacklist interdisant l'utilisateur d'utiliser le bot. + + + La commande {0} a désormais {1}s de temps de recharge. + + + La commande {0} n'a pas de temps de recharge et tous les temps de recharge ont été réinitialisés. + + + Aucune commande n'a de temps de recharge. + + + Coût de la commande : + + + Usage de {0} {1} désactivé sur le salon {2}. + + + Usage de {0} {1} activé sur le salon {2}. + + + Refusé + + + Ajout du mot {0} à la liste des mots filtrés. + + + Liste Des Mots Filtrés + + + Suppression du mot {0} de la liste des mots filtrés. + + + Second paramètre invalide. (nécessite un nombre entre {0} et {1}) + + + Filtrage des invitations désactivé sur ce salon. + + + Filtrage des invitations activé sur ce salon. + + + Filtrage des invitations désactivé sur le serveur. + + + Filtrage des invitations activé sur le serveur. + + + Permission {0} déplacée de #{1} à #{2} + + + Impossible de trouver la permission à l'index #{0} + + + Aucun coût défini. + + + Commande + Gen (of command) + + + Module + Gen. (of module) + + + Page {0} des permissions + + + Le rôle des permissions actuelles est {0}. + + + Il faut maintenant avoir le rôle {0} pour modifier les permissions. + + + Aucune permission trouvée à cet index. + + + Supression des permissions #{0} - {1} + + + Usage de {0} {1} désactivé pour le rôle {2}. + + + Usage de {0} {1} activé pour le rôle {2}. + + + sec. + Short of seconds. + + + Usage de {0} {1} désactivé pour le serveur. + + + Usage de {0} {1} activé pour le serveur. + + + Débanni {0} avec l'ID {1} + + + Non modifiable + + + Usage de {0} {1} désactivé pour l'utilisateur {2}. + + + Usage de {0} {1} activé pour l'utilisateur {2}. + + + Je n'afficherai plus les avertissements des permissions. + + + J'afficherai désormais les avertissements des permissions. + + + Filtrage des mots désactivé sur ce salon. + + + Filtrage des mots activé sur ce salon. + + + Filtrage des mots désactivé sur le serveur. + + + Filtrage des mots activé sur le serveur. + + + Capacités + + + Pas encore d'anime préféré + + + Traduction automatique des messages activée sur ce salon. Les messages utilisateurs vont désormais être supprimés. + + + Votre langue de traduction à été supprimée. + + + Votre langue de traduction a été changée de {0} à {1} + + + Traduction automatique des messages commencée sur ce salon. + + + Traduction automatique des messages arrêtée sur ce salon. + + + Le format est invalide ou une erreur s'est produite. + + + Impossible de trouver cette carte. + + + fait + + + Chapitres + + + Bande dessinée # + + + Parties compétitives perdues + + + Parties compétitives jouées + + + Rang en compétitif + + + Parties compétitives gagnées + + + Complétés + + + Condition + + + Coût + + + Date + + + Définis: + + + Abandonnés + droppped as in, "stopped watching" referring to shows/anime + + + Episodes + + + Une erreur s'est produite. + + + Exemple + + + Impossible de trouver cet anime. + + + Impossible de trouver ce manga. + + + Genres + + + Impossible de trouver une définition pour ce hashtag. + + + Taille/Poid + + + {0}m/{1}kg + + + Humidité + + + Recherche d'images pour: + + + Impossible de trouver ce film. + + + Langue d'origine ou de destination invalide. + + + Blagues non chargées. + + + Lat/Long + + + Niveau + + + Liste de tags pour {0}place. + Don't translate {0}place + + + Emplacement + + + Les objets magiques ne sont pas chargés. + + + Profil MAL de {0} + + + Le propriétaire du Bot n'a pas spécifié de clé d'API Mashape (MashapeApiKey). Fonctionnalité non disponible + + + Min/Max + + + Aucun salon trouvé. + + + Aucun résultat trouvé. + + + En attente + + + Url originale + + + Une clé d'API osu! est nécessaire. + + + Impossible de récupérer la signature osu!. + + + Trouvé dans {0} images. Affichage de {0} aléatoires. + + + Utilisateur non trouvé! Veuillez vérifier la région ainsi que le BattleTag avant de réessayer. + + + Prévus de regarder + Je ne pense pas que le sens de la traduction soit le bon. + + + Plateforme + + + Attaque non trouvée. + + + Pokémon non trouvé. + + + Lien du profil : + + + Qualité + + + Durée en Jeux Rapides + + + Victoires Rapides + + + Évaluation + + + Score: + + + Chercher pour: + recherche plutôt non ? + + + Impossible de réduire cette Url. + + + Url réduite + + + Une erreur s'est produite. + + + Veuillez spécifier les paramètres de recherche. + + + Statut + + + Url stockée + + + Le streamer {0} est hors ligne. + + + Le streamer {0} est en ligne avec {1} viewers. + + + Vous suivez {0} streams sur ce serveur. + + + Vous ne suivez aucun stream sur ce serveur. + + + Aucun stream de ce nom. + + + Ce stream n'existe probablement pas. + + + Stream de {0} ({1}) retirée des notifications. + + + Je préviendrai ce salon lors d'un changement de statut. + + + Aube + + + Crépuscule + + + Température + + + Titre: + + + Top 3 anime favoris + + + Traduction: + + + Types + + + Impossible de trouver une définition pour ce terme. + + + Url + + + Viewers + + + En écoute + + + Impossible de trouver ce terme sur le wikia spécifié. + + + Entrez un wikia cible, suivi d'une requête de recherche. + + + Page non trouvée. + + + Vitesse du vent + + + Les {0} champions les plus bannis + + + Impossible de yodifier votre phrase. + + + Rejoint + + + `{0}.` {1} [{2:F2}/s] - {3} total + /s and total need to be localized to fit the context - +`1.` + + + Page d'activité #{0} + + + {0} utilisateurs en total. + + + Créateur + + + ID du Bot + + + Liste des fonctions pour la commande {0}calc + + + {0} de ce salon est {1} + + + Sujet du salon + + + Commandes exécutées + + + {0} {1} est équivalent à {2} {3} + + + Unités pouvant être converties : + + + Impossible de convertir {0} en {1}: unités non trouvées + + + Impossible de convertir {0} en {1} : les types des unités ne sont pas compatibles. + + + Créé le + + + Salon inter-serveur rejoint. + + + Salon inter-serveur quitté. + + + Voici votre jeton CSC + + + Emojis personnalisées + + + Erreur + + + Fonctionnalités + + + ID + + + Index hors limites. + + + Voici une liste des utilisateurs dans ces rôles : + + + Vous ne pouvez pas utiliser cette commande sur un rôle incluant beaucoup d'utilisateurs afin d'éviter les abus. + + + Valeur {0} invalide. + Invalid months value/ Invalid hours value + + + Discord rejoint + + + Serveur rejoint + + + ID: {0} +Membres: {1} +OwnerID: {2} + + + Aucun serveur trouvée sur cette page. + + + Liste des messages répétés + + + Membres + + + Mémoire + + + Messages + + + Répéteur de messages + + + Nom + + + Pseudonyme + + + Personne ne joue à ce jeu. + + + Aucune répétition active. + + + Aucun rôle sur cette page. + + + Aucun shard sur cette page. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Aucun sujet choisi. + + + Propriétaire + + + ID des propriétaires + + + Présence + + + {0} Serveurs +{1} Salons Textuels +{2} Salons Vocaux + + + Toutes les citations possédant le mot-clé {0} ont été supprimées. + + + Page {0} des citations + + + Aucune citation sur cette page. + + + Aucune citation que vous puissiez supprimer n'a été trouvé. + + + Citation ajoutée + + + Une citation aléatoire a été supprimée. + + + Région + + + Inscrit sur + + + Je vais vous rappeler {0} pour {1} dans {2} `({3:d.M.yyyy} à {4:HH:mm})` + + + Format de date non valide. Vérifiez la liste des commandes. + + + Nouveau modèle de rappel défini. + + + Répétition de {0} chaque {1} jour(s), {2} heure(s) et {3} minute(s). + + + Liste des répétitions + + + Aucune répétition active sur ce serveur. + + + #{0} arrêté. + + + Pas de message répété trouvé sur ce serveur. + + + Résultat + + + Rôles + + + Page #{0} de tout les rôles sur ce serveur. + + + Page #{0} des rôles pour {1} + + + Aucune couleur n'est dans le bon format. Utilisez `#00ff00` par exemple. + + + Couleurs alternées pour le rôle {0} activées. + + + Couleurs alternées pour le rôle {0} arrêtées + + + {0} de ce serveur est {1} + + + Info du serveur + + + Shard + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Statistique des shards + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + Le shard **#{0}** est en état {1} avec {2} serveurs. + Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. + + + **Nom:** {0} **Lien:** {1} + + + Pas d'emojis spéciaux trouvés. + + + Joue actuellement {0} musiques, {1} en attente. + + + Salons textuels + + + Voici le lien pour votre salon: + + + Durée de fonctionnement + + + {0} de l'utilisateur {1} est {2} + Id of the user kwoth#1234 is 123123123123 + + + Utilisateurs + + + Salons vocaux + + + \ No newline at end of file From fb574f0925172596886cdf6f54bdabc585b792b4 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 21:37:01 +0100 Subject: [PATCH 251/256] Update ResponseStrings.de-DE.resx (POEditor.com) --- src/NadekoBot/Resources/ResponseStrings.de-DE.resx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx index 04611d86..8b3974b3 100644 --- a/src/NadekoBot/Resources/ResponseStrings.de-DE.resx +++ b/src/NadekoBot/Resources/ResponseStrings.de-DE.resx @@ -241,7 +241,7 @@ Dein Typ ist bereits {0} - benutzt {0}{1} auf {2}{3} für {4} Schaden. + benutzte {0}{1} an {2}{3} für {4} Schaden. Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage. @@ -281,7 +281,7 @@ Dein Typ wurde verändert von {0} mit einem {1} - Das ist effektiv. + Das ist etwas effektiv. Das ist sehr effektiv! @@ -838,7 +838,7 @@ __ignoredChannels__: {2} Ich habe keine **Rollenmanagement**- und/oder **Kanalmanagement**-Rechte, sodass ich `voice+text` auf dem Server {0} nicht ausführen kann. - Sie schalten diese Funktion ein bzw. aus und **ich habe keine ADMINISTRATORRECHTE**. Dies könnte einige Probleme hervorrufen, sodass sie ihre Textkanäle eventuell selber aufräumen musst. + Sie schalten diese Funktion ein bzw. aus und **ich habe keine ADMINISTRATORRECHTE**. Dies könnte einige Probleme hervorrufen, sodass sie ihre Textkanäle eventuell selber aufräumen müssen. Ich benötige zumindest **Rollenmanagement**- und **Kanalmanagement**-Rechte um diese Funktion einzuschalten. (Bevorzugt Administratorrechte) @@ -1181,7 +1181,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu Akrophobia läuft bereits in diesem Kanal. - Spiel gestartet. Erstelle einen Satz aus dem folgenden Akronym. + Spiel gestartet. Erstelle einen Satz aus dem folgenden Akronym: {0}. Sie haben {0} Sekunden für ihre Einsendung. @@ -1942,7 +1942,7 @@ Vergessen sie bitte nicht, ihren Discord-Namen oder ihre ID in der Nachricht zu {0} totale Benutzer. - Autor(in) + Autor ID des Bots @@ -2091,7 +2091,7 @@ ID des Besitzers: {2} Zitat hinzugefügt - Zufälliger Zitat wurde gelöscht. + Zufälliges Zitat wurde gelöscht. Region From 8b859ab0278c94f66e2c2298dd48644740508ce6 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 21:37:05 +0100 Subject: [PATCH 252/256] Update ResponseStrings.pt-BR.resx (POEditor.com) From 5e06ccb5bfc4802bd2f2892b71da0ef5af919944 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Wed, 1 Mar 2017 21:37:08 +0100 Subject: [PATCH 253/256] Update ResponseStrings.ru-RU.resx (POEditor.com) --- .../Resources/ResponseStrings.ru-RU.resx | 86 ++++++------------- 1 file changed, 24 insertions(+), 62 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx index 21b36460..12a8b4d2 100644 --- a/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx +++ b/src/NadekoBot/Resources/ResponseStrings.ru-RU.resx @@ -131,19 +131,15 @@ У {0} есть **НЕ ЗАХВАЧЕННАЯ** база #{1}, ведущая войну против {2} - Fuzzy - {0} захватил базу #{1} ведя войну против {2} - Fuzzy + {0} захватил базу #{1}, ведя войну против {2} @{0} Вы уже захватили базу #{1}. Вы не можете захватить новую базу. - Fuzzy Время действия запроса от @{0} на войну против {1} истёкло. - Fuzzy Враг @@ -161,8 +157,7 @@ Список активных войн - не захваченна - Fuzzy + не захвачена Вы не участвуете в этой войне. @@ -180,8 +175,7 @@ Война против {0} уже началась. - Война против {0} была создана. - Fuzzy + Война против {0} была начата. Закончилась война против {0}. @@ -194,50 +188,39 @@ Вся статистика настраиваемых реакций стёрта. - Fuzzy - Настраиваемая реакция удалена. - Fuzzy + Настраиваемая реакция удалена Недостаточно прав. Необходимо владеть Бот-ом для глобальных настраиваемых реакций или быть Администратором для реакций по серверу. - Fuzzy - Список всех настраиваемых реакций. - Fuzzy + Список всех настраиваемых реакций - Настроить ответы. - Fuzzy + Настраиваемые реакции - Новый ответ. - Fuzzy + Новая настраиваемая реакция Не найдено настраиваемых реакций. - Fuzzy Не найдено настраиваемых реакций с таким номером. - Fuzzy Ответ - Статистика настраеваемых реакций. - Fuzzy + Статистика настраеваемых реакций Статистика удалена для настраеваемой реакции {0}. - Fuzzy - Не найдено статистики для этого запроса, никаких действий не применено. - Fuzzy + Не найдено статистики для этого активатора, никаких действий не применено. Активатор @@ -278,41 +261,33 @@ Вы не можете использовать {0}. Напишите '{1}ml', чтобы увидеть список доступных Вам приёмов. - Fuzzy Список приёмов {0} типа Эта атака не эффективна. - Fuzzy У вас не достаточно {0} воскресил {0}, использовав один {1} - Fuzzy Вы воскресили себя, использовав один {0} - Fuzzy Ваш тип изменён с {0} на {1} - Fuzzy Эта атака немного эффективна. - Fuzzy Эта атака очень эффективна! - Fuzzy Вы использовали слишком много приёмов подряд и не можете двигаться! - Fuzzy Тип {0} — {1} @@ -398,7 +373,6 @@ Успешное оглушение. - Fuzzy Сервер {0} удален. @@ -420,7 +394,6 @@ Успешно добавлен новый донатор. Общее количество пожертвований от этого пользователя: {0} 👑 - Fuzzy Спасибо всем, указанным ниже, что помогли этому проекту! @@ -444,19 +417,19 @@ Приветственные сообщения будут удаляться через {0} секунд. - Приветствие, используемое в настоящий момент: {0} + Приветственное ЛС, используемое в настоящий момент: {0} - Чтобы включить приветствия, напишите {0} + Чтобы включить приветственное ЛС, напишите {0} - Новое приветствие установлено. + Новое приветственное ЛС установлено. - Приветствия в личных сообщениях отключены. + Приветственые ЛС отключены. - Приветствия в личных сообщениях включены. + Приветственные ЛС включены. Текущее привественное сообщение: {0} @@ -560,25 +533,21 @@ Сообщение изменено в #{0} - Заглушёны - PLURAL (users have been muted) -Fuzzy + Заглушены + PLURAL (users have been muted) Заглушён - singular "User muted." -Fuzzy + singular "User muted." Скорее всего, у меня нет необходимых прав. - Новая немая роль установлена. - Fuzzy + Новая роль заглушения установлена. Мне нужно право **Администратор**, чтобы это сделать. - Fuzzy Новое сообщение @@ -793,7 +762,7 @@ Fuzzy Отключено заглушение. - Вкл. звук + Вернут звук singular Fuzzy @@ -810,12 +779,10 @@ Fuzzy Пользователь заблокирован - {0} получил **запрет** на разговор в чате. - Fuzzy + {0} получил **запрет** на разговор в текстовых каналах. - {0} потерял **запрет** на разговор в чате. - Fuzzy + {0} получил **разрешение** на разговор в текстовых каналах. Пользователь присоединился @@ -824,8 +791,7 @@ Fuzzy Пользователь вышел - {0} получил **запрет** на разговор в текстовом и голосовом чатах. - Fuzzy + {0} получил **запрет** на разговор в текстовых и голосовых каналах Добавлена роль пользователя @@ -837,8 +803,7 @@ Fuzzy {0} теперь {1} - {0} потерял **запрет** на разговор в текстовом и голосовом чатах. - Fuzzy + {0} получил **разрешение** на разговор в текстовых и голосовых каналах. {0} присоединился к голосовому каналу {1}. @@ -851,11 +816,9 @@ Fuzzy **Выключен микрофон** у {0}. - Fuzzy **Включён микрофон** у {0}. - Fuzzy Голосовой канал создан @@ -1128,7 +1091,7 @@ Paypal <{1}> Смены чувств - В браке + Является мужем Fuzzy @@ -1383,7 +1346,6 @@ Paypal <{1}> справедливое воспроизведение - Fuzzy Песня завершилась. From bef5549f1198281d2413220c59f9613af6a52d4b Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 21:38:05 +0100 Subject: [PATCH 254/256] Commented out unfinished languages from the list of supported ones --- .../Modules/Administration/Commands/LocalizationCommands.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs index 135a1da7..5707429b 100644 --- a/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs +++ b/src/NadekoBot/Modules/Administration/Commands/LocalizationCommands.cs @@ -22,10 +22,10 @@ namespace NadekoBot.Modules.Administration {"fr-FR", "French, France"}, {"ru-RU", "Russian, Russia"}, {"de-DE", "German, Germany"}, - {"nl-NL", "Dutch, Netherlands"}, - {"ja-JP", "Japanese, Japan"}, + //{"nl-NL", "Dutch, Netherlands"}, + //{"ja-JP", "Japanese, Japan"}, {"pt-BR", "Portuguese, Brazil"}, - {"sr-Cyrl-RS", "Serbian, Serbia - Cyrillic"} + //{"sr-Cyrl-RS", "Serbian, Serbia - Cyrillic"} }.ToImmutableDictionary(); [NadekoCommand, Usage, Description, Aliases] From ccef094388335e680eff64911560b1dc069614ee Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 21:42:20 +0100 Subject: [PATCH 255/256] Fixed smp string --- src/NadekoBot/Modules/Music/Music.cs | 2 +- src/NadekoBot/Modules/NadekoModule.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NadekoBot/Modules/Music/Music.cs b/src/NadekoBot/Modules/Music/Music.cs index 79dd3d42..eb4cbbdf 100644 --- a/src/NadekoBot/Modules/Music/Music.cs +++ b/src/NadekoBot/Modules/Music/Music.cs @@ -592,7 +592,7 @@ namespace NadekoBot.Modules.Music if (seconds == 0) await ReplyConfirmLocalized("max_playtime_none").ConfigureAwait(false); else - await ReplyConfirmLocalized("max_playtime_set").ConfigureAwait(false); + await ReplyConfirmLocalized("max_playtime_set", seconds).ConfigureAwait(false); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/Modules/NadekoModule.cs b/src/NadekoBot/Modules/NadekoModule.cs index ea4a0049..d71a2ba9 100644 --- a/src/NadekoBot/Modules/NadekoModule.cs +++ b/src/NadekoBot/Modules/NadekoModule.cs @@ -69,7 +69,7 @@ namespace NadekoBot.Modules LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS."); text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!"; if (string.IsNullOrWhiteSpace(text)) - return "I cant tell if you command is executed, because there was an error printing out the response. Key '" + + return "I can't tell you is the command executed, because there was an error printing out the response. Key '" + lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this."; } return text; From f53c68e11372af2d4c71cd55028dfbad00d1628a Mon Sep 17 00:00:00 2001 From: Kwoth Date: Wed, 1 Mar 2017 22:06:37 +0100 Subject: [PATCH 256/256] version upped to 1.2 --- .../Resources/ResponseStrings.fr-fr.resx | 44 +++++++++---------- src/NadekoBot/Services/Impl/StatsService.cs | 2 +- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx index 1c802a95..ac95afb6 100644 --- a/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx +++ b/src/NadekoBot/Resources/ResponseStrings.fr-fr.resx @@ -628,7 +628,7 @@ Raison : {1} Erreur due à un manque de permissions ou à une couleur invalide. - L'utilisateur {1} n'a plus le rôle {0}. + L'utilisateur {1} n'appartient plus au rôle {0}. Impossible de supprimer ce rôle. Je ne possède pas les permissions suffisantes. @@ -705,7 +705,7 @@ Raison : {1} Vous avez désormais le rôle {0}. - L'utilisateur {1} a désormais le rôle {0}. + L'ajout du rôle {0} pour l'utilisateur {1} a été réalisé avec succès. Impossible d'ajouter un rôle. Je ne possède pas les permissions suffisantes. @@ -727,7 +727,7 @@ Raison : {1} Nouveau sujet du salon défini. - Shard {0} reconnecté. + Shard {0} reconnectée. Ne pas confondre Shard et Shared ;) Dans discord, le mot shard n'a pas d'équivalent francophone. @@ -758,13 +758,13 @@ Raison : {1} Si un utilisateur poste {0} le même message à la suite, je le {1}. - __SalonsIgnorés__: {2} + __SalonsIgnorés__: {2} Salon textuel crée. - Salon textuel supprimé. + Salon textuel détruit. Son activé avec succès. @@ -774,7 +774,7 @@ Raison : {1} singular - Nom d'utilisateur + Nom d'utilisateur. Nom d'utilisateur modifié. @@ -798,7 +798,7 @@ Raison : {1} L'utilisateur a quitté - {0} est maintenant **muet** à la fois sur les salons textuels et vocaux. + {0} est maintenant **muet** à la fois sur le salon textuel et vocal. Rôle ajouté à l'utilisateur @@ -819,7 +819,7 @@ Raison : {1} {0} a quitté le salon vocal {1}. - {0} est allé du salon vocal {1} à {2}. + {0} est allé du salon vocal {1} au {2}. {0} est maintenant **muet**. @@ -831,7 +831,7 @@ Raison : {1} Salon vocal crée. - Salon vocal supprimé. + Salon vocal détruit. Fonctionnalités vocales et textuelles désactivées. @@ -843,10 +843,10 @@ Raison : {1} Je n'ai pas la permission **Gérer les rôles** et/ou **Gérer les salons**, je ne peux donc pas utiliser `voice+text` sur le serveur {0}. - Vous activez/désactivez cette fonctionnalité et **je n'ai pas la permission ADMINISTRATEUR**. Cela pourrait causer des dysfonctionnements, et vous devrez nettoyer les salons textuels vous-même après ça. + Vous activez/désactivez cette fonctionnalité et **je n'ai pas les permissions ADMINISTRATEURS**. Cela pourrait causer des dysfonctionnements, et vous devrez nettoyer les salons textuels vous-même après ça. - Je nécessite au moins les permissions **Gérer les rôles** et **Gérer les salons** pour activer cette fonctionnalité. (Permission Administrateur de préférence) + Je nécessite au moins les permissions **Gérer les rôles** et **Gérer les salons** pour activer cette fonctionnalité. (permissions administateurs préférées) Utilisateur {0} depuis un salon textuel. @@ -880,11 +880,10 @@ Raison: {1} a récompensé {0} à {1} - Plus de chance la prochaine fois ^_^ - C'est vraiment québecois ca.. On ne le dit pas comme ca ici xD + Meilleure chance la prochaine fois ^_^ - Félicitations! Vous avez gagné {0} pour avoir lancé au dessus de {1}. + Félicitations! Vous avez gagné {0} pour avoir lancé au dessus de {1} Deck remélangé. @@ -894,7 +893,7 @@ Raison: {1} User flipped tails. - Vous avez deviné! Vous avez gagné {0} + Vous l'avez deviné! Vous avez gagné {0} Nombre spécifié invalide. Vous pouvez lancer 1 à {0} pièces. @@ -1339,7 +1338,7 @@ La nouvelle valeur de {0} est {1} ! {0} contre {1} - Tentative d'ajouter {0} musiques à la file d'attente... + Tentative d'ajouter {0} à la file d'attente... Lecture automatique désactivée. @@ -1348,7 +1347,7 @@ La nouvelle valeur de {0} est {1} ! Lecture automatique activée. - Volume par défaut défini à {0}% + Volume de base défini à {0}% File d'attente complète. @@ -1375,10 +1374,10 @@ La nouvelle valeur de {0} est {1} ! Entrée invalide. - Le temps maximum de lecture est désormais illimité. + Le temps maximum de lecture n'a désormais plus de limite. - Temps maximum de lecture défini à {0} seconde(s). + Le temps de lecture maximum a été mis à {0} seconde(s). La taille de la file d'attente est désormais illmitée. @@ -1438,7 +1437,7 @@ La nouvelle valeur de {0} est {1} ! Liste d'attente - Musique ajoutée à la file d'attente + Son ajouté à la file d'attente Liste d'attente effacée. @@ -1447,7 +1446,7 @@ La nouvelle valeur de {0} est {1} ! Liste d'attente complète ({0}/{0}). - Musique retirée + Son retiré context: "removed song #5" @@ -1666,6 +1665,7 @@ La nouvelle valeur de {0} est {1} ! Votre langue de traduction a été changée de {0} à {1} + Fuzzy Traduction automatique des messages commencée sur ce salon. @@ -2145,7 +2145,7 @@ OwnerID: {2} Page #{0} des rôles pour {1} - Aucune couleur n'est dans le bon format. Utilisez `#00ff00` par exemple. + Aucunes couleurs ne sont dans le format correct. Utilisez `#00ff00` par exemple. Couleurs alternées pour le rôle {0} activées. diff --git a/src/NadekoBot/Services/Impl/StatsService.cs b/src/NadekoBot/Services/Impl/StatsService.cs index f3058f97..0390cbda 100644 --- a/src/NadekoBot/Services/Impl/StatsService.cs +++ b/src/NadekoBot/Services/Impl/StatsService.cs @@ -16,7 +16,7 @@ namespace NadekoBot.Services.Impl private readonly DiscordShardedClient _client; private readonly DateTime _started; - public const string BotVersion = "1.2-beta"; + public const string BotVersion = "1.2"; public string Author => "Kwoth#2560"; public string Library => "Discord.Net";