Merge remote-tracking branch 'Kwoth/dev' into dev

This commit is contained in:
Shikhir Arora 2017-02-25 21:09:57 -05:00
commit 080ffcce57
75 changed files with 7474 additions and 1339 deletions

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; }

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))
@ -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));
@ -1068,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

@ -17,7 +17,7 @@ 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; } = new ConcurrentDictionary<ulong, List<ClashWar>>();

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

@ -272,12 +272,12 @@ namespace NadekoBot.Modules.Gambling
} }
private string GetText(string text) private string GetText(string text)
=> NadekoModule.GetTextStatic(text, => NadekoTopLevelModule.GetTextStatic(text,
NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild), NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild),
typeof(Gambling).Name.ToLowerInvariant()); typeof(Gambling).Name.ToLowerInvariant());
private string GetText(string text, params object[] replacements) private string GetText(string text, params object[] replacements)
=> NadekoModule.GetTextStatic(text, => NadekoTopLevelModule.GetTextStatic(text,
NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild), NadekoBot.Localization.GetCultureInfo(_raceChannel.Guild),
typeof(Gambling).Name.ToLowerInvariant(), typeof(Gambling).Name.ToLowerInvariant(),
replacements); replacements);

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
@ -35,7 +36,7 @@ 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);
@ -120,12 +121,12 @@ 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", await Context.Channel.SendFileAsync(ms, "dice.png",
Context.User.Mention + Context.User.Mention + " " +
GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) + GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) +
" " + GetText("Total: {1} Average: {2}", " " + GetText("total_average",
Format.Bold(values.Sum().ToString()), Format.Bold(values.Sum().ToString()),
Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false); Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false);
} }

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,18 +52,23 @@ namespace NadekoBot.Modules.Gambling
return; return;
} }
var imgs = new Image[count]; var imgs = new Image[count];
using (var heads = _images.Heads.ToStream())
using(var tails = _images.Tails.ToStream())
{
for (var i = 0; i < count; i++) for (var i = 0; i < count; i++)
{ {
imgs[i] = rng.Next(0, 10) < 5 ? using (var heads = _images.Heads.ToStream())
new Image(heads) : using (var tails = _images.Tails.ToStream())
new Image(tails); {
if (rng.Next(0, 10) < 5)
{
imgs[i] = new Image(heads);
}
else
{
imgs[i] = new Image(tails);
}
}
} }
await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false); await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
} }
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Betflip(int amount, string guess) public async Task Betflip(int amount, string guess)

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,28 +164,13 @@ 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 file = _images.SlotEmojis[numbers[i]].ToStream())
using (var randomImage = new ImageSharp.Image(file))
{ {
var randomImage = new ImageSharp.Image(file); bgImage.DrawImage(randomImage, 100, default(Size), new Point(95 + 142 * i, 330));
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];
}
}
}
} }
} }
@ -195,21 +181,9 @@ namespace NadekoBot.Modules.Gambling
{ {
var digit = printWon % 10; var digit = printWon % 10;
using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream()) using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream())
using (var img = new ImageSharp.Image(fs))
{ {
var img = new ImageSharp.Image(fs); bgImage.DrawImage(img, 100, default(Size), new Point(230 - n * 16, 462));
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++; n++;
} while ((printWon /= 10) != 0); } while ((printWon /= 10) != 0);
@ -220,25 +194,12 @@ namespace NadekoBot.Modules.Gambling
{ {
var digit = printAmount % 10; var digit = printAmount % 10;
using (var fs = _images.SlotNumbers[digit].ToStream()) using (var fs = _images.SlotNumbers[digit].ToStream())
using (var img = new ImageSharp.Image(fs))
{ {
var img = new ImageSharp.Image(fs); bgImage.DrawImage(img, 100, default(Size), new Point(395 - n * 16, 462));
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++; n++;
} while ((printAmount /= 10) != 0); } while ((printAmount /= 10) != 0);
}
var msg = GetText("better_luck"); var msg = GetText("better_luck");
if (result.Multiplier != 0) if (result.Multiplier != 0)

View File

@ -368,7 +368,7 @@ namespace NadekoBot.Modules.Gambling
if (waifus.Count == 0) if (waifus.Count == 0)
{ {
await ReplyConfirmLocalized("waifu_none").ConfigureAwait(false); await ReplyConfirmLocalized("waifus_none").ConfigureAwait(false);
return; return;
} }
@ -423,6 +423,8 @@ 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 nobody = GetText("nobody");
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
@ -432,7 +434,7 @@ namespace NadekoBot.Modules.Gambling
.AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true)) .AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true)) .AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName(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);
} }

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; }

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>();
@ -47,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);
} }
} }
} }
@ -61,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()
@ -107,18 +107,18 @@ namespace NadekoBot.Modules.Games
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()
@ -127,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)
@ -139,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",
Format.Bold(_submissions.First().Value.ToString())))
.WithFooter(efb => efb.WithText(_submissions.First().Key.ToLowerInvariant().ToTitleCase())))
.ConfigureAwait(false); .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)
{ {
@ -176,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;
@ -187,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();
@ -229,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))
@ -248,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;
} }
} }
@ -271,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("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

@ -1,37 +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 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]
@ -41,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);
@ -56,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,11 +186,12 @@ 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);
if (del != null)
await del.ConfigureAwait(false); 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("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,20 +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)
{
case 0:
return "🚀"; return "🚀";
if (p == 1) case 1:
return "📎"; return "📎";
default:
return "✂️"; return "✂️";
}
}; };
int pick; int pick;
@ -71,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

@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Music.Classes
//pwetty //pwetty
public string PrettyProvider => public string PrettyProvider =>
$"{(SongInfo.Provider ?? "No Provider")}"; $"{(SongInfo.Provider ?? "???")}";
public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime; public string PrettyFullTime => PrettyCurrentTime + " / " + PrettyTotalTime;

View File

@ -20,7 +20,7 @@ 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>();
@ -155,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]
@ -184,34 +191,31 @@ 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)
@ -221,19 +225,22 @@ namespace NadekoBot.Modules.Music
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;
@ -254,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}"));
@ -271,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())
@ -293,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").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -307,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]
@ -326,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 =>
@ -367,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]
@ -393,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",
@ -435,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);
@ -505,8 +512,7 @@ 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]
@ -532,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;
} }
@ -544,9 +550,9 @@ 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.SongUrl) .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);
@ -564,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]
@ -580,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").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -601,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);
} }
@ -618,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]
@ -655,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]
@ -670,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 ReplyConfirmLocalized("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;
@ -686,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;
@ -706,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);
@ -717,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)
{ {
@ -733,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)
{ {
@ -781,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]
@ -793,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]
@ -806,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("player_none").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;
@ -845,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);
@ -866,7 +878,10 @@ namespace NadekoBot.Modules.Music
true).ConfigureAwait(false); true).ConfigureAwait(false);
} }
} }
catch { } catch
{
// ignored
}
}; };
mp.OnStarted += async (player, song) => mp.OnStarted += async (player, song) =>
@ -876,7 +891,7 @@ namespace NadekoBot.Modules.Music
{ {
// ignored // ignored
} }
var sender = player as MusicPlayer; var sender = player;
if (sender == null) if (sender == null)
return; return;
try try
@ -884,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) =>
{ {
@ -897,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) =>
@ -911,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();
@ -939,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)
@ -948,8 +976,8 @@ namespace NadekoBot.Modules.Music
{ {
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false); //var queuedMessage = await textCh.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);

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;
@ -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,10 +87,9 @@ 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()
{ {
@ -94,9 +99,8 @@ namespace NadekoBot.Modules.Permissions
}, (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,14 +512,27 @@ 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())
{ {
@ -461,11 +553,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);
} }
if (action.Value)
{
await ReplyConfirmLocalized("cx_enable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(chnl.Name)).ConfigureAwait(false);
} }
catch (Exception ex) { else
_log.Error(ex); {
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,15 +19,13 @@ 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
@ -49,9 +45,9 @@ namespace NadekoBot.Modules.Searches
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 +71,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 +102,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 +122,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 +172,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 +181,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 +199,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 +208,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,23 +1,44 @@
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
{ {
[Group]
public class MemegenCommands : NadekoSubmodule
{
private static readonly ImmutableDictionary<char, string> _map = new Dictionary<char, string>()
{
{'?', "~q"},
{'%', "~p"},
{'#', "~h"},
{'/', "~s"},
{' ', "-"},
{'-', "--"},
{'_', "__"},
{'"', "''"}
}.ToImmutableDictionary();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Memelist() public async Task Memelist()
{ {
HttpClientHandler handler = new HttpClientHandler(); var handler = new HttpClientHandler
{
AllowAutoRedirect = false
};
handler.AllowAutoRedirect = false;
using (var http = new HttpClient(handler)) using (var http = new HttpClient(handler))
{ {
@ -32,10 +53,27 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Memegen(string meme, string topText, string botText) public async Task Memegen(string meme, string topText, string botText)
{ {
var top = Uri.EscapeDataString(topText.Replace(' ', '-')); var top = Replace(topText);
var bot = Uri.EscapeDataString(botText.Replace(' ', '-')); var bot = Replace(botText);
await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg") await Context.Channel.SendMessageAsync($"http://memegen.link/{meme}/{top}/{bot}.jpg")
.ConfigureAwait(false); .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();
}
}
} }
} }

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,14 +365,20 @@ 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);
@ -122,7 +118,7 @@ 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)
@ -174,7 +170,7 @@ 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)
@ -205,7 +201,7 @@ 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)
@ -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);
} }
@ -289,7 +286,7 @@ 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.ToString()) .WithTitle(Context.User.ToString())
@ -298,25 +295,21 @@ namespace NadekoBot.Modules.Searches
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
{ {
@ -327,49 +320,45 @@ 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
{ {
@ -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,15 +605,13 @@ 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 allUsrs = users.Append(Context.User);
var allUsrsArray = allUsrs.ToArray(); var allUsrsArray = allUsrs.ToArray();
@ -641,12 +619,7 @@ namespace NadekoBot.Modules.Searches
str += new NadekoRandom().Next(); str += new NadekoRandom().Next();
foreach (var usr in allUsrsArray) foreach (var usr in allUsrsArray)
{ {
await (await (usr as IGuildUser).CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false); await (await usr.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()
.Distinct(new MethodInfoEqualityComparer())
.Select(x => x.Name)
.Except(new[]
{ {
return x.Name; "ToString",
})
.Except(new[] { "ToString",
"Equals", "Equals",
"GetHashCode", "GetHashCode",
"GetType"}); "GetType"
await Context.Channel.SendConfirmAsync("Available functions in calc", string.Join(", ", selection)); });
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) { }
_log.Warn(ex); catch
{
// 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,12 +5,10 @@ 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.Diagnostics;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@ -22,14 +20,16 @@ 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; }
@ -39,8 +39,16 @@ namespace NadekoBot.Modules.Utility
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); //if (channel == null)
//{
// var guild = NadekoBot.Client.GetGuild(repeater.GuildId);
// Channel = guild.GetTextChannel(repeater.ChannelId);
//}
//else
// Channel = channel;
Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
if (Channel == null) if (Channel == null)
return; return;
Task.Run(Run); Task.Run(Run);
@ -101,22 +109,21 @@ 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.Select(gr => new RepeatRunner(gr))
.Where(gr => gr.Channel != null)))); .Where(gr => gr.Channel != null))));
_ready = true;
sw.Stop(); });
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -124,11 +131,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 +145,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 +160,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 +190,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 +201,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;
@ -216,13 +230,18 @@ namespace NadekoBot.Modules.Utility
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,26 +249,32 @@ 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

@ -33,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]
@ -113,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]
@ -135,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
{ {
@ -144,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)
@ -172,7 +173,7 @@ 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,7 +151,6 @@ 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 + " ";
} }
@ -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("channelidd", "🆔", 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("emojis_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 {
@ -2381,33 +2381,6 @@ 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 delmsgoncmd. /// Looks up a localized string similar to delmsgoncmd.
/// </summary> /// </summary>
@ -3579,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 {
@ -3588,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 {
@ -5756,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>
@ -6701,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>

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>
@ -2443,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>
@ -2569,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>
@ -3141,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

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

@ -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

@ -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

@ -14,9 +14,9 @@ namespace NadekoBot.Services.Impl
public class StatsService : IStatsService public class StatsService : IStatsService
{ {
private readonly 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-beta";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";

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-00090",
"ImageSharp.Processing": "1.0.0-alpha2-00078",
"ImageSharp.Formats.Png": "1.0.0-alpha2-00086",
"ImageSharp.Drawing": "1.0.0-alpha2-00090",
"ImageSharp.Drawing.Paths": "1.0.0-alpha2-00040",
//"ImageSharp.Formats.Jpeg": "1.0.0-alpha2-00090",
"Microsoft.EntityFrameworkCore": "1.1.0", "Microsoft.EntityFrameworkCore": "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",