Merge pull request #5 from Kwoth/dev

ups
This commit is contained in:
samvaio 2017-03-02 15:15:25 +05:30 committed by GitHub
commit ad5916fc19
95 changed files with 25828 additions and 1904 deletions

View File

@ -1,14 +1,12 @@
![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true) ![img](https://ci.appveyor.com/api/projects/status/gmu6b3ltc80hr3k9?svg=true)
[![Discord](https://discordapp.com/api/guilds/117523346618318850/widget.png)](https://discord.gg/nadekobot) [![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) [![Documentation Status](https://readthedocs.org/projects/nadekobot/badge/?version=latest)](http://nadekobot.readthedocs.io/en/latest/?badge=latest)
# NadekoBot [![nadeko0](https://cdn.discordapp.com/attachments/266240393639755778/281920716809699328/part1.png)](http://nadekobot.xyz)
[![nadeko1](https://cdn.discordapp.com/attachments/155726317222887425/252095170676391936/A1.jpg)](https://discordapp.com/oauth2/authorize?client_id=170254782546575360&scope=bot&permissions=66186303) [![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/155726317222887425/252095207514832896/A2.jpg)](http://nadekobot.readthedocs.io/en/latest/Commands%20List/) [![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/)
##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. |

View File

@ -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<T> : Lazy<Task<T>>
{
public AsyncLazy(Func<T> valueFactory) :
base(() => Task.Factory.StartNew(valueFactory))
{ }
public AsyncLazy(Func<Task<T>> taskFactory) :
base(() => Task.Factory.StartNew(taskFactory).Unwrap())
{ }
public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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<DateTime>(
name: "DateAdded",
table: "WaifuUpdates",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "WaifuInfo",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "PokeGame",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "SelfAssignableRoles",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "Reminders",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "RaceAnimals",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "Quotes",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "PlaylistSong",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "PlayingStatus",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "Permission",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "MutedUserId",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "MusicPlaylists",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "ModulePrefixes",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "LogSettings",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "IgnoredVoicePresenceCHannels",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "IgnoredLogChannels",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "GuildRepeater",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "GuildConfigs",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "GCChannelId",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "FollowedStream",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "FilteredWord",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "FilterChannelId",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "EightBallResponses",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "Donators",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "DiscordUser",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "CustomReactions",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "CurrencyTransactions",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "Currency",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "ConversionUnits",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "CommandPrice",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "CommandCooldown",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "ClashOfClans",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "ClashCallers",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "BotConfig",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "BlacklistItem",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "AntiSpamSetting",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "DateAdded",
table: "AntiSpamIgnore",
nullable: true);
migrationBuilder.AddColumn<DateTime>(
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");
}
}
}

View File

@ -24,6 +24,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Action"); b.Property<int>("Action");
b.Property<DateTime?>("DateAdded");
b.Property<int>("GuildConfigId"); b.Property<int>("GuildConfigId");
b.Property<int>("Seconds"); b.Property<int>("Seconds");
@ -47,6 +49,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("AntiSpamSettingId"); b.HasIndex("AntiSpamSettingId");
@ -61,6 +65,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Action"); b.Property<int>("Action");
b.Property<DateTime?>("DateAdded");
b.Property<int>("GuildConfigId"); b.Property<int>("GuildConfigId");
b.Property<int>("MessageThreshold"); b.Property<int>("MessageThreshold");
@ -80,6 +86,8 @@ namespace NadekoBot.Migrations
b.Property<int?>("BotConfigId"); b.Property<int?>("BotConfigId");
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("ItemId"); b.Property<ulong>("ItemId");
b.Property<int>("Type"); b.Property<int>("Type");
@ -120,6 +128,8 @@ namespace NadekoBot.Migrations
b.Property<string>("DMHelpString"); b.Property<string>("DMHelpString");
b.Property<DateTime?>("DateAdded");
b.Property<string>("ErrorColor"); b.Property<string>("ErrorColor");
b.Property<bool>("ForwardMessages"); b.Property<bool>("ForwardMessages");
@ -158,6 +168,8 @@ namespace NadekoBot.Migrations
b.Property<int>("ClashWarId"); b.Property<int>("ClashWarId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("SequenceNumber"); b.Property<int?>("SequenceNumber");
b.Property<int>("Stars"); b.Property<int>("Stars");
@ -178,6 +190,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("EnemyClan"); b.Property<string>("EnemyClan");
b.Property<ulong>("GuildId"); b.Property<ulong>("GuildId");
@ -200,6 +214,8 @@ namespace NadekoBot.Migrations
b.Property<string>("CommandName"); b.Property<string>("CommandName");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.Property<int>("Seconds"); b.Property<int>("Seconds");
@ -220,6 +236,8 @@ namespace NadekoBot.Migrations
b.Property<string>("CommandName"); b.Property<string>("CommandName");
b.Property<DateTime?>("DateAdded");
b.Property<int>("Price"); b.Property<int>("Price");
b.HasKey("Id"); b.HasKey("Id");
@ -237,6 +255,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<string>("InternalTrigger"); b.Property<string>("InternalTrigger");
b.Property<decimal>("Modifier"); b.Property<decimal>("Modifier");
@ -255,6 +275,8 @@ namespace NadekoBot.Migrations
b.Property<long>("Amount"); b.Property<long>("Amount");
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
b.HasKey("Id"); b.HasKey("Id");
@ -272,6 +294,8 @@ namespace NadekoBot.Migrations
b.Property<long>("Amount"); b.Property<long>("Amount");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Reason"); b.Property<string>("Reason");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
@ -286,6 +310,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<ulong?>("GuildId"); b.Property<ulong?>("GuildId");
b.Property<bool>("IsRegex"); b.Property<bool>("IsRegex");
@ -308,6 +334,8 @@ namespace NadekoBot.Migrations
b.Property<string>("AvatarId"); b.Property<string>("AvatarId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Discriminator"); b.Property<string>("Discriminator");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
@ -328,6 +356,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Amount"); b.Property<int>("Amount");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Name"); b.Property<string>("Name");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
@ -347,6 +377,8 @@ namespace NadekoBot.Migrations
b.Property<int?>("BotConfigId"); b.Property<int?>("BotConfigId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Text"); b.Property<string>("Text");
b.HasKey("Id"); b.HasKey("Id");
@ -363,6 +395,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.Property<int?>("GuildConfigId1"); b.Property<int?>("GuildConfigId1");
@ -381,6 +415,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.Property<string>("Word"); b.Property<string>("Word");
@ -399,6 +435,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.Property<ulong>("GuildId"); b.Property<ulong>("GuildId");
@ -421,6 +459,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.HasKey("Id"); b.HasKey("Id");
@ -455,6 +495,8 @@ namespace NadekoBot.Migrations
b.Property<bool>("CleverbotEnabled"); b.Property<bool>("CleverbotEnabled");
b.Property<DateTime?>("DateAdded");
b.Property<float>("DefaultMusicVolume"); b.Property<float>("DefaultMusicVolume");
b.Property<bool>("DeleteMessageOnCommand"); b.Property<bool>("DeleteMessageOnCommand");
@ -512,6 +554,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.Property<ulong>("GuildId"); b.Property<ulong>("GuildId");
@ -534,6 +578,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("LogSettingId"); b.Property<int?>("LogSettingId");
b.HasKey("Id"); b.HasKey("Id");
@ -550,6 +596,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<int?>("LogSettingId"); b.Property<int?>("LogSettingId");
b.HasKey("Id"); b.HasKey("Id");
@ -578,6 +626,8 @@ namespace NadekoBot.Migrations
b.Property<ulong?>("ChannelUpdatedId"); b.Property<ulong?>("ChannelUpdatedId");
b.Property<DateTime?>("DateAdded");
b.Property<bool>("IsLogging"); b.Property<bool>("IsLogging");
b.Property<ulong?>("LogOtherId"); b.Property<ulong?>("LogOtherId");
@ -638,6 +688,8 @@ namespace NadekoBot.Migrations
b.Property<int?>("BotConfigId"); b.Property<int?>("BotConfigId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("ModuleName"); b.Property<string>("ModuleName");
b.Property<string>("Prefix"); b.Property<string>("Prefix");
@ -658,6 +710,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("AuthorId"); b.Property<ulong>("AuthorId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Name"); b.Property<string>("Name");
b.HasKey("Id"); b.HasKey("Id");
@ -670,6 +724,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("GuildConfigId"); b.Property<int?>("GuildConfigId");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
@ -686,6 +742,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("NextId"); b.Property<int?>("NextId");
b.Property<int>("PrimaryTarget"); b.Property<int>("PrimaryTarget");
@ -713,6 +771,8 @@ namespace NadekoBot.Migrations
b.Property<int?>("BotConfigId"); b.Property<int?>("BotConfigId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Status"); b.Property<string>("Status");
b.HasKey("Id"); b.HasKey("Id");
@ -727,6 +787,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("MusicPlaylistId"); b.Property<int?>("MusicPlaylistId");
b.Property<string>("Provider"); b.Property<string>("Provider");
@ -756,6 +818,8 @@ namespace NadekoBot.Migrations
b.Property<string>("AuthorName") b.Property<string>("AuthorName")
.IsRequired(); .IsRequired();
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("GuildId"); b.Property<ulong>("GuildId");
b.Property<string>("Keyword") b.Property<string>("Keyword")
@ -776,6 +840,8 @@ namespace NadekoBot.Migrations
b.Property<int?>("BotConfigId"); b.Property<int?>("BotConfigId");
b.Property<DateTime?>("DateAdded");
b.Property<string>("Icon"); b.Property<string>("Icon");
b.Property<string>("Name"); b.Property<string>("Name");
@ -794,6 +860,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("ChannelId"); b.Property<ulong>("ChannelId");
b.Property<DateTime?>("DateAdded");
b.Property<bool>("IsPrivate"); b.Property<bool>("IsPrivate");
b.Property<string>("Message"); b.Property<string>("Message");
@ -814,6 +882,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("GuildId"); b.Property<ulong>("GuildId");
b.Property<ulong>("RoleId"); b.Property<ulong>("RoleId");
@ -831,6 +901,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<ulong>("UserId"); b.Property<ulong>("UserId");
b.Property<string>("type"); b.Property<string>("type");
@ -852,6 +924,8 @@ namespace NadekoBot.Migrations
b.Property<int?>("ClaimerId"); b.Property<int?>("ClaimerId");
b.Property<DateTime?>("DateAdded");
b.Property<int>("Price"); b.Property<int>("Price");
b.Property<int>("WaifuId"); b.Property<int>("WaifuId");
@ -873,6 +947,8 @@ namespace NadekoBot.Migrations
b.Property<int>("Id") b.Property<int>("Id")
.ValueGeneratedOnAdd(); .ValueGeneratedOnAdd();
b.Property<DateTime?>("DateAdded");
b.Property<int?>("NewId"); b.Property<int?>("NewId");
b.Property<int?>("OldId"); b.Property<int?>("OldId");

View File

@ -16,7 +16,7 @@ using NLog;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
{ {
[NadekoModule("Administration", ".")] [NadekoModule("Administration", ".")]
public partial class Administration : NadekoModule public partial class Administration : NadekoTopLevelModule
{ {
private static ConcurrentHashSet<ulong> deleteMessagesOnCommand { get; } private static ConcurrentHashSet<ulong> deleteMessagesOnCommand { get; }
@ -440,6 +440,7 @@ namespace NadekoBot.Modules.Administration
var enumerable = (await Context.Channel.GetMessagesAsync().Flatten()).AsEnumerable(); var enumerable = (await Context.Channel.GetMessagesAsync().Flatten()).AsEnumerable();
enumerable = enumerable.Where(x => x.Author.Id == user.Id); enumerable = enumerable.Where(x => x.Author.Id == user.Id);
await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
Context.Message.DeleteAfter(3);
} }
// prune x // prune x
@ -451,7 +452,6 @@ namespace NadekoBot.Modules.Administration
{ {
if (count < 1) if (count < 1)
return; return;
count += 1;
await Context.Message.DeleteAsync().ConfigureAwait(false); await Context.Message.DeleteAsync().ConfigureAwait(false);
int limit = (count < 100) ? count : 100; int limit = (count < 100) ? count : 100;
var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten().ConfigureAwait(false)); var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten().ConfigureAwait(false));
@ -474,6 +474,8 @@ namespace NadekoBot.Modules.Administration
int limit = (count < 100) ? count : 100; int limit = (count < 100) ? count : 100;
var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten()).Where(m => m.Author == user); var enumerable = (await Context.Channel.GetMessagesAsync(limit: limit).Flatten()).Where(m => m.Author == user);
await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false); await Context.Channel.DeleteMessagesAsync(enumerable).ConfigureAwait(false);
Context.Message.DeleteAfter(3);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -18,8 +18,14 @@ namespace NadekoBot.Modules.Administration
{ {
private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>() private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
{ {
{"en-US", "English, United States" }, {"en-US", "English, United States"},
{"sr-cyrl-rs", "Serbian, Cyrillic" } {"fr-FR", "French, France"},
{"ru-RU", "Russian, Russia"},
{"de-DE", "German, Germany"},
//{"nl-NL", "Dutch, Netherlands"},
//{"ja-JP", "Japanese, Japan"},
{"pt-BR", "Portuguese, Brazil"},
//{"sr-Cyrl-RS", "Serbian, Serbia - Cyrillic"}
}.ToImmutableDictionary(); }.ToImmutableDictionary();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -94,9 +100,10 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task LanguagesList() public async Task LanguagesList()
{ {
await ReplyConfirmLocalized("lang_list", await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
string.Join("\n", supportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}"))) .WithTitle(GetText("lang_list", ""))
.ConfigureAwait(false); .WithDescription(string.Join("\n",
supportedLocales.Select(x => $"{Format.Code(x.Key), -10} => {x.Value}"))));
} }
} }
} }

View File

@ -126,7 +126,6 @@ namespace NadekoBot.Modules.Administration
{ {
embed.WithTitle("👥" + g.GetLogText("avatar_changed")) embed.WithTitle("👥" + g.GetLogText("avatar_changed"))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithThumbnailUrl(before.AvatarUrl) .WithThumbnailUrl(before.AvatarUrl)
.WithImageUrl(after.AvatarUrl) .WithImageUrl(after.AvatarUrl)
.WithFooter(fb => fb.WithText(currentTime)) .WithFooter(fb => fb.WithText(currentTime))
@ -530,7 +529,7 @@ namespace NadekoBot.Modules.Administration
else if (afterVch == null) else if (afterVch == null)
{ {
str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft", 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 ?? "")); Format.Bold(beforeVch.Name ?? ""));
} }
if (str != null) if (str != null)
@ -705,17 +704,18 @@ namespace NadekoBot.Modules.Administration
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name)) .WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name))
.WithDescription($"{msg.Author}") .WithDescription(msg.Author.ToString())
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .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)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false))
.WithFooter(efb => efb.WithText(currentTime)); .WithFooter(efb => efb.WithText(currentTime));
if (msg.Attachments.Any()) 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); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch catch (Exception ex)
{ {
_log.Warn(ex);
// ignored // ignored
} }
} }
@ -753,8 +753,8 @@ namespace NadekoBot.Modules.Administration
.WithOkColor() .WithOkColor()
.WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name)) .WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name))
.WithDescription(after.Author.ToString()) .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("old_msg")).WithValue(string.IsNullOrWhiteSpace(before.Content) ? "-" : 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("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)) .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false))
.WithFooter(efb => efb.WithText(currentTime)); .WithFooter(efb => efb.WithText(currentTime));
@ -988,7 +988,9 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task LogEvents() public async Task LogEvents()
{ {
await ReplyConfirmLocalized("log_events", string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>())).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("log_events") + "\n" +
string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>()))
.ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -1066,7 +1068,7 @@ namespace NadekoBot.Modules.Administration
public static class GuildExtensions public static class GuildExtensions
{ {
public static string GetLogText(this IGuild guild, string key, params object[] replacements) public static string GetLogText(this IGuild guild, string key, params object[] replacements)
=> NadekoModule.GetTextStatic(key, => NadekoTopLevelModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(guild), NadekoBot.Localization.GetCultureInfo(guild),
typeof(Administration).Name.ToLowerInvariant(), typeof(Administration).Name.ToLowerInvariant(),
replacements); replacements);

View File

@ -72,23 +72,42 @@ namespace NadekoBot.Modules.Administration
{ {
if (_forwardDMs && ownerChannels.Any()) if (_forwardDMs && ownerChannels.Any())
{ {
var title = var title = GetTextStatic("dm_from",
GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo, NadekoBot.Localization.DefaultCultureInfo,
typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})"; 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) if (_forwardDMsToAllOwners)
{ {
await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id) 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 else
{ {
var firstOwnerChannel = ownerChannels.First(); var firstOwnerChannel = ownerChannels.First();
if (firstOwnerChannel.Recipient.Id != msg.Author.Id) 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 catch
{ {
// ignored // ignored
} }
}
} }
} }
} }

View File

@ -11,15 +11,13 @@ using NadekoBot.Services.Database.Models;
using System.Linq; using System.Linq;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Threading; using System.Threading;
using System.Diagnostics;
using NLog;
namespace NadekoBot.Modules.ClashOfClans namespace NadekoBot.Modules.ClashOfClans
{ {
[NadekoModule("ClashOfClans", ",")] [NadekoModule("ClashOfClans", ",")]
public class ClashOfClans : NadekoModule public class ClashOfClans : NadekoTopLevelModule
{ {
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>(); public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; }
private static Timer checkWarTimer { get; } private static Timer checkWarTimer { get; }
@ -82,11 +80,9 @@ namespace NadekoBot.Modules.ClashOfClans
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task CreateWar(int size, [Remainder] string enemyClan = null) public async Task CreateWar(int size, [Remainder] string enemyClan = null)
{ {
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(enemyClan)) if (string.IsNullOrWhiteSpace(enemyClan))
return; return;

View File

@ -135,7 +135,7 @@ namespace NadekoBot.Modules.ClashOfClans
public static string Localize(this ClashWar cw, string key) public static string Localize(this ClashWar cw, string key)
{ {
return NadekoModule.GetTextStatic(key, return NadekoTopLevelModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId), NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId),
typeof(ClashOfClans).Name.ToLowerInvariant()); typeof(ClashOfClans).Name.ToLowerInvariant());
} }

View File

@ -17,7 +17,7 @@ using NadekoBot.DataStructures;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Modules.CustomReactions
{ {
[NadekoModule("CustomReactions", ".")] [NadekoModule("CustomReactions", ".")]
public class CustomReactions : NadekoModule public class CustomReactions : NadekoTopLevelModule
{ {
private static CustomReaction[] _globalReactions = new CustomReaction[] { }; private static CustomReaction[] _globalReactions = new CustomReaction[] { };
public static CustomReaction[] GlobalReactions => _globalReactions; public static CustomReaction[] GlobalReactions => _globalReactions;

View File

@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Gambling
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix); var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix);
if (ar.Fail) 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] [NadekoCommand, Usage, Description, Aliases]
@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Gambling
AnimalRace ar; AnimalRace ar;
if (!AnimalRaces.TryGetValue(Context.Guild.Id, out 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; return;
} }
await ar.JoinRace(Context.User as IGuildUser, amount); await ar.JoinRace(Context.User as IGuildUser, amount);
@ -56,22 +56,22 @@ namespace NadekoBot.Modules.Gambling
public bool Fail { get; set; } public bool Fail { get; set; }
public List<Participant> participants = new List<Participant>(); private readonly List<Participant> _participants = new List<Participant>();
private ulong serverId; private readonly ulong _serverId;
private int messagesSinceGameStarted = 0; private int _messagesSinceGameStarted;
private readonly string _prefix; private readonly string _prefix;
private Logger _log { get; } private readonly Logger _log;
public ITextChannel raceChannel { get; set; } private readonly ITextChannel _raceChannel;
public bool Started { get; private set; } = false; public bool Started { get; private set; }
public AnimalRace(ulong serverId, ITextChannel ch, string prefix) public AnimalRace(ulong serverId, ITextChannel ch, string prefix)
{ {
this._prefix = prefix; _prefix = prefix;
this._log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.serverId = serverId; _serverId = serverId;
this.raceChannel = ch; _raceChannel = ch;
if (!AnimalRaces.TryAdd(serverId, this)) if (!AnimalRaces.TryAdd(serverId, this))
{ {
Fail = true; Fail = true;
@ -90,8 +90,8 @@ namespace NadekoBot.Modules.Gambling
{ {
try try
{ {
await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.", await _raceChannel.SendConfirmAsync(GetText("animal_race"), GetText("animal_race_starting"),
footer: $"Type {_prefix}jr to join the race."); footer: GetText("animal_race_join_instr", _prefix));
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -102,16 +102,16 @@ namespace NadekoBot.Modules.Gambling
cancelSource.Cancel(); cancelSource.Cancel();
if (t == fullgame) 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 else
{ {
try { await raceChannel.SendErrorAsync("Animal Race", "Failed to start since there was not enough participants."); } catch (Exception ex) { _log.Warn(ex); } try { await _raceChannel.SendErrorAsync(GetText("animal_race"), GetText("animal_race_failed")); } catch (Exception ex) { _log.Warn(ex); }
var p = participants.FirstOrDefault(); var p = _participants.FirstOrDefault();
if (p != null && p.AmountBet > 0) if (p != null && p.AmountBet > 0)
await CurrencyHandler.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, false).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(p.User, "BetRace", p.AmountBet, false).ConfigureAwait(false);
@ -128,7 +128,7 @@ namespace NadekoBot.Modules.Gambling
private void End() private void End()
{ {
AnimalRace throwaway; AnimalRace throwaway;
AnimalRaces.TryRemove(serverId, out throwaway); AnimalRaces.TryRemove(_serverId, out throwaway);
} }
private async Task StartRace() private async Task StartRace()
@ -136,21 +136,21 @@ namespace NadekoBot.Modules.Gambling
var rng = new NadekoRandom(); var rng = new NadekoRandom();
Participant winner = null; Participant winner = null;
IUserMessage msg = null; IUserMessage msg = null;
int place = 1; var place = 1;
try try
{ {
NadekoBot.Client.MessageReceived += Client_MessageReceived; NadekoBot.Client.MessageReceived += Client_MessageReceived;
while (!participants.All(p => p.Total >= 60)) while (!_participants.All(p => p.Total >= 60))
{ {
//update the state //update the state
participants.ForEach(p => _participants.ForEach(p =>
{ {
p.Total += 1 + rng.Next(0, 10); p.Total += 1 + rng.Next(0, 10);
}); });
participants _participants
.OrderByDescending(p => p.Total) .OrderByDescending(p => p.Total)
.ForEach(p => .ForEach(p =>
{ {
@ -170,14 +170,14 @@ namespace NadekoBot.Modules.Gambling
//draw the state //draw the state
var text = $@"|🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🏁🔚| 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) if (msg != null)
try { await msg.DeleteAsync(); } catch { } try { await msg.DeleteAsync(); } catch { }
messagesSinceGameStarted = 0; _messagesSinceGameStarted = 0;
try { msg = await raceChannel.SendMessageAsync(text).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try { msg = await _raceChannel.SendMessageAsync(text).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
} }
else else
{ {
@ -187,22 +187,33 @@ namespace NadekoBot.Modules.Gambling
await Task.Delay(2500); await Task.Delay(2500);
} }
} }
catch { } catch
{
// ignored
}
finally finally
{ {
NadekoBot.Client.MessageReceived -= Client_MessageReceived; 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 CurrencyHandler.AddCurrencyAsync(winner.User, "Won a Race", wonAmount, true)
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race and {wonAmount}{CurrencySign}!**").ConfigureAwait(false); .ConfigureAwait(false);
} await _raceChannel.SendConfirmAsync(GetText("animal_race"),
else Format.Bold(GetText("animal_race_won_money", winner.User.Mention,
{ winner.Animal, wonAmount + CurrencySign)))
await raceChannel.SendConfirmAsync("Animal Race", $"{winner.User.Mention} as {winner.Animal} **Won the race!**").ConfigureAwait(false); .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; var msg = imsg as SocketUserMessage;
if (msg == null) if (msg == null)
return Task.CompletedTask; 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; return Task.CompletedTask;
messagesSinceGameStarted++; _messagesSinceGameStarted++;
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -228,51 +239,66 @@ namespace NadekoBot.Modules.Gambling
public async Task JoinRace(IGuildUser u, int amount = 0) public async Task JoinRace(IGuildUser u, int amount = 0)
{ {
var animal = ""; string animal;
if (!animals.TryDequeue(out 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; return;
} }
var p = new Participant(u, animal, amount); 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; return;
} }
if (Started) if (Started)
{ {
await raceChannel.SendErrorAsync($"{u.Mention} `Race is already started`").ConfigureAwait(false); await _raceChannel.SendErrorAsync(GetText("animal_race_already_started")).ConfigureAwait(false);
return; return;
} }
if (amount > 0) 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; return;
} }
participants.Add(p); _participants.Add(p);
await raceChannel.SendConfirmAsync("Animal Race", $"{u.Mention} **joined as a {p.Animal}" + (amount > 0 ? $" and bet {amount} {CurrencySign}!**" : "**")) string confStr;
.ConfigureAwait(false); 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)
=> NadekoTopLevelModule.GetTextStatic(text,
NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild),
typeof(Gambling).Name.ToLowerInvariant());
private string GetText(string text, params object[] replacements)
=> NadekoTopLevelModule.GetTextStatic(text,
NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild),
typeof(Gambling).Name.ToLowerInvariant(),
replacements);
} }
public class Participant public class Participant
{ {
public IGuildUser User { get; set; } public IGuildUser User { get; }
public string Animal { get; set; } public string Animal { get; }
public int AmountBet { get; set; } public int AmountBet { get; }
public float Coeff { get; set; } public float Coeff { get; set; }
public int Total { 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) public Participant(IGuildUser u, string a, int amount)
{ {
this.User = u; User = u;
this.Animal = a; Animal = a;
this.AmountBet = amount; AmountBet = amount;
} }
public override int GetHashCode() => User.GetHashCode(); public override int GetHashCode() => User.GetHashCode();
@ -288,23 +314,13 @@ namespace NadekoBot.Modules.Gambling
var str = new string('‣', Total) + Animal; var str = new string('‣', Total) + Animal;
if (Place == 0) if (Place == 0)
return str; 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;
} }
} }
} }

View File

@ -43,7 +43,7 @@ namespace NadekoBot.Modules.Gambling
switch (e) switch (e)
{ {
case CurrencyEvent.FlowerReaction: case CurrencyEvent.FlowerReaction:
await FlowerReactionEvent(Context).ConfigureAwait(false); await FlowerReactionEvent(Context, arg).ConfigureAwait(false);
break; break;
case CurrencyEvent.SneakyGameStatus: case CurrencyEvent.SneakyGameStatus:
await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false); await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
@ -115,23 +115,26 @@ namespace NadekoBot.Modules.Gambling
return Task.Delay(0); 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 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 footer = GetText("flowerreaction_footer", 24);
var msg = await context.Channel.SendConfirmAsync(title, var msg = await context.Channel.SendConfirmAsync(title,
desc, footer: footer) desc, footer: footer)
.ConfigureAwait(false); .ConfigureAwait(false);
await new FlowerReactionEvent().Start(msg, context); await new FlowerReactionEvent().Start(msg, context, amount);
} }
} }
} }
public abstract class CurrencyEvent 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 public class FlowerReactionEvent : CurrencyEvent
@ -172,7 +175,7 @@ namespace NadekoBot.Modules.Gambling
return Task.CompletedTask; 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; msg = umsg;
NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler; 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)) 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); .ConfigureAwait(false);
} }
} }

View File

@ -10,6 +10,7 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using ImageSharp.Formats;
using Image = ImageSharp.Image; using Image = ImageSharp.Image;
namespace NadekoBot.Modules.Gambling namespace NadekoBot.Modules.Gambling
@ -22,7 +23,7 @@ namespace NadekoBot.Modules.Gambling
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled); private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled); private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);
private readonly char[] fateRolls = new[] { '-', ' ', '+' }; private readonly char[] _fateRolls = { '-', ' ', '+' };
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Roll() public async Task Roll()
@ -35,12 +36,14 @@ namespace NadekoBot.Modules.Gambling
var imageStream = await Task.Run(() => var imageStream = await Task.Run(() =>
{ {
var ms = new MemoryStream(); var ms = new MemoryStream();
new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms); new[] { GetDice(num1), GetDice(num2) }.Merge().Save(ms);
ms.Position = 0; ms.Position = 0;
return ms; return ms;
}).ConfigureAwait(false); }).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 public enum RollOrderType
@ -82,7 +85,7 @@ namespace NadekoBot.Modules.Gambling
{ {
if (num < 1 || num > 30) 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; return;
} }
@ -118,9 +121,14 @@ namespace NadekoBot.Modules.Gambling
var bitmap = dice.Merge(); var bitmap = dice.Merge();
var ms = new MemoryStream(); var ms = new MemoryStream();
bitmap.SaveAsPng(ms); bitmap.Save(ms);
ms.Position = 0; 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_average",
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) private async Task InternallDndRoll(string arg, bool ordered)
@ -138,9 +146,9 @@ namespace NadekoBot.Modules.Gambling
for (int i = 0; i < n1; i++) 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")) .AddField(efb => efb.WithName(Format.Bold("Result"))
.WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]"))))); .WithValue(string.Join(" ", rolls.Select(c => Format.Code($"[{c}]")))));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -164,7 +172,7 @@ namespace NadekoBot.Modules.Gambling
} }
var sum = arr.Sum(); 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")) .AddField(efb => efb.WithName(Format.Bold("Rolls"))
.WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString()))))) .WithValue(string.Join(" ", (ordered ? arr.OrderBy(x => x).AsEnumerable() : arr).Select(x => Format.Code(x.ToString())))))
.AddField(efb => efb.WithName(Format.Bold("Sum")) .AddField(efb => efb.WithName(Format.Bold("Sum"))
@ -177,30 +185,26 @@ namespace NadekoBot.Modules.Gambling
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task NRoll([Remainder] string range) public async Task NRoll([Remainder] string range)
{ {
try int rolled;
if (range.Contains("-"))
{ {
int rolled; var arr = range.Split('-')
if (range.Contains("-")) .Take(2)
.Select(int.Parse)
.ToArray();
if (arr[0] > arr[1])
{ {
var arr = range.Split('-') await ReplyErrorLocalized("second_larger_than_first").ConfigureAwait(false);
.Take(2) return;
.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);
} }
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); await ReplyConfirmLocalized("dice_rolled", Format.Bold(rolled.ToString())).ConfigureAwait(false);
}
catch (Exception ex)
{
await Context.Channel.SendErrorAsync($":anger: {ex.Message}").ConfigureAwait(false);
}
} }
private Image GetDice(int num) private Image GetDice(int num)

View File

@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Gambling
images.Add(new Image(stream)); images.Add(new Image(stream));
} }
MemoryStream bitmapStream = new MemoryStream(); MemoryStream bitmapStream = new MemoryStream();
images.Merge().SaveAsPng(bitmapStream); images.Merge().Save(bitmapStream);
bitmapStream.Position = 0; bitmapStream.Position = 0;
var toSend = $"{Context.User.Mention}"; var toSend = $"{Context.User.Mention}";
if (cardObjects.Count == 5) if (cardObjects.Count == 5)

View File

@ -52,17 +52,22 @@ namespace NadekoBot.Modules.Gambling
return; return;
} }
var imgs = new Image[count]; var imgs = new Image[count];
using (var heads = _images.Heads.ToStream()) for (var i = 0; i < count; i++)
using(var tails = _images.Tails.ToStream())
{ {
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 ? if (rng.Next(0, 10) < 5)
new Image(heads) : {
new Image(tails); 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] [NadekoCommand, Usage, Description, Aliases]
@ -105,7 +110,7 @@ namespace NadekoBot.Modules.Gambling
{ {
var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier); var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier);
str = Context.User.Mention + " " + GetText("flip_guess", toWin + CurrencySign); 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 else
{ {

View File

@ -1,5 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using ImageSharp;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
@ -163,83 +164,43 @@ namespace NadekoBot.Modules.Gambling
var result = SlotMachine.Pull(); var result = SlotMachine.Pull();
int[] numbers = result.Numbers; 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()) bgImage.DrawImage(randomImage, 100, default(Size), new Point(95 + 142 * i, 330));
{
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];
}
}
}
}
} }
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"); var msg = GetText("better_luck");
if (result.Multiplier != 0) if (result.Multiplier != 0)
{ {

View File

@ -3,13 +3,11 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Gambling namespace NadekoBot.Modules.Gambling
@ -49,8 +47,8 @@ namespace NadekoBot.Modules.Gambling
[Group] [Group]
public class WaifuClaimCommands : NadekoSubmodule public class WaifuClaimCommands : NadekoSubmodule
{ {
private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>(); private static ConcurrentDictionary<ulong, DateTime> divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>(); private static ConcurrentDictionary<ulong, DateTime> affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
enum WaifuClaimResult enum WaifuClaimResult
{ {
@ -65,20 +63,19 @@ namespace NadekoBot.Modules.Gambling
{ {
if (amount < 50) 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; return;
} }
if (target.Id == Context.User.Id) 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; return;
} }
WaifuClaimResult result = WaifuClaimResult.NotEnoughFunds; WaifuClaimResult result;
int? oldPrice = null;
WaifuInfo w; WaifuInfo w;
var isAffinity = false; bool isAffinity;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
w = uow.Waifus.ByWaifuUserId(target.Id); w = uow.Waifus.ByWaifuUserId(target.Id);
@ -120,7 +117,6 @@ namespace NadekoBot.Modules.Gambling
{ {
var oldClaimer = w.Claimer; var oldClaimer = w.Claimer;
w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User); w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User);
oldPrice = w.Price;
w.Price = amount + (amount / 4); w.Price = amount + (amount / 4);
result = WaifuClaimResult.Success; result = WaifuClaimResult.Success;
@ -143,7 +139,6 @@ namespace NadekoBot.Modules.Gambling
{ {
var oldClaimer = w.Claimer; var oldClaimer = w.Claimer;
w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User); w.Claimer = uow.DiscordUsers.GetOrCreate(Context.User);
oldPrice = w.Price;
w.Price = amount; w.Price = amount;
result = WaifuClaimResult.Success; result = WaifuClaimResult.Success;
@ -165,22 +160,20 @@ namespace NadekoBot.Modules.Gambling
if (result == WaifuClaimResult.InsufficientAmount) 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; 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}!") await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false);
.ConfigureAwait(false); return;
}
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);
} }
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 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Divorce([Remainder]IUser target) public async Task Divorce([Remainder]IUser target)
@ -200,19 +193,19 @@ namespace NadekoBot.Modules.Gambling
if (target.Id == Context.User.Id) if (target.Id == Context.User.Id)
return; return;
var result = DivorceResult.NotYourWife; DivorceResult result;
TimeSpan difference = TimeSpan.Zero; var difference = TimeSpan.Zero;
var amount = 0; var amount = 0;
WaifuInfo w = null; WaifuInfo w = null;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
w = uow.Waifus.ByWaifuUserId(target.Id); w = uow.Waifus.ByWaifuUserId(target.Id);
var now = DateTime.UtcNow; 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; result = DivorceResult.NotYourWife;
else if (_divorceCooldowns.AddOrUpdate(Context.User.Id, else if (divorceCooldowns.AddOrUpdate(Context.User.Id,
now, now,
(key, old) => ((difference = now.Subtract(old)) > DivorceLimit) ? now : old) != now) (key, old) => ((difference = now.Subtract(old)) > _divorceLimit) ? now : old) != now)
{ {
result = DivorceResult.Cooldown; result = DivorceResult.Cooldown;
} }
@ -249,37 +242,39 @@ namespace NadekoBot.Modules.Gambling
if (result == DivorceResult.SucessWithPenalty) 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) 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) 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 else
{ {
var remaining = DivorceLimit.Subtract(difference); 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); 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task WaifuClaimerAffinity([Remainder]IUser u = null) public async Task WaifuClaimerAffinity([Remainder]IUser u = null)
{ {
if (u?.Id == Context.User.Id) 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; return;
} }
DiscordUser oldAff = null; DiscordUser oldAff = null;
var sucess = false; var sucess = false;
var cooldown = false; var cooldown = false;
TimeSpan difference = TimeSpan.Zero; var difference = TimeSpan.Zero;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var w = uow.Waifus.ByWaifuUserId(Context.User.Id); var w = uow.Waifus.ByWaifuUserId(Context.User.Id);
@ -287,13 +282,11 @@ namespace NadekoBot.Modules.Gambling
var now = DateTime.UtcNow; var now = DateTime.UtcNow;
if (w?.Affinity?.UserId == u?.Id) if (w?.Affinity?.UserId == u?.Id)
{ {
sucess = false;
} }
else if (_affinityCooldowns.AddOrUpdate(Context.User.Id, else if (affinityCooldowns.AddOrUpdate(Context.User.Id,
now, 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; cooldown = true;
} }
else if (w == null) else if (w == null)
@ -338,19 +331,29 @@ namespace NadekoBot.Modules.Gambling
{ {
if (cooldown) if (cooldown)
{ {
var remaining = AffinityLimit.Subtract(difference); 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); await ReplyErrorLocalized("waifu_affinity_cooldown",
Format.Bold(((int)remaining.TotalHours).ToString()),
Format.Bold(remaining.Minutes.ToString())).ConfigureAwait(false);
} }
else 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; return;
} }
if (u == null) 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) 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 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] [NadekoCommand, Usage, Description, Aliases]
@ -365,19 +368,20 @@ namespace NadekoBot.Modules.Gambling
if (waifus.Count == 0) if (waifus.Count == 0)
{ {
await Context.Channel.SendConfirmAsync("No waifus have been claimed yet.").ConfigureAwait(false); await ReplyConfirmLocalized("waifus_none").ConfigureAwait(false);
return; return;
} }
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithTitle("Top Waifus") .WithTitle(GetText("waifus_top_waifus"))
.WithOkColor(); .WithOkColor();
for (int i = 0; i < waifus.Count; i++) for (var i = 0; i < waifus.Count; i++)
{ {
var w = waifus[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); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -419,15 +423,18 @@ namespace NadekoBot.Modules.Gambling
var claimInfo = GetClaimTitle(target.Id); var claimInfo = GetClaimTitle(target.Id);
var affInfo = GetAffinityTitle(target.Id); var affInfo = GetAffinityTitle(target.Id);
var rng = new NadekoRandom();
var nobody = GetText("nobody");
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("Waifu " + w.Waifu + " - \"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(GetText("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(GetText("claimed_by")).WithValue(w.Claimer?.ToString() ?? nobody).WithIsInline(true))
.AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true)) .AddField(efb => efb.WithName(GetText("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(GetText("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(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); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -447,13 +454,13 @@ namespace NadekoBot.Modules.Gambling
private static WaifuProfileTitle GetClaimTitle(ulong userId) private static WaifuProfileTitle GetClaimTitle(ulong userId)
{ {
int count = 0; int count;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
count = uow.Waifus.ByClaimerUserId(userId).Count; count = uow.Waifus.ByClaimerUserId(userId).Count;
} }
ClaimTitles title = ClaimTitles.Lonely; ClaimTitles title;
if (count == 0) if (count == 0)
title = ClaimTitles.Lonely; title = ClaimTitles.Lonely;
else if (count == 1) else if (count == 1)
@ -484,13 +491,13 @@ namespace NadekoBot.Modules.Gambling
private static WaifuProfileTitle GetAffinityTitle(ulong userId) private static WaifuProfileTitle GetAffinityTitle(ulong userId)
{ {
int count = 0; int count;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
count = uow._context.WaifuUpdates.Count(w => w.User.UserId == userId && w.UpdateType == WaifuUpdateType.AffinityChanged); count = uow._context.WaifuUpdates.Count(w => w.User.UserId == userId && w.UpdateType == WaifuUpdateType.AffinityChanged);
} }
AffinityTitles title = AffinityTitles.Pure; AffinityTitles title;
if (count < 1) if (count < 1)
title = AffinityTitles.Pure; title = AffinityTitles.Pure;
else if (count < 2) else if (count < 2)

View File

@ -12,7 +12,7 @@ using System.Collections.Generic;
namespace NadekoBot.Modules.Gambling namespace NadekoBot.Modules.Gambling
{ {
[NadekoModule("Gambling", "$")] [NadekoModule("Gambling", "$")]
public partial class Gambling : NadekoModule public partial class Gambling : NadekoTopLevelModule
{ {
public static string CurrencyName { get; set; } public static string CurrencyName { get; set; }
public static string CurrencyPluralName { get; set; } public static string CurrencyPluralName { get; set; }
@ -49,8 +49,10 @@ namespace NadekoBot.Modules.Gambling
[Priority(0)] [Priority(0)]
public async Task Cash([Remainder] IUser user = null) public async Task Cash([Remainder] IUser user = null)
{ {
user = user ?? Context.User; if(user == null)
await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -238,9 +240,7 @@ namespace NadekoBot.Modules.Gambling
(int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false); (int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false);
} }
} }
Console.WriteLine("started sending");
await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false);
Console.WriteLine("done sending");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -19,7 +19,7 @@ namespace NadekoBot.Modules.Games
public partial class Games public partial class Games
{ {
[Group] [Group]
public class Acropobia : ModuleBase public class Acropobia : NadekoSubmodule
{ {
//channelId, game //channelId, game
public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>(); public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>();
@ -28,6 +28,8 @@ namespace NadekoBot.Modules.Games
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Acro(int time = 60) public async Task Acro(int time = 60)
{ {
if (time < 10 || time > 120)
return;
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
var game = new AcrophobiaGame(channel, time); var game = new AcrophobiaGame(channel, time);
@ -45,7 +47,7 @@ namespace NadekoBot.Modules.Games
} }
else else
{ {
await channel.SendErrorAsync("Acrophobia game is already running in this channel.").ConfigureAwait(false); await ReplyErrorLocalized("acro_running").ConfigureAwait(false);
} }
} }
} }
@ -59,44 +61,44 @@ namespace NadekoBot.Modules.Games
public class AcrophobiaGame public class AcrophobiaGame
{ {
private readonly ITextChannel channel; private readonly ITextChannel _channel;
private readonly int time; private readonly int _time;
private readonly NadekoRandom rng; private readonly NadekoRandom _rng;
private readonly ImmutableArray<char> startingLetters; private readonly ImmutableArray<char> _startingLetters;
private readonly CancellationTokenSource source; private readonly CancellationTokenSource _source;
private AcroPhase phase { get; set; } = AcroPhase.Submitting; private AcroPhase phase { get; set; } = AcroPhase.Submitting;
private readonly ConcurrentDictionary<string, IGuildUser> submissions = new ConcurrentDictionary<string, IGuildUser>(); private readonly ConcurrentDictionary<string, IGuildUser> _submissions = new ConcurrentDictionary<string, IGuildUser>();
public IReadOnlyDictionary<string, IGuildUser> Submissions => submissions; public IReadOnlyDictionary<string, IGuildUser> Submissions => _submissions;
private readonly ConcurrentHashSet<ulong> usersWhoSubmitted = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> _usersWhoSubmitted = new ConcurrentHashSet<ulong>();
private readonly ConcurrentHashSet<ulong> usersWhoVoted = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> _usersWhoVoted = new ConcurrentHashSet<ulong>();
private int spamCount = 0; private int _spamCount;
//text, votes //text, votes
private readonly ConcurrentDictionary<string, int> votes = new ConcurrentDictionary<string, int>(); private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>();
private readonly Logger _log; private readonly Logger _log;
public AcrophobiaGame(ITextChannel channel, int time) public AcrophobiaGame(ITextChannel channel, int time)
{ {
this._log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.channel = channel; _channel = channel;
this.time = time; _time = time;
this.source = new CancellationTokenSource(); _source = new CancellationTokenSource();
this.rng = new NadekoRandom(); _rng = new NadekoRandom();
var wordCount = rng.Next(3, 6); var wordCount = _rng.Next(3, 6);
var lettersArr = new char[wordCount]; var lettersArr = new char[wordCount];
for (int i = 0; i < wordCount; i++) for (int i = 0; i < wordCount; i++)
{ {
var randChar = (char)rng.Next(65, 91); var randChar = (char)_rng.Next(65, 91);
lettersArr[i] = randChar == 'X' ? (char)rng.Next(65, 88) : randChar; lettersArr[i] = randChar == 'X' ? (char)_rng.Next(65, 88) : randChar;
} }
startingLetters = lettersArr.ToImmutableArray(); _startingLetters = lettersArr.ToImmutableArray();
} }
private EmbedBuilder GetEmbed() private EmbedBuilder GetEmbed()
@ -104,19 +106,19 @@ namespace NadekoBot.Modules.Games
var i = 0; var i = 0;
return phase == AcroPhase.Submitting return phase == AcroPhase.Submitting
? new EmbedBuilder().WithOkColor() ? new EmbedBuilder().WithOkColor()
.WithTitle("Acrophobia") .WithTitle(GetText("acrophobia"))
.WithDescription($"Game started. Create a sentence with the following acronym: **{string.Join(".", startingLetters)}.**\n") .WithDescription(GetText("acro_started", Format.Bold(string.Join(".", _startingLetters))))
.WithFooter(efb => efb.WithText("You have " + this.time + " seconds to make a submission.")) .WithFooter(efb => efb.WithText(GetText("acro_started_footer", _time)))
: new EmbedBuilder() : new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("Acrophobia - Submissions Closed") .WithTitle(GetText("acrophobia") + " - " + GetText("submissions_closed"))
.WithDescription($@"Acronym was **{string.Join(".", startingLetters)}.** .WithDescription(GetText("acro_nym_was", Format.Bold(string.Join(".", _startingLetters)) + "\n" +
-- $@"--
{this.submissions.Aggregate("", (agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")} {_submissions.Aggregate("",(agg, cur) => agg + $"`{++i}.` **{cur.Key.ToLowerInvariant().ToTitleCase()}**\n")}
--") --"))
.WithFooter(efb => efb.WithText("Vote by typing a number of the submission")); .WithFooter(efb => efb.WithText(GetText("acro_vote")));
} }
public async Task Run() public async Task Run()
@ -125,10 +127,10 @@ namespace NadekoBot.Modules.Games
var embed = GetEmbed(); var embed = GetEmbed();
//SUBMISSIONS PHASE //SUBMISSIONS PHASE
await channel.EmbedAsync(embed).ConfigureAwait(false); await _channel.EmbedAsync(embed).ConfigureAwait(false);
try try
{ {
await Task.Delay(time * 1000, source.Token).ConfigureAwait(false); await Task.Delay(_time * 1000, _source.Token).ConfigureAwait(false);
phase = AcroPhase.Idle; phase = AcroPhase.Idle;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
@ -137,30 +139,32 @@ namespace NadekoBot.Modules.Games
} }
//var i = 0; //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; return;
} }
else if (submissions.Count == 1) if (_submissions.Count == 1)
{ {
await channel.EmbedAsync(new EmbedBuilder().WithOkColor() await _channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription($"{submissions.First().Value.Mention} is the winner for being the only user who made a submission!") .WithDescription(
.WithFooter(efb => efb.WithText(submissions.First().Key.ToLowerInvariant().ToTitleCase()))) GetText("acro_winner_only",
.ConfigureAwait(false); Format.Bold(_submissions.First().Value.ToString())))
.WithFooter(efb => efb.WithText(_submissions.First().Key.ToLowerInvariant().ToTitleCase())))
.ConfigureAwait(false);
return; return;
} }
var submissionClosedEmbed = GetEmbed(); var submissionClosedEmbed = GetEmbed();
await channel.EmbedAsync(submissionClosedEmbed).ConfigureAwait(false); await _channel.EmbedAsync(submissionClosedEmbed).ConfigureAwait(false);
//VOTING PHASE //VOTING PHASE
this.phase = AcroPhase.Voting; phase = AcroPhase.Voting;
try try
{ {
//30 secondds for voting //30 secondds for voting
await Task.Delay(30000, source.Token).ConfigureAwait(false); await Task.Delay(30000, _source.Token).ConfigureAwait(false);
this.phase = AcroPhase.Idle; phase = AcroPhase.Idle;
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -174,10 +178,10 @@ namespace NadekoBot.Modules.Games
try try
{ {
var msg = arg as SocketUserMessage; 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; return;
++spamCount; ++_spamCount;
var guildUser = (IGuildUser)msg.Author; var guildUser = (IGuildUser)msg.Author;
@ -185,37 +189,39 @@ namespace NadekoBot.Modules.Games
if (phase == AcroPhase.Submitting) if (phase == AcroPhase.Submitting)
{ {
if (spamCount > 10) if (_spamCount > 10)
{ {
spamCount = 0; _spamCount = 0;
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } try { await _channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
catch { } catch { }
} }
var inputWords = input.Split(' '); //get all words 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; 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 if (!inputWords[i].StartsWith(letter.ToString())) // all first letters must match
return; return;
} }
if (!usersWhoSubmitted.Add(guildUser.Id)) if (!_usersWhoSubmitted.Add(guildUser.Id))
return; return;
//try adding it to the list of answers //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; return;
} }
// all good. valid input. answer recorded // 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 try
{ {
await msg.DeleteAsync(); await msg.DeleteAsync();
@ -227,14 +233,13 @@ namespace NadekoBot.Modules.Games
} }
else if (phase == AcroPhase.Voting) else if (phase == AcroPhase.Voting)
{ {
if (spamCount > 10) if (_spamCount > 10)
{ {
spamCount = 0; _spamCount = 0;
try { await channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); } try { await _channel.EmbedAsync(GetEmbed()).ConfigureAwait(false); }
catch { } catch { }
} }
IGuildUser usr;
//if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id) //if (submissions.TryGetValue(input, out usr) && usr.Id != guildUser.Id)
//{ //{
// if (!usersWhoVoted.Add(guildUser.Id)) // if (!usersWhoVoted.Add(guildUser.Id))
@ -246,17 +251,17 @@ namespace NadekoBot.Modules.Games
//} //}
int num; 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; var usr = kvp.Value;
//can't vote for yourself, can't vote multiple times //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; return;
votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old); _votes.AddOrUpdate(kvp.Key, 1, (key, old) => ++old);
await channel.SendConfirmAsync("Acrophobia", $"{guildUser.Mention} cast their vote!").ConfigureAwait(false); await _channel.SendConfirmAsync(GetText("acrophobia"),
GetText("acro_vote_cast", Format.Bold(guildUser.ToString()))).ConfigureAwait(false);
await msg.DeleteAsync().ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false);
return;
} }
} }
@ -269,27 +274,34 @@ namespace NadekoBot.Modules.Games
public async Task End() 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("acro_no_votes_cast")).ConfigureAwait(false);
return; return;
} }
var table = votes.OrderByDescending(v => v.Value); var table = _votes.OrderByDescending(v => v.Value);
var winner = table.First(); var winner = table.First();
var embed = new EmbedBuilder().WithOkColor() var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Acrophobia") .WithTitle(GetText("acrophobia"))
.WithDescription($"Winner is {submissions[winner.Key].Mention} with {winner.Value} points.\n") .WithDescription(GetText("acro_winner", Format.Bold(_submissions[winner.Key].ToString()),
Format.Bold(winner.Value.ToString())))
.WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase())); .WithFooter(efb => efb.WithText(winner.Key.ToLowerInvariant().ToTitleCase()));
await channel.EmbedAsync(embed).ConfigureAwait(false); await _channel.EmbedAsync(embed).ConfigureAwait(false);
} }
public void EnsureStopped() public void EnsureStopped()
{ {
NadekoBot.Client.MessageReceived -= PotentialAcro; NadekoBot.Client.MessageReceived -= PotentialAcro;
if (!source.IsCancellationRequested) if (!_source.IsCancellationRequested)
source.Cancel(); _source.Cancel();
} }
private string GetText(string key, params object[] replacements)
=> GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(_channel.Guild),
typeof(Games).Name.ToLowerInvariant(),
replacements);
} }
} }
} }

View File

@ -17,11 +17,11 @@ namespace NadekoBot.Modules.Games
public partial class Games public partial class Games
{ {
[Group] [Group]
public class CleverBotCommands : ModuleBase public class CleverBotCommands : NadekoSubmodule
{ {
private static Logger _log { get; } private new static Logger _log { get; }
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(); public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; }
static CleverBotCommands() static CleverBotCommands()
{ {
@ -96,7 +96,7 @@ namespace NadekoBot.Modules.Games
uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false); uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false);
await uow.CompleteAsync().ConfigureAwait(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; return;
} }
@ -110,7 +110,7 @@ namespace NadekoBot.Modules.Games
await uow.CompleteAsync().ConfigureAwait(false); 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);
} }
} }
} }

View File

@ -9,8 +9,9 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Modules.Games.Commands.Hangman;
namespace NadekoBot.Modules.Games.Commands.Hangman namespace NadekoBot.Modules.Games.Hangman
{ {
public class HangmanTermPool public class HangmanTermPool
{ {
@ -70,7 +71,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
return $" {c}"; return $" {c}";
c = char.ToUpperInvariant(c); c = char.ToUpperInvariant(c);
return Guesses.Contains(c) ? $" {c}" : " _"; return Guesses.Contains(c) ? $" {c}" : " ";
})) + "`"; })) + "`";
public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant() public bool GuessedAll => Guesses.IsSupersetOf(Term.Word.ToUpperInvariant()
@ -145,7 +146,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
MessagesSinceLastPost = 0; MessagesSinceLastPost = 0;
++Errors; ++Errors;
if (Errors < MaxErrors) 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); footer: string.Join(" ", Guesses)).ConfigureAwait(false);
else else
await End().ConfigureAwait(false); await End().ConfigureAwait(false);
@ -158,7 +159,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
{ {
if (GuessedAll) 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); await End().ConfigureAwait(false);
return; return;
@ -166,7 +167,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
MessagesSinceLastPost = 0; MessagesSinceLastPost = 0;
try 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); footer: string.Join(" ", Guesses)).ConfigureAwait(false);
} }
catch { } catch { }
@ -177,7 +178,7 @@ namespace NadekoBot.Modules.Games.Commands.Hangman
MessagesSinceLastPost = 0; MessagesSinceLastPost = 0;
++Errors; ++Errors;
if (Errors < MaxErrors) 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); footer: string.Join(" ", Guesses)).ConfigureAwait(false);
else else
await End().ConfigureAwait(false); await End().ConfigureAwait(false);

View File

@ -1,36 +1,25 @@
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Commands.Hangman;
using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Modules.Games.Hangman;
using Discord;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
{ {
public partial class Games public partial class Games
{ {
[Group] [Group]
public class HangmanCommands : ModuleBase public class HangmanCommands : NadekoSubmodule
{ {
private static Logger _log { get; }
//channelId, game //channelId, game
public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>(); public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>();
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] [NadekoCommand, Usage, Description, Aliases]
public async Task Hangmanlist() 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] [NadekoCommand, Usage, Description, Aliases]
@ -40,11 +29,11 @@ namespace NadekoBot.Modules.Games
if (!HangmanGames.TryAdd(Context.Channel.Id, hm)) 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; return;
} }
hm.OnEnded += (g) => hm.OnEnded += g =>
{ {
HangmanGame throwaway; HangmanGame throwaway;
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway); HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
@ -55,14 +44,14 @@ namespace NadekoBot.Modules.Games
} }
catch (Exception ex) 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; HangmanGame throwaway;
HangmanGames.TryRemove(Context.Channel.Id, out throwaway); HangmanGames.TryRemove(Context.Channel.Id, out throwaway);
throwaway.Dispose(); throwaway.Dispose();
return; 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());
} }
} }
} }

View File

@ -11,10 +11,7 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
@ -31,7 +28,7 @@ namespace NadekoBot.Modules.Games
[Group] [Group]
public class PlantPickCommands : NadekoSubmodule public class PlantPickCommands : NadekoSubmodule
{ {
private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>(); private static ConcurrentHashSet<ulong> generationChannels { get; }
//channelid/message //channelid/message
private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>(); private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
//channelId/last generation //channelId/last generation
@ -82,25 +79,17 @@ namespace NadekoBot.Modules.Games
if (dropAmount > 0) if (dropAmount > 0)
{ {
var msgs = new IUserMessage[dropAmount]; var msgs = new IUserMessage[dropAmount];
var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name];
string firstPart; var toSend = dropAmount == 1
if (dropAmount == 1) ? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign, prefix)
{ : GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign, prefix);
firstPart = $"A random { NadekoBot.BotConfig.CurrencyName } appeared!";
}
else
{
firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!";
}
var file = GetRandomCurrencyImage(); var file = GetRandomCurrencyImage();
using (var fileStream = file.Value.ToStream()) using (var fileStream = file.Value.ToStream())
{ {
var sent = await channel.SendFileAsync( var sent = await channel.SendFileAsync(
fileStream, fileStream,
file.Key, file.Key,
string.Format("❗ {0} Pick it up by typing `{1}pick`", firstPart, toSend).ConfigureAwait(false);
NadekoBot.ModulePrefixes[typeof(Games).Name]))
.ConfigureAwait(false);
msgs[0] = sent; msgs[0] = sent;
} }
@ -117,6 +106,12 @@ namespace NadekoBot.Modules.Games
return Task.CompletedTask; 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Pick() 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 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); 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); 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); var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false);
if (!removed) 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; return;
} }
var imgData = GetRandomCurrencyImage(); 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; IUserMessage msg;
using (var toSend = imgData.Value.ToStream()) using (var toSend = imgData.Value.ToStream())
@ -203,11 +204,11 @@ namespace NadekoBot.Modules.Games
} }
if (enabled) if (enabled)
{ {
await channel.SendConfirmAsync("Currency generation enabled on this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("curgen_enabled").ConfigureAwait(false);
} }
else else
{ {
await channel.SendConfirmAsync("Currency generation disabled on this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false);
} }
} }

View File

@ -3,12 +3,10 @@ using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
@ -16,7 +14,7 @@ namespace NadekoBot.Modules.Games
public partial class Games public partial class Games
{ {
[Group] [Group]
public class PollCommands : ModuleBase public class PollCommands : NadekoSubmodule
{ {
public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>(); public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>();
@ -24,20 +22,20 @@ namespace NadekoBot.Modules.Games
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public Task Poll([Remainder] string arg = null) public Task Poll([Remainder] string arg = null)
=> InternalStartPoll(arg, isPublic: false); => InternalStartPoll(arg, false);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public Task PublicPoll([Remainder] string arg = null) public Task PublicPoll([Remainder] string arg = null)
=> InternalStartPoll(arg, isPublic: true); => InternalStartPoll(arg, true);
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task PollStats() public async Task PollStats()
{ {
Games.Poll poll; Poll poll;
if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll)) if (!ActivePolls.TryGetValue(Context.Guild.Id, out poll))
return; return;
@ -48,8 +46,6 @@ namespace NadekoBot.Modules.Games
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
if (!(Context.User as IGuildUser).GuildPermissions.ManageChannels)
return;
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";")) if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
return; return;
var data = arg.Split(';'); var data = arg.Split(';');
@ -80,27 +76,25 @@ namespace NadekoBot.Modules.Games
public class Poll public class Poll
{ {
private readonly IUserMessage originalMessage; private readonly IUserMessage _originalMessage;
private readonly IGuild guild; private readonly IGuild _guild;
private string[] Answers { get; } private string[] answers { get; }
private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>(); private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>();
private readonly string question; private readonly string _question;
private DateTime started;
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
public bool IsPublic { get; } public bool IsPublic { get; }
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false) public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{ {
this.originalMessage = umsg; _originalMessage = umsg;
this.guild = ((ITextChannel)umsg.Channel).Guild; _guild = ((ITextChannel)umsg.Channel).Guild;
this.question = question; _question = question;
this.Answers = enumerable as string[] ?? enumerable.ToArray(); answers = enumerable as string[] ?? enumerable.ToArray();
this.IsPublic = isPublic; IsPublic = isPublic;
} }
public EmbedBuilder GetStats(string title) 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)) .ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
.OrderByDescending(kvp => kvp.Value) .OrderByDescending(kvp => kvp.Value)
.ToArray(); .ToArray();
@ -108,7 +102,7 @@ namespace NadekoBot.Modules.Games
var eb = new EmbedBuilder().WithTitle(title); var eb = new EmbedBuilder().WithTitle(title);
var sb = new StringBuilder() var sb = new StringBuilder()
.AppendLine(Format.Bold(question)) .AppendLine(Format.Bold(_question))
.AppendLine(); .AppendLine();
var totalVotesCast = 0; var totalVotesCast = 0;
@ -121,7 +115,7 @@ namespace NadekoBot.Modules.Games
for (int i = 0; i < results.Length; i++) for (int i = 0; i < results.Length; i++)
{ {
var result = results[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; totalVotesCast += result.Value;
} }
} }
@ -135,22 +129,21 @@ namespace NadekoBot.Modules.Games
public async Task StartPoll() public async Task StartPoll()
{ {
started = DateTime.Now;
NadekoBot.Client.MessageReceived += Vote; 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; 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) if (!IsPublic)
msgToSend += "\n**Private Message me with the corresponding number of the answer.**"; msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
else else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**"; 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() public async Task StopPoll()
{ {
NadekoBot.Client.MessageReceived -= Vote; 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) private async Task Vote(SocketMessage imsg)
@ -166,14 +159,14 @@ namespace NadekoBot.Modules.Games
int vote; int vote;
if (!int.TryParse(imsg.Content, out vote)) if (!int.TryParse(imsg.Content, out vote))
return; return;
if (vote < 1 || vote > Answers.Length) if (vote < 1 || vote > answers.Length)
return; return;
IMessageChannel ch; IMessageChannel ch;
if (IsPublic) if (IsPublic)
{ {
//if public, channel must be the same the poll started in //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; return;
ch = imsg.Channel; ch = imsg.Channel;
} }
@ -184,13 +177,13 @@ namespace NadekoBot.Modules.Games
return; return;
// user must be a member of the guild this poll is in // user must be a member of the guild this poll is in
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false); var guildUsers = await _guild.GetUsersAsync().ConfigureAwait(false);
if (!guildUsers.Any(u => u.Id == imsg.Author.Id)) if (guildUsers.All(u => u.Id != imsg.Author.Id))
return; return;
} }
//user can vote only once //user can vote only once
if (participants.TryAdd(msg.Author.Id, vote)) if (_participants.TryAdd(msg.Author.Id, vote))
{ {
if (!IsPublic) if (!IsPublic)
{ {

View File

@ -147,7 +147,7 @@ namespace NadekoBot.Modules.Games
} }
[Group] [Group]
public class SpeedTypingCommands : ModuleBase public class SpeedTypingCommands : NadekoSubmodule
{ {
public static List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>(); public static List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>();

View File

@ -4,9 +4,7 @@ using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -15,21 +13,13 @@ namespace NadekoBot.Modules.Games
{ {
public partial class Games public partial class Games
{ {
//todo timeout
[Group] [Group]
public class TicTacToeCommands : ModuleBase public class TicTacToeCommands : NadekoSubmodule
{ {
//channelId/game //channelId/game
private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>(); private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>();
private readonly Logger _log;
public TicTacToeCommands() private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1);
{
_log = LogManager.GetCurrentClassLogger();
}
private readonly SemaphoreSlim sem = new SemaphoreSlim(1, 1);
private readonly object tttLockObj = new object();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -37,7 +27,7 @@ namespace NadekoBot.Modules.Games
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
await sem.WaitAsync(1000); await _sem.WaitAsync(1000);
try try
{ {
TicTacToe game; TicTacToe game;
@ -51,7 +41,7 @@ namespace NadekoBot.Modules.Games
} }
game = new TicTacToe(channel, (IGuildUser)Context.User); game = new TicTacToe(channel, (IGuildUser)Context.User);
_games.Add(channel.Id, game); _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) => game.OnEnded += (g) =>
{ {
@ -60,7 +50,7 @@ namespace NadekoBot.Modules.Games
} }
finally finally
{ {
sem.Release(); _sem.Release();
} }
} }
} }
@ -75,46 +65,49 @@ namespace NadekoBot.Modules.Games
} }
private readonly ITextChannel _channel; private readonly ITextChannel _channel;
private readonly Logger _log;
private readonly IGuildUser[] _users; private readonly IGuildUser[] _users;
private readonly int?[,] _state; private readonly int?[,] _state;
private Phase _phase; private Phase _phase;
int curUserIndex = 0; private int _curUserIndex;
private readonly SemaphoreSlim moveLock; 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<TicTacToe> OnEnded; public Action<TicTacToe> OnEnded;
private IUserMessage previousMessage = null; private IUserMessage _previousMessage;
private Timer timeoutTimer; private Timer _timeoutTimer;
public TicTacToe(ITextChannel channel, IGuildUser firstUser) public TicTacToe(ITextChannel channel, IGuildUser firstUser)
{ {
_channel = channel; _channel = channel;
_users = new IGuildUser[2] { firstUser, null }; _users = new[] { firstUser, null };
_state = new int?[3, 3] { _state = new int?[,] {
{ null, null, null }, { null, null, null },
{ null, null, null }, { null, null, null },
{ null, null, null }, { null, null, null },
}; };
_log = LogManager.GetCurrentClassLogger();
_log.Warn($"User {firstUser} created a TicTacToe game.");
_phase = Phase.Starting; _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() public string GetState()
{ {
var sb = new StringBuilder(); 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) if (j < _state.GetLength(1) - 1)
sb.Append("┃"); sb.Append("┃");
} }
@ -130,7 +123,7 @@ namespace NadekoBot.Modules.Games
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithDescription(Environment.NewLine + GetState()) .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)) if (!string.IsNullOrWhiteSpace(title))
embed.WithTitle(title); embed.WithTitle(title);
@ -138,12 +131,12 @@ namespace NadekoBot.Modules.Games
if (_winner == null) if (_winner == null)
{ {
if (_phase == Phase.Ended) if (_phase == Phase.Ended)
embed.WithFooter(efb => efb.WithText($"No moves left!")); embed.WithFooter(efb => efb.WithText(GetText("ttt_no_moves")));
else else
embed.WithFooter(efb => efb.WithText($"{_users[curUserIndex]}'s move")); embed.WithFooter(efb => efb.WithText(GetText("ttt_users_move", _users[_curUserIndex])));
} }
else else
embed.WithFooter(efb => efb.WithText($"{_winner} Won!")); embed.WithFooter(efb => efb.WithText(GetText("ttt_has_won", _winner)));
return embed; return embed;
} }
@ -169,23 +162,22 @@ namespace NadekoBot.Modules.Games
{ {
if (_phase == Phase.Started || _phase == Phase.Ended) 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; return;
} }
else if (_users[0] == user) 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; return;
} }
_users[1] = user; _users[1] = user;
_log.Warn($"User {user} joined a TicTacToe game.");
_phase = Phase.Started; _phase = Phase.Started;
timeoutTimer = new Timer(async (_) => _timeoutTimer = new Timer(async (_) =>
{ {
await moveLock.WaitAsync(); await _moveLock.WaitAsync();
try try
{ {
if (_phase == Phase.Ended) if (_phase == Phase.Ended)
@ -194,12 +186,13 @@ namespace NadekoBot.Modules.Games
_phase = Phase.Ended; _phase = Phase.Ended;
if (_users[1] != null) if (_users[1] != null)
{ {
_winner = _users[curUserIndex ^= 1]; _winner = _users[_curUserIndex ^= 1];
var del = previousMessage?.DeleteAsync(); var del = _previousMessage?.DeleteAsync();
try try
{ {
await _channel.EmbedAsync(GetEmbed("Time Expired!")).ConfigureAwait(false); await _channel.EmbedAsync(GetEmbed(GetText("ttt_time_expired"))).ConfigureAwait(false);
await del.ConfigureAwait(false); if (del != null)
await del.ConfigureAwait(false);
} }
catch { } catch { }
} }
@ -209,21 +202,21 @@ namespace NadekoBot.Modules.Games
catch { } catch { }
finally finally
{ {
moveLock.Release(); _moveLock.Release();
} }
}, null, 15000, Timeout.Infinite); }, null, 15000, Timeout.Infinite);
NadekoBot.Client.MessageReceived += Client_MessageReceived; 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() 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) if (_state[i, j] == null)
return false; return false;
@ -236,10 +229,10 @@ namespace NadekoBot.Modules.Games
{ {
var _ = Task.Run(async () => var _ = Task.Run(async () =>
{ {
await moveLock.WaitAsync().ConfigureAwait(false); await _moveLock.WaitAsync().ConfigureAwait(false);
try try
{ {
var curUser = _users[curUserIndex]; var curUser = _users[_curUserIndex];
if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id) if (_phase == Phase.Ended || msg.Author?.Id != curUser.Id)
return; return;
@ -249,53 +242,53 @@ namespace NadekoBot.Modules.Games
index <= 9 && index <= 9 &&
_state[index / 3, index % 3] == null) _state[index / 3, index % 3] == null)
{ {
_state[index / 3, index % 3] = curUserIndex; _state[index / 3, index % 3] = _curUserIndex;
// i'm lazy // i'm lazy
if (_state[index / 3, 0] == _state[index / 3, 1] && _state[index / 3, 1] == _state[index / 3, 2]) 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, 0] = _curUserIndex + 2;
_state[index / 3, 1] = curUserIndex + 2; _state[index / 3, 1] = _curUserIndex + 2;
_state[index / 3, 2] = curUserIndex + 2; _state[index / 3, 2] = _curUserIndex + 2;
_phase = Phase.Ended; _phase = Phase.Ended;
} }
else if (_state[0, index % 3] == _state[1, index % 3] && _state[1, index % 3] == _state[2, index % 3]) 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[0, index % 3] = _curUserIndex + 2;
_state[1, index % 3] = curUserIndex + 2; _state[1, index % 3] = _curUserIndex + 2;
_state[2, index % 3] = curUserIndex + 2; _state[2, index % 3] = _curUserIndex + 2;
_phase = Phase.Ended; _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[0, 0] = _curUserIndex + 2;
_state[1, 1] = curUserIndex + 2; _state[1, 1] = _curUserIndex + 2;
_state[2, 2] = curUserIndex + 2; _state[2, 2] = _curUserIndex + 2;
_phase = Phase.Ended; _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[0, 2] = _curUserIndex + 2;
_state[1, 1] = curUserIndex + 2; _state[1, 1] = _curUserIndex + 2;
_state[2, 0] = curUserIndex + 2; _state[2, 0] = _curUserIndex + 2;
_phase = Phase.Ended; _phase = Phase.Ended;
} }
string reason = ""; var reason = "";
if (_phase == Phase.Ended) // if user won, stop receiving moves if (_phase == Phase.Ended) // if user won, stop receiving moves
{ {
reason = "Matched three!"; reason = GetText("ttt_matched_three");
_winner = _users[curUserIndex]; _winner = _users[_curUserIndex];
NadekoBot.Client.MessageReceived -= Client_MessageReceived; NadekoBot.Client.MessageReceived -= Client_MessageReceived;
OnEnded?.Invoke(this); OnEnded?.Invoke(this);
} }
else if (IsDraw()) else if (IsDraw())
{ {
reason = "A draw!"; reason = GetText("ttt_a_draw");
_phase = Phase.Ended; _phase = Phase.Ended;
NadekoBot.Client.MessageReceived -= Client_MessageReceived; NadekoBot.Client.MessageReceived -= Client_MessageReceived;
OnEnded?.Invoke(this); OnEnded?.Invoke(this);
@ -304,19 +297,19 @@ namespace NadekoBot.Modules.Games
var sendstate = Task.Run(async () => var sendstate = Task.Run(async () =>
{ {
var del1 = msg.DeleteAsync(); var del1 = msg.DeleteAsync();
var del2 = previousMessage?.DeleteAsync(); var del2 = _previousMessage?.DeleteAsync();
try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { } try { _previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
try { await del1; } catch { } try { await del1; } catch { }
try { if (del2 != null) await del2; } catch { } try { if (del2 != null) await del2; } catch { }
}); });
curUserIndex ^= 1; _curUserIndex ^= 1;
timeoutTimer.Change(15000, Timeout.Infinite); _timeoutTimer.Change(15000, Timeout.Infinite);
} }
} }
finally finally
{ {
moveLock.Release(); _moveLock.Release();
} }
}); });

View File

@ -17,36 +17,42 @@ namespace NadekoBot.Modules.Games.Trivia
public class TriviaGame public class TriviaGame
{ {
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1);
private Logger _log { get; } private readonly Logger _log;
public IGuild guild { get; } public IGuild Guild { get; }
public ITextChannel channel { get; } public ITextChannel Channel { get; }
private int QuestionDurationMiliseconds { get; } = 30000; private int questionDurationMiliseconds { get; } = 30000;
private int HintTimeoutMiliseconds { get; } = 6000; private int hintTimeoutMiliseconds { get; } = 6000;
public bool ShowHints { get; } = true; public bool ShowHints { get; }
private CancellationTokenSource triviaCancelSource { get; set; } private CancellationTokenSource triviaCancelSource { get; set; }
public TriviaQuestion CurrentQuestion { get; private set; } public TriviaQuestion CurrentQuestion { get; private set; }
public HashSet<TriviaQuestion> oldQuestions { get; } = new HashSet<TriviaQuestion>(); public HashSet<TriviaQuestion> OldQuestions { get; } = new HashSet<TriviaQuestion>();
public ConcurrentDictionary<IGuildUser, int> Users { get; } = new ConcurrentDictionary<IGuildUser, int>(); public ConcurrentDictionary<IGuildUser, int> Users { get; } = new ConcurrentDictionary<IGuildUser, int>();
public bool GameActive { get; private set; } = false; public bool GameActive { get; private set; }
public bool ShouldStopGame { 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) public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq)
{ {
this._log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.ShowHints = showHints; ShowHints = showHints;
this.guild = guild; Guild = guild;
this.channel = channel; Channel = channel;
this.WinRequirement = winReq; 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() public async Task StartGame()
{ {
while (!ShouldStopGame) while (!ShouldStopGame)
@ -55,26 +61,24 @@ namespace NadekoBot.Modules.Games.Trivia
triviaCancelSource = new CancellationTokenSource(); triviaCancelSource = new CancellationTokenSource();
// load question // load question
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions); CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(OldQuestions);
if (CurrentQuestion == null || if (string.IsNullOrWhiteSpace(CurrentQuestion?.Answer) || string.IsNullOrWhiteSpace(CurrentQuestion.Question))
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; 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; EmbedBuilder questionEmbed;
IUserMessage questionMessage; IUserMessage questionMessage;
try try
{ {
questionEmbed = new EmbedBuilder().WithOkColor() questionEmbed = new EmbedBuilder().WithOkColor()
.WithTitle("Trivia Game") .WithTitle(GetText("trivia_game"))
.AddField(eab => eab.WithName("Category").WithValue(CurrentQuestion.Category)) .AddField(eab => eab.WithName(GetText("category")).WithValue(CurrentQuestion.Category))
.AddField(eab => eab.WithName("Question").WithValue(CurrentQuestion.Question)); .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 || catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.NotFound ||
ex.HttpCode == System.Net.HttpStatusCode.Forbidden || ex.HttpCode == System.Net.HttpStatusCode.Forbidden ||
@ -99,7 +103,7 @@ namespace NadekoBot.Modules.Games.Trivia
try try
{ {
//hint //hint
await Task.Delay(HintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false); await Task.Delay(hintTimeoutMiliseconds, triviaCancelSource.Token).ConfigureAwait(false);
if (ShowHints) if (ShowHints)
try try
{ {
@ -113,7 +117,7 @@ namespace NadekoBot.Modules.Games.Trivia
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
//timeout //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 catch (TaskCanceledException) { } //means someone guessed the answer
@ -124,7 +128,7 @@ namespace NadekoBot.Modules.Games.Trivia
NadekoBot.Client.MessageReceived -= PotentialGuess; NadekoBot.Client.MessageReceived -= PotentialGuess;
} }
if (!triviaCancelSource.IsCancellationRequested) 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); await Task.Delay(2000).ConfigureAwait(false);
} }
} }
@ -133,7 +137,7 @@ namespace NadekoBot.Modules.Games.Trivia
{ {
ShouldStopGame = true; ShouldStopGame = true;
await channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Trivia Game Ended")) .WithAuthor(eab => eab.WithName("Trivia Game Ended"))
.WithTitle("Final Results") .WithTitle("Final Results")
.WithDescription(GetLeaderboard())).ConfigureAwait(false); .WithDescription(GetLeaderboard())).ConfigureAwait(false);
@ -144,7 +148,7 @@ namespace NadekoBot.Modules.Games.Trivia
var old = ShouldStopGame; var old = ShouldStopGame;
ShouldStopGame = true; ShouldStopGame = true;
if (!old) 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) private async Task PotentialGuess(SocketMessage imsg)
@ -155,11 +159,9 @@ namespace NadekoBot.Modules.Games.Trivia
return; return;
var umsg = imsg as SocketUserMessage; var umsg = imsg as SocketUserMessage;
if (umsg == null)
return;
var textChannel = umsg.Channel as ITextChannel; var textChannel = umsg?.Channel as ITextChannel;
if (textChannel == null || textChannel.Guild != guild) if (textChannel == null || textChannel.Guild != Guild)
return; return;
var guildUser = (IGuildUser)umsg.Author; var guildUser = (IGuildUser)umsg.Author;
@ -182,13 +184,24 @@ namespace NadekoBot.Modules.Games.Trivia
if (Users[guildUser] == WinRequirement) if (Users[guildUser] == WinRequirement)
{ {
ShouldStopGame = true; 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; var reward = NadekoBot.BotConfig.TriviaCurrencyReward;
if (reward > 0) if (reward > 0)
await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false);
return; return;
} }
await channel.SendConfirmAsync("Trivia Game", $"{guildUser.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false); await Channel.SendConfirmAsync(GetText("trivia_game"),
GetText("trivia_guess", guildUser.Mention, Format.Bold(CurrentQuestion.Answer))).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
@ -197,13 +210,13 @@ namespace NadekoBot.Modules.Games.Trivia
public string GetLeaderboard() public string GetLeaderboard()
{ {
if (Users.Count == 0) if (Users.Count == 0)
return "No results."; return GetText("no_results");
var sb = new StringBuilder(); var sb = new StringBuilder();
foreach (var kvp in Users.OrderByDescending(kvp => kvp.Value)) 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(); return sb.ToString();

View File

@ -3,9 +3,7 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Trivia; using NadekoBot.Modules.Games.Trivia;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,7 +12,7 @@ namespace NadekoBot.Modules.Games
public partial class Games public partial class Games
{ {
[Group] [Group]
public class TriviaCommands : ModuleBase public class TriviaCommands : NadekoSubmodule
{ {
public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>(); public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>();
@ -31,7 +29,7 @@ namespace NadekoBot.Modules.Games
var showHints = !additionalArgs.Contains("nohint"); 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)) if (RunningTrivias.TryAdd(channel.Guild.Id, trivia))
{ {
try try
@ -45,8 +43,9 @@ namespace NadekoBot.Modules.Games
} }
return; 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] [NadekoCommand, Usage, Description, Aliases]
@ -58,11 +57,11 @@ namespace NadekoBot.Modules.Games
TriviaGame trivia; TriviaGame trivia;
if (RunningTrivias.TryGetValue(channel.Guild.Id, out 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; return;
} }
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false); await ReplyErrorLocalized("trivia_none").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -78,7 +77,7 @@ namespace NadekoBot.Modules.Games
return; return;
} }
await channel.SendErrorAsync("No trivia is running on this server.").ConfigureAwait(false); await ReplyErrorLocalized("trivia_none").ConfigureAwait(false);
} }
} }
} }

View File

@ -4,16 +4,29 @@ using NadekoBot.Services;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using System; using System;
using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Immutable;
using System.IO;
using System.Threading;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Net.Http;
using ImageSharp;
using NadekoBot.DataStructures;
using NLog;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
{ {
[NadekoModule("Games", ">")] [NadekoModule("Games", ">")]
public partial class Games : NadekoModule public partial class Games : NadekoTopLevelModule
{ {
private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray(); private static readonly ImmutableArray<string> _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] [NadekoCommand, Usage, Description, Aliases]
public async Task Choose([Remainder] string list = null) public async Task Choose([Remainder] string list = null)
@ -21,7 +34,7 @@ namespace NadekoBot.Modules.Games
if (string.IsNullOrWhiteSpace(list)) if (string.IsNullOrWhiteSpace(list))
return; return;
var listArr = list.Split(';'); var listArr = list.Split(';');
if (listArr.Count() < 2) if (listArr.Length < 2)
return; return;
var rng = new NadekoRandom(); var rng = new NadekoRandom();
await Context.Channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false); await Context.Channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
@ -34,21 +47,24 @@ namespace NadekoBot.Modules.Games
return; return;
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false)) .AddField(efb => efb.WithName("❓ " + GetText("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("8ball")).WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Rps(string input) public async Task Rps(string input)
{ {
Func<int,string> GetRPSPick = (p) => Func<int,string> getRpsPick = (p) =>
{ {
if (p == 0) switch (p)
return "🚀"; {
else if (p == 1) case 0:
return "📎"; return "🚀";
else case 1:
return "✂️"; return "📎";
default:
return "✂️";
}
}; };
int pick; int pick;
@ -72,19 +88,176 @@ namespace NadekoBot.Modules.Games
return; return;
} }
var nadekoPick = new NadekoRandom().Next(0, 3); var nadekoPick = new NadekoRandom().Next(0, 3);
var msg = ""; string msg;
if (pick == nadekoPick) if (pick == nadekoPick)
msg = $"It's a draw! Both picked {GetRPSPick(pick)}"; msg = GetText("rps_draw", getRpsPick(pick));
else if ((pick == 0 && nadekoPick == 1) || else if ((pick == 0 && nadekoPick == 1) ||
(pick == 1 && nadekoPick == 2) || (pick == 1 && nadekoPick == 2) ||
(pick == 2 && nadekoPick == 0)) (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 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); await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
} }
private static readonly ConcurrentDictionary<ulong, GirlRating> _girlRatings = new ConcurrentDictionary<ulong, GirlRating>();
public class GirlRating
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
public double Crazy { get; }
public double Hot { get; }
public int Roll { get; }
public string Advice { get; }
public AsyncLazy<string> 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<string>(async () =>
{
try
{
using (var ms = new MemoryStream(NadekoBot.Images.WifeMatrix.ToArray(), false))
using (var img = new ImageSharp.Image(ms))
{
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));
using (var pointMs = new MemoryStream(NadekoBot.Images.RategirlDot.ToArray(), false))
using (var pointImg = new ImageSharp.Image(pointMs))
{
img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10));
}
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);
if ((uid == 185968432783687681 ||
uid == 265642040950390784) && roll >= 900)
roll = 1000;
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(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.";
}
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] [NadekoCommand, Usage, Description, Aliases]
public async Task Linux(string guhnoo, string loonix) public async Task Linux(string guhnoo, string loonix)
{ {

View File

@ -13,7 +13,7 @@ using System.Collections.Generic;
namespace NadekoBot.Modules.Help namespace NadekoBot.Modules.Help
{ {
[NadekoModule("Help", "-")] [NadekoModule("Help", "-")]
public class Help : NadekoModule public class Help : NadekoTopLevelModule
{ {
private static string helpString { get; } = NadekoBot.BotConfig.HelpString; 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 HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]);

View File

@ -77,10 +77,10 @@ namespace NadekoBot.Modules.Music.Classes
public IVoiceChannel PlaybackVoiceChannel { get; private set; } public IVoiceChannel PlaybackVoiceChannel { get; private set; }
public ITextChannel OutputTextChannel { get; set; } public ITextChannel OutputTextChannel { get; set; }
private bool destroyed { get; set; } = false; private bool destroyed { get; set; }
public bool RepeatSong { get; private set; } = false; public bool RepeatSong { get; private set; }
public bool RepeatPlaylist { get; private set; } = false; public bool RepeatPlaylist { get; private set; }
public bool Autoplay { get; set; } = false; public bool Autoplay { get; set; }
public uint MaxQueueSize { get; set; } = 0; public uint MaxQueueSize { get; set; } = 0;
private ConcurrentQueue<Action> actionQueue { get; } = new ConcurrentQueue<Action>(); private ConcurrentQueue<Action> actionQueue { get; } = new ConcurrentQueue<Action>();

View File

@ -5,12 +5,9 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.IO; using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using VideoLibrary;
using System.Net; using System.Net;
namespace NadekoBot.Modules.Music.Classes namespace NadekoBot.Modules.Music.Classes
@ -32,22 +29,22 @@ namespace NadekoBot.Modules.Music.Classes
public string QueuerName { get; set; } public string QueuerName { get; set; }
public TimeSpan TotalTime { get; set; } = TimeSpan.Zero; 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; private const int _milliseconds = 20;
const int samplesPerFrame = (48000 / 1000) * milliseconds; private const int _samplesPerFrame = (48000 / 1000) * _milliseconds;
const int frameBytes = 3840; //16-bit, 2 channels private const int _frameBytes = 3840; //16-bit, 2 channels
private ulong bytesSent { get; set; } = 0; private ulong bytesSent { get; set; }
//pwetty //pwetty
public string PrettyProvider => public string PrettyProvider =>
$"{(SongInfo.Provider ?? "No Provider")}"; $"{(SongInfo.Provider ?? "???")}";
public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime; 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}"; public string PrettyInfo => $"{MusicPlayer.PrettyVolume} | {PrettyTotalTime} | {PrettyProvider} | {QueuerName}";
@ -65,22 +62,19 @@ namespace NadekoBot.Modules.Music.Classes
} }
} }
private string PrettyTotalTime { public string PrettyTotalTime {
get { get
{
if (TotalTime == TimeSpan.Zero) if (TotalTime == TimeSpan.Zero)
return "(?)"; return "(?)";
else if (TotalTime == TimeSpan.MaxValue) if (TotalTime == TimeSpan.MaxValue)
return "∞"; 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) if (hrs > 0)
return hrs + ":" + time; return hrs + ":" + time;
else return time;
return time;
}
} }
} }
@ -89,13 +83,13 @@ namespace NadekoBot.Modules.Music.Classes
switch (SongInfo.ProviderType) switch (SongInfo.ProviderType)
{ {
case MusicType.Radio: 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: case MusicType.Normal:
//todo have videoid in songinfo from the start //todo have videoid in songinfo from the start
var videoId = Regex.Match(SongInfo.Query, "<=v=[a-zA-Z0-9-]+(?=&)|(?<=[0-9])[^&\n]+|(?<=v=)[^&\n]+"); 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"; return $"https://img.youtube.com/vi/{ videoId }/0.jpg";
case MusicType.Local: 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: case MusicType.Soundcloud:
return SongInfo.AlbumArt; return SongInfo.AlbumArt;
default: default:
@ -104,7 +98,7 @@ namespace NadekoBot.Modules.Music.Classes
} }
} }
private string songUrl { public string SongUrl {
get { get {
switch (SongInfo.ProviderType) switch (SongInfo.ProviderType)
{ {
@ -122,36 +116,32 @@ namespace NadekoBot.Modules.Music.Classes
} }
} }
private int skipTo = 0; public int SkipTo { get; set; }
public int SkipTo {
get { return skipTo; }
set {
skipTo = value;
bytesSent = (ulong)skipTo * 3840 * 50;
}
}
private readonly Logger _log; private readonly Logger _log;
public Song(SongInfo songInfo) public Song(SongInfo songInfo)
{ {
this.SongInfo = songInfo; SongInfo = songInfo;
this._log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
} }
public Song Clone() public Song Clone()
{ {
var s = new Song(SongInfo); var s = new Song(SongInfo)
s.MusicPlayer = MusicPlayer; {
s.QueuerName = QueuerName; MusicPlayer = MusicPlayer,
QueuerName = QueuerName
};
return s; return s;
} }
public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken) public async Task Play(IAudioClient voiceClient, CancellationToken cancelToken)
{ {
bytesSent = (ulong) SkipTo * 3840 * 50;
var filename = Path.Combine(Music.MusicDataPath, DateTime.Now.UnixTimestamp().ToString()); 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); var bufferTask = inStream.BufferSong(cancelToken).ConfigureAwait(false);
try try
@ -200,22 +190,22 @@ namespace NadekoBot.Modules.Music.Classes
var outStream = voiceClient.CreatePCMStream(960); 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 while (!cancelToken.IsCancellationRequested && //song canceled for whatever reason
!(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime !(MusicPlayer.MaxPlaytimeSeconds != 0 && CurrentTime.TotalSeconds >= MusicPlayer.MaxPlaytimeSeconds)) // or exceedded max playtime
{ {
//Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------"); //Console.WriteLine($"Read: {songBuffer.ReadPosition}\nWrite: {songBuffer.WritePosition}\nContentLength:{songBuffer.ContentLength}\n---------");
var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false); var read = await inStream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
//await inStream.CopyToAsync(voiceClient.OutputStream); //await inStream.CopyToAsync(voiceClient.OutputStream);
if (read < frameBytes) if (read < _frameBytes)
_log.Debug("read {0}", read); _log.Debug("read {0}", read);
unchecked unchecked
{ {
bytesSent += (ulong)read; bytesSent += (ulong)read;
} }
if (read < frameBytes) if (read < _frameBytes)
{ {
if (read == 0) if (read == 0)
{ {
@ -231,12 +221,12 @@ namespace NadekoBot.Modules.Music.Classes
_log.Warn("Slow connection has disrupted music, waiting a bit for buffer"); _log.Warn("Slow connection has disrupted music, waiting a bit for buffer");
await Task.Delay(1000, cancelToken).ConfigureAwait(false); await Task.Delay(1000, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds; nextTime = Environment.TickCount + _milliseconds;
} }
else else
{ {
await Task.Delay(100, cancelToken).ConfigureAwait(false); await Task.Delay(100, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds; nextTime = Environment.TickCount + _milliseconds;
} }
} }
else else
@ -245,16 +235,16 @@ namespace NadekoBot.Modules.Music.Classes
else else
attempt = 0; attempt = 0;
while (this.MusicPlayer.Paused) while (MusicPlayer.Paused)
{ {
await Task.Delay(200, cancelToken).ConfigureAwait(false); await Task.Delay(200, cancelToken).ConfigureAwait(false);
nextTime = Environment.TickCount + milliseconds; nextTime = Environment.TickCount + _milliseconds;
} }
buffer = AdjustVolume(buffer, MusicPlayer.Volume); buffer = AdjustVolume(buffer, MusicPlayer.Volume);
if (read != frameBytes) continue; if (read != _frameBytes) continue;
nextTime = unchecked(nextTime + milliseconds); nextTime = unchecked(nextTime + _milliseconds);
int delayMillis = unchecked(nextTime - Environment.TickCount); int delayMillis = unchecked(nextTime - Environment.TickCount);
if (delayMillis > 0) if (delayMillis > 0)
await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false); await Task.Delay(delayMillis, cancelToken).ConfigureAwait(false);
@ -264,8 +254,7 @@ namespace NadekoBot.Modules.Music.Classes
finally finally
{ {
await bufferTask; await bufferTask;
if (inStream != null) inStream.Dispose();
inStream.Dispose();
} }
} }
@ -279,25 +268,20 @@ namespace NadekoBot.Modules.Music.Classes
} }
//aidiakapi ftw //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);
Contract.Requires(volume >= 0f && volume <= 1f);
Contract.Assert(BitConverter.IsLittleEndian);
if (Math.Abs(volume - 1f) < 0.0001f) return audioSamples; if (Math.Abs(volume - 1f) < 0.0001f) return audioSamples;
// 16-bit precision for the multiplication // 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) 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); *src = (short)(((*src) * volumeFixed) >> 16);
} }

View File

@ -6,12 +6,14 @@ using System.Net.Http;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using NLog;
using VideoLibrary; using VideoLibrary;
namespace NadekoBot.Modules.Music.Classes namespace NadekoBot.Modules.Music.Classes
{ {
public static class SongHandler public static class SongHandler
{ {
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal) public static async Task<Song> ResolveSong(string query, MusicType musicType = MusicType.Normal)
{ {
if (string.IsNullOrWhiteSpace(query)) if (string.IsNullOrWhiteSpace(query))
@ -106,7 +108,8 @@ namespace NadekoBot.Modules.Music.Classes
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine($"Failed resolving the link.{ex.Message}"); _log.Warn($"Failed resolving the link.{ex.Message}");
_log.Warn(ex);
return null; return null;
} }
} }
@ -137,7 +140,7 @@ namespace NadekoBot.Modules.Music.Classes
} }
catch catch
{ {
Console.WriteLine($"Failed reading .pls:\n{file}"); _log.Warn($"Failed reading .pls:\n{file}");
return null; return null;
} }
} }
@ -156,7 +159,7 @@ namespace NadekoBot.Modules.Music.Classes
} }
catch catch
{ {
Console.WriteLine($"Failed reading .m3u:\n{file}"); _log.Warn($"Failed reading .m3u:\n{file}");
return null; return null;
} }
@ -172,7 +175,7 @@ namespace NadekoBot.Modules.Music.Classes
} }
catch catch
{ {
Console.WriteLine($"Failed reading .asx:\n{file}"); _log.Warn($"Failed reading .asx:\n{file}");
return null; return null;
} }
} }
@ -192,7 +195,7 @@ namespace NadekoBot.Modules.Music.Classes
} }
catch catch
{ {
Console.WriteLine($"Failed reading .xspf:\n{file}"); _log.Warn($"Failed reading .xspf:\n{file}");
return null; return null;
} }
} }

View File

@ -14,14 +14,13 @@ using System.Net.Http;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
namespace NadekoBot.Modules.Music namespace NadekoBot.Modules.Music
{ {
[NadekoModule("Music", "!!")] [NadekoModule("Music", "!!")]
[DontAutoLoad] [DontAutoLoad]
public partial class Music : NadekoModule public class Music : NadekoTopLevelModule
{ {
public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>(); public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
@ -78,7 +77,10 @@ namespace NadekoBot.Modules.Music
} }
} }
catch { } catch
{
// ignored
}
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -153,7 +155,14 @@ namespace NadekoBot.Modules.Music
return; return;
var val = musicPlayer.FairPlay = !musicPlayer.FairPlay; 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] [NadekoCommand, Usage, Description, Aliases]
@ -182,57 +191,56 @@ namespace NadekoBot.Modules.Music
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ListQueue(int page = 1) public async Task ListQueue(int page = 1)
{ {
Song currentSong;
MusicPlayer musicPlayer; 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_player").ConfigureAwait(false);
return; return;
} }
if (page <= 0) if (page <= 0)
return; 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 { } try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
const int itemsPerPage = 10; const int itemsPerPage = 10;
var total = musicPlayer.TotalPlaytime; 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 maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
var lastPage = musicPlayer.Playlist.Count / itemsPerPage; var lastPage = musicPlayer.Playlist.Count / itemsPerPage;
Func<int, EmbedBuilder> printAction = (curPage) => Func<int, EmbedBuilder> printAction = curPage =>
{ {
int startAt = itemsPerPage * (curPage - 1); var startAt = itemsPerPage * (curPage - 1);
var number = 0 + startAt; var number = 0 + startAt;
var desc = string.Join("\n", musicPlayer.Playlist var desc = string.Join("\n", musicPlayer.Playlist
.Skip(startAt) .Skip(startAt)
.Take(itemsPerPage) .Take(itemsPerPage)
.Select(v => $"`{++number}.` {v.PrettyFullName}")); .Select(v => $"`{++number}.` {v.PrettyFullName}"));
if (currentSong != null) desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc;
desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc;
if (musicPlayer.RepeatSong) if (musicPlayer.RepeatSong)
desc = "🔂 Repeating Current Song\n\n" + desc; desc = "🔂 " + GetText("repeating_cur_song") +"\n\n" + desc;
else if (musicPlayer.RepeatPlaylist) else if (musicPlayer.RepeatPlaylist)
desc = "🔁 Repeating Playlist\n\n" + desc; desc = "🔁 " + GetText("repeating_playlist")+"\n\n" + desc;
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}") .WithAuthor(eab => eab.WithName(GetText("player_queue", curPage, lastPage + 1))
.WithMusicIcon()) .WithMusicIcon())
.WithDescription(desc) .WithDescription(desc)
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " + .WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " + $"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
(musicPlayer.FairPlay ? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit"))) (musicPlayer.FairPlay
? "✔️" + GetText("fairplay")
: "✖️" + GetText("fairplay")) + " | " +
(maxPlaytime == 0 ? "unlimited" : GetText("play_limit", maxPlaytime))))
.WithOkColor(); .WithOkColor();
return embed; return embed;
@ -253,7 +261,7 @@ namespace NadekoBot.Modules.Music
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
var embed = new EmbedBuilder().WithOkColor() var embed = new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon()) .WithAuthor(eab => eab.WithName(GetText("now_playing")).WithMusicIcon())
.WithDescription(currentSong.PrettyName) .WithDescription(currentSong.PrettyName)
.WithThumbnailUrl(currentSong.Thumbnail) .WithThumbnailUrl(currentSong.Thumbnail)
.WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}")); .WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
@ -270,21 +278,22 @@ namespace NadekoBot.Modules.Music
return; return;
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return; return;
if (val < 0) if (val < 0 || val > 100)
{
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
return; return;
}
var volume = musicPlayer.SetVolume(val); 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Defvol([Remainder] int val) public async Task Defvol([Remainder] int val)
{ {
if (val < 0 || val > 100) 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; return;
} }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
@ -292,7 +301,7 @@ namespace NadekoBot.Modules.Music
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f; uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
uow.Complete(); uow.Complete();
} }
await Context.Channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false); await ReplyConfirmLocalized("defvol_set", val).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -306,13 +315,10 @@ namespace NadekoBot.Modules.Music
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel) if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return; return;
if (musicPlayer.Playlist.Count < 2) if (musicPlayer.Playlist.Count < 2)
{
await Context.Channel.SendErrorAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false);
return; return;
}
musicPlayer.Shuffle(); musicPlayer.Shuffle();
await Context.Channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false); await ReplyConfirmLocalized("songs_shuffled").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -325,29 +331,31 @@ namespace NadekoBot.Modules.Music
return; return;
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild) 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; return;
} }
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault(); var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
if (plId == null) if (plId == null)
{ {
await Context.Channel.SendErrorAsync("No search results for that query."); await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
return; return;
} }
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false); var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
if (!ids.Any()) if (!ids.Any())
{ {
await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false); await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
return; return;
} }
var count = ids.Count(); 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 cancelSource = new CancellationTokenSource();
var gusr = (IGuildUser)Context.User; var gusr = (IGuildUser)Context.User;
//todo use grouping
while (ids.Any() && !cancelSource.IsCancellationRequested) while (ids.Any() && !cancelSource.IsCancellationRequested)
{ {
var tasks = Task.WhenAll(ids.Take(5).Select(async id => var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
@ -366,7 +374,7 @@ namespace NadekoBot.Modules.Music
ids = ids.Skip(5); 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] [NadekoCommand, Usage, Description, Aliases]
@ -392,7 +400,7 @@ namespace NadekoBot.Modules.Music
{ {
try try
{ {
mp.AddSong(new Song(new Classes.SongInfo mp.AddSong(new Song(new SongInfo
{ {
Title = svideo.FullName, Title = svideo.FullName,
Provider = "SoundCloud", Provider = "SoundCloud",
@ -434,20 +442,20 @@ namespace NadekoBot.Modules.Music
// ignored // ignored
} }
} }
await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false); await ReplyConfirmLocalized("dir_queue_complete").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Radio(string radio_link) public async Task Radio(string radioLink)
{ {
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild) 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; 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) if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
{ {
Context.Message.DeleteAfter(10); Context.Message.DeleteAfter(10);
@ -504,20 +512,20 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return; if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return;
musicPlayer.ClearQueue(); musicPlayer.ClearQueue();
await Context.Channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false); await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
return;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task MoveSong([Remainder] string fromto) public async Task MoveSong([Remainder] string fromto)
{ {
if (string.IsNullOrWhiteSpace(fromto))
return;
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
{
return; return;
}
fromto = fromto?.Trim(); fromto = fromto?.Trim();
var fromtoArr = fromto.Split('>'); var fromtoArr = fromto.Split('>');
@ -530,7 +538,7 @@ namespace NadekoBot.Modules.Music
!int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 || !int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 ||
n1 > playlist.Count || n2 > playlist.Count) n1 > playlist.Count || n2 > playlist.Count)
{ {
await Context.Channel.SendErrorAsync("Invalid input.").ConfigureAwait(false); await ReplyConfirmLocalized("invalid_input").ConfigureAwait(false);
return; return;
} }
@ -541,10 +549,10 @@ namespace NadekoBot.Modules.Music
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithTitle($"{s.SongInfo.Title.TrimTo(70)}") .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")) .WithAuthor(eab => eab.WithName(GetText("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(GetText("from_position")).WithValue($"#{n1}").WithIsInline(true))
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("to_position")).WithValue($"#{n2}").WithIsInline(true))
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -562,7 +570,11 @@ namespace NadekoBot.Modules.Music
return; return;
musicPlayer.MaxQueueSize = size; 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] [NadekoCommand, Usage, Description, Aliases]
@ -578,9 +590,9 @@ namespace NadekoBot.Modules.Music
return; return;
musicPlayer.MaxPlaytimeSeconds = seconds; musicPlayer.MaxPlaytimeSeconds = seconds;
if (seconds == 0) if (seconds == 0)
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now."); await ReplyConfirmLocalized("max_playtime_none").ConfigureAwait(false);
else else
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds."); await ReplyConfirmLocalized("max_playtime_set", seconds).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -599,11 +611,11 @@ namespace NadekoBot.Modules.Music
if (currentValue) if (currentValue)
await Context.Channel.EmbedAsync(new EmbedBuilder() await Context.Channel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track")) .WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 " + GetText("repeating_track")))
.WithDescription(currentSong.PrettyName) .WithDescription(currentSong.PrettyName)
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false); .WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync($"🔂 Current track repeat stopped.") await Context.Channel.SendConfirmAsync("🔂 " + GetText("repeating_track_stopped"))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -616,7 +628,10 @@ namespace NadekoBot.Modules.Music
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return; return;
var currentValue = musicPlayer.ToggleRepeatPlaylist(); 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] [NadekoCommand, Usage, Description, Aliases]
@ -653,7 +668,10 @@ namespace NadekoBot.Modules.Music
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -668,11 +686,11 @@ namespace NadekoBot.Modules.Music
if (mpl == null) 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; return;
} }
IUserMessage msg = null; 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 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) foreach (var item in mpl.Songs)
{ {
var usr = (IGuildUser)Context.User; var usr = (IGuildUser)Context.User;
@ -684,15 +702,13 @@ namespace NadekoBot.Modules.Music
catch { break; } catch { break; }
} }
if (msg != null) 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Playlists([Remainder] int num = 1) public async Task Playlists([Remainder] int num = 1)
{ {
if (num <= 0) if (num <= 0)
return; return;
@ -704,8 +720,9 @@ namespace NadekoBot.Modules.Music
} }
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon()) .WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon())
.WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by *{r.Author}* ({r.Songs.Count} songs)"))) .WithDescription(string.Join("\n", playlists.Select(r =>
GetText("playlists", "#" + r.Id, r.Name, r.Author, r.Songs.Count))))
.WithOkColor(); .WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -715,13 +732,12 @@ namespace NadekoBot.Modules.Music
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task DeletePlaylist([Remainder] int id) public async Task DeletePlaylist([Remainder] int id)
{ {
bool success = false; var success = false;
MusicPlaylist pl = null;
try try
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
pl = uow.MusicPlaylists.Get(id); var pl = uow.MusicPlaylists.Get(id);
if (pl != null) if (pl != null)
{ {
@ -731,15 +747,13 @@ namespace NadekoBot.Modules.Music
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
success = true; success = true;
} }
else
success = false;
} }
} }
if (!success) 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 else
await Context.Channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false); await ReplyConfirmLocalized("playlist_deleted").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -779,7 +793,7 @@ namespace NadekoBot.Modules.Music
if (seconds.Length == 1) if (seconds.Length == 1)
seconds = "0" + seconds; 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] [NadekoCommand, Usage, Description, Aliases]
@ -791,9 +805,9 @@ namespace NadekoBot.Modules.Music
return; return;
if (!musicPlayer.ToggleAutoplay()) if (!musicPlayer.ToggleAutoplay())
await Context.Channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false); await ReplyConfirmLocalized("autoplay_disabled").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false); await ReplyConfirmLocalized("autoplay_enabled").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -804,29 +818,29 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer; MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out 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("no_player").ConfigureAwait(false);
return; return;
} }
musicPlayer.OutputTextChannel = (ITextChannel)Context.Channel; 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 (voiceCh == null || voiceCh.Guild != textCh.Guild)
{ {
if (!silent) 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)); throw new ArgumentNullException(nameof(voiceCh));
} }
if (string.IsNullOrWhiteSpace(query) || query.Length < 3) 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 => 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()) using (var uow = DbHandler.UnitOfWork())
{ {
vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume; vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
@ -843,7 +857,7 @@ namespace NadekoBot.Modules.Music
try try
{ {
lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() 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) .WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))) .WithFooter(ef => ef.WithText(song.PrettyInfo)))
.ConfigureAwait(false); .ConfigureAwait(false);
@ -861,17 +875,23 @@ namespace NadekoBot.Modules.Music
textCh, textCh,
voiceCh, voiceCh,
relatedVideos[new NadekoRandom().Next(0, relatedVideos.Count)], relatedVideos[new NadekoRandom().Next(0, relatedVideos.Count)],
true, true).ConfigureAwait(false);
musicType).ConfigureAwait(false);
} }
} }
catch { } catch
{
// ignored
}
}; };
mp.OnStarted += async (player, song) => mp.OnStarted += async (player, song) =>
{ {
try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { } try { await mp.UpdateSongDurationsAsync().ConfigureAwait(false); }
var sender = player as MusicPlayer; catch
{
// ignored
}
var sender = player;
if (sender == null) if (sender == null)
return; return;
try try
@ -879,12 +899,15 @@ namespace NadekoBot.Modules.Music
playingMessage?.DeleteAfter(0); playingMessage?.DeleteAfter(0);
playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor() 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) .WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))) .WithFooter(ef => ef.WithText(song.PrettyInfo)))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
}; };
mp.OnPauseChanged += async (paused) => mp.OnPauseChanged += async (paused) =>
{ {
@ -892,13 +915,16 @@ namespace NadekoBot.Modules.Music
{ {
IUserMessage msg; IUserMessage msg;
if (paused) if (paused)
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false); msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("paused")).ConfigureAwait(false);
else else
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false); msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("resumed")).ConfigureAwait(false);
msg?.DeleteAfter(10); msg?.DeleteAfter(10);
} }
catch { } catch
{
// ignored
}
}; };
mp.SongRemoved += async (song, index) => mp.SongRemoved += async (song, index) =>
@ -906,7 +932,7 @@ namespace NadekoBot.Modules.Music
try try
{ {
var embed = new EmbedBuilder() 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) .WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo)) .WithFooter(ef => ef.WithText(song.PrettyInfo))
.WithErrorColor(); .WithErrorColor();
@ -914,7 +940,10 @@ namespace NadekoBot.Modules.Music
await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false); await mp.OutputTextChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
}; };
return mp; return mp;
}); });
@ -931,7 +960,14 @@ namespace NadekoBot.Modules.Music
} }
catch (PlaylistFullException) 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; throw;
} }
if (!silent) if (!silent)
@ -940,15 +976,17 @@ namespace NadekoBot.Modules.Music
{ {
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); //var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor() var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon()) .WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
.WithDescription($"{resolvedSong.PrettyName}\nQueue ") .WithDescription($"{resolvedSong.PrettyName}\n{GetText("queue")} ")
.WithThumbnailUrl(resolvedSong.Thumbnail) .WithThumbnailUrl(resolvedSong.Thumbnail)
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider))) .WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider)))
.ConfigureAwait(false); .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
} }
} }
} }

View File

@ -7,8 +7,6 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Services; using NadekoBot.Services;
using System.Net.Http; using System.Net.Http;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Xml; using System.Xml;
using System.Threading; using System.Threading;
@ -17,11 +15,11 @@ using System.Collections.Concurrent;
namespace NadekoBot.Modules.NSFW namespace NadekoBot.Modules.NSFW
{ {
[NadekoModule("NSFW", "~")] [NadekoModule("NSFW", "~")]
public class NSFW : NadekoModule public class NSFW : NadekoTopLevelModule
{ {
private static readonly ConcurrentDictionary<ulong, Timer> AutoHentaiTimers = new ConcurrentDictionary<ulong, Timer>(); private static readonly ConcurrentDictionary<ulong, Timer> _autoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
private static readonly ConcurrentHashSet<ulong> HentaiBombBlacklist = new ConcurrentHashSet<ulong>(); private static readonly ConcurrentHashSet<ulong> _hentaiBombBlacklist = new ConcurrentHashSet<ulong>();
private async Task InternalHentai(IMessageChannel channel, string tag, bool noError) private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
{ {
@ -72,7 +70,7 @@ namespace NadekoBot.Modules.NSFW
if (interval == 0) 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 t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false); await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false);
@ -99,7 +97,7 @@ namespace NadekoBot.Modules.NSFW
} }
}, null, interval * 1000, interval * 1000); }, 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); old.Change(Timeout.Infinite, Timeout.Infinite);
return t; return t;
@ -114,7 +112,7 @@ namespace NadekoBot.Modules.NSFW
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task HentaiBomb([Remainder] string tag = null) public async Task HentaiBomb([Remainder] string tag = null)
{ {
if (!HentaiBombBlacklist.Add(Context.User.Id)) if (!_hentaiBombBlacklist.Add(Context.User.Id))
return; return;
try try
{ {
@ -138,17 +136,17 @@ namespace NadekoBot.Modules.NSFW
finally finally
{ {
await Task.Delay(5000).ConfigureAwait(false); await Task.Delay(5000).ConfigureAwait(false);
HentaiBombBlacklist.TryRemove(Context.User.Id); _hentaiBombBlacklist.TryRemove(Context.User.Id);
} }
} }
#endif #endif
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public Task Yandere([Remainder] string tag = null) 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] [NadekoCommand, Usage, Description, Aliases]
public Task Konachan([Remainder] string tag = null) 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] [NadekoCommand, Usage, Description, Aliases]
public async Task E621([Remainder] string tag = null) public async Task E621([Remainder] string tag = null)
@ -169,7 +167,7 @@ namespace NadekoBot.Modules.NSFW
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public Task Rule34([Remainder] string tag = null) 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] [NadekoCommand, Usage, Description, Aliases]
public async Task Danbooru([Remainder] string tag = null) public async Task Danbooru([Remainder] string tag = null)
@ -212,7 +210,7 @@ namespace NadekoBot.Modules.NSFW
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public Task Gelbooru([Remainder] string tag = null) 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] [NadekoCommand, Usage, Description, Aliases]
public async Task Cp() public async Task Cp()
@ -289,5 +287,22 @@ namespace NadekoBot.Modules.NSFW
public static Task<string> GetGelbooruImageLink(string tag) => public static Task<string> GetGelbooruImageLink(string tag) =>
Searches.Searches.InternalDapiSearch(tag, Searches.Searches.DapiSearchType.Gelbooru); 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);
}
} }
} }

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules namespace NadekoBot.Modules
{ {
public abstract class NadekoModule : ModuleBase public abstract class NadekoTopLevelModule : ModuleBase
{ {
protected readonly Logger _log; protected readonly Logger _log;
protected CultureInfo _cultureInfo; protected CultureInfo _cultureInfo;
@ -17,7 +17,7 @@ namespace NadekoBot.Modules
public readonly string ModuleTypeName; public readonly string ModuleTypeName;
public readonly string LowerModuleTypeName; public readonly string LowerModuleTypeName;
protected NadekoModule(bool isTopLevelModule = true) protected NadekoTopLevelModule(bool isTopLevelModule = true)
{ {
//if it's top level module //if it's top level module
ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name; ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name;
@ -69,7 +69,7 @@ namespace NadekoBot.Modules
LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS."); 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!"; text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!";
if (string.IsNullOrWhiteSpace(text)) 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."; lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this.";
} }
return text; return text;
@ -120,7 +120,7 @@ namespace NadekoBot.Modules
} }
} }
public abstract class NadekoSubmodule : NadekoModule public abstract class NadekoSubmodule : NadekoTopLevelModule
{ {
protected NadekoSubmodule() : base(false) protected NadekoSubmodule() : base(false)
{ {

View File

@ -21,11 +21,11 @@ namespace NadekoBot.Modules.Permissions
} }
[Group] [Group]
public class BlacklistCommands : ModuleBase public class BlacklistCommands : NadekoSubmodule
{ {
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } = new ConcurrentHashSet<ulong>(); public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; }
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } = new ConcurrentHashSet<ulong>(); public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; }
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } = new ConcurrentHashSet<ulong>(); public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; }
static BlacklistCommands() static BlacklistCommands()
{ {
@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Permissions
} }
break; break;
case BlacklistType.Channel: 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); Games.Games.TriviaCommands.RunningTrivias.TryRemove(item.Key, out tg);
if (tg != null) if (tg != null)
{ {
@ -124,16 +124,14 @@ namespace NadekoBot.Modules.Permissions
break; break;
case BlacklistType.User: case BlacklistType.User:
break; break;
default:
break;
} }
} }
if(action == AddRemove.Add) 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 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);
} }
} }
} }

View File

@ -21,15 +21,15 @@ namespace NadekoBot.Modules.Permissions
} }
[Group] [Group]
public class CmdCdsCommands : ModuleBase public class CmdCdsCommands : NadekoSubmodule
{ {
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> commandCooldowns { get; } public static ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>> CommandCooldowns { get; }
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> activeCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>(); private static ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>> activeCooldowns { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ActiveCooldown>>();
static CmdCdsCommands() static CmdCdsCommands()
{ {
var configs = NadekoBot.AllGuildConfigs; var configs = NadekoBot.AllGuildConfigs;
commandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns))); CommandCooldowns = new ConcurrentDictionary<ulong, ConcurrentHashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => new ConcurrentHashSet<CommandCooldown>(v.CommandCooldowns)));
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -38,14 +38,14 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
if (secs < 0 || secs > 3600) 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; return;
} }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.CommandCooldowns)); var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.CommandCooldowns));
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>()); var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant()); config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
localSet.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<ActiveCooldown>()); var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant()); 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.") await ReplyConfirmLocalized("cmdcd_cleared",
.ConfigureAwait(false); Format.Bold(command.Aliases.First())).ConfigureAwait(false);
} }
else else
{ {
await channel.SendConfirmAsync($"✅ Command **{command.Aliases.First()}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.") await ReplyConfirmLocalized("cmdcd_add",
.ConfigureAwait(false); Format.Bold(command.Aliases.First()),
Format.Bold(secs.ToString())).ConfigureAwait(false);
} }
} }
@ -80,19 +81,19 @@ namespace NadekoBot.Modules.Permissions
public async Task AllCmdCooldowns() public async Task AllCmdCooldowns()
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>()); var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
if (!localSet.Any()) if (!localSet.Any())
await channel.SendConfirmAsync(" `No command cooldowns set.`").ConfigureAwait(false); await ReplyConfirmLocalized("cmdcd_none").ConfigureAwait(false);
else 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) public static bool HasCooldown(CommandInfo cmd, IGuild guild, IUser user)
{ {
if (guild == null) if (guild == null)
return false; return false;
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>()); var cmdcds = CmdCdsCommands.CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule; CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Aliases.First().ToLowerInvariant())) != null) if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Aliases.First().ToLowerInvariant())) != null)
{ {

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Permissions
public partial class Permissions public partial class Permissions
{ {
[Group] [Group]
public class CommandCostCommands : ModuleBase public class CommandCostCommands : NadekoSubmodule
{ {
private static readonly ConcurrentDictionary<string, int> _commandCosts = new ConcurrentDictionary<string, int>(); private static readonly ConcurrentDictionary<string, int> _commandCosts = new ConcurrentDictionary<string, int>();
public static IReadOnlyDictionary<string, int> CommandCosts => _commandCosts; public static IReadOnlyDictionary<string, int> CommandCosts => _commandCosts;
@ -29,29 +29,29 @@ namespace NadekoBot.Modules.Permissions
// x => x.Cost)); // x => x.Cost));
} }
[NadekoCommand, Usage, Description, Aliases] //[NadekoCommand, Usage, Description, Aliases]
public async Task CmdCosts(int page = 1) //public async Task CmdCosts(int page = 1)
{ //{
var prices = _commandCosts.ToList(); // var prices = _commandCosts.ToList();
if (!prices.Any()) // if (!prices.Any())
{ // {
await Context.Channel.SendConfirmAsync("No costs set.").ConfigureAwait(false); // await Context.Channel.SendConfirmAsync(GetText("no_costs")).ConfigureAwait(false);
return; // return;
} // }
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => { // await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => {
var embed = new EmbedBuilder().WithOkColor() // var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Command Costs"); // .WithTitle(GetText("command_costs"));
var current = prices.Skip((curPage - 1) * 9) // var current = prices.Skip((curPage - 1) * 9)
.Take(9); // .Take(9);
foreach (var price in current) // foreach (var price in current)
{ // {
embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true)); // embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true));
} // }
return embed; // return embed;
}, prices.Count / 9).ConfigureAwait(false); // }, prices.Count / 9).ConfigureAwait(false);
} //}
//[NadekoCommand, Usage, Description, Aliases] //[NadekoCommand, Usage, Description, Aliases]
//public async Task CommandCost(int cost, CommandInfo cmd) //public async Task CommandCost(int cost, CommandInfo cmd)

View File

@ -13,13 +13,13 @@ namespace NadekoBot.Modules.Permissions
public partial class Permissions public partial class Permissions
{ {
[Group] [Group]
public class FilterCommands : ModuleBase public class FilterCommands : NadekoSubmodule
{ {
public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; } public static ConcurrentHashSet<ulong> InviteFilteringChannels { get; }
public static ConcurrentHashSet<ulong> InviteFilteringServers { get; } public static ConcurrentHashSet<ulong> InviteFilteringServers { get; }
//serverid, filteredwords //serverid, filteredwords
private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> ServerFilteredWords { get; } private static ConcurrentDictionary<ulong, ConcurrentHashSet<string>> serverFilteredWords { get; }
public static ConcurrentHashSet<ulong> WordFilteringChannels { get; } public static ConcurrentHashSet<ulong> WordFilteringChannels { get; }
public static ConcurrentHashSet<ulong> WordFilteringServers { get; } public static ConcurrentHashSet<ulong> WordFilteringServers { get; }
@ -28,7 +28,7 @@ namespace NadekoBot.Modules.Permissions
{ {
ConcurrentHashSet<string> words = new ConcurrentHashSet<string>(); ConcurrentHashSet<string> words = new ConcurrentHashSet<string>();
if(WordFilteringChannels.Contains(channelId)) if(WordFilteringChannels.Contains(channelId))
ServerFilteredWords.TryGetValue(guildId, out words); serverFilteredWords.TryGetValue(guildId, out words);
return words; return words;
} }
@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Permissions
{ {
var words = new ConcurrentHashSet<string>(); var words = new ConcurrentHashSet<string>();
if(WordFilteringServers.Contains(guildId)) if(WordFilteringServers.Contains(guildId))
ServerFilteredWords.TryGetValue(guildId, out words); serverFilteredWords.TryGetValue(guildId, out words);
return words; return words;
} }
@ -49,7 +49,7 @@ namespace NadekoBot.Modules.Permissions
var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word))); var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict); serverFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords); var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId)); WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
@ -74,12 +74,12 @@ namespace NadekoBot.Modules.Permissions
if (enabled) if (enabled)
{ {
InviteFilteringServers.Add(channel.Guild.Id); 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 else
{ {
InviteFilteringServers.TryRemove(channel.Guild.Id); 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) if (removed == 0)
{ {
InviteFilteringChannels.Add(channel.Id); InviteFilteringChannels.Add(channel.Id);
await channel.SendConfirmAsync("Invite filtering enabled on this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("invite_filter_channel_on").ConfigureAwait(false);
} }
else else
{ {
InviteFilteringChannels.TryRemove(channel.Id); await ReplyConfirmLocalized("invite_filter_channel_off").ConfigureAwait(false);
await channel.SendConfirmAsync("Invite filtering disabled on this channel.").ConfigureAwait(false);
} }
} }
@ -133,12 +132,12 @@ namespace NadekoBot.Modules.Permissions
if (enabled) if (enabled)
{ {
WordFilteringServers.Add(channel.Guild.Id); 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 else
{ {
WordFilteringServers.TryRemove(channel.Guild.Id); 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) if (removed == 0)
{ {
WordFilteringChannels.Add(channel.Id); WordFilteringChannels.Add(channel.Id);
await channel.SendConfirmAsync("Word filtering enabled on this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("word_filter_channel_on").ConfigureAwait(false);
} }
else else
{ {
WordFilteringChannels.TryRemove(channel.Id); 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); await uow.CompleteAsync().ConfigureAwait(false);
} }
var filteredWords = ServerFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<string>()); var filteredWords = serverFilteredWords.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<string>());
if (removed == 0) if (removed == 0)
{ {
filteredWords.Add(word); filteredWords.Add(word);
await channel.SendConfirmAsync($"Word `{word}` successfully added to the list of filtered words.") await ReplyConfirmLocalized("filter_word_add", Format.Code(word)).ConfigureAwait(false);
.ConfigureAwait(false);
} }
else else
{ {
filteredWords.TryRemove(word); filteredWords.TryRemove(word);
await channel.SendConfirmAsync($"Word `{word}` removed from the list of filtered words.") await ReplyConfirmLocalized("filter_word_remove", Format.Code(word)).ConfigureAwait(false);
.ConfigureAwait(false);
} }
} }
@ -222,9 +219,9 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
ConcurrentHashSet<string> filteredWords; ConcurrentHashSet<string> 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); .ConfigureAwait(false);
} }
} }

View File

@ -15,7 +15,7 @@ using NLog;
namespace NadekoBot.Modules.Permissions namespace NadekoBot.Modules.Permissions
{ {
[NadekoModule("Permissions", ";")] [NadekoModule("Permissions", ";")]
public partial class Permissions : NadekoModule public partial class Permissions : NadekoTopLevelModule
{ {
public class PermissionCache public class PermissionCache
{ {
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Permissions
static Permissions() static Permissions()
{ {
var _log = LogManager.GetCurrentClassLogger(); var log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Permissions
} }
sw.Stop(); sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -65,8 +65,14 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.Verbose = config.VerbosePermissions; return old; }); }, (id, old) => { old.Verbose = config.VerbosePermissions; return old; });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
if (action.Value)
await Context.Channel.SendConfirmAsync(" I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false); {
await ReplyConfirmLocalized("verbose_true").ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("verbose_false").ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -81,22 +87,20 @@ namespace NadekoBot.Modules.Permissions
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
if (role == null) 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; return;
} }
else { config.PermissionRole = role.Name.Trim();
config.PermissionRole = role.Name.Trim(); Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() {
{ PermRole = config.PermissionRole,
PermRole = config.PermissionRole, RootPermission = Permission.GetDefaultRoot(),
RootPermission = Permission.GetDefaultRoot(), Verbose = config.VerbosePermissions
Verbose = config.VerbosePermissions }, (id, old) => { old.PermRole = role.Name.Trim(); return old; });
}, (id, old) => { old.PermRole = role.Name.Trim(); return old; }); await uow.CompleteAsync().ConfigureAwait(false);
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] [NadekoCommand, Usage, Description, Aliases]
@ -105,12 +109,18 @@ namespace NadekoBot.Modules.Permissions
{ {
if (page < 1 || page > 4) if (page < 1 || page > 4)
return; return;
string toSend = ""; string toSend;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission; var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission;
var i = 1 + 20 * (page - 1); 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); await Context.Channel.SendMessageAsync(toSend).ConfigureAwait(false);
@ -132,7 +142,7 @@ namespace NadekoBot.Modules.Permissions
{ {
return; return;
} }
else if (index == 0) if (index == 0)
{ {
p = perms; p = perms;
config.RootPermission = perms.Next; config.RootPermission = perms.Next;
@ -155,12 +165,13 @@ namespace NadekoBot.Modules.Permissions
uow2._context.Remove<Permission>(p); uow2._context.Remove<Permission>(p);
uow2._context.SaveChanges(); uow2._context.SaveChanges();
} }
await ReplyConfirmLocalized("removed",
await Context.Channel.SendConfirmAsync($"✅ {Context.User.Mention} removed permission **{p.GetCommand((SocketGuild)Context.Guild)}** from position #{index + 1}.").ConfigureAwait(false); 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 config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id);
var perms = config.RootPermission; var perms = config.RootPermission;
var root = perms;
var index = 0; var index = 0;
var fromFound = false; var fromFound = false;
var toFound = false; var toFound = false;
@ -207,13 +217,13 @@ namespace NadekoBot.Modules.Permissions
{ {
if (!fromFound) if (!fromFound)
{ {
await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++from}`").ConfigureAwait(false); await ReplyErrorLocalized("not_found", ++from).ConfigureAwait(false);
return; return;
} }
if (!toFound) if (!toFound)
{ {
await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++to}`").ConfigureAwait(false); await ReplyErrorLocalized("not_found", ++to).ConfigureAwait(false);
return; return;
} }
} }
@ -230,7 +240,6 @@ namespace NadekoBot.Modules.Permissions
next.Previous = pre; next.Previous = pre;
if (from == 0) if (from == 0)
{ {
root = next;
} }
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
//Inserting //Inserting
@ -263,14 +272,18 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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; return;
} }
catch (Exception e) when (e is ArgumentOutOfRangeException || e is IndexOutOfRangeException) 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] [NadekoCommand, Usage, Description, Aliases]
@ -297,7 +310,19 @@ namespace NadekoBot.Modules.Permissions
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -323,7 +348,19 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -349,7 +386,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -375,7 +426,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -404,7 +469,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -433,39 +512,62 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ChnlCmd(CommandInfo command, PermissionAction action, [Remainder] ITextChannel chnl) 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,
PrimaryTarget = PrimaryPermissionType.Channel, SecondaryTarget = SecondaryPermissionType.Command,
PrimaryTargetId = chnl.Id, SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
SecondaryTarget = SecondaryPermissionType.Command, State = action.Value,
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(), };
State = action.Value, var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
}; Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm); {
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache() PermRole = config.PermissionRole,
{ RootPermission = config.RootPermission,
PermRole = config.PermissionRole, Verbose = config.VerbosePermissions
RootPermission = config.RootPermission, }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
Verbose = config.VerbosePermissions await uow.CompleteAsync().ConfigureAwait(false);
}, (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] [NadekoCommand, Usage, Description, Aliases]
@ -491,7 +593,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -517,7 +633,17 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -546,7 +672,17 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -572,7 +708,17 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -609,7 +755,15 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; }); }, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false); 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);
}
} }
} }
} }

View File

@ -16,7 +16,7 @@ using System.Collections.Concurrent;
namespace NadekoBot.Modules.Pokemon namespace NadekoBot.Modules.Pokemon
{ {
[NadekoModule("Pokemon", ">")] [NadekoModule("Pokemon", ">")]
public class Pokemon : NadekoModule public class Pokemon : NadekoTopLevelModule
{ {
private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>(); private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>();
private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>(); private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>();

View File

@ -1,6 +1,5 @@
using AngleSharp; using AngleSharp;
using AngleSharp.Dom.Html; using AngleSharp.Dom.Html;
using AngleSharp.Extensions;
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
@ -8,7 +7,6 @@ using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models; using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@ -21,37 +19,37 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [Group]
public class AnimeSearchCommands : ModuleBase public class AnimeSearchCommands : NadekoSubmodule
{ {
private static Timer anilistTokenRefresher { get; } private static readonly Timer anilistTokenRefresher;
private static Logger _log { get; }
private static string anilistToken { get; set; } private static string anilistToken { get; set; }
static AnimeSearchCommands() static AnimeSearchCommands()
{ {
_log = LogManager.GetCurrentClassLogger();
anilistTokenRefresher = new Timer(async (state) => anilistTokenRefresher = new Timer(async (state) =>
{ {
try try
{ {
var headers = new Dictionary<string, string> { var headers = new Dictionary<string, string>
{"grant_type", "client_credentials"}, {
{"client_id", "kwoth-w0ki9"}, {"grant_type", "client_credentials"},
{"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"}, {"client_id", "kwoth-w0ki9"},
}; {"client_secret", "Qd6j4FIAi1ZK6Pc7N7V4Z"},
};
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.AddFakeHeaders(); //http.AddFakeHeaders();
http.DefaultRequestHeaders.Clear();
var formContent = new FormUrlEncodedContent(headers); 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); var stringContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
anilistToken = JObject.Parse(stringContent)["access_token"].ToString(); anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
} }
} }
catch (Exception ex) catch
{ {
_log.Error(ex); // ignored
} }
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29)); }, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
} }
@ -75,7 +73,7 @@ namespace NadekoBot.Modules.Searches
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc"); 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) if (favorites[0].QuerySelector("p") == null)
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a") favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
.Shuffle() .Shuffle()
@ -106,14 +104,14 @@ namespace NadekoBot.Modules.Searches
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle($"{name}'s MAL profile") .WithTitle(GetText("mal_profile", name))
.AddField(efb => efb.WithName("💚 Watching").WithValue(stats[0]).WithIsInline(true)) .AddField(efb => efb.WithName("💚 " + GetText("watching")).WithValue(stats[0]).WithIsInline(true))
.AddField(efb => efb.WithName("💙 Completed").WithValue(stats[1]).WithIsInline(true)); .AddField(efb => efb.WithName("💙 " + GetText("completed")).WithValue(stats[1]).WithIsInline(true));
if (info.Count < 3) 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 embed
.AddField(efb => efb.WithName("💔 Dropped").WithValue(stats[3]).WithIsInline(true)) .AddField(efb => efb.WithName("💔 " + GetText("dropped")).WithValue(stats[3]).WithIsInline(true))
.AddField(efb => efb.WithName("⚪ Plan to watch").WithValue(stats[4]).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[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("📊 " + 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)) .AddField(efb => efb.WithName(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1).WithValue(info[0].Item2.TrimTo(20)).WithIsInline(true))
@ -126,7 +124,7 @@ namespace NadekoBot.Modules.Searches
.WithDescription($@" .WithDescription($@"
** https://myanimelist.net/animelist/{ name } ** ** https://myanimelist.net/animelist/{ name } **
**Top 3 Favorite Anime:** **{GetText("top_3_fav_anime")}**
{favAnime}" {favAnime}"
//**[Manga List](https://myanimelist.net/mangalist/{name})** //**[Manga List](https://myanimelist.net/mangalist/{name})**
@ -176,7 +174,7 @@ namespace NadekoBot.Modules.Searches
if (animeData == null) if (animeData == null)
{ {
await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false); await ReplyErrorLocalized("failed_finding_anime").ConfigureAwait(false);
return; return;
} }
@ -185,10 +183,10 @@ namespace NadekoBot.Modules.Searches
.WithTitle(animeData.title_english) .WithTitle(animeData.title_english)
.WithUrl(animeData.Link) .WithUrl(animeData.Link)
.WithImageUrl(animeData.image_url_lge) .WithImageUrl(animeData.image_url_lge)
.AddField(efb => efb.WithName("Episodes").WithValue(animeData.total_episodes.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("episodes")).WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Status").WithValue(animeData.AiringStatus.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("status")).WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", animeData.Genres)).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", animeData.Genres)).WithIsInline(true))
.WithFooter(efb => efb.WithText("Score: " + animeData.average_score + " / 100")); .WithFooter(efb => efb.WithText(GetText("score") + " " + animeData.average_score + " / 100"));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -203,7 +201,7 @@ namespace NadekoBot.Modules.Searches
if (mangaData == null) if (mangaData == null)
{ {
await Context.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false); await ReplyErrorLocalized("failed_finding_manga").ConfigureAwait(false);
return; return;
} }
@ -212,10 +210,10 @@ namespace NadekoBot.Modules.Searches
.WithTitle(mangaData.title_english) .WithTitle(mangaData.title_english)
.WithUrl(mangaData.Link) .WithUrl(mangaData.Link)
.WithImageUrl(mangaData.image_url_lge) .WithImageUrl(mangaData.image_url_lge)
.AddField(efb => efb.WithName("Episodes").WithValue(mangaData.total_chapters.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("chapters")).WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Status").WithValue(mangaData.publishing_status.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("status")).WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", mangaData.Genres)).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", mangaData.Genres)).WithIsInline(true))
.WithFooter(efb => efb.WithText("Score: " + mangaData.average_score + " / 100")); .WithFooter(efb => efb.WithText(GetText("score") + " " + mangaData.average_score + " / 100"));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq; using System;
using Newtonsoft.Json.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
@ -13,7 +14,7 @@ namespace NadekoBot.Modules.Searches
public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator()); public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator());
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x); public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
private Dictionary<string, string> _languageDictionary; private readonly Dictionary<string, string> _languageDictionary;
static GoogleTranslator() { } static GoogleTranslator() { }
private GoogleTranslator() { private GoogleTranslator() {
@ -153,13 +154,18 @@ namespace NadekoBot.Modules.Searches
public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage) public async Task<string> 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}", 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(sourceLanguage),
ConvertToLanguageCode(targetLanguage), ConvertToLanguageCode(targetLanguage),
WebUtility.UrlEncode(sourceText)); 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"); 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); text = await http.GetStringAsync(url).ConfigureAwait(false);
@ -170,7 +176,7 @@ namespace NadekoBot.Modules.Searches
private string ConvertToLanguageCode(string language) private string ConvertToLanguageCode(string language)
{ {
string mode = string.Empty; string mode;
_languageDictionary.TryGetValue(language, out mode); _languageDictionary.TryGetValue(language, out mode);
return mode; return mode;
} }

View File

@ -7,7 +7,6 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog; using NLog;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -18,11 +17,11 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [Group]
public class JokeCommands : ModuleBase public class JokeCommands : NadekoSubmodule
{ {
private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>(); private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>();
private static List<MagicItem> magicItems { get; } = new List<MagicItem>(); private static List<MagicItem> magicItems { get; } = new List<MagicItem>();
private static Logger _log { get; } private new static readonly Logger _log;
static JokeCommands() static JokeCommands()
{ {
@ -78,7 +77,7 @@ namespace NadekoBot.Modules.Searches
{ {
if (!wowJokes.Any()) if (!wowJokes.Any())
{ {
await Context.Channel.SendErrorAsync("Jokes not loaded.").ConfigureAwait(false); await ReplyErrorLocalized("jokes_not_loaded").ConfigureAwait(false);
return; return;
} }
var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)]; var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)];
@ -90,7 +89,7 @@ namespace NadekoBot.Modules.Searches
{ {
if (!wowJokes.Any()) if (!wowJokes.Any())
{ {
await Context.Channel.SendErrorAsync("MagicItems not loaded.").ConfigureAwait(false); await ReplyErrorLocalized("magicitems_not_loaded").ConfigureAwait(false);
return; return;
} }
var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)]; var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)];

View File

@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Lolban() 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 //http://api.champion.gg/stats/champs/mostBanned?api_key=YOUR_API_TOKEN&page=1&limit=2
try try
{ {
@ -44,19 +44,20 @@ namespace NadekoBot.Modules.Searches
$"limit={showCount}") $"limit={showCount}")
.ConfigureAwait(false))["data"] as JArray; .ConfigureAwait(false))["data"] as JArray;
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList(); var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions")); var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline(GetText("x_most_banned_champs",dataList.Count)));
for (var i = 0; i < dataList.Count; i++) foreach (var champ in dataList)
{ {
var champ = dataList[i]; var champ1 = champ;
eb.AddField(efb => efb.WithName(champ["name"].ToString()).WithValue(champ["general"]["banRate"] + "%").WithIsInline(true)); 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); 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);
} }
} }
} }

View File

@ -1,41 +1,79 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using System.Net.Http; using System.Net.Http;
using System.Text;
using Discord.Commands;
using NadekoBot.Extensions; using NadekoBot.Extensions;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
public partial class Searches public partial class Searches
{ {
[NadekoCommand, Usage, Description, Aliases] [Group]
public async Task Memelist() public class MemegenCommands : NadekoSubmodule
{ {
HttpClientHandler handler = new HttpClientHandler(); private static readonly ImmutableDictionary<char, string> _map = new Dictionary<char, string>()
handler.AllowAutoRedirect = false;
using (var http = new HttpClient(handler))
{ {
var rawJson = await http.GetStringAsync("https://memegen.link/api/templates/").ConfigureAwait(false); {'?', "~q"},
var data = JsonConvert.DeserializeObject<Dictionary<string, string>>(rawJson) {'%', "~p"},
.Select(kvp => Path.GetFileName(kvp.Value)); {'#', "~h"},
{'/', "~s"},
{' ', "-"},
{'-', "--"},
{'_', "__"},
{'"', "''"}
await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false); }.ToImmutableDictionary();
[NadekoCommand, Usage, Description, Aliases]
public async Task Memelist()
{
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<Dictionary<string, string>>(rawJson)
.Select(kvp => Path.GetFileName(kvp.Value));
await Context.Channel.SendTableAsync(data, x => $"{x,-17}", 3).ConfigureAwait(false);
}
}
[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();
} }
} }
[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(' ', '-'));
await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg")
.ConfigureAwait(false);
}
} }
} }

View File

@ -33,22 +33,23 @@ namespace NadekoBot.Modules.Searches.Models
public string[] Evos { get; set; } public string[] Evos { get; set; }
public string[] EggGroups { get; set; } public string[] EggGroups { get; set; }
public override string ToString() => $@"`Name:` {Species} // public override string ToString() => $@"`Name:` {Species}
`Types:` {string.Join(", ", Types)} //`Types:` {string.Join(", ", Types)}
`Stats:` {BaseStats} //`Stats:` {BaseStats}
`Height:` {HeightM,4}m `Weight:` {WeightKg}kg //`Height:` {HeightM,4}m `Weight:` {WeightKg}kg
`Abilities:` {string.Join(", ", Abilities.Values)}"; //`Abilities:` {string.Join(", ", Abilities.Values)}";
} }
public class SearchPokemonAbility public class SearchPokemonAbility
{ {
public string Desc { get; set; } public string Desc { get; set; }
public string ShortDesc { get; set; }
public string Name { get; set; } public string Name { get; set; }
public float Rating { get; set; } public float Rating { get; set; }
public override string ToString() => $@"`Name:` : {Name} // public override string ToString() => $@"`Name:` : {Name}
`Rating:` {Rating} //`Rating:` {Rating}
`Description:` {Desc}"; //`Description:` {Desc}";
} }
} }

View File

@ -3,7 +3,6 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using NLog;
using System; using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -16,21 +15,15 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [Group]
public class OsuCommands : ModuleBase public class OsuCommands : NadekoSubmodule
{ {
private static Logger _log { get; }
static OsuCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Osu(string usr, [Remainder] string mode = null) public async Task Osu(string usr, [Remainder] string mode = null)
{ {
if (string.IsNullOrWhiteSpace(usr)) if (string.IsNullOrWhiteSpace(usr))
return; return;
using (HttpClient http = new HttpClient()) using (var http = new HttpClient())
{ {
try try
{ {
@ -42,15 +35,15 @@ namespace NadekoBot.Modules.Searches
http.AddFakeHeaders(); 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); 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); res.CopyTo(ms);
ms.Position = 0; ms.Position = 0;
await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **Profile Link:** <https://new.ppy.sh/u/{Uri.EscapeDataString(usr)}>\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false); await Context.Channel.SendFileAsync(ms, $"{usr}.png", $"🎧 **{GetText("profile_link")}** <https://new.ppy.sh/u/{Uri.EscapeDataString(usr)}>\n`Image provided by https://lemmmy.pw/osusig`").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync("Failed retrieving osu signature.").ConfigureAwait(false); await ReplyErrorLocalized("osu_failed").ConfigureAwait(false);
_log.Warn(ex, "Osu command failed"); _log.Warn(ex);
} }
} }
} }
@ -60,7 +53,7 @@ namespace NadekoBot.Modules.Searches
{ {
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) 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; return;
} }
@ -75,8 +68,8 @@ namespace NadekoBot.Modules.Searches
var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&{mapId}"; 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 obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0];
var sb = new System.Text.StringBuilder(); var sb = new System.Text.StringBuilder();
var starRating = Math.Round(Double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2); var starRating = Math.Round(double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2);
var time = TimeSpan.FromSeconds(Double.Parse($"{obj["total_length"]}")).ToString(@"mm\:ss"); 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($"{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}"); 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); await Context.Channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
@ -84,8 +77,8 @@ namespace NadekoBot.Modules.Searches
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync("Something went wrong."); await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false);
_log.Warn(ex, "Osub command failed"); _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 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 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 acc = CalculateAcc(item, m);
var mods = ResolveMods(Int32.Parse($"{item["enabled_mods"]}")); var mods = ResolveMods(int.Parse($"{item["enabled_mods"]}"));
if (mods != "+") sb.AppendLine(mods != "+"
sb.AppendLine($"{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} | **{mods,-10}** | /b/{item["beatmap_id"]}"
else : $"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}");
sb.AppendLine($"{pp + "pp",-7} | {acc + "%",-7} | {map["artist"] + "-" + map["title"] + " (" + map["version"] + ")",-40} | /b/{item["beatmap_id"]}");
} }
sb.Append("```"); sb.Append("```");
await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false); await channel.SendMessageAsync(sb.ToString()).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await channel.SendErrorAsync("Something went wrong."); await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false);
_log.Warn(ex, "Osu5 command failed"); _log.Warn(ex);
} }
} }
} }
//https://osu.ppy.sh/wiki/Accuracy //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) if (mode == 0)
{ {
var hitPoints = Double.Parse($"{play["count50"]}") * 50 + Double.Parse($"{play["count100"]}") * 100 + Double.Parse($"{play["count300"]}") * 300; 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 totalHits = double.Parse($"{play["count50"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}") + double.Parse($"{play["countmiss"]}");
totalHits *= 300; totalHits *= 300;
return Math.Round(hitPoints / totalHits * 100, 2); return Math.Round(hitPoints / totalHits * 100, 2);
} }
else if (mode == 1) else if (mode == 1)
{ {
var hitPoints = Double.Parse($"{play["countmiss"]}") * 0 + Double.Parse($"{play["count100"]}") * 0.5 + Double.Parse($"{play["count300"]}") * 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 totalHits = double.Parse($"{play["countmiss"]}") + double.Parse($"{play["count100"]}") + double.Parse($"{play["count300"]}");
hitPoints *= 300; hitPoints *= 300;
totalHits *= 300; totalHits *= 300;
return Math.Round(hitPoints / totalHits * 100, 2); return Math.Round(hitPoints / totalHits * 100, 2);
} }
else if (mode == 2) else if (mode == 2)
{ {
var fruitsCaught = Double.Parse($"{play["count50"]}") + Double.Parse($"{play["count100"]}") + Double.Parse($"{play["count300"]}"); 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 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); return Math.Round(fruitsCaught / totalFruits * 100, 2);
} }
else 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 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 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; totalHits *= 300;
return Math.Round(hitPoints / totalHits * 100, 2); return Math.Round(hitPoints / totalHits * 100, 2);
} }
@ -176,10 +168,10 @@ namespace NadekoBot.Modules.Searches
private static string ResolveMap(string mapLink) private static string ResolveMap(string mapLink)
{ {
Match s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink); var s = new Regex(@"osu.ppy.sh\/s\/", RegexOptions.IgnoreCase).Match(mapLink);
Match b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink); var b = new Regex(@"osu.ppy.sh\/b\/", RegexOptions.IgnoreCase).Match(mapLink);
Match p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink); var p = new Regex(@"osu.ppy.sh\/p\/", RegexOptions.IgnoreCase).Match(mapLink);
Match m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink); var m = new Regex(@"&m=", RegexOptions.IgnoreCase).Match(mapLink);
if (s.Success) if (s.Success)
{ {
var mapId = mapLink.Substring(mapLink.IndexOf("/s/") + 3); var mapId = mapLink.Substring(mapLink.IndexOf("/s/") + 3);

View File

@ -4,7 +4,6 @@ using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models; using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog;
using System.Net.Http; using System.Net.Http;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,13 +13,8 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [Group]
public class OverwatchCommands : ModuleBase public class OverwatchCommands : NadekoSubmodule
{ {
private readonly Logger _log;
public OverwatchCommands()
{
_log = LogManager.GetCurrentClassLogger();
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Overwatch(string region, [Remainder] string query = null) public async Task Overwatch(string region, [Remainder] string query = null)
{ {
@ -34,9 +28,9 @@ namespace NadekoBot.Modules.Searches
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var model = await GetProfile(region, battletag); var model = await GetProfile(region, battletag);
var rankimg = $"{model.Competitive.rank_img}"; var rankimg = model.Competitive.rank_img;
var rank = $"{model.Competitive.rank}"; var rank = model.Competitive.rank;
//var competitiveplay = $"{model.Games.Competitive.played}";
if (string.IsNullOrWhiteSpace(rank)) if (string.IsNullOrWhiteSpace(rank))
{ {
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
@ -44,10 +38,10 @@ namespace NadekoBot.Modules.Searches
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}") .WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}")) .WithIconUrl($"{model.avatar}"))
.WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png") .WithThumbnailUrl("https://cdn.discordapp.com/attachments/155726317222887425/255653487512256512/YZ4w2ey.png")
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("quick_wins")).WithValue($"{model.Games.Quick.wins}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue("0").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue("0").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithOkColor(); .WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -58,22 +52,21 @@ namespace NadekoBot.Modules.Searches
.WithUrl($"https://www.overbuff.com/players/pc/{battletag}") .WithUrl($"https://www.overbuff.com/players/pc/{battletag}")
.WithIconUrl($"{model.avatar}")) .WithIconUrl($"{model.avatar}"))
.WithThumbnailUrl(rankimg) .WithThumbnailUrl(rankimg)
.AddField(fb => fb.WithName("**Level**").WithValue($"{model.level}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("level")).WithValue($"{model.level}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Wins**").WithValue($"{model.Games.Quick.wins}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("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(GetText("compet_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(GetText("compet_losses")).WithValue($"{model.Games.Competitive.lost}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Played**").WithValue($"{model.Games.Competitive.played}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Games.Competitive.played}").WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Rank**").WithValue(rank).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("compet_rank")).WithValue(rank).WithIsInline(true))
.AddField(fb => fb.WithName("**Competitive Playtime**").WithValue($"{model.Playtime.competitive}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("compet_played")).WithValue($"{model.Playtime.competitive}").WithIsInline(true))
.AddField(fb => fb.WithName("**Quick Playtime**").WithValue($"{model.Playtime.quick}").WithIsInline(true)) .AddField(fb => fb.WithName(GetText("quick_playtime")).WithValue($"{model.Playtime.quick}").WithIsInline(true))
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
return;
} }
} }
catch 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<OverwatchApiModel.OverwatchPlayer.Data> GetProfile(string region, string battletag) public async Task<OverwatchApiModel.OverwatchPlayer.Data> GetProfile(string region, string battletag)
@ -82,8 +75,8 @@ namespace NadekoBot.Modules.Searches
{ {
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var Url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile"); var url = await http.GetStringAsync($"https://api.lootbox.eu/pc/{region.ToLower()}/{battletag}/profile");
var model = JsonConvert.DeserializeObject<OverwatchApiModel.OverwatchPlayer>(Url); var model = JsonConvert.DeserializeObject<OverwatchApiModel.OverwatchPlayer>(url);
return model.data; return model.data;
} }
} }

View File

@ -10,10 +10,9 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [Group]
public class PlaceCommands : ModuleBase public class PlaceCommands : NadekoSubmodule
{ {
private static string typesStr { get; } = private static string typesStr { get; } = string.Join(", ", Enum.GetNames(typeof(PlaceType)));
string.Format("`List of \"{0}place\" tags:`\n", NadekoBot.ModulePrefixes[typeof(Searches).Name]) + String.Join(", ", Enum.GetNames(typeof(PlaceType)));
public enum PlaceType public enum PlaceType
{ {
@ -30,14 +29,15 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Placelist() 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); .ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Place(PlaceType placeType, uint width = 0, uint height = 0) public async Task Place(PlaceType placeType, uint width = 0, uint height = 0)
{ {
string url = ""; var url = "";
switch (placeType) switch (placeType)
{ {
case PlaceType.Cage: case PlaceType.Cage:

View File

@ -6,7 +6,6 @@ using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog; using NLog;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -16,7 +15,7 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [Group]
public class PokemonSearchCommands : ModuleBase public class PokemonSearchCommands : NadekoSubmodule
{ {
private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>(); private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>();
private static Dictionary<string, SearchPokemonAbility> pokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>(); private static Dictionary<string, SearchPokemonAbility> pokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>();
@ -24,7 +23,7 @@ namespace NadekoBot.Modules.Searches
public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json"; public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json";
public const string PokemonListFile = "data/pokemon/pokemon_list7.json"; public const string PokemonListFile = "data/pokemon/pokemon_list7.json";
private static Logger _log { get; } private new static readonly Logger _log;
static PokemonSearchCommands() static PokemonSearchCommands()
{ {
@ -57,14 +56,13 @@ namespace NadekoBot.Modules.Searches
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(kvp.Key.ToTitleCase()) .WithTitle(kvp.Key.ToTitleCase())
.WithDescription(p.BaseStats.ToString()) .WithDescription(p.BaseStats.ToString())
.AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("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(GetText("height_weight")).WithValue(GetText("height_weight_val", p.HeightM, p.WeightKg)).WithIsInline(true))
.AddField(efb => efb.WithName("Abilitities").WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("abilities")).WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true)));
);
return; return;
} }
} }
await Context.Channel.SendErrorAsync("No pokemon found."); await ReplyErrorLocalized("pokemon_none").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -79,13 +77,16 @@ namespace NadekoBot.Modules.Searches
{ {
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(kvp.Value.Name) .WithTitle(kvp.Value.Name)
.WithDescription(kvp.Value.Desc) .WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)
.AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true)) ? kvp.Value.ShortDesc
: kvp.Value.Desc)
.AddField(efb => efb.WithName(GetText("rating"))
.WithValue(kvp.Value.Rating.ToString(_cultureInfo)).WithIsInline(true))
).ConfigureAwait(false); ).ConfigureAwait(false);
return; return;
} }
} }
await Context.Channel.SendErrorAsync("No ability found."); await ReplyErrorLocalized("pokemon_ability_none").ConfigureAwait(false);
} }
} }
} }

View File

@ -12,9 +12,7 @@ using System.Net.Http;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json; using Newtonsoft.Json;
using NLog;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Diagnostics;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
@ -65,23 +63,19 @@ namespace NadekoBot.Modules.Searches
} }
[Group] [Group]
public class StreamNotificationCommands : ModuleBase public class StreamNotificationCommands : NadekoSubmodule
{ {
private static Timer checkTimer { get; } private static readonly Timer _checkTimer;
private static ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); private static readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static Logger _log { get; }
private static bool FirstPass { get; set; } = true; private static bool firstPass { get; set; } = true;
static StreamNotificationCommands() static StreamNotificationCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _checkTimer = new Timer(async (state) =>
checkTimer = new Timer(async (state) =>
{ {
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses); var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses);
cachedStatuses.Clear(); _cachedStatuses.Clear();
IEnumerable<FollowedStream> streams; IEnumerable<FollowedStream> streams;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -93,7 +87,7 @@ namespace NadekoBot.Modules.Searches
try try
{ {
var newStatus = await GetStreamStatus(fs).ConfigureAwait(false); var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
if (FirstPass) if (firstPass)
{ {
return; return;
} }
@ -108,7 +102,7 @@ namespace NadekoBot.Modules.Searches
return; return;
try try
{ {
var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false); await channel.EmbedAsync(fs.GetEmbed(newStatus, channel.Guild.Id)).ConfigureAwait(false);
} }
catch catch
{ {
@ -122,7 +116,7 @@ namespace NadekoBot.Modules.Searches
} }
})); }));
FirstPass = false; firstPass = false;
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(60)); }, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
} }
@ -134,7 +128,7 @@ namespace NadekoBot.Modules.Searches
{ {
case FollowedStream.FollowedStreamType.Hitbox: case FollowedStream.FollowedStreamType.Hitbox:
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}"; 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; return result;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
@ -149,11 +143,11 @@ namespace NadekoBot.Modules.Searches
ApiLink = hitboxUrl, ApiLink = hitboxUrl,
Views = hbData.Views Views = hbData.Views
}; };
cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result); _cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
return result; return result;
case FollowedStream.FollowedStreamType.Twitch: case FollowedStream.FollowedStreamType.Twitch:
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6"; 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; return result;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
@ -170,11 +164,11 @@ namespace NadekoBot.Modules.Searches
ApiLink = twitchUrl, ApiLink = twitchUrl,
Views = twData.Stream?.Viewers.ToString() ?? "0" Views = twData.Stream?.Viewers.ToString() ?? "0"
}; };
cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result); _cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
return result; return result;
case FollowedStream.FollowedStreamType.Beam: case FollowedStream.FollowedStreamType.Beam:
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}"; 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; return result;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
@ -190,7 +184,7 @@ namespace NadekoBot.Modules.Searches
ApiLink = beamUrl, ApiLink = beamUrl,
Views = bmData.ViewersCurrent.ToString() Views = bmData.ViewersCurrent.ToString()
}; };
cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result); _cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
return result; return result;
default: default:
break; break;
@ -234,17 +228,21 @@ namespace NadekoBot.Modules.Searches
if (!streams.Any()) 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; return;
} }
var text = string.Join("\n", await Task.WhenAll(streams.Select(async snc => var text = string.Join("\n", await Task.WhenAll(streams.Select(async snc =>
{ {
var ch = await Context.Guild.GetTextChannelAsync(snc.ChannelId); 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] [NadekoCommand, Usage, Description, Aliases]
@ -271,10 +269,13 @@ namespace NadekoBot.Modules.Searches
} }
if (!removed) if (!removed)
{ {
await Context.Channel.SendErrorAsync("No such stream.").ConfigureAwait(false); await ReplyErrorLocalized("stream_no").ConfigureAwait(false);
return; 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] [NadekoCommand, Usage, Description, Aliases]
@ -293,20 +294,24 @@ namespace NadekoBot.Modules.Searches
})); }));
if (streamStatus.IsLive) 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 else
{ {
await Context.Channel.SendConfirmAsync($"Streamer {username} is offline."); await ReplyConfirmLocalized("streamer_offline",
username).ConfigureAwait(false);
} }
} }
catch 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(); username = username.ToLowerInvariant().Trim();
var fs = new FollowedStream var fs = new FollowedStream
@ -324,7 +329,7 @@ namespace NadekoBot.Modules.Searches
} }
catch catch
{ {
await channel.SendErrorAsync("Stream probably doesn't exist.").ConfigureAwait(false); await ReplyErrorLocalized("stream_not_exist").ConfigureAwait(false);
return; return;
} }
@ -335,24 +340,24 @@ namespace NadekoBot.Modules.Searches
.Add(fs); .Add(fs);
await uow.CompleteAsync().ConfigureAwait(false); 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 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) var embed = new EmbedBuilder().WithTitle(fs.Username)
.WithUrl(fs.GetLink()) .WithUrl(fs.GetLink())
.AddField(efb => efb.WithName("Status") .AddField(efb => efb.WithName(fs.GetText("status"))
.WithValue(status.IsLive ? "Online" : "Offline") .WithValue(status.IsLive ? "Online" : "Offline")
.WithIsInline(true)) .WithIsInline(true))
.AddField(efb => efb.WithName("Viewers") .AddField(efb => efb.WithName(fs.GetText("viewers"))
.WithValue(status.IsLive ? status.Views : "-") .WithValue(status.IsLive ? status.Views : "-")
.WithIsInline(true)) .WithIsInline(true))
.AddField(efb => efb.WithName("Platform") .AddField(efb => efb.WithName(fs.GetText("platform"))
.WithValue(fs.Type.ToString()) .WithValue(fs.Type.ToString())
.WithIsInline(true)) .WithIsInline(true))
.WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor); .WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor);
@ -360,15 +365,21 @@ namespace NadekoBot.Modules.Searches
return embed; 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) if (fs.Type == FollowedStream.FollowedStreamType.Hitbox)
return $"http://www.hitbox.tv/{fs.Username}/"; 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}/"; 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}/"; return $"https://beam.pro/{fs.Username}/";
else return "??";
return "??";
} }
} }
} }

View File

@ -19,10 +19,10 @@ namespace NadekoBot.Modules.Searches
} }
[Group] [Group]
public class TranslateCommands : ModuleBase public class TranslateCommands : NadekoSubmodule
{ {
private static ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>(); private static ConcurrentDictionary<ulong, bool> translatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
private static ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>(); private static ConcurrentDictionary<UserChannelPair, string> userLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
static TranslateCommands() static TranslateCommands()
{ {
@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Searches
return; return;
bool autoDelete; bool autoDelete;
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete)) if (!translatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
return; return;
var key = new UserChannelPair() var key = new UserChannelPair()
{ {
@ -44,10 +44,10 @@ namespace NadekoBot.Modules.Searches
}; };
string langs; string langs;
if (!UserLanguages.TryGetValue(key, out langs)) if (!userLanguages.TryGetValue(key, out langs))
return; return;
var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore), true) var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore))
.ConfigureAwait(false); .ConfigureAwait(false);
if (autoDelete) if (autoDelete)
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { } try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
@ -64,21 +64,21 @@ namespace NadekoBot.Modules.Searches
{ {
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var translation = await TranslateInternal(langs, text); 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 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<string> TranslateInternal(string langs, [Remainder] string text = null, bool silent = false) private static async Task<string> TranslateInternal(string langs, [Remainder] string text = null)
{ {
var langarr = langs.ToLowerInvariant().Split('>'); var langarr = langs.ToLowerInvariant().Split('>');
if (langarr.Length != 2) if (langarr.Length != 2)
throw new ArgumentException(); throw new ArgumentException();
string from = langarr[0]; var from = langarr[0];
string to = langarr[1]; var to = langarr[1];
text = text?.Trim(); text = text?.Trim();
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
throw new ArgumentException(); throw new ArgumentException();
@ -101,20 +101,20 @@ namespace NadekoBot.Modules.Searches
if (autoDelete == AutoDeleteAutoTranslate.Del) if (autoDelete == AutoDeleteAutoTranslate.Del)
{ {
TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true); 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 { } await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false);
return; return;
} }
bool throwaway; 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; 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 (string.IsNullOrWhiteSpace(langs))
{ {
if (UserLanguages.TryRemove(ucp, out langs)) if (userLanguages.TryRemove(ucp, out langs))
await Context.Channel.SendConfirmAsync($"{Context.User.Mention}'s auto-translate language has been removed.").ConfigureAwait(false); await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false);
return; return;
} }
@ -143,20 +143,20 @@ namespace NadekoBot.Modules.Searches
if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to)) 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; 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Translangs() 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);
} }
} }

View File

@ -12,9 +12,9 @@ namespace NadekoBot.Modules.Searches
public partial class Searches public partial class Searches
{ {
[Group] [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] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(1)]
@ -24,9 +24,9 @@ namespace NadekoBot.Modules.Searches
{ {
using (var http = new HttpClient()) 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<XkcdComic>(res); var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var sent = await Context.Channel.SendMessageAsync($"{Context.User.Mention} " + comic.ToString()) var sent = await Context.Channel.SendMessageAsync($"{Context.User.Mention} " + comic)
.ConfigureAwait(false); .ConfigureAwait(false);
await Task.Delay(10000).ConfigureAwait(false); await Task.Delay(10000).ConfigureAwait(false);
@ -47,14 +47,14 @@ namespace NadekoBot.Modules.Searches
using (var http = new HttpClient()) 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<XkcdComic>(res); var comic = JsonConvert.DeserializeObject<XkcdComic>(res);
var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor) var embed = new EmbedBuilder().WithColor(NadekoBot.OkColor)
.WithImageUrl(comic.ImageLink) .WithImageUrl(comic.ImageLink)
.WithAuthor(eab => eab.WithName(comic.Title).WithUrl($"{xkcdUrl}/{num}").WithIconUrl("http://xkcd.com/s/919f27.ico")) .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(GetText("comic_number")).WithValue(comic.Num.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Date").WithValue($"{comic.Month}/{comic.Year}").WithIsInline(true)); .AddField(efb => efb.WithName(GetText("date")).WithValue($"{comic.Month}/{comic.Year}").WithIsInline(true));
var sent = await Context.Channel.EmbedAsync(embed) var sent = await Context.Channel.EmbedAsync(embed)
.ConfigureAwait(false); .ConfigureAwait(false);
@ -75,9 +75,6 @@ namespace NadekoBot.Modules.Searches
[JsonProperty("img")] [JsonProperty("img")]
public string ImageLink { get; set; } public string ImageLink { get; set; }
public string Alt { get; set; } public string Alt { get; set; }
public override string ToString()
=> $"`Comic:` #{Num} `Title:` {Title} `Date:` {Month}/{Year}\n{ImageLink}";
} }
} }
} }

View File

@ -1,5 +1,4 @@
using Discord; using Discord;
using Discord.Commands;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System; using System;
@ -8,27 +7,26 @@ using System.Text;
using System.Net.Http; using System.Net.Http;
using NadekoBot.Services; using NadekoBot.Services;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Attributes;
using System.Text.RegularExpressions;
using System.Net; using System.Net;
using NadekoBot.Modules.Searches.Models; using NadekoBot.Modules.Searches.Models;
using System.Collections.Generic; using System.Collections.Generic;
using ImageSharp;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.IO; using System.IO;
using NadekoBot.Modules.Searches.Commands.OMDB; using NadekoBot.Modules.Searches.Commands.OMDB;
using NadekoBot.Modules.Searches.Commands.Models; using NadekoBot.Modules.Searches.Commands.Models;
using AngleSharp.Parser.Html;
using AngleSharp; using AngleSharp;
using AngleSharp.Dom.Html; using AngleSharp.Dom.Html;
using AngleSharp.Dom; using AngleSharp.Dom;
using System.Xml; using System.Xml;
using System.Xml.Linq; using Configuration = AngleSharp.Configuration;
using NadekoBot.Attributes;
using Discord.Commands;
using ImageSharp.Processing.Processors;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
[NadekoModule("Searches", "~")] [NadekoModule("Searches", "~")]
public partial class Searches : NadekoModule public partial class Searches : NadekoTopLevelModule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Weather([Remainder] string query) public async Task Weather([Remainder] string query)
@ -43,15 +41,15 @@ namespace NadekoBot.Modules.Searches
var data = JsonConvert.DeserializeObject<WeatherData>(response); var data = JsonConvert.DeserializeObject<WeatherData>(response);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true)) .AddField(fb => fb.WithName("🌍 " + GetText("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("📏 " + GetText("latlong")).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("☁ " + GetText("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("😓 " + GetText("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("💨 " + GetText("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("🌡 " + GetText("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("🔆 " + GetText("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("🌄 " + GetText("sunrise")).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("sunset")).WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.WithOkColor() .WithOkColor()
.WithFooter(efb => efb.WithText("Powered by http://openweathermap.org")); .WithFooter(efb => efb.WithText("Powered by http://openweathermap.org"));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -60,17 +58,15 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Youtube([Remainder] string query = null) 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(); var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault();
if (string.IsNullOrWhiteSpace(result)) if (string.IsNullOrWhiteSpace(result))
{ {
await Context.Channel.SendErrorAsync("No results found for that query.").ConfigureAwait(false); await ReplyErrorLocalized("no_results").ConfigureAwait(false);
return; return;
} }
await Context.Channel.SendMessageAsync(result).ConfigureAwait(false); 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] [NadekoCommand, Usage, Description, Aliases]
@ -82,7 +78,7 @@ namespace NadekoBot.Modules.Searches
var movie = await OmdbProvider.FindMovie(query); var movie = await OmdbProvider.FindMovie(query);
if (movie == null) if (movie == null)
{ {
await Context.Channel.SendErrorAsync("Failed to find that movie.").ConfigureAwait(false); await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false);
return; return;
} }
await Context.Channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false); await Context.Channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false);
@ -94,7 +90,7 @@ namespace NadekoBot.Modules.Searches
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var res = JObject.Parse(await http.GetStringAsync("http://www.random.cat/meow").ConfigureAwait(false)); 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);
} }
} }
@ -122,12 +118,12 @@ namespace NadekoBot.Modules.Searches
var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false); var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .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") .WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link) .WithDescription(res.Link)
.WithImageUrl(res.Link) .WithImageUrl(res.Link)
.WithTitle(Context.User.Mention); .WithTitle(Context.User.ToString());
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch catch
@ -157,7 +153,7 @@ namespace NadekoBot.Modules.Searches
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source) .WithDescription(source)
.WithImageUrl(source) .WithImageUrl(source)
.WithTitle(Context.User.Mention); .WithTitle(Context.User.ToString());
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
} }
@ -174,12 +170,12 @@ namespace NadekoBot.Modules.Searches
var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false); var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .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") .WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link) .WithDescription(res.Link)
.WithImageUrl(res.Link) .WithImageUrl(res.Link)
.WithTitle(Context.User.Mention); .WithTitle(Context.User.ToString());
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch catch
@ -205,12 +201,12 @@ namespace NadekoBot.Modules.Searches
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50)) .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
.WithUrl(fullQueryLink) .WithUrl(fullQueryLink)
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?")) .WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source) .WithDescription(source)
.WithImageUrl(source) .WithImageUrl(source)
.WithTitle(Context.User.Mention); .WithTitle(Context.User.ToString());
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
} }
@ -235,13 +231,14 @@ namespace NadekoBot.Modules.Searches
if (shortened == arg) 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) await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("Original Url") .AddField(efb => efb.WithName(GetText("original_url"))
.WithValue($"<{arg}>")) .WithValue($"<{arg}>"))
.AddField(efb => efb.WithName("Short Url") .AddField(efb => efb.WithName(GetText("short_url"))
.WithValue($"<{shortened}>"))) .WithValue($"<{shortened}>")))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -273,7 +270,7 @@ namespace NadekoBot.Modules.Searches
var results = elems.Select<IElement, GoogleSearchResult?>(elem => var results = elems.Select<IElement, GoogleSearchResult?>(elem =>
{ {
var aTag = (elem.Children.FirstOrDefault().Children.FirstOrDefault() as IHtmlAnchorElement); // <h3> -> <a> var aTag = (elem.Children.FirstOrDefault()?.Children.FirstOrDefault() as IHtmlAnchorElement); // <h3> -> <a>
var href = aTag?.Href; var href = aTag?.Href;
var name = aTag?.TextContent; var name = aTag?.TextContent;
if (href == null || name == null) if (href == null || name == null)
@ -289,35 +286,31 @@ namespace NadekoBot.Modules.Searches
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50)) .WithAuthor(eab => eab.WithName(GetText("search_for") + " " + terms.TrimTo(50))
.WithUrl(fullQueryLink) .WithUrl(fullQueryLink)
.WithIconUrl("http://i.imgur.com/G46fm8J.png")) .WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithTitle(Context.User.Mention) .WithTitle(Context.User.ToString())
.WithFooter(efb => efb.WithText(totalResults)); .WithFooter(efb => efb.WithText(totalResults));
var desc = await Task.WhenAll(results.Select(async res => var desc = await Task.WhenAll(results.Select(async res =>
$"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n")) $"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
.ConfigureAwait(false); .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] [NadekoCommand, Usage, Description, Aliases]
public async Task MagicTheGathering([Remainder] string name = null) public async Task MagicTheGathering([Remainder] string name)
{ {
var arg = name; var arg = name;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
return; return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
string response = "";
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}") var response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}")
.ConfigureAwait(false); .ConfigureAwait(false);
try try
{ {
var items = JArray.Parse(response).ToArray(); var items = JArray.Parse(response).ToArray();
@ -327,50 +320,46 @@ namespace NadekoBot.Modules.Searches
var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString()); var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
var cost = item["cost"].ToString(); var cost = item["cost"].ToString();
var desc = item["text"].ToString(); var desc = item["text"].ToString();
var types = String.Join(",\n", item["types"].ToObject<string[]>()); var types = string.Join(",\n", item["types"].ToObject<string[]>());
var img = item["editions"][0]["image_url"].ToString(); var img = item["editions"][0]["image_url"].ToString();
var embed = new EmbedBuilder().WithOkColor() var embed = new EmbedBuilder().WithOkColor()
.WithTitle(item["name"].ToString()) .WithTitle(item["name"].ToString())
.WithDescription(desc) .WithDescription(desc)
.WithImageUrl(img) .WithImageUrl(img)
.AddField(efb => efb.WithName("Store Url").WithValue(storeUrl).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true))
.AddField(efb => efb.WithName("Cost").WithValue(cost).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true))
.AddField(efb => efb.WithName("Types").WithValue(types).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)); //.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); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch 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] [NadekoCommand, Usage, Description, Aliases]
public async Task Hearthstone([Remainder] string name = null) public async Task Hearthstone([Remainder] string name)
{ {
var arg = name; var arg = name;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
return; return;
}
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) 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; return;
} }
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
string response = "";
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); 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)}") var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}")
.ConfigureAwait(false); .ConfigureAwait(false);
try try
{ {
var items = JArray.Parse(response).Shuffle().ToList(); var items = JArray.Parse(response).Shuffle().ToList();
@ -393,17 +382,17 @@ namespace NadekoBot.Modules.Searches
string msg = null; string msg = null;
if (items.Count > 4) if (items.Count > 4)
{ {
msg = "⚠ Found over 4 images. Showing random 4."; msg = GetText("hs_over_x", 4);
} }
var ms = new MemoryStream(); var ms = new MemoryStream();
await Task.Run(() => images.AsEnumerable().Merge().SaveAsPng(ms)); await Task.Run(() => images.AsEnumerable().Merge().Save(ms));
ms.Position = 0; ms.Position = 0;
await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false); await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync($"Error occured.").ConfigureAwait(false);
_log.Error(ex); _log.Error(ex);
await ReplyErrorLocalized("error_occured").ConfigureAwait(false);
} }
} }
} }
@ -413,23 +402,20 @@ namespace NadekoBot.Modules.Searches
{ {
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) 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; return;
} }
var arg = query; if (string.IsNullOrWhiteSpace(query))
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a sentence.").ConfigureAwait(false);
return; return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
http.DefaultRequestHeaders.Add("Accept", "text/plain"); 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 try
{ {
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
@ -441,7 +427,7 @@ namespace NadekoBot.Modules.Searches
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("Failed to yodify your sentence.").ConfigureAwait(false); await ReplyErrorLocalized("yodify_error").ConfigureAwait(false);
} }
} }
} }
@ -451,22 +437,19 @@ namespace NadekoBot.Modules.Searches
{ {
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) 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; return;
} }
var arg = query; if (string.IsNullOrWhiteSpace(query))
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
return; return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Accept", "application/json"); 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 try
{ {
var items = JObject.Parse(res); var items = JObject.Parse(res);
@ -482,7 +465,7 @@ namespace NadekoBot.Modules.Searches
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("Failed finding a definition for that term.").ConfigureAwait(false); await ReplyErrorLocalized("ud_error").ConfigureAwait(false);
} }
} }
} }
@ -509,39 +492,36 @@ namespace NadekoBot.Modules.Searches
definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString(); definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
var embed = new EmbedBuilder().WithOkColor() var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Define: " + word) .WithTitle(GetText("define") + " " + word)
.WithDescription(definition) .WithDescription(definition)
.WithFooter(efb => efb.WithText(sense.Gramatical_info?.type)); .WithFooter(efb => efb.WithText(sense.Gramatical_info?.type));
if (sense.Examples != null) 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); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [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(query))
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
return; return;
}
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) 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; return;
} }
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var res = ""; string res;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); 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 try
@ -560,7 +540,7 @@ namespace NadekoBot.Modules.Searches
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("Failed finding a definition for that tag.").ConfigureAwait(false); await ReplyErrorLocalized("hashtag_error").ConfigureAwait(false);
} }
} }
@ -574,7 +554,7 @@ namespace NadekoBot.Modules.Searches
return; return;
var fact = JObject.Parse(response)["facts"][0].ToString(); 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);
} }
} }
@ -611,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 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<WikipediaApiModel>(result); var data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
if (data.Query.Pages[0].Missing) 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 else
await Context.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false); await Context.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false);
} }
@ -625,28 +605,21 @@ namespace NadekoBot.Modules.Searches
return; return;
var img = new ImageSharp.Image(50, 50); var img = new ImageSharp.Image(50, 50);
img.BackgroundColor(new ImageSharp.Color(color)); img.ApplyProcessor(new BackgroundColorProcessor<ImageSharp.Color>(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] [NadekoCommand, Usage, Description, Aliases]
public async Task Videocall([Remainder] params IUser[] users) public async Task Videocall(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); await (await usr.CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false);
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);
} }
} }
@ -666,11 +639,11 @@ namespace NadekoBot.Modules.Searches
} }
[NadekoCommand, Usage, Description, Aliases] [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)) 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; return;
} }
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -682,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 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 items = JObject.Parse(res);
var found = items["items"][0]; var found = items["items"][0];
var response = $@"`Title:` {found["title"].ToString()} var response = $@"`{GetText("title")}` {found["title"]}
`Quality:` {found["quality"]} `{GetText("quality")}` {found["quality"]}
`URL:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}"; `{GetText("url")}:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
await Context.Channel.SendMessageAsync(response).ConfigureAwait(false); await Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false); await ReplyErrorLocalized("wikia_error").ConfigureAwait(false);
} }
} }
} }
[NadekoCommand, Usage, Description, Aliases] //[NadekoCommand, Usage, Description, Aliases]
public async Task MCPing([Remainder] string query = null) //public async Task MCPing([Remainder] string query2 = null)
{ //{
var arg = query; // var query = query2;
if (string.IsNullOrWhiteSpace(arg)) // if (string.IsNullOrWhiteSpace(query))
{ // return;
await Context.Channel.SendErrorAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false); // await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
return; // using (var http = new HttpClient())
} // {
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); // http.DefaultRequestHeaders.Clear();
using (var http = new HttpClient()) // var ip = query.Split(':')[0];
{ // var port = query.Split(':')[1];
http.DefaultRequestHeaders.Clear(); // var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
string ip = arg.Split(':')[0]; // try
string port = arg.Split(':')[1]; // {
var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false); // var items = JObject.Parse(res);
try // var sb = new StringBuilder();
{ // var ping = (int)Math.Ceiling(double.Parse(items["latency"].ToString()));
var items = JObject.Parse(res); // sb.AppendLine($"`Server:` {query}");
var sb = new StringBuilder(); // sb.AppendLine($"`Version:` {items["version"]["name"]} / Protocol {items["version"]["protocol"]}");
int ping = (int)Math.Ceiling(Double.Parse(items["latency"].ToString())); // sb.AppendLine($"`Description:` {items["description"]}");
sb.AppendLine($"`Server:` {arg}"); // sb.AppendLine($"`Online Players:` {items["players"]["online"]}/{items["players"]["max"]}");
sb.AppendLine($"`Version:` {items["version"]["name"].ToString()} / Protocol {items["version"]["protocol"].ToString()}"); // sb.Append($"`Latency:` {ping}");
sb.AppendLine($"`Description:` {items["description"].ToString()}"); // await Context.Channel.SendMessageAsync(sb.ToString());
sb.AppendLine($"`Online Players:` {items["players"]["online"].ToString()}/{items["players"]["max"].ToString()}"); // }
sb.Append($"`Latency:` {ping}"); // catch
await Context.Channel.SendMessageAsync(sb.ToString()); // {
} // await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false);
catch // }
{ // }
await Context.Channel.SendErrorAsync($"Failed finding `{arg}`.").ConfigureAwait(false); //}
}
}
}
[NadekoCommand, Usage, Description, Aliases] //[NadekoCommand, Usage, Description, Aliases]
public async Task MCQ([Remainder] string query = null) //public async Task MCQ([Remainder] string query = null)
{ //{
var arg = query; // var arg = query;
if (string.IsNullOrWhiteSpace(arg)) // if (string.IsNullOrWhiteSpace(arg))
{ // {
await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false); // await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
return; // return;
} // }
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); // await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient()) // using (var http = new HttpClient())
{ // {
http.DefaultRequestHeaders.Clear(); // http.DefaultRequestHeaders.Clear();
try // try
{ // {
string ip = arg.Split(':')[0]; // var ip = arg.Split(':')[0];
string port = arg.Split(':')[1]; // var port = arg.Split(':')[1];
var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false); // var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
var items = JObject.Parse(res); // var items = JObject.Parse(res);
var sb = new StringBuilder(); // var sb = new StringBuilder();
sb.AppendLine($"`Server:` {arg.ToString()} 〘Status: {items["status"]}〙"); // sb.AppendLine($"`Server:` {arg} 〘Status: {items["status"]}〙");
sb.AppendLine($"`Player List (First 5):`"); // sb.AppendLine("`Player List (First 5):`");
foreach (var item in items["Playerlist"].Take(5)) // foreach (var item in items["Playerlist"].Take(5))
{ // {
sb.AppendLine($":rosette: {item}"); // sb.AppendLine($":rosette: {item}");
} // }
sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}"); // sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}");
sb.AppendLine($"`Plugins:` {items["Plugins"]}"); // sb.AppendLine($"`Plugins:` {items["Plugins"]}");
sb.Append($"`Version:` {items["Version"]}"); // sb.Append($"`Version:` {items["Version"]}");
await Context.Channel.SendMessageAsync(sb.ToString()); // await Context.Channel.SendMessageAsync(sb.ToString());
} // }
catch // catch
{ // {
await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false); // await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false);
} // }
} // }
} //}
public enum DapiSearchType public enum DapiSearchType
{ {
@ -776,7 +746,7 @@ namespace NadekoBot.Modules.Searches
Yandere 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; var channel = umsg.Channel;
@ -785,7 +755,7 @@ namespace NadekoBot.Modules.Searches
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false); var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
if (url == null) if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results."); await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results"));
else else
await channel.EmbedAsync(new EmbedBuilder().WithOkColor() await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag) .WithDescription(umsg.Author.Mention + " " + tag)
@ -796,7 +766,7 @@ namespace NadekoBot.Modules.Searches
public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type) public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type)
{ {
tag = tag?.Replace(" ", "_"); tag = tag?.Replace(" ", "_");
string website = ""; var website = "";
switch (type) switch (type)
{ {
case DapiSearchType.Safebooru: case DapiSearchType.Safebooru:
@ -841,10 +811,10 @@ namespace NadekoBot.Modules.Searches
return null; return null;
} }
} }
public static async Task<bool> ValidateQuery(IMessageChannel ch, string query) public async Task<bool> ValidateQuery(IMessageChannel ch, string query)
{ {
if (!string.IsNullOrEmpty(query.Trim())) return true; if (!string.IsNullOrWhiteSpace(query)) return true;
await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false); await ch.SendErrorAsync(GetText("specify_search_params")).ConfigureAwait(false);
return false; return false;
} }
} }

View File

@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class CalcCommands : ModuleBase public class CalcCommands : NadekoSubmodule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Calculate([Remainder] string expression) public async Task Calculate([Remainder] string expression)
@ -21,9 +21,9 @@ namespace NadekoBot.Modules.Utility
expr.EvaluateParameter += Expr_EvaluateParameter; expr.EvaluateParameter += Expr_EvaluateParameter;
var result = expr.Evaluate(); var result = expr.Evaluate();
if (expr.Error == null) if (expr.Error == null)
await Context.Channel.SendConfirmAsync("Result", $"{result}"); await Context.Channel.SendConfirmAsync("⚙ " + GetText("result"), result.ToString());
else 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) private static void Expr_EvaluateParameter(string name, NCalc.ParameterArgs args)
@ -42,29 +42,26 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task CalcOps() public async Task CalcOps()
{ {
var selection = typeof(Math).GetTypeInfo().GetMethods().Except(typeof(object).GetTypeInfo().GetMethods()).Distinct(new MethodInfoEqualityComparer()).Select(x => var selection = typeof(Math).GetTypeInfo()
{ .GetMethods()
return x.Name; .Distinct(new MethodInfoEqualityComparer())
}) .Select(x => x.Name)
.Except(new[] { "ToString", .Except(new[]
"Equals", {
"GetHashCode", "ToString",
"GetType"}); "Equals",
await Context.Channel.SendConfirmAsync("Available functions in calc", string.Join(", ", selection)); "GetHashCode",
"GetType"
});
await Context.Channel.SendConfirmAsync(GetText("utility_calcops", Prefix), string.Join(", ", selection));
} }
} }
class MethodInfoEqualityComparer : IEqualityComparer<MethodInfo> private class MethodInfoEqualityComparer : IEqualityComparer<MethodInfo>
{ {
public bool Equals(MethodInfo x, MethodInfo y) => x.Name == y.Name; public bool Equals(MethodInfo x, MethodInfo y) => x.Name == y.Name;
public int GetHashCode(MethodInfo obj) => obj.Name.GetHashCode(); public int GetHashCode(MethodInfo obj) => obj.Name.GetHashCode();
} }
class ExpressionContext
{
public double Pi { get; set; } = Math.PI;
}
} }
} }

View File

@ -3,8 +3,6 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NLog;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -14,12 +12,11 @@ namespace NadekoBot.Modules.Utility
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class CrossServerTextChannel : ModuleBase public class CrossServerTextChannel : NadekoSubmodule
{ {
static CrossServerTextChannel() static CrossServerTextChannel()
{ {
_log = LogManager.GetCurrentClassLogger(); NadekoBot.Client.MessageReceived += async imsg =>
NadekoBot.Client.MessageReceived += async (imsg) =>
{ {
try try
{ {
@ -37,23 +34,32 @@ namespace NadekoBot.Modules.Utility
var set = subscriber.Value; var set = subscriber.Value;
if (!set.Contains(channel)) if (!set.Contains(channel))
continue; 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) { catch
_log.Warn(ex); {
// ignored
} }
}; };
} }
private static string GetText(IGuild server, ITextChannel channel, IGuildUser user, IUserMessage message) => private static string GetMessage(ITextChannel channel, IGuildUser user, IUserMessage message) =>
$"**{server.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions(); $"**{channel.Guild.Name} | {channel.Name}** `{user.Username}`: " + message.Content.SanitizeMentions();
public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers = new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>(); public static readonly ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>> Subscribers =
private static Logger _log { get; } new ConcurrentDictionary<int, ConcurrentHashSet<ITextChannel>>();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -64,8 +70,9 @@ namespace NadekoBot.Modules.Utility
var set = new ConcurrentHashSet<ITextChannel>(); var set = new ConcurrentHashSet<ITextChannel>();
if (Subscribers.TryAdd(token, set)) if (Subscribers.TryAdd(token, set))
{ {
set.Add((ITextChannel)Context.Channel); set.Add((ITextChannel) Context.Channel);
await ((IGuildUser)Context.User).SendConfirmAsync("This is your CSC token", token.ToString()).ConfigureAwait(false); await ((IGuildUser) Context.User).SendConfirmAsync(GetText("csc_token"), token.ToString())
.ConfigureAwait(false);
} }
} }
@ -77,8 +84,8 @@ namespace NadekoBot.Modules.Utility
ConcurrentHashSet<ITextChannel> set; ConcurrentHashSet<ITextChannel> set;
if (!Subscribers.TryGetValue(token, out set)) if (!Subscribers.TryGetValue(token, out set))
return; return;
set.Add((ITextChannel)Context.Channel); set.Add((ITextChannel) Context.Channel);
await Context.Channel.SendConfirmAsync("Joined cross server channel.").ConfigureAwait(false); await ReplyConfirmLocalized("csc_join").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -88,9 +95,9 @@ namespace NadekoBot.Modules.Utility
{ {
foreach (var subscriber in Subscribers) 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);
} }
} }
} }

View File

@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class InfoCommands : ModuleBase public class InfoCommands : NadekoSubmodule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Utility
if (string.IsNullOrWhiteSpace(guildName)) if (string.IsNullOrWhiteSpace(guildName))
guild = channel.Guild; guild = channel.Guild;
else 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) if (guild == null)
return; return;
var ownername = await guild.GetUserAsync(guild.OwnerId); var ownername = await guild.GetUserAsync(guild.OwnerId);
@ -37,22 +37,22 @@ namespace NadekoBot.Modules.Utility
if (string.IsNullOrWhiteSpace(features)) if (string.IsNullOrWhiteSpace(features))
features = "-"; features = "-";
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName("Server Info")) .WithAuthor(eab => eab.WithName(GetText("server_info")))
.WithTitle(guild.Name) .WithTitle(guild.Name)
.AddField(fb => fb.WithName("**ID**").WithValue(guild.Id.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("id")).WithValue(guild.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Owner**").WithValue(ownername.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("owner")).WithValue(ownername.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("members")).WithValue(users.Count.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("text_channels")).WithValue(textchn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("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(GetText("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(GetText("region")).WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("roles")).WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("features")).WithValue(features).WithIsInline(true))
.WithImageUrl(guild.IconUrl) .WithImageUrl(guild.IconUrl)
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
if (guild.Emojis.Any()) 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(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); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -69,9 +69,9 @@ namespace NadekoBot.Modules.Utility
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithTitle(ch.Name) .WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions()) .WithDescription(ch.Topic?.SanitizeMentions())
.AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName(GetText("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(GetText("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("users")).WithValue(usercount.ToString()).WithIsInline(true))
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -86,15 +86,15 @@ namespace NadekoBot.Modules.Utility
return; return;
var embed = new EmbedBuilder() 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)) 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)) embed.AddField(fb => fb.WithName(GetText("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(GetText("joined_server")).WithValue($"{user.JoinedAt?.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(GetText("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)) .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); .WithColor(NadekoBot.OkColor);
if (user.AvatarId != null) if (user.AvatarId != null)
@ -119,12 +119,17 @@ namespace NadekoBot.Modules.Utility
StringBuilder str = new StringBuilder(); StringBuilder str = new StringBuilder();
foreach (var kvp in NadekoBot.CommandHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page*activityPerPage).Take(activityPerPage)) 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() .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())); .WithDescription(str.ToString()));
} }
} }

View File

@ -5,45 +5,47 @@ using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.WebSocket;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class RepeatCommands : ModuleBase public class RepeatCommands : NadekoSubmodule
{ {
//guildid/RepeatRunners //guildid/RepeatRunners
public static ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> repeaters { get; } public static ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> Repeaters { get; set; }
private static bool _ready;
public class RepeatRunner public class RepeatRunner
{ {
private Logger _log { get; } private readonly Logger _log;
private CancellationTokenSource source { get; set; } private CancellationTokenSource source { get; set; }
private CancellationToken token { get; set; } private CancellationToken token { get; set; }
public Repeater Repeater { get; } 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) public RepeatRunner(Repeater repeater, ITextChannel channel = null)
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
this.Repeater = repeater; Repeater = repeater;
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId); Channel = channel;
if (Channel == null)
return; Guild = NadekoBot.Client.GetGuild(repeater.GuildId);
Task.Run(Run); if(Guild!=null)
Task.Run(Run);
} }
@ -64,10 +66,21 @@ namespace NadekoBot.Modules.Utility
// continue; // continue;
if (oldMsg != null) if (oldMsg != null)
try { await oldMsg.DeleteAsync(); } catch { } try
{
await oldMsg.DeleteAsync();
}
catch
{
// ignored
}
try 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) catch (HttpException ex) when (ex.HttpCode == System.Net.HttpStatusCode.Forbidden)
{ {
@ -85,7 +98,9 @@ namespace NadekoBot.Modules.Utility
} }
} }
} }
catch (OperationCanceledException) { } catch (OperationCanceledException)
{
}
} }
public void Reset() public void Reset()
@ -101,22 +116,23 @@ namespace NadekoBot.Modules.Utility
public override string ToString() 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() static RepeatCommands()
{ {
var _log = LogManager.GetCurrentClassLogger(); var _ = Task.Run(async () =>
var sw = Stopwatch.StartNew(); {
await Task.Delay(5000).ConfigureAwait(false);
repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(NadekoBot.AllGuildConfigs Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(NadekoBot.AllGuildConfigs
.ToDictionary(gc => gc.GuildId, .ToDictionary(gc => gc.GuildId,
gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr)) gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters
.Where(gr => gr.Channel != null)))); .Select(gr => new RepeatRunner(gr))
.Where(x => x.Guild != null))));
sw.Stop(); _ready = true;
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); });
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -124,11 +140,13 @@ namespace NadekoBot.Modules.Utility
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
public async Task RepeatInvoke(int index) public async Task RepeatInvoke(int index)
{ {
if (!_ready)
return;
index -= 1; index -= 1;
ConcurrentQueue<RepeatRunner> rep; ConcurrentQueue<RepeatRunner> 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; return;
} }
@ -136,7 +154,7 @@ namespace NadekoBot.Modules.Utility
if (index >= repList.Count) if (index >= repList.Count)
{ {
await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false); await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false);
return; return;
} }
var repeater = repList[index].Repeater; var repeater = repList[index].Repeater;
@ -151,19 +169,21 @@ namespace NadekoBot.Modules.Utility
[Priority(0)] [Priority(0)]
public async Task RepeatRemove(int index) public async Task RepeatRemove(int index)
{ {
if (!_ready)
return;
if (index < 1) if (index < 1)
return; return;
index -= 1; index -= 1;
ConcurrentQueue<RepeatRunner> rep; ConcurrentQueue<RepeatRunner> rep;
if (!repeaters.TryGetValue(Context.Guild.Id, out rep)) if (!Repeaters.TryGetValue(Context.Guild.Id, out rep))
return; return;
var repeaterList = rep.ToList(); var repeaterList = rep.ToList();
if (index >= repeaterList.Count) if (index >= repeaterList.Count)
{ {
await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false); await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false);
return; return;
} }
@ -179,8 +199,9 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
if (repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(repeaterList), rep)) if (Repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(repeaterList), rep))
await Context.Channel.SendConfirmAsync("Message Repeater",$"#{index+1} stopped.\n\n{repeater.ToString()}").ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("message_repeater"),
GetText("repeater_stopped", index + 1) + $"\n\n{repeater}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -189,6 +210,8 @@ namespace NadekoBot.Modules.Utility
[Priority(1)] [Priority(1)]
public async Task Repeat(int minutes, [Remainder] string message) public async Task Repeat(int minutes, [Remainder] string message)
{ {
if (!_ready)
return;
if (minutes < 1 || minutes > 10080) if (minutes < 1 || minutes > 10080)
return; return;
@ -214,15 +237,20 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync().ConfigureAwait(false); 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<RepeatRunner>(new[] { rep }), (key, old) => Repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(new[] {rep}), (key, old) =>
{ {
old.Enqueue(rep); old.Enqueue(rep);
return old; 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] [NadekoCommand, Usage, Description, Aliases]
@ -230,27 +258,33 @@ namespace NadekoBot.Modules.Utility
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
public async Task RepeatList() public async Task RepeatList()
{ {
if (!_ready)
return;
ConcurrentQueue<RepeatRunner> repRunners; ConcurrentQueue<RepeatRunner> 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; return;
} }
var replist = repRunners.ToList(); var replist = repRunners.ToList();
var sb = new StringBuilder(); var sb = new StringBuilder();
for (int i = 0; i < replist.Count; i++) for (var i = 0; i < replist.Count; i++)
{ {
var rep = replist[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() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle("List Of Repeaters") .WithTitle(GetText("list_of_repeaters"))
.WithDescription(sb.ToString())) .WithDescription(desc))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }
} }

View File

@ -8,13 +8,14 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.DataStructures;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class QuoteCommands : ModuleBase public class QuoteCommands : NadekoSubmodule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -32,10 +33,11 @@ namespace NadekoBot.Modules.Utility
} }
if (quotes.Any()) 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); .ConfigureAwait(false);
else else
await Context.Channel.SendErrorAsync("No quotes on this page.").ConfigureAwait(false); await ReplyErrorLocalized("quotes_page_none").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -56,8 +58,40 @@ namespace NadekoBot.Modules.Utility
if (quote == null) if (quote == null)
return; 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()); 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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -80,7 +114,7 @@ namespace NadekoBot.Modules.Utility
}); });
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("✅ Quote added.").ConfigureAwait(false); await ReplyConfirmLocalized("quote_added").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -102,7 +136,7 @@ namespace NadekoBot.Modules.Utility
if (qs == null || !qs.Any()) if (qs == null || !qs.Any())
{ {
sucess = false; sucess = false;
response = "No quotes found which you can remove."; response = GetText("quotes_remove_none");
} }
else else
{ {
@ -111,7 +145,7 @@ namespace NadekoBot.Modules.Utility
uow.Quotes.Remove(q); uow.Quotes.Remove(q);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
sucess = true; sucess = true;
response = "🗑 **Deleted a random quote.**"; response = GetText("quote_deleted");
} }
} }
if(sucess) if(sucess)
@ -139,8 +173,8 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await Context.Channel.SendConfirmAsync($"🗑 **Deleted all quotes** with **{keyword}** keyword."); await ReplyConfirmLocalized("quotes_deleted", Format.Bold(keyword)).ConfigureAwait(false);
} }
} }
} }
} }

View File

@ -17,21 +17,21 @@ namespace NadekoBot.Modules.Utility
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class RemindCommands : ModuleBase public class RemindCommands : NadekoSubmodule
{ {
readonly Regex _regex = new Regex(@"^(?:(?<months>\d)mo)?(?:(?<weeks>\d)w)?(?:(?<days>\d{1,2})d)?(?:(?<hours>\d{1,2})h)?(?:(?<minutes>\d{1,2})m)?$",
Regex regex = new Regex(@"^(?:(?<months>\d)mo)?(?:(?<weeks>\d)w)?(?:(?<days>\d{1,2})d)?(?:(?<hours>\d{1,2})h)?(?:(?<minutes>\d{1,2})m)?$",
RegexOptions.Compiled | RegexOptions.Multiline); RegexOptions.Compiled | RegexOptions.Multiline);
private static string RemindMessageFormat { get; } private static string remindMessageFormat { get; }
private static IDictionary<string, Func<Reminder, string>> replacements = new Dictionary<string, Func<Reminder, string>> private static readonly IDictionary<string, Func<Reminder, string>> _replacements = new Dictionary<string, Func<Reminder, string>>
{ {
{ "%message%" , (r) => r.Message }, { "%message%" , (r) => r.Message },
{ "%user%", (r) => $"<@!{r.UserId}>" }, { "%user%", (r) => $"<@!{r.UserId}>" },
{ "%target%", (r) => r.IsPrivate ? "Direct Message" : $"<#{r.ChannelId}>"} { "%target%", (r) => r.IsPrivate ? "Direct Message" : $"<#{r.ChannelId}>"}
}; };
private static Logger _log { get; }
private new static readonly Logger _log;
static RemindCommands() static RemindCommands()
{ {
@ -41,7 +41,7 @@ namespace NadekoBot.Modules.Utility
{ {
reminders = uow.Reminders.GetAll().ToList(); reminders = uow.Reminders.GetAll().ToList();
} }
RemindMessageFormat = NadekoBot.BotConfig.RemindMessageFormat; remindMessageFormat = NadekoBot.BotConfig.RemindMessageFormat;
foreach (var r in reminders) foreach (var r in reminders)
{ {
@ -58,10 +58,10 @@ namespace NadekoBot.Modules.Utility
if (time.TotalMilliseconds > int.MaxValue) if (time.TotalMilliseconds > int.MaxValue)
return; return;
await Task.Delay(time); await Task.Delay(time).ConfigureAwait(false);
try try
{ {
IMessageChannel ch = null; IMessageChannel ch;
if (r.IsPrivate) if (r.IsPrivate)
{ {
ch = await NadekoBot.Client.GetDMChannelAsync(r.ChannelId).ConfigureAwait(false); ch = await NadekoBot.Client.GetDMChannelAsync(r.ChannelId).ConfigureAwait(false);
@ -74,7 +74,7 @@ namespace NadekoBot.Modules.Utility
return; return;
await ch.SendMessageAsync( await ch.SendMessageAsync(
replacements.Aggregate(RemindMessageFormat, _replacements.Aggregate(remindMessageFormat,
(cur, replace) => cur.Replace(replace.Key, replace.Value(r))) (cur, replace) => cur.Replace(replace.Key, replace.Value(r)))
.SanitizeMentions() .SanitizeMentions()
).ConfigureAwait(false); //it works trust me ).ConfigureAwait(false); //it works trust me
@ -119,27 +119,21 @@ namespace NadekoBot.Modules.Utility
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
if (ch == null) var m = _regex.Match(timeStr);
{
await channel.SendErrorAsync($"{Context.User.Mention} Something went wrong (channel cannot be found) ;(").ConfigureAwait(false);
return;
}
var m = regex.Match(timeStr);
if (m.Length == 0) 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; return;
} }
string output = ""; string output = "";
var namesAndValues = new Dictionary<string, int>(); var namesAndValues = new Dictionary<string, int>();
foreach (var groupName in regex.GetGroupNames()) foreach (var groupName in _regex.GetGroupNames())
{ {
if (groupName == "0") continue; if (groupName == "0") continue;
int value = 0; int value;
int.TryParse(m.Groups[groupName].Value, out value); int.TryParse(m.Groups[groupName].Value, out value);
if (string.IsNullOrEmpty(m.Groups[groupName].Value)) if (string.IsNullOrEmpty(m.Groups[groupName].Value))
@ -147,7 +141,7 @@ namespace NadekoBot.Modules.Utility
namesAndValues[groupName] = 0; namesAndValues[groupName] = 0;
continue; continue;
} }
else if (value < 1 || if (value < 1 ||
(groupName == "months" && value > 1) || (groupName == "months" && value > 1) ||
(groupName == "weeks" && value > 4) || (groupName == "weeks" && value > 4) ||
(groupName == "days" && value >= 7) || (groupName == "days" && value >= 7) ||
@ -157,8 +151,7 @@ namespace NadekoBot.Modules.Utility
await channel.SendErrorAsync($"Invalid {groupName} value.").ConfigureAwait(false); await channel.SendErrorAsync($"Invalid {groupName} value.").ConfigureAwait(false);
return; return;
} }
else namesAndValues[groupName] = value;
namesAndValues[groupName] = value;
output += m.Groups[groupName].Value + " " + groupName + " "; output += m.Groups[groupName].Value + " " + groupName + " ";
} }
var time = DateTime.Now + new TimeSpan(30 * namesAndValues["months"] + var time = DateTime.Now + new TimeSpan(30 * namesAndValues["months"] +
@ -184,17 +177,26 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync(); 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); await StartReminder(rem);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task RemindTemplate([Remainder] string arg) public async Task RemindTemplate([Remainder] string arg)
{ {
var channel = (ITextChannel)Context.Channel;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
return; return;
@ -203,7 +205,8 @@ namespace NadekoBot.Modules.Utility
uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim(); uow.BotConfig.GetOrCreate().RemindMessageFormat = arg.Trim();
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await channel.SendConfirmAsync("🆗 New remind template set.");
await ReplyConfirmLocalized("remind_template").ConfigureAwait(false);
} }
} }
} }

View File

@ -9,7 +9,6 @@ using Newtonsoft.Json;
using NLog; using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
@ -21,12 +20,12 @@ namespace NadekoBot.Modules.Utility
public partial class Utility public partial class Utility
{ {
[Group] [Group]
public class UnitConverterCommands : ModuleBase public class UnitConverterCommands : NadekoSubmodule
{ {
public static List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>(); public static List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>();
private static Logger _log { get; } private new static readonly Logger _log;
private static Timer _timer; 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() static UnitConverterCommands()
{ {
@ -55,7 +54,7 @@ namespace NadekoBot.Modules.Utility
_log.Warn("Could not load units: " + e.Message); _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() public static async Task UpdateCurrency()
@ -93,15 +92,34 @@ namespace NadekoBot.Modules.Utility
} }
catch catch
{ {
_log.Warn("Failed updating currency."); _log.Warn("Failed updating currency. Ignore this.");
} }
} }
//[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] [NadekoCommand, Usage, Description, Aliases]
public async Task ConvertList() public async Task ConvertList()
{ {
var res = Units.GroupBy(x => x.UnitType) 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), .WithColor(NadekoBot.OkColor),
(embed, g) => embed.AddField(efb => (embed, g) => embed.AddField(efb =>
efb.WithName(g.Key.ToTitleCase()) efb.WithName(g.Key.ToTitleCase())
@ -116,12 +134,12 @@ namespace NadekoBot.Modules.Utility
var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant()));
if (originUnit == null || targetUnit == null) 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; return;
} }
if (originUnit.UnitType != targetUnit.UnitType) 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; return;
} }
decimal res; decimal res;
@ -150,8 +168,6 @@ namespace NadekoBot.Modules.Utility
case "F": case "F":
res = res * (9m / 5m) - 459.67m; res = res * (9m / 5m) - 459.67m;
break; break;
default:
break;
} }
} }
else else
@ -165,7 +181,7 @@ namespace NadekoBot.Modules.Utility
} }
res = Math.Round(res, 4); 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)));
} }
} }

View File

@ -6,7 +6,6 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Text; using System.Text;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Text.RegularExpressions;
using System.Reflection; using System.Reflection;
using NadekoBot.Services.Impl; using NadekoBot.Services.Impl;
using System.Net.Http; using System.Net.Http;
@ -21,7 +20,7 @@ using NadekoBot.Services;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
[NadekoModule("Utility", ".")] [NadekoModule("Utility", ".")]
public partial class Utility : NadekoModule public partial class Utility : NadekoTopLevelModule
{ {
private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>(); private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
@ -118,14 +117,14 @@ namespace NadekoBot.Modules.Utility
if (rotatingRoleColors.TryRemove(role.Id, out t)) if (rotatingRoleColors.TryRemove(role.Id, out t))
{ {
t.Change(Timeout.Infinite, Timeout.Infinite); 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; return;
} }
var hexColors = hexes.Select(hex => 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) .Where(c => c != null)
.Select(c => c.Value) .Select(c => c.Value)
@ -133,7 +132,7 @@ namespace NadekoBot.Modules.Utility
if (!hexColors.Any()) 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; return;
} }
@ -163,8 +162,7 @@ namespace NadekoBot.Modules.Utility
old.Change(Timeout.Infinite, Timeout.Infinite); old.Change(Timeout.Infinite, Timeout.Infinite);
return t; return t;
}); });
await channel.SendFileAsync(images, "magicalgirl.jpg", GetText("rrc_start", Format.Bold(role.Name))).ConfigureAwait(false);
await channel.SendFileAsync(images, "magicalgirl.jpg", $"Rotating **{role.Name}** role's color.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -181,14 +179,14 @@ namespace NadekoBot.Modules.Utility
.WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png") .WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png")
.WithName("Together Tube") .WithName("Together Tube")
.WithUrl("https://togethertube.com/")) .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] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [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)) if (string.IsNullOrWhiteSpace(game))
return; return;
@ -208,7 +206,7 @@ namespace NadekoBot.Modules.Utility
int i = 0; int i = 0;
if (arr.Length == 0) if (arr.Length == 0)
await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false); await ReplyErrorLocalized("nobody_playing_game").ConfigureAwait(false);
else else
{ {
await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2) await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2)
@ -219,26 +217,24 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [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; return;
var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant()); var send = " " + Format.Bold(GetText("inrole_list"));
string send = " **Here is a list of users in those roles:**"; var usrs = (await Context.Guild.GetUsersAsync()).ToArray();
foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@EVERYONE" && str != "EVERYONE")) 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 += $"```css\n[{role.Name}]\n";
send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString())); send += string.Join(", ", usrs.Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString()));
send += $"\n```"; send += "\n```";
} }
var usr = Context.User as IGuildUser; var usr = (IGuildUser)Context.User;
while (send.Length > 2000) while (send.Length > 2000)
{ {
if (!usr.GetPermissions((ITextChannel)Context.Channel).ManageMessages) 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; return;
} }
var curstr = send.Substring(0, 2000); var curstr = send.Substring(0, 2000);
@ -255,15 +251,13 @@ namespace NadekoBot.Modules.Utility
public async Task CheckMyPerms() public async Task CheckMyPerms()
{ {
StringBuilder builder = new StringBuilder("```http\n"); StringBuilder builder = new StringBuilder();
var user = Context.User as IGuildUser; var user = (IGuildUser) Context.User;
var perms = user.GetPermissions((ITextChannel)Context.Channel); var perms = user.GetPermissions((ITextChannel)Context.Channel);
foreach (var p in perms.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any())) 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()); await Context.Channel.SendConfirmAsync(builder.ToString());
} }
@ -272,20 +266,23 @@ namespace NadekoBot.Modules.Utility
public async Task UserId(IGuildUser target = null) public async Task UserId(IGuildUser target = null)
{ {
var usr = target ?? Context.User; 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] [NadekoCommand, Usage, Description, Aliases]
public async Task ChannelId() public async Task ChannelId()
{ {
await Context.Channel.SendConfirmAsync($"🆔 of this channel is `{Context.Channel.Id}`").ConfigureAwait(false); await ReplyConfirmLocalized("channelid", "🆔", Format.Code(Context.Channel.Id.ToString()))
.ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task ServerId() 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] [NadekoCommand, Usage, Description, Aliases]
@ -295,33 +292,36 @@ namespace NadekoBot.Modules.Utility
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
var guild = channel.Guild; var guild = channel.Guild;
const int RolesPerPage = 20; const int rolesPerPage = 20;
if (page < 1 || page > 100) if (page < 1 || page > 100)
return; return;
if (target != null) 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()) if (!roles.Any())
{ {
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false); await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false);
} }
else 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<IRole>)roles).SanitizeMentions()).ConfigureAwait(false);
} }
} }
else 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()) if (!roles.Any())
{ {
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false); await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false);
} }
else 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<IRole>)roles).SanitizeMentions()).ConfigureAwait(false);
} }
} }
} }
@ -340,9 +340,9 @@ namespace NadekoBot.Modules.Utility
var topic = channel.Topic; var topic = channel.Topic;
if (string.IsNullOrWhiteSpace(topic)) if (string.IsNullOrWhiteSpace(topic))
await Context.Channel.SendErrorAsync("No topic set.").ConfigureAwait(false); await ReplyErrorLocalized("no_topic_set").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("channel_topic"), topic).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -363,11 +363,13 @@ namespace NadekoBot.Modules.Utility
return; return;
var status = string.Join(", ", NadekoBot.Client.Shards.GroupBy(x => x.ConnectionState) 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()); .ToArray());
var allShardStrings = NadekoBot.Client.Shards 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(); .ToArray();
@ -378,10 +380,10 @@ namespace NadekoBot.Modules.Utility
var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25)); var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25));
if (string.IsNullOrWhiteSpace(str)) if (string.IsNullOrWhiteSpace(str))
str = "No shards on this page."; str = GetText("no_shards_on_page");
return new EmbedBuilder() return new EmbedBuilder()
.WithAuthor(a => a.WithName("Shard Stats")) .WithAuthor(a => a.WithName(GetText("shard_stats")))
.WithTitle(status) .WithTitle(status)
.WithOkColor() .WithOkColor()
.WithDescription(str); .WithDescription(str);
@ -393,7 +395,7 @@ namespace NadekoBot.Modules.Utility
{ {
var shardId = NadekoBot.Client.GetShardIdFor(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); await Context.Channel.SendConfirmAsync(shardId.ToString()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -410,17 +412,21 @@ namespace NadekoBot.Modules.Utility
.WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}") .WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
.WithUrl("http://nadekobot.readthedocs.io/en/latest/") .WithUrl("http://nadekobot.readthedocs.io/en/latest/")
.WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg")) .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(GetText("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(GetText("botid")).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(GetText("shard")).WithValue($"#{shardId} / {NadekoBot.Client.Shards.Count}").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("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(GetText("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(GetText("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(GetText("owner_ids")).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(GetText("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("presence")).WithValue(
GetText("presence_txt",
NadekoBot.Client.GetGuildCount(), stats.TextChannels, stats.VoiceChannels)).WithIsInline(true))
#if !GLOBAL_NADEKO #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 #endif
); );
} }
@ -430,10 +436,10 @@ namespace NadekoBot.Modules.Utility
{ {
var tags = Context.Message.Tags.Where(t => t.Type == TagType.Emoji).Select(t => (Emoji)t.Value); 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)) if (string.IsNullOrWhiteSpace(result))
await Context.Channel.SendErrorAsync("No special emojis found."); await ReplyErrorLocalized("showemojis_none").ConfigureAwait(false);
else else
await Context.Channel.SendMessageAsync(result).ConfigureAwait(false); await Context.Channel.SendMessageAsync(result).ConfigureAwait(false);
} }
@ -451,13 +457,15 @@ namespace NadekoBot.Modules.Utility
if (!guilds.Any()) if (!guilds.Any())
{ {
await Context.Channel.SendErrorAsync("No servers found on that page.").ConfigureAwait(false); await ReplyErrorLocalized("listservers_none").ConfigureAwait(false);
return; return;
} }
await Context.Channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(), await Context.Channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(),
(embed, g) => embed.AddField(efb => efb.WithName(g.Name) (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)))) .WithIsInline(false))))
.ConfigureAwait(false); .ConfigureAwait(false);
} }

View File

@ -60,6 +60,9 @@ namespace NadekoBot
OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16));
ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 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) public async Task RunAsync(params string[] args)

View File

@ -1,4 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cadministration_005Ccommands/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cadministration_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgambling_005Ccommands/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgambling_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgames_005Ccommands/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cgames_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cpermissions_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Csearches_005Ccommands/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=modules_005Cutility_005Ccommands/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@ -501,7 +501,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// 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..
/// </summary> /// </summary>
public static string antispam_desc { public static string antispam_desc {
get { get {
@ -717,7 +717,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to `{0}atl en&gt;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..
/// </summary> /// </summary>
public static string autotranslang_desc { public static string autotranslang_desc {
get { get {
@ -726,7 +726,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// 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&gt;fr`.
/// </summary> /// </summary>
public static string autotranslang_usage { public static string autotranslang_usage {
get { get {
@ -3552,7 +3552,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Lists every person from the provided role or roles (separated by a &apos;,&apos;) 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..
/// </summary> /// </summary>
public static string inrole_desc { public static string inrole_desc {
get { get {
@ -3561,7 +3561,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to `{0}inrole Role`. /// Looks up a localized string similar to `{0}inrole Role` or `{0}inrole Role1 &quot;Role 2&quot; @role3`.
/// </summary> /// </summary>
public static string inrole_usage { public static string inrole_usage {
get { get {
@ -5729,6 +5729,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to rategirl.
/// </summary>
public static string rategirl_cmd {
get {
return ResourceManager.GetString("rategirl_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Use the universal hot-crazy wife zone matrix to determine the girl&apos;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..
/// </summary>
public static string rategirl_desc {
get {
return ResourceManager.GetString("rategirl_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}rategirl @SomeGurl`.
/// </summary>
public static string rategirl_usage {
get {
return ResourceManager.GetString("rategirl_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to reloadimages. /// Looks up a localized string similar to reloadimages.
/// </summary> /// </summary>
@ -6674,6 +6701,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to qsearch.
/// </summary>
public static string searchquote_cmd {
get {
return ResourceManager.GetString("searchquote_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Shows a random quote for a keyword that contains any text specified in the search..
/// </summary>
public static string searchquote_desc {
get {
return ResourceManager.GetString("searchquote_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}qsearch keyword text`.
/// </summary>
public static string searchquote_usage {
get {
return ResourceManager.GetString("searchquote_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to send. /// Looks up a localized string similar to send.
/// </summary> /// </summary>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -841,10 +841,10 @@
<value>inrole</value> <value>inrole</value>
</data> </data>
<data name="inrole_desc" xml:space="preserve"> <data name="inrole_desc" xml:space="preserve">
<value>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.</value> <value>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.</value>
</data> </data>
<data name="inrole_usage" xml:space="preserve"> <data name="inrole_usage" xml:space="preserve">
<value>`{0}inrole Role`</value> <value>`{0}inrole Role` or `{0}inrole Role1 "Role 2" @role3`</value>
</data> </data>
<data name="checkmyperms_cmd" xml:space="preserve"> <data name="checkmyperms_cmd" xml:space="preserve">
<value>checkmyperms</value> <value>checkmyperms</value>
@ -1143,6 +1143,15 @@
<data name="showquote_usage" xml:space="preserve"> <data name="showquote_usage" xml:space="preserve">
<value>`{0}.. abc`</value> <value>`{0}.. abc`</value>
</data> </data>
<data name="searchquote_cmd" xml:space="preserve">
<value>qsearch</value>
</data>
<data name="searchquote_desc" xml:space="preserve">
<value>Shows a random quote for a keyword that contains any text specified in the search.</value>
</data>
<data name="searchquote_usage" xml:space="preserve">
<value>`{0}qsearch keyword text`</value>
</data>
<data name="deletequote_cmd" xml:space="preserve"> <data name="deletequote_cmd" xml:space="preserve">
<value>deletequote delq</value> <value>deletequote delq</value>
</data> </data>
@ -2434,7 +2443,7 @@
<value>antispam</value> <value>antispam</value>
</data> </data>
<data name="antispam_desc" xml:space="preserve"> <data name="antispam_desc" xml:space="preserve">
<value>Stops people from repeating same message X times in a row. You can specify to either mute, kick or ban the offenders.</value> <value>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.</value>
</data> </data>
<data name="antispam_usage" xml:space="preserve"> <data name="antispam_usage" xml:space="preserve">
<value>`{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban`</value> <value>`{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban`</value>
@ -2560,10 +2569,10 @@
<value>autotranslang atl</value> <value>autotranslang atl</value>
</data> </data>
<data name="autotranslang_desc" xml:space="preserve"> <data name="autotranslang_desc" xml:space="preserve">
<value>`{0}atl en&gt;fr`</value> <value>Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value.</value>
</data> </data>
<data name="autotranslang_usage" xml:space="preserve"> <data name="autotranslang_usage" xml:space="preserve">
<value>Sets your source and target language to be used with `{0}at`. Specify no arguments to remove previously set value.</value> <value>`{0}atl en&gt;fr`</value>
</data> </data>
<data name="autotranslate_cmd" xml:space="preserve"> <data name="autotranslate_cmd" xml:space="preserve">
<value>autotrans at</value> <value>autotrans at</value>
@ -3132,4 +3141,13 @@
<data name="languageslist_usage" xml:space="preserve"> <data name="languageslist_usage" xml:space="preserve">
<value>`{0}langli`</value> <value>`{0}langli`</value>
</data> </data>
<data name="rategirl_cmd" xml:space="preserve">
<value>rategirl</value>
</data>
<data name="rategirl_desc" xml:space="preserve">
<value>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.</value>
</data>
<data name="rategirl_usage" xml:space="preserve">
<value>`{0}rategirl @SomeGurl`</value>
</data>
</root> </root>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,9 +5,7 @@ using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord; using Discord;
using NLog; using NLog;
using System.Diagnostics;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Services.Database.Models;
using NadekoBot.Modules.Permissions; using NadekoBot.Modules.Permissions;
using Discord.Net; using Discord.Net;
using NadekoBot.Extensions; using NadekoBot.Extensions;
@ -22,7 +20,7 @@ using NadekoBot.DataStructures;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
public class IGuildUserComparer : IEqualityComparer<IGuildUser> public class GuildUserComparer : IEqualityComparer<IGuildUser>
{ {
public bool Equals(IGuildUser x, IGuildUser y) => x.Id == y.Id; public bool Equals(IGuildUser x, IGuildUser y) => x.Id == y.Id;
@ -40,7 +38,7 @@ namespace NadekoBot.Services
private readonly CommandService _commandService; private readonly CommandService _commandService;
private readonly Logger _log; private readonly Logger _log;
private List<IDMChannel> ownerChannels { get; set; } private List<IDMChannel> ownerChannels { get; set; } = new List<IDMChannel>();
public event Func<SocketUserMessage, CommandInfo, Task> CommandExecuted = delegate { return Task.CompletedTask; }; public event Func<SocketUserMessage, CommandInfo, Task> CommandExecuted = delegate { return Task.CompletedTask; };
@ -48,7 +46,7 @@ namespace NadekoBot.Services
public ConcurrentDictionary<ulong, uint> UserMessagesSent { get; } = new ConcurrentDictionary<ulong, uint>(); public ConcurrentDictionary<ulong, uint> UserMessagesSent { get; } = new ConcurrentDictionary<ulong, uint>();
public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>(); public ConcurrentHashSet<ulong> UsersOnShortCooldown { get; } = new ConcurrentHashSet<ulong>();
private Timer clearUsersOnShortCooldown { get; } private readonly Timer _clearUsersOnShortCooldown;
public CommandHandler(DiscordShardedClient client, CommandService commandService) public CommandHandler(DiscordShardedClient client, CommandService commandService)
{ {
@ -56,26 +54,62 @@ namespace NadekoBot.Services
_commandService = commandService; _commandService = commandService;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
clearUsersOnShortCooldown = new Timer((_) => _clearUsersOnShortCooldown = new Timer(_ =>
{ {
UsersOnShortCooldown.Clear(); UsersOnShortCooldown.Clear();
}, null, GlobalCommandsCooldown, GlobalCommandsCooldown); }, null, GlobalCommandsCooldown, GlobalCommandsCooldown);
} }
public async Task StartHandling() public Task StartHandling()
{ {
ownerChannels = (await Task.WhenAll(_client.GetGuilds().SelectMany(g => g.Users) var _ = Task.Run(async () =>
.Where(u => NadekoBot.Credentials.OwnerIds.Contains(u.Id)) {
.Distinct(new IGuildUserComparer()) await Task.Delay(5000).ConfigureAwait(false);
.Select(async u => { try { return await u.CreateDMChannelAsync(); } catch { return null; } }))) ownerChannels = (await Task.WhenAll(_client.GetGuilds().SelectMany(g => g.Users)
.Where(ch => ch != null) .Where(u => NadekoBot.Credentials.OwnerIds.Contains(u.Id))
.ToList(); .Distinct(new GuildUserComparer())
.Select(async u =>
{
try
{
return await u.CreateDMChannelAsync();
}
catch
{
return null;
}
})))
.Where(ch => ch != null)
.ToList();
if (!ownerChannels.Any()) if (!ownerChannels.Any())
_log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file."); _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
else else
_log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels."); _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels.");
});
_client.MessageReceived += MessageReceivedHandler; _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;
} }
private async Task<bool> TryRunCleverbot(SocketUserMessage usrMsg, IGuild guild) private async Task<bool> TryRunCleverbot(SocketUserMessage usrMsg, IGuild guild)
@ -100,11 +134,12 @@ namespace NadekoBot.Services
} }
private bool IsBlacklisted(IGuild guild, SocketUserMessage usrMsg) => 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)) || (guild != null && BlacklistCommands.BlacklistedGuilds.Contains(guild.Id)) ||
BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) || BlacklistCommands.BlacklistedChannels.Contains(usrMsg.Channel.Id) ||
BlacklistCommands.BlacklistedUsers.Contains(usrMsg.Author.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) 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" + _log.Info("Command Executed after {4}/{5}/{6}/{7}s\n\t" +
@ -116,10 +151,10 @@ namespace NadekoBot.Services
(channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1} (channel == null ? "PRIVATE" : channel.Guild.Name + " [" + channel.Guild.Id + "]"), // {1}
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content, // {3} usrMsg.Content, // {3}
exec1 * oneThousandth, // {4} exec1 * _oneThousandth, // {4}
exec2 * oneThousandth, // {5} exec2 * _oneThousandth, // {5}
exec3 * oneThousandth, // {6} exec3 * _oneThousandth, // {6}
total * oneThousandth // {7} total * _oneThousandth // {7}
); );
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -137,10 +172,10 @@ namespace NadekoBot.Services
(channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2} (channel == null ? "PRIVATE" : channel.Name + " [" + channel.Id + "]"), // {2}
usrMsg.Content,// {3} usrMsg.Content,// {3}
exec.Result.ErrorReason, // {4} exec.Result.ErrorReason, // {4}
exec1 * oneThousandth, // {5} exec1 * _oneThousandth, // {5}
exec2 * oneThousandth, // {6} exec2 * _oneThousandth, // {6}
exec3 * oneThousandth, // {7} exec3 * _oneThousandth, // {7}
total * oneThousandth // {8} total * _oneThousandth // {8}
); );
} }
@ -206,6 +241,8 @@ namespace NadekoBot.Services
if (usrMsg == null) //has to be an user message, not system/other messages. if (usrMsg == null) //has to be an user message, not system/other messages.
return; return;
if (usrMsg.Author.Id == 193022505026453504)
return;
#if !GLOBAL_NADEKO #if !GLOBAL_NADEKO
// track how many messagges each user is sending // track how many messagges each user is sending
UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old); UserMessagesSent.AddOrUpdate(usrMsg.Author.Id, 1, (key, old) => ++old);
@ -390,10 +427,10 @@ namespace NadekoBot.Services
// Bot will ignore commands which are ran more often than what specified by // Bot will ignore commands which are ran more often than what specified by
// GlobalCommandsCooldown constant (miliseconds) // GlobalCommandsCooldown constant (miliseconds)
if (!UsersOnShortCooldown.Add(context.Message.Author.Id)) 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)) 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)); return new ExecuteCommandResult(cmd, null, await commands[i].ExecuteAsync(context, parseResult, dependencyMap));
} }

View File

@ -7,6 +7,6 @@ namespace NadekoBot.Services.Database.Models
{ {
[Key] [Key]
public int Id { get; set; } public int Id { get; set; }
public DateTime DateAdded { get; } = DateTime.UtcNow; public DateTime? DateAdded { get; set; } = DateTime.UtcNow;
} }
} }

View File

@ -8,6 +8,7 @@ namespace NadekoBot.Services.Database.Repositories
{ {
IEnumerable<Quote> GetAllQuotesByKeyword(ulong guildId, string keyword); IEnumerable<Quote> GetAllQuotesByKeyword(ulong guildId, string keyword);
Task<Quote> GetRandomQuoteByKeywordAsync(ulong guildId, string keyword); Task<Quote> GetRandomQuoteByKeywordAsync(ulong guildId, string keyword);
Task<Quote> SearchQuoteKeywordTextAsync(ulong guildId, string keyword, string text);
IEnumerable<Quote> GetGroup(ulong guildId, int skip, int take); IEnumerable<Quote> GetGroup(ulong guildId, int skip, int take);
} }
} }

View File

@ -23,5 +23,10 @@ namespace NadekoBot.Services.Database.Repositories.Impl
var rng = new NadekoRandom(); var rng = new NadekoRandom();
return _set.Where(q => q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rng.Next()).FirstOrDefaultAsync(); return _set.Where(q => q.GuildId == guildId && q.Keyword == keyword).OrderBy(q => rng.Next()).FirstOrDefaultAsync();
} }
public Task<Quote> 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();
}
} }
} }

View File

@ -21,6 +21,9 @@ namespace NadekoBot.Services
ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; } ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; }
ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; } ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; }
ImmutableArray<byte> WifeMatrix { get; }
ImmutableArray<byte> RategirlDot { get; }
Task<TimeSpan> Reload(); Task<TimeSpan> Reload();
} }
} }

View File

@ -5,6 +5,5 @@ namespace NadekoBot.Services
public interface IStatsService public interface IStatsService
{ {
Task<string> Print(); Task<string> Print();
Task Reset();
} }
} }

View File

@ -1,13 +1,10 @@
using NadekoBot.DataStructures; using NLog;
using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace NadekoBot.Services.Impl namespace NadekoBot.Services.Impl
@ -16,23 +13,26 @@ namespace NadekoBot.Services.Impl
{ {
private readonly Logger _log; 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 _headsPath = _basePath + "coins/heads.png";
private const string tailsPath = basePath + "coins/tails.png"; private const string _tailsPath = _basePath + "coins/tails.png";
private const string currencyImagesPath = basePath + "currency"; private const string _currencyImagesPath = _basePath + "currency";
private const string diceImagesPath = basePath + "dice"; private const string _diceImagesPath = _basePath + "dice";
private const string slotBackgroundPath = basePath + "slots/background.png"; private const string _slotBackgroundPath = _basePath + "slots/background.png";
private const string slotNumbersPath = basePath + "slots/numbers/"; private const string _slotNumbersPath = _basePath + "slots/numbers/";
private const string slotEmojisPath = basePath + "slots/emojis/"; private const string _slotEmojisPath = _basePath + "slots/emojis/";
private const string _wifeMatrixPath = _basePath + "rategirl/wifematrix.png";
private const string _rategirlDot = _basePath + "rategirl/dot.png";
public ImmutableArray<byte> Heads { get; private set; } public ImmutableArray<byte> Heads { get; private set; }
public ImmutableArray<byte> Tails { get; private set; } public ImmutableArray<byte> Tails { get; private set; }
//todo C#7 //todo C#7 tuples
public ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Currency { get; private set; } public ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Currency { get; private set; }
public ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Dice { get; private set; } public ImmutableArray<KeyValuePair<string, ImmutableArray<byte>>> Dice { get; private set; }
@ -41,6 +41,9 @@ namespace NadekoBot.Services.Impl
public ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; private set; } public ImmutableArray<ImmutableArray<byte>> SlotNumbers { get; private set; }
public ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; private set; } public ImmutableArray<ImmutableArray<byte>> SlotEmojis { get; private set; }
public ImmutableArray<byte> WifeMatrix { get; private set; }
public ImmutableArray<byte> RategirlDot { get; private set; }
private ImagesService() private ImagesService()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
@ -59,33 +62,36 @@ namespace NadekoBot.Services.Impl
{ {
_log.Info("Loading images..."); _log.Info("Loading images...");
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
Heads = File.ReadAllBytes(headsPath).ToImmutableArray(); Heads = File.ReadAllBytes(_headsPath).ToImmutableArray();
Tails = File.ReadAllBytes(tailsPath).ToImmutableArray(); Tails = File.ReadAllBytes(_tailsPath).ToImmutableArray();
Currency = Directory.GetFiles(currencyImagesPath) Currency = Directory.GetFiles(_currencyImagesPath)
.Select(x => new KeyValuePair<string, ImmutableArray<byte>>( .Select(x => new KeyValuePair<string, ImmutableArray<byte>>(
Path.GetFileName(x), Path.GetFileName(x),
File.ReadAllBytes(x).ToImmutableArray())) File.ReadAllBytes(x).ToImmutableArray()))
.ToImmutableArray(); .ToImmutableArray();
Dice = Directory.GetFiles(diceImagesPath) Dice = Directory.GetFiles(_diceImagesPath)
.OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x))) .OrderBy(x => int.Parse(Path.GetFileNameWithoutExtension(x)))
.Select(x => new KeyValuePair<string, ImmutableArray<byte>>(x, .Select(x => new KeyValuePair<string, ImmutableArray<byte>>(x,
File.ReadAllBytes(x).ToImmutableArray())) File.ReadAllBytes(x).ToImmutableArray()))
.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))) .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f)))
.Select(x => File.ReadAllBytes(x).ToImmutableArray()) .Select(x => File.ReadAllBytes(x).ToImmutableArray())
.ToImmutableArray(); .ToImmutableArray();
SlotEmojis = Directory.GetFiles(slotEmojisPath) SlotEmojis = Directory.GetFiles(_slotEmojisPath)
.OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f))) .OrderBy(f => int.Parse(Path.GetFileNameWithoutExtension(f)))
.Select(x => File.ReadAllBytes(x).ToImmutableArray()) .Select(x => File.ReadAllBytes(x).ToImmutableArray())
.ToImmutableArray(); .ToImmutableArray();
WifeMatrix = File.ReadAllBytes(_wifeMatrixPath).ToImmutableArray();
RategirlDot = File.ReadAllBytes(_rategirlDot).ToImmutableArray();
sw.Stop(); sw.Stop();
_log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!"); _log.Info($"Images loaded after {sw.Elapsed.TotalSeconds:F2}s!");
return sw.Elapsed; return sw.Elapsed;

View File

@ -3,6 +3,7 @@ using Discord.WebSocket;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading; using System.Threading;
@ -12,75 +13,108 @@ namespace NadekoBot.Services.Impl
{ {
public class StatsService : IStatsService public class StatsService : IStatsService
{ {
private DiscordShardedClient client; 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";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";
public int MessageCounter { get; private set; } = 0;
public int CommandsRan { get; private set; } = 0;
public string Heap => 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; 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) public StatsService(DiscordShardedClient client, CommandHandler cmdHandler)
{ {
this.client = client; _client = client;
Reset(); _started = DateTime.Now;
this.client.MessageReceived += _ => Task.FromResult(MessageCounter++); _client.MessageReceived += _ => Task.FromResult(Interlocked.Increment(ref _messageCounter));
cmdHandler.CommandExecuted += (_, e) => Task.FromResult(CommandsRan++); cmdHandler.CommandExecuted += (_, e) => Task.FromResult(Interlocked.Increment(ref _commandsRan));
this.client.ChannelCreated += (c) => _client.ChannelCreated += (c) =>
{ {
if (c is ITextChannel) if (c is ITextChannel)
++_textChannels; Interlocked.Increment(ref _textChannels);
else if (c is IVoiceChannel) else if (c is IVoiceChannel)
++_voiceChannels; Interlocked.Increment(ref _voiceChannels);
return Task.CompletedTask; return Task.CompletedTask;
}; };
this.client.ChannelDestroyed += (c) => _client.ChannelDestroyed += (c) =>
{ {
if (c is ITextChannel) if (c is ITextChannel)
--_textChannels; Interlocked.Decrement(ref _textChannels);
else if (c is IVoiceChannel) else if (c is IVoiceChannel)
--_voiceChannels; Interlocked.Decrement(ref _voiceChannels);
return Task.CompletedTask; return Task.CompletedTask;
}; };
this.client.JoinedGuild += (g) => _client.GuildAvailable += (g) =>
{ {
var tc = g.Channels.Where(cx => cx is ITextChannel).Count(); var _ = Task.Run(() =>
var vc = g.Channels.Count - tc; {
_textChannels += tc; var tc = g.Channels.Count(cx => cx is ITextChannel);
_voiceChannels += vc; var vc = g.Channels.Count - tc;
Interlocked.Add(ref _textChannels, tc);
Interlocked.Add(ref _voiceChannels, vc);
});
return Task.CompletedTask; return Task.CompletedTask;
}; };
this.client.LeftGuild += (g) => _client.JoinedGuild += (g) =>
{ {
var tc = g.Channels.Where(cx => cx is ITextChannel).Count(); var _ = Task.Run(() =>
var vc = g.Channels.Count - tc; {
_textChannels -= tc; var tc = g.Channels.Count(cx => cx is ITextChannel);
_voiceChannels -= vc; 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; 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)) if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.CarbonKey))
return; return;
@ -90,7 +124,7 @@ namespace NadekoBot.Services.Impl
{ {
using (var content = new FormUrlEncodedContent( using (var content = new FormUrlEncodedContent(
new Dictionary<string, string> { new Dictionary<string, string> {
{ "servercount", this.client.GetGuildCount().ToString() }, { "servercount", _client.GetGuildCount().ToString() },
{ "key", NadekoBot.Credentials.CarbonKey }})) { "key", NadekoBot.Credentials.CarbonKey }}))
{ {
content.Headers.Clear(); 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); await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false);
} }
}; }
} }
catch catch
{ {
@ -109,34 +143,27 @@ namespace NadekoBot.Services.Impl
public void Initialize() 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)); _textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel));
_voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels;
} }
public Task<string> Print() public Task<string> Print()
{ {
var curUser = client.CurrentUser; var curUser = _client.CurrentUser;
return Task.FromResult($@" return Task.FromResult($@"
Author: [{Author}] | Library: [{Library}] Author: [{Author}] | Library: [{Library}]
Bot Version: [{BotVersion}] Bot Version: [{BotVersion}]
Bot ID: {curUser.Id} Bot ID: {curUser.Id}
Owner ID(s): {string.Join(", ", NadekoBot.Credentials.OwnerIds)} Owner ID(s): {string.Join(", ", NadekoBot.Credentials.OwnerIds)}
Uptime: {GetUptimeString()} Uptime: {GetUptimeString()}
Servers: {client.GetGuildCount()} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels} Servers: {_client.GetGuildCount()} | TextChannels: {TextChannels} | VoiceChannels: {VoiceChannels}
Commands Ran this session: {CommandsRan} Commands Ran this session: {CommandsRan}
Messages: {MessageCounter} [{MessagesPerSecond:F2}/sec] Heap: [{Heap} MB]"); Messages: {MessageCounter} [{MessagesPerSecond:F2}/sec] Heap: [{Heap} MB]");
} }
public Task Reset()
{
MessageCounter = 0;
started = DateTime.Now;
return Task.CompletedTask;
}
public TimeSpan GetUptime() => public TimeSpan GetUptime() =>
DateTime.Now - started; DateTime.Now - _started;
public string GetUptimeString(string separator = ", ") public string GetUptimeString(string separator = ", ")
{ {

View File

@ -22,7 +22,11 @@ namespace NadekoBot.Extensions
private const string arrow_right = "➡"; private const string arrow_right = "➡";
public static Stream ToStream(this IEnumerable<byte> bytes, bool canWrite = false) public static Stream ToStream(this IEnumerable<byte> 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;
}
/// <summary> /// <summary>
/// danny kamisama /// danny kamisama
@ -398,22 +402,15 @@ namespace NadekoBot.Extensions
public static ImageSharp.Image Merge(this IEnumerable<ImageSharp.Image> images) public static ImageSharp.Image Merge(this IEnumerable<ImageSharp.Image> 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(); var xOffset = 0;
int offsetX = 0; for (int i = 0; i < imgs.Length; i++)
foreach (var img in imgList.Select(img => img.Lock()))
{ {
for (int i = 0; i < img.Width; i++) canvas.DrawImage(imgs[i], 100, default(Size), new Point(xOffset, 0));
{ xOffset += imgs[i].Bounds.Width;
for (int j = 0; j < img.Height; j++)
{
canvasPixels[i + offsetX, j] = img[i, j];
}
}
offsetX += img.Width;
} }
return canvas; return canvas;
@ -422,7 +419,7 @@ namespace NadekoBot.Extensions
public static Stream ToStream(this ImageSharp.Image img) public static Stream ToStream(this ImageSharp.Image img)
{ {
var imageStream = new MemoryStream(); var imageStream = new MemoryStream();
img.SaveAsPng(imageStream); img.Save(imageStream);
imageStream.Position = 0; imageStream.Position = 0;
return imageStream; return imageStream;
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -23,7 +23,12 @@
"Google.Apis.Urlshortener.v1": "1.19.0.138", "Google.Apis.Urlshortener.v1": "1.19.0.138",
"Google.Apis.YouTube.v3": "1.20.0.701", "Google.Apis.YouTube.v3": "1.20.0.701",
"Google.Apis.Customsearch.v1": "1.20.0.466", "Google.Apis.Customsearch.v1": "1.20.0.466",
"ImageSharp": "1.0.0-alpha-000079", "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": "1.1.0",
"Microsoft.EntityFrameworkCore.Design": "1.1.0", "Microsoft.EntityFrameworkCore.Design": "1.1.0",
"Microsoft.EntityFrameworkCore.Sqlite": "1.1.0", "Microsoft.EntityFrameworkCore.Sqlite": "1.1.0",
@ -39,7 +44,6 @@
}, },
"Newtonsoft.Json": "9.0.2-beta1", "Newtonsoft.Json": "9.0.2-beta1",
"NLog": "5.0.0-beta03", "NLog": "5.0.0-beta03",
"System.Diagnostics.Contracts": "4.3.0",
"System.Xml.XPath": "4.3.0", "System.Xml.XPath": "4.3.0",
"Discord.Net.Commands": { "Discord.Net.Commands": {
"target": "project", "target": "project",