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

View File

@ -16,7 +16,7 @@ using NLog;
namespace NadekoBot.Modules.Administration
{
[NadekoModule("Administration", ".")]
public partial class Administration : NadekoModule
public partial class Administration : NadekoTopLevelModule
{
private static ConcurrentHashSet<ulong> deleteMessagesOnCommand { get; }

View File

@ -126,7 +126,6 @@ namespace NadekoBot.Modules.Administration
{
embed.WithTitle("👥" + g.GetLogText("avatar_changed"))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithThumbnailUrl(before.AvatarUrl)
.WithImageUrl(after.AvatarUrl)
.WithFooter(fb => fb.WithText(currentTime))
@ -705,17 +704,18 @@ namespace NadekoBot.Modules.Administration
var embed = new EmbedBuilder()
.WithOkColor()
.WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name))
.WithDescription($"{msg.Author}")
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.WithDescription(msg.Author.ToString())
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(string.IsNullOrWhiteSpace(msg.Content) ? "-" : msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false))
.WithFooter(efb => efb.WithText(currentTime));
if (msg.Attachments.Any())
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);
}
catch
catch (Exception ex)
{
_log.Warn(ex);
// ignored
}
}
@ -753,8 +753,8 @@ namespace NadekoBot.Modules.Administration
.WithOkColor()
.WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name))
.WithDescription(after.Author.ToString())
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(string.IsNullOrWhiteSpace(before.Content) ? "-" : before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(string.IsNullOrWhiteSpace(after.Content) ? "-" : after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false))
.WithFooter(efb => efb.WithText(currentTime));
@ -1068,7 +1068,7 @@ namespace NadekoBot.Modules.Administration
public static class GuildExtensions
{
public static string GetLogText(this IGuild guild, string key, params object[] replacements)
=> NadekoModule.GetTextStatic(key,
=> NadekoTopLevelModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(guild),
typeof(Administration).Name.ToLowerInvariant(),
replacements);

View File

@ -17,7 +17,7 @@ using NLog;
namespace NadekoBot.Modules.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>>();

View File

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

View File

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

View File

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

View File

@ -10,6 +10,7 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using ImageSharp.Formats;
using Image = ImageSharp.Image;
namespace NadekoBot.Modules.Gambling
@ -35,7 +36,7 @@ namespace NadekoBot.Modules.Gambling
var imageStream = await Task.Run(() =>
{
var ms = new MemoryStream();
new[] { GetDice(num1), GetDice(num2) }.Merge().SaveAsPng(ms);
new[] { GetDice(num1), GetDice(num2) }.Merge().Save(ms);
ms.Position = 0;
return ms;
}).ConfigureAwait(false);
@ -120,12 +121,12 @@ namespace NadekoBot.Modules.Gambling
var bitmap = dice.Merge();
var ms = new MemoryStream();
bitmap.SaveAsPng(ms);
bitmap.Save(ms);
ms.Position = 0;
await Context.Channel.SendFileAsync(ms, "dice.png",
Context.User.Mention +
Context.User.Mention + " " +
GetText("dice_rolled_num", Format.Bold(values.Count.ToString())) +
" " + GetText("Total: {1} Average: {2}",
" " + GetText("total_average",
Format.Bold(values.Sum().ToString()),
Format.Bold((values.Sum() / (1.0f * values.Count)).ToString("N2")))).ConfigureAwait(false);
}

View File

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

View File

@ -52,17 +52,22 @@ namespace NadekoBot.Modules.Gambling
return;
}
var imgs = new Image[count];
using (var heads = _images.Heads.ToStream())
using(var tails = _images.Tails.ToStream())
for (var i = 0; i < count; i++)
{
for (var i = 0; i < count; i++)
using (var heads = _images.Heads.ToStream())
using (var tails = _images.Tails.ToStream())
{
imgs[i] = rng.Next(0, 10) < 5 ?
new Image(heads) :
new Image(tails);
if (rng.Next(0, 10) < 5)
{
imgs[i] = new Image(heads);
}
else
{
imgs[i] = new Image(tails);
}
}
await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
}
await Context.Channel.SendFileAsync(imgs.Merge().ToStream(), $"{count} coins.png").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]

View File

@ -1,5 +1,6 @@
using Discord;
using Discord.Commands;
using ImageSharp;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
@ -163,83 +164,43 @@ namespace NadekoBot.Modules.Gambling
var result = SlotMachine.Pull();
int[] numbers = result.Numbers;
using (var bgPixels = bgImage.Lock())
for (int i = 0; i < 3; i++)
{
for (int i = 0; i < 3; i++)
using (var file = _images.SlotEmojis[numbers[i]].ToStream())
using (var randomImage = new ImageSharp.Image(file))
{
using (var file = _images.SlotEmojis[numbers[i]].ToStream())
{
var randomImage = new ImageSharp.Image(file);
using (var toAdd = randomImage.Lock())
{
for (int j = 0; j < toAdd.Width; j++)
{
for (int k = 0; k < toAdd.Height; k++)
{
var x = 95 + 142 * i + j;
int y = 330 + k;
var toSet = toAdd[j, k];
if (toSet.A < _alphaCutOut)
continue;
bgPixels[x, y] = toAdd[j, k];
}
}
}
}
bgImage.DrawImage(randomImage, 100, default(Size), new Point(95 + 142 * i, 330));
}
var won = amount * result.Multiplier;
var printWon = won;
var n = 0;
do
{
var digit = printWon % 10;
using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream())
{
var img = new ImageSharp.Image(fs);
using (var pixels = img.Lock())
{
for (int i = 0; i < pixels.Width; i++)
{
for (int j = 0; j < pixels.Height; j++)
{
if (pixels[i, j].A < _alphaCutOut)
continue;
var x = 230 - n * 16 + i;
bgPixels[x, 462 + j] = pixels[i, j];
}
}
}
}
n++;
} while ((printWon /= 10) != 0);
var printAmount = amount;
n = 0;
do
{
var digit = printAmount % 10;
using (var fs = _images.SlotNumbers[digit].ToStream())
{
var img = new ImageSharp.Image(fs);
using (var pixels = img.Lock())
{
for (int i = 0; i < pixels.Width; i++)
{
for (int j = 0; j < pixels.Height; j++)
{
if (pixels[i, j].A < _alphaCutOut)
continue;
var x = 395 - n * 16 + i;
bgPixels[x, 462 + j] = pixels[i, j];
}
}
}
}
n++;
} while ((printAmount /= 10) != 0);
}
var won = amount * result.Multiplier;
var printWon = won;
var n = 0;
do
{
var digit = printWon % 10;
using (var fs = NadekoBot.Images.SlotNumbers[digit].ToStream())
using (var img = new ImageSharp.Image(fs))
{
bgImage.DrawImage(img, 100, default(Size), new Point(230 - n * 16, 462));
}
n++;
} while ((printWon /= 10) != 0);
var printAmount = amount;
n = 0;
do
{
var digit = printAmount % 10;
using (var fs = _images.SlotNumbers[digit].ToStream())
using (var img = new ImageSharp.Image(fs))
{
bgImage.DrawImage(img, 100, default(Size), new Point(395 - n * 16, 462));
}
n++;
} while ((printAmount /= 10) != 0);
var msg = GetText("better_luck");
if (result.Multiplier != 0)
{

View File

@ -368,7 +368,7 @@ namespace NadekoBot.Modules.Gambling
if (waifus.Count == 0)
{
await ReplyConfirmLocalized("waifu_none").ConfigureAwait(false);
await ReplyConfirmLocalized("waifus_none").ConfigureAwait(false);
return;
}
@ -423,6 +423,8 @@ namespace NadekoBot.Modules.Gambling
var claimInfo = GetClaimTitle(target.Id);
var affInfo = GetAffinityTitle(target.Id);
var rng = new NadekoRandom();
var nobody = GetText("nobody");
var embed = new EmbedBuilder()
.WithOkColor()
@ -432,7 +434,7 @@ namespace NadekoBot.Modules.Gambling
.AddField(efb => efb.WithName(GetText("likes")).WithValue(w.Affinity?.ToString() ?? nobody).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("changes_of_heart")).WithValue($"{affInfo.Count} - \"the {affInfo.Title}\"").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("divorces")).WithValue(divorces.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.Select(x => x.Waifu))).WithIsInline(true));
.AddField(efb => efb.WithName($"Waifus ({claims.Count})").WithValue(claims.Count == 0 ? nobody : string.Join("\n", claims.OrderBy(x => rng.Next()).Take(40).Select(x => x.Waifu))).WithIsInline(true));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}

View File

@ -12,7 +12,7 @@ using System.Collections.Generic;
namespace NadekoBot.Modules.Gambling
{
[NadekoModule("Gambling", "$")]
public partial class Gambling : NadekoModule
public partial class Gambling : NadekoTopLevelModule
{
public static string CurrencyName { get; set; }
public static string CurrencyPluralName { get; set; }

View File

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

View File

@ -1,37 +1,25 @@
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Modules.Games.Commands.Hangman;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using NadekoBot.Modules.Games.Hangman;
using Discord;
namespace NadekoBot.Modules.Games
{
public partial class Games
{
[Group]
public class HangmanCommands : ModuleBase
public class HangmanCommands : NadekoSubmodule
{
private static Logger _log { get; }
//channelId, game
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]
public async Task Hangmanlist()
{
await Context.Channel.SendConfirmAsync(typesStr);
await Context.Channel.SendConfirmAsync(Format.Code(GetText("hangman_types", Prefix)) + "\n" + string.Join(", ", HangmanTermPool.data.Keys));
}
[NadekoCommand, Usage, Description, Aliases]
@ -41,11 +29,11 @@ namespace NadekoBot.Modules.Games
if (!HangmanGames.TryAdd(Context.Channel.Id, hm))
{
await Context.Channel.SendErrorAsync("Hangman game already running on this channel.").ConfigureAwait(false);
await ReplyErrorLocalized("hangman_running").ConfigureAwait(false);
return;
}
hm.OnEnded += (g) =>
hm.OnEnded += g =>
{
HangmanGame throwaway;
HangmanGames.TryRemove(g.GameChannel.Id, out throwaway);
@ -56,14 +44,14 @@ namespace NadekoBot.Modules.Games
}
catch (Exception ex)
{
try { await Context.Channel.SendErrorAsync($"Starting errored: {ex.Message}").ConfigureAwait(false); } catch { }
try { await Context.Channel.SendErrorAsync(GetText("hangman_start_errored") + " " + ex.Message).ConfigureAwait(false); } catch { }
HangmanGame throwaway;
HangmanGames.TryRemove(Context.Channel.Id, out throwaway);
throwaway.Dispose();
return;
}
await Context.Channel.SendConfirmAsync("Hangman game started", hm.ScrambledWord + "\n" + hm.GetHangman());
await Context.Channel.SendConfirmAsync(GetText("hangman_game_started"), hm.ScrambledWord + "\n" + hm.GetHangman());
}
}
}

View File

@ -11,10 +11,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Games
@ -31,7 +28,7 @@ namespace NadekoBot.Modules.Games
[Group]
public class PlantPickCommands : NadekoSubmodule
{
private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
private static ConcurrentHashSet<ulong> generationChannels { get; }
//channelid/message
private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>();
//channelId/last generation
@ -82,25 +79,17 @@ namespace NadekoBot.Modules.Games
if (dropAmount > 0)
{
var msgs = new IUserMessage[dropAmount];
string firstPart;
if (dropAmount == 1)
{
firstPart = $"A random { NadekoBot.BotConfig.CurrencyName } appeared!";
}
else
{
firstPart = $"{dropAmount} random { NadekoBot.BotConfig.CurrencyPluralName } appeared!";
}
var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name];
var toSend = dropAmount == 1
? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign, prefix)
: GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign, prefix);
var file = GetRandomCurrencyImage();
using (var fileStream = file.Value.ToStream())
{
var sent = await channel.SendFileAsync(
fileStream,
file.Key,
string.Format("❗ {0} Pick it up by typing `{1}pick`", firstPart,
NadekoBot.ModulePrefixes[typeof(Games).Name]))
.ConfigureAwait(false);
toSend).ConfigureAwait(false);
msgs[0] = sent;
}
@ -117,6 +106,12 @@ namespace NadekoBot.Modules.Games
return Task.CompletedTask;
}
public static string GetLocalText(ITextChannel channel, string key, params object[] replacements) =>
NadekoTopLevelModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(channel.GuildId),
typeof(Games).Name.ToLowerInvariant(),
replacements);
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Pick()
@ -135,7 +130,8 @@ namespace NadekoBot.Modules.Games
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false);
var msg = await ReplyConfirmLocalized("picked", msgs.Count + NadekoBot.BotConfig.CurrencySign)
.ConfigureAwait(false);
msg.DeleteAfter(10);
}
@ -149,14 +145,19 @@ namespace NadekoBot.Modules.Games
var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false);
if (!removed)
{
await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencyPluralName}.").ConfigureAwait(false);
await ReplyErrorLocalized("not_enough", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
return;
}
var imgData = GetRandomCurrencyImage();
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]);
var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {Prefix}pick";
//todo upload all currency images to transfer.sh and use that one as cdn
//and then
var msgToSend = GetText("planted",
Format.Bold(Context.User.ToString()),
amount + NadekoBot.BotConfig.CurrencySign,
Prefix);
IUserMessage msg;
using (var toSend = imgData.Value.ToStream())
@ -203,11 +204,11 @@ namespace NadekoBot.Modules.Games
}
if (enabled)
{
await channel.SendConfirmAsync("Currency generation enabled on this channel.").ConfigureAwait(false);
await ReplyConfirmLocalized("curgen_enabled").ConfigureAwait(false);
}
else
{
await channel.SendConfirmAsync("Currency generation disabled on this channel.").ConfigureAwait(false);
await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,16 +4,29 @@ using NadekoBot.Services;
using System.Threading.Tasks;
using NadekoBot.Attributes;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Threading;
using NadekoBot.Extensions;
using System.Net.Http;
using ImageSharp;
using NadekoBot.DataStructures;
using NLog;
namespace NadekoBot.Modules.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]
public async Task Choose([Remainder] string list = null)
@ -21,7 +34,7 @@ namespace NadekoBot.Modules.Games
if (string.IsNullOrWhiteSpace(list))
return;
var listArr = list.Split(';');
if (listArr.Count() < 2)
if (listArr.Length < 2)
return;
var rng = new NadekoRandom();
await Context.Channel.SendConfirmAsync("🤔", listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
@ -34,20 +47,24 @@ namespace NadekoBot.Modules.Games
return;
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))
.AddField(efb => efb.WithName("🎱 8Ball").WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
.AddField(efb => efb.WithName("❓ " + GetText("question") ).WithValue(question).WithIsInline(false))
.AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false)));
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Rps(string input)
{
Func<int,string> GetRPSPick = (p) =>
Func<int,string> getRpsPick = (p) =>
{
if (p == 0)
return "🚀";
if (p == 1)
return "📎";
return "✂️";
switch (p)
{
case 0:
return "🚀";
case 1:
return "📎";
default:
return "✂️";
}
};
int pick;
@ -71,19 +88,176 @@ namespace NadekoBot.Modules.Games
return;
}
var nadekoPick = new NadekoRandom().Next(0, 3);
var msg = "";
string msg;
if (pick == nadekoPick)
msg = $"It's a draw! Both picked {GetRPSPick(pick)}";
msg = GetText("rps_draw", getRpsPick(pick));
else if ((pick == 0 && nadekoPick == 1) ||
(pick == 1 && nadekoPick == 2) ||
(pick == 2 && nadekoPick == 0))
msg = $"{NadekoBot.Client.CurrentUser.Mention} won! {GetRPSPick(nadekoPick)} beats {GetRPSPick(pick)}";
msg = GetText("rps_win", NadekoBot.Client.CurrentUser.Mention,
getRpsPick(nadekoPick), getRpsPick(pick));
else
msg = $"{Context.User.Mention} won! {GetRPSPick(pick)} beats {GetRPSPick(nadekoPick)}";
msg = GetText("rps_win", Context.User.Mention, getRpsPick(pick),
getRpsPick(nadekoPick));
await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
}
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]
public async Task Linux(string guhnoo, string loonix)
{

View File

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

View File

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

View File

@ -20,7 +20,7 @@ namespace NadekoBot.Modules.Music
{
[NadekoModule("Music", "!!")]
[DontAutoLoad]
public partial class Music : NadekoModule
public class Music : NadekoTopLevelModule
{
public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
@ -155,7 +155,14 @@ namespace NadekoBot.Modules.Music
return;
var val = musicPlayer.FairPlay = !musicPlayer.FairPlay;
await channel.SendConfirmAsync("Fair play " + (val ? "enabled" : "disabled") + ".").ConfigureAwait(false);
if (val)
{
await ReplyConfirmLocalized("fp_enabled").ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("fp_disabled").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -184,34 +191,31 @@ namespace NadekoBot.Modules.Music
[RequireContext(ContextType.Guild)]
public async Task ListQueue(int page = 1)
{
Song currentSong;
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer) ||
(currentSong = musicPlayer?.CurrentSong) == null)
{
await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
await ReplyErrorLocalized("no_player").ConfigureAwait(false);
return;
}
if (page <= 0)
return;
var currentSong = musicPlayer.CurrentSong;
if (currentSong == null)
{
await Context.Channel.SendErrorAsync("🎵 No active music player.").ConfigureAwait(false);
return;
}
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
const int itemsPerPage = 10;
var total = musicPlayer.TotalPlaytime;
var totalStr = total == TimeSpan.MaxValue ? "∞" : $"{(int)total.TotalHours}h {total.Minutes}m {total.Seconds}s";
var totalStr = total == TimeSpan.MaxValue ? "∞" : GetText("time_format",
(int) total.TotalHours,
total.Minutes,
total.Seconds);
var maxPlaytime = musicPlayer.MaxPlaytimeSeconds;
var lastPage = musicPlayer.Playlist.Count / itemsPerPage;
Func<int, EmbedBuilder> printAction = (curPage) =>
Func<int, EmbedBuilder> printAction = curPage =>
{
int startAt = itemsPerPage * (curPage - 1);
var startAt = itemsPerPage * (curPage - 1);
var number = 0 + startAt;
var desc = string.Join("\n", musicPlayer.Playlist
.Skip(startAt)
@ -221,19 +225,22 @@ namespace NadekoBot.Modules.Music
desc = $"`🔊` {currentSong.PrettyFullName}\n\n" + desc;
if (musicPlayer.RepeatSong)
desc = "🔂 Repeating Current Song\n\n" + desc;
desc = "🔂 " + GetText("repeating_cur_song") +"\n\n" + desc;
else if (musicPlayer.RepeatPlaylist)
desc = "🔁 Repeating Playlist\n\n" + desc;
desc = "🔁 " + GetText("repeating_playlist")+"\n\n" + desc;
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Player Queue - Page {curPage}/{lastPage + 1}")
.WithMusicIcon())
.WithAuthor(eab => eab.WithName(GetText("player_queue", curPage, lastPage + 1))
.WithMusicIcon())
.WithDescription(desc)
.WithFooter(ef => ef.WithText($"{musicPlayer.PrettyVolume} | {musicPlayer.Playlist.Count} " +
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
(musicPlayer.FairPlay ? "✔fairplay" : "✖fairplay") + $" | " + (maxPlaytime == 0 ? "unlimited" : $"{maxPlaytime}s limit")))
$"{("tracks".SnPl(musicPlayer.Playlist.Count))} | {totalStr} | " +
(musicPlayer.FairPlay
? "✔️" + GetText("fairplay")
: "✖️" + GetText("fairplay")) + " | " +
(maxPlaytime == 0 ? "unlimited" : GetText("play_limit", maxPlaytime))))
.WithOkColor();
return embed;
@ -254,7 +261,7 @@ namespace NadekoBot.Modules.Music
try { await musicPlayer.UpdateSongDurationsAsync().ConfigureAwait(false); } catch { }
var embed = new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Now Playing").WithMusicIcon())
.WithAuthor(eab => eab.WithName(GetText("now_playing")).WithMusicIcon())
.WithDescription(currentSong.PrettyName)
.WithThumbnailUrl(currentSong.Thumbnail)
.WithFooter(ef => ef.WithText(musicPlayer.PrettyVolume + " | " + currentSong.PrettyFullTime + $" | {currentSong.PrettyProvider} | {currentSong.QueuerName}"));
@ -271,21 +278,22 @@ namespace NadekoBot.Modules.Music
return;
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return;
if (val < 0)
if (val < 0 || val > 100)
{
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
return;
}
var volume = musicPlayer.SetVolume(val);
await Context.Channel.SendConfirmAsync($"🎵 Volume set to {volume}%").ConfigureAwait(false);
await ReplyConfirmLocalized("volume_set", volume).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Defvol([Remainder] int val)
{
if (val < 0 || val > 100)
{
await Context.Channel.SendErrorAsync("Volume number invalid. Must be between 0 and 100").ConfigureAwait(false);
await ReplyErrorLocalized("volume_input_invalid").ConfigureAwait(false);
return;
}
using (var uow = DbHandler.UnitOfWork())
@ -293,7 +301,7 @@ namespace NadekoBot.Modules.Music
uow.GuildConfigs.For(Context.Guild.Id, set => set).DefaultMusicVolume = val / 100.0f;
uow.Complete();
}
await Context.Channel.SendConfirmAsync($"🎵 Default volume set to {val}%").ConfigureAwait(false);
await ReplyConfirmLocalized("defvol_set").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -307,13 +315,10 @@ namespace NadekoBot.Modules.Music
if (((IGuildUser)Context.User).VoiceChannel != musicPlayer.PlaybackVoiceChannel)
return;
if (musicPlayer.Playlist.Count < 2)
{
await Context.Channel.SendErrorAsync("💢 Not enough songs in order to perform the shuffle.").ConfigureAwait(false);
return;
}
musicPlayer.Shuffle();
await Context.Channel.SendConfirmAsync("🎵 Songs shuffled.").ConfigureAwait(false);
await ReplyConfirmLocalized("songs_shuffled").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -326,29 +331,31 @@ namespace NadekoBot.Modules.Music
return;
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
{
await Context.Channel.SendErrorAsync($"💢 You need to be in a **voice channel** on this server.").ConfigureAwait(false);
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
return;
}
var plId = (await NadekoBot.Google.GetPlaylistIdsByKeywordsAsync(arg).ConfigureAwait(false)).FirstOrDefault();
if (plId == null)
{
await Context.Channel.SendErrorAsync("No search results for that query.");
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
return;
}
var ids = await NadekoBot.Google.GetPlaylistTracksAsync(plId, 500).ConfigureAwait(false);
if (!ids.Any())
{
await Context.Channel.SendErrorAsync($"🎵 Failed to find any songs.").ConfigureAwait(false);
await ReplyErrorLocalized("no_search_results").ConfigureAwait(false);
return;
}
var count = ids.Count();
var msg = await Context.Channel.SendMessageAsync($"🎵 Attempting to queue **{count}** songs".SnPl(count) + "...").ConfigureAwait(false);
var msg = await Context.Channel.SendMessageAsync(GetText("attempting_to_queue",
Format.Bold(count.ToString())))
.ConfigureAwait(false);
var cancelSource = new CancellationTokenSource();
var gusr = (IGuildUser)Context.User;
//todo use grouping
while (ids.Any() && !cancelSource.IsCancellationRequested)
{
var tasks = Task.WhenAll(ids.Take(5).Select(async id =>
@ -367,7 +374,7 @@ namespace NadekoBot.Modules.Music
ids = ids.Skip(5);
}
await msg.ModifyAsync(m => m.Content = "✅ Playlist queue complete.").ConfigureAwait(false);
await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -393,7 +400,7 @@ namespace NadekoBot.Modules.Music
{
try
{
mp.AddSong(new Song(new Classes.SongInfo
mp.AddSong(new Song(new SongInfo
{
Title = svideo.FullName,
Provider = "SoundCloud",
@ -435,20 +442,20 @@ namespace NadekoBot.Modules.Music
// ignored
}
}
await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
await ReplyConfirmLocalized("dir_queue_complete").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Radio(string radio_link)
public async Task Radio(string radioLink)
{
if (((IGuildUser)Context.User).VoiceChannel?.Guild != Context.Guild)
{
await Context.Channel.SendErrorAsync("💢 You need to be in a voice channel on this server.\n If you are already in a voice (ITextChannel)Context.Channel, try rejoining it.").ConfigureAwait(false);
await ReplyErrorLocalized("must_be_in_voice").ConfigureAwait(false);
return;
}
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radio_link, musicType: MusicType.Radio).ConfigureAwait(false);
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, radioLink, musicType: MusicType.Radio).ConfigureAwait(false);
if ((await Context.Guild.GetCurrentUserAsync()).GetPermissions((IGuildChannel)Context.Channel).ManageMessages)
{
Context.Message.DeleteAfter(10);
@ -505,8 +512,7 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer)) return;
musicPlayer.ClearQueue();
await Context.Channel.SendConfirmAsync($"🎵 Queue cleared!").ConfigureAwait(false);
return;
await ReplyConfirmLocalized("queue_cleared").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -532,7 +538,7 @@ namespace NadekoBot.Modules.Music
!int.TryParse(fromtoArr[1], out n2) || n1 < 1 || n2 < 1 || n1 == n2 ||
n1 > playlist.Count || n2 > playlist.Count)
{
await Context.Channel.SendErrorAsync("Invalid input.").ConfigureAwait(false);
await ReplyConfirmLocalized("invalid_input").ConfigureAwait(false);
return;
}
@ -544,9 +550,9 @@ namespace NadekoBot.Modules.Music
var embed = new EmbedBuilder()
.WithTitle($"{s.SongInfo.Title.TrimTo(70)}")
.WithUrl(s.SongUrl)
.WithAuthor(eab => eab.WithName("Song Moved").WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
.AddField(fb => fb.WithName("**From Position**").WithValue($"#{n1}").WithIsInline(true))
.AddField(fb => fb.WithName("**To Position**").WithValue($"#{n2}").WithIsInline(true))
.WithAuthor(eab => eab.WithName(GetText("song_moved")).WithIconUrl("https://cdn.discordapp.com/attachments/155726317222887425/258605269972549642/music1.png"))
.AddField(fb => fb.WithName(GetText("from_position")).WithValue($"#{n1}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("to_position")).WithValue($"#{n2}").WithIsInline(true))
.WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -564,7 +570,11 @@ namespace NadekoBot.Modules.Music
return;
musicPlayer.MaxQueueSize = size;
await Context.Channel.SendConfirmAsync($"🎵 Max queue set to {(size == 0 ? ("unlimited") : size + " tracks")}.");
if(size == 0)
await ReplyConfirmLocalized("max_queue_unlimited").ConfigureAwait(false);
else
await ReplyConfirmLocalized("max_queue_x", size).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -580,9 +590,9 @@ namespace NadekoBot.Modules.Music
return;
musicPlayer.MaxPlaytimeSeconds = seconds;
if (seconds == 0)
await channel.SendConfirmAsync($"🎵 Max playtime has no limit now.");
await ReplyConfirmLocalized("max_playtime_none").ConfigureAwait(false);
else
await channel.SendConfirmAsync($"🎵 Max playtime set to {seconds} seconds.");
await ReplyConfirmLocalized("max_playtime_set").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -601,11 +611,11 @@ namespace NadekoBot.Modules.Music
if (currentValue)
await Context.Channel.EmbedAsync(new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 Repeating track"))
.WithAuthor(eab => eab.WithMusicIcon().WithName("🔂 " + GetText("repeating_track")))
.WithDescription(currentSong.PrettyName)
.WithFooter(ef => ef.WithText(currentSong.PrettyInfo))).ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync($"🔂 Current track repeat stopped.")
await Context.Channel.SendConfirmAsync("🔂 " + GetText("repeating_track_stopped"))
.ConfigureAwait(false);
}
@ -618,7 +628,10 @@ namespace NadekoBot.Modules.Music
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
return;
var currentValue = musicPlayer.ToggleRepeatPlaylist();
await Context.Channel.SendConfirmAsync($"🔁 Repeat playlist {(currentValue ? "**enabled**." : "**disabled**.")}").ConfigureAwait(false);
if(currentValue)
await ReplyConfirmLocalized("rpl_enabled").ConfigureAwait(false);
else
await ReplyConfirmLocalized("rpl_disabled").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -655,7 +668,10 @@ namespace NadekoBot.Modules.Music
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync(($"🎵 Saved playlist as **{name}**, ID: {playlist.Id}.")).ConfigureAwait(false);
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(GetText("playlist_saved"))
.AddField(efb => efb.WithName(GetText("name")).WithValue(name))
.AddField(efb => efb.WithName(GetText("id")).WithValue(playlist.Id.ToString())));
}
[NadekoCommand, Usage, Description, Aliases]
@ -670,11 +686,11 @@ namespace NadekoBot.Modules.Music
if (mpl == null)
{
await Context.Channel.SendErrorAsync("Can't find playlist with that ID.").ConfigureAwait(false);
await ReplyErrorLocalized("playlist_id_not_found").ConfigureAwait(false);
return;
}
IUserMessage msg = null;
try { msg = await Context.Channel.SendMessageAsync($"🎶 Attempting to load **{mpl.Songs.Count}** songs...").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
try { msg = await ReplyConfirmLocalized("attempting_to_queue", Format.Bold(mpl.Songs.Count.ToString())).ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); }
foreach (var item in mpl.Songs)
{
var usr = (IGuildUser)Context.User;
@ -686,15 +702,13 @@ namespace NadekoBot.Modules.Music
catch { break; }
}
if (msg != null)
await msg.ModifyAsync(m => m.Content = $"✅ Done loading playlist **{mpl.Name}**.").ConfigureAwait(false);
await msg.ModifyAsync(m => m.Content = GetText("playlist_queue_complete")).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Playlists([Remainder] int num = 1)
{
if (num <= 0)
return;
@ -706,8 +720,9 @@ namespace NadekoBot.Modules.Music
}
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName($"Page {num} of Saved Playlists").WithMusicIcon())
.WithDescription(string.Join("\n", playlists.Select(r => $"`#{r.Id}` - **{r.Name}** by *{r.Author}* ({r.Songs.Count} songs)")))
.WithAuthor(eab => eab.WithName(GetText("playlists_page", num)).WithMusicIcon())
.WithDescription(string.Join("\n", playlists.Select(r =>
GetText("playlists", "#" + r.Id, r.Name, r.Author, r.Songs.Count))))
.WithOkColor();
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -717,13 +732,12 @@ namespace NadekoBot.Modules.Music
[RequireContext(ContextType.Guild)]
public async Task DeletePlaylist([Remainder] int id)
{
bool success = false;
MusicPlaylist pl = null;
var success = false;
try
{
using (var uow = DbHandler.UnitOfWork())
{
pl = uow.MusicPlaylists.Get(id);
var pl = uow.MusicPlaylists.Get(id);
if (pl != null)
{
@ -733,15 +747,13 @@ namespace NadekoBot.Modules.Music
await uow.CompleteAsync().ConfigureAwait(false);
success = true;
}
else
success = false;
}
}
if (!success)
await Context.Channel.SendErrorAsync("Failed to delete that playlist. It either doesn't exist, or you are not its author.").ConfigureAwait(false);
await ReplyErrorLocalized("playlist_delete_fail").ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync("🗑 Playlist successfully **deleted**.").ConfigureAwait(false);
await ReplyConfirmLocalized("playlist_deleted").ConfigureAwait(false);
}
catch (Exception ex)
{
@ -781,7 +793,7 @@ namespace NadekoBot.Modules.Music
if (seconds.Length == 1)
seconds = "0" + seconds;
await Context.Channel.SendConfirmAsync($"Skipped to `{minutes}:{seconds}`").ConfigureAwait(false);
await ReplyConfirmLocalized("skipped_to", minutes, seconds).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -793,9 +805,9 @@ namespace NadekoBot.Modules.Music
return;
if (!musicPlayer.ToggleAutoplay())
await Context.Channel.SendConfirmAsync("❌ Autoplay disabled.").ConfigureAwait(false);
await ReplyConfirmLocalized("autoplay_disabled").ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync("✅ Autoplay enabled.").ConfigureAwait(false);
await ReplyConfirmLocalized("autoplay_enabled").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -806,29 +818,29 @@ namespace NadekoBot.Modules.Music
MusicPlayer musicPlayer;
if (!MusicPlayers.TryGetValue(Context.Guild.Id, out musicPlayer))
{
await Context.Channel.SendErrorAsync("Music must be playing before you set an ouput channel.").ConfigureAwait(false);
await ReplyErrorLocalized("player_none").ConfigureAwait(false);
return;
}
musicPlayer.OutputTextChannel = (ITextChannel)Context.Channel;
await Context.Channel.SendConfirmAsync("I will now output playing, finished, paused and removed songs in this channel.").ConfigureAwait(false);
await ReplyConfirmLocalized("set_music_channel").ConfigureAwait(false);
}
public static async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
public async Task QueueSong(IGuildUser queuer, ITextChannel textCh, IVoiceChannel voiceCh, string query, bool silent = false, MusicType musicType = MusicType.Normal)
{
if (voiceCh == null || voiceCh.Guild != textCh.Guild)
{
if (!silent)
await textCh.SendErrorAsync($"💢 You need to be in a voice channel on this server.").ConfigureAwait(false);
await textCh.SendErrorAsync(GetText("must_be_in_voice")).ConfigureAwait(false);
throw new ArgumentNullException(nameof(voiceCh));
}
if (string.IsNullOrWhiteSpace(query) || query.Length < 3)
throw new ArgumentException("💢 Invalid query for queue song.", nameof(query));
throw new ArgumentException("Invalid song query.", nameof(query));
var musicPlayer = MusicPlayers.GetOrAdd(textCh.Guild.Id, server =>
{
float vol = 1;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
float vol;// SpecificConfigurations.Default.Of(server.Id).DefaultMusicVolume;
using (var uow = DbHandler.UnitOfWork())
{
vol = uow.GuildConfigs.For(textCh.Guild.Id, set => set).DefaultMusicVolume;
@ -845,7 +857,7 @@ namespace NadekoBot.Modules.Music
try
{
lastFinishedMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Finished Song").WithMusicIcon())
.WithAuthor(eab => eab.WithName(GetText("finished_song")).WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
.ConfigureAwait(false);
@ -866,7 +878,10 @@ namespace NadekoBot.Modules.Music
true).ConfigureAwait(false);
}
}
catch { }
catch
{
// ignored
}
};
mp.OnStarted += async (player, song) =>
@ -876,7 +891,7 @@ namespace NadekoBot.Modules.Music
{
// ignored
}
var sender = player as MusicPlayer;
var sender = player;
if (sender == null)
return;
try
@ -884,12 +899,15 @@ namespace NadekoBot.Modules.Music
playingMessage?.DeleteAfter(0);
playingMessage = await mp.OutputTextChannel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Playing Song").WithMusicIcon())
.WithAuthor(eab => eab.WithName(GetText("playing_song")).WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo)))
.ConfigureAwait(false);
}
catch { }
catch
{
// ignored
}
};
mp.OnPauseChanged += async (paused) =>
{
@ -897,13 +915,16 @@ namespace NadekoBot.Modules.Music
{
IUserMessage msg;
if (paused)
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **paused**.").ConfigureAwait(false);
msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("paused")).ConfigureAwait(false);
else
msg = await mp.OutputTextChannel.SendConfirmAsync("🎵 Music playback **resumed**.").ConfigureAwait(false);
msg = await mp.OutputTextChannel.SendConfirmAsync(GetText("resumed")).ConfigureAwait(false);
msg?.DeleteAfter(10);
}
catch { }
catch
{
// ignored
}
};
mp.SongRemoved += async (song, index) =>
@ -911,7 +932,7 @@ namespace NadekoBot.Modules.Music
try
{
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName("Removed song #" + (index + 1)).WithMusicIcon())
.WithAuthor(eab => eab.WithName(GetText("removed_song") + " #" + (index + 1)).WithMusicIcon())
.WithDescription(song.PrettyName)
.WithFooter(ef => ef.WithText(song.PrettyInfo))
.WithErrorColor();
@ -939,7 +960,14 @@ namespace NadekoBot.Modules.Music
}
catch (PlaylistFullException)
{
try { await textCh.SendConfirmAsync($"🎵 Queue is full at **{musicPlayer.MaxQueueSize}/{musicPlayer.MaxQueueSize}**."); } catch { }
try
{
await textCh.SendConfirmAsync(GetText("queue_full", musicPlayer.MaxQueueSize));
}
catch
{
// ignored
}
throw;
}
if (!silent)
@ -948,8 +976,8 @@ namespace NadekoBot.Modules.Music
{
//var queuedMessage = await textCh.SendConfirmAsync($"🎵 Queued **{resolvedSong.SongInfo.Title}** at `#{musicPlayer.Playlist.Count + 1}`").ConfigureAwait(false);
var queuedMessage = await textCh.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithAuthor(eab => eab.WithName("Queued Song #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
.WithDescription($"{resolvedSong.PrettyName}\nQueue ")
.WithAuthor(eab => eab.WithName(GetText("queued_song") + " #" + (musicPlayer.Playlist.Count + 1)).WithMusicIcon())
.WithDescription($"{resolvedSong.PrettyName}\n{GetText("queue")} ")
.WithThumbnailUrl(resolvedSong.Thumbnail)
.WithFooter(ef => ef.WithText(resolvedSong.PrettyProvider)))
.ConfigureAwait(false);

View File

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

View File

@ -9,7 +9,7 @@ using System.Threading.Tasks;
namespace NadekoBot.Modules
{
public abstract class NadekoModule : ModuleBase
public abstract class NadekoTopLevelModule : ModuleBase
{
protected readonly Logger _log;
protected CultureInfo _cultureInfo;
@ -17,7 +17,7 @@ namespace NadekoBot.Modules
public readonly string ModuleTypeName;
public readonly string LowerModuleTypeName;
protected NadekoModule(bool isTopLevelModule = true)
protected NadekoTopLevelModule(bool isTopLevelModule = true)
{
//if it's top level module
ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name;
@ -120,7 +120,7 @@ namespace NadekoBot.Modules
}
}
public abstract class NadekoSubmodule : NadekoModule
public abstract class NadekoSubmodule : NadekoTopLevelModule
{
protected NadekoSubmodule() : base(false)
{

View File

@ -21,11 +21,11 @@ namespace NadekoBot.Modules.Permissions
}
[Group]
public class BlacklistCommands : ModuleBase
public class BlacklistCommands : NadekoSubmodule
{
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; } = new ConcurrentHashSet<ulong>();
public static ConcurrentHashSet<ulong> BlacklistedUsers { get; set; }
public static ConcurrentHashSet<ulong> BlacklistedGuilds { get; set; }
public static ConcurrentHashSet<ulong> BlacklistedChannels { get; set; }
static BlacklistCommands()
{
@ -115,7 +115,7 @@ namespace NadekoBot.Modules.Permissions
}
break;
case BlacklistType.Channel:
var item = Games.Games.TriviaCommands.RunningTrivias.FirstOrDefault(kvp => kvp.Value.channel.Id == id);
var item = Games.Games.TriviaCommands.RunningTrivias.FirstOrDefault(kvp => kvp.Value.Channel.Id == id);
Games.Games.TriviaCommands.RunningTrivias.TryRemove(item.Key, out tg);
if (tg != null)
{
@ -124,16 +124,14 @@ namespace NadekoBot.Modules.Permissions
break;
case BlacklistType.User:
break;
default:
break;
}
}
if(action == AddRemove.Add)
await Context.Channel.SendConfirmAsync($"Blacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
await ReplyConfirmLocalized("blacklisted", Format.Code(type.ToString()), Format.Code(id.ToString())).ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync($"Unblacklisted a `{type}` with id `{id}`").ConfigureAwait(false);
await ReplyConfirmLocalized("unblacklisted", Format.Code(type.ToString()), Format.Code(id.ToString())).ConfigureAwait(false);
}
}
}

View File

@ -21,15 +21,15 @@ namespace NadekoBot.Modules.Permissions
}
[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>>();
static CmdCdsCommands()
{
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]
[RequireContext(ContextType.Guild)]
@ -38,14 +38,14 @@ namespace NadekoBot.Modules.Permissions
var channel = (ITextChannel)Context.Channel;
if (secs < 0 || secs > 3600)
{
await channel.SendErrorAsync("Invalid second parameter. (Must be a number between 0 and 3600)").ConfigureAwait(false);
await ReplyErrorLocalized("invalid_second_param_between", 0, 3600).ConfigureAwait(false);
return;
}
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.CommandCooldowns));
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
localSet.RemoveWhere(cc => cc.CommandName == command.Aliases.First().ToLowerInvariant());
@ -65,13 +65,14 @@ namespace NadekoBot.Modules.Permissions
{
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<ActiveCooldown>());
activeCds.RemoveWhere(ac => ac.Command == command.Aliases.First().ToLowerInvariant());
await channel.SendConfirmAsync($"🚮 Command **{command.Aliases.First()}** has no coooldown now and all existing cooldowns have been cleared.")
.ConfigureAwait(false);
await ReplyConfirmLocalized("cmdcd_cleared",
Format.Bold(command.Aliases.First())).ConfigureAwait(false);
}
else
{
await channel.SendConfirmAsync($"✅ Command **{command.Aliases.First()}** now has a **{secs} {"seconds".SnPl(secs)}** cooldown.")
.ConfigureAwait(false);
await ReplyConfirmLocalized("cmdcd_add",
Format.Bold(command.Aliases.First()),
Format.Bold(secs.ToString())).ConfigureAwait(false);
}
}
@ -80,19 +81,19 @@ namespace NadekoBot.Modules.Permissions
public async Task AllCmdCooldowns()
{
var channel = (ITextChannel)Context.Channel;
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
var localSet = CommandCooldowns.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CommandCooldown>());
if (!localSet.Any())
await channel.SendConfirmAsync(" `No command cooldowns set.`").ConfigureAwait(false);
await ReplyConfirmLocalized("cmdcd_none").ConfigureAwait(false);
else
await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + " secs"), s => $"{s,-30}", 2).ConfigureAwait(false);
await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + GetText("sec")), s => $"{s,-30}", 2).ConfigureAwait(false);
}
public static bool HasCooldown(CommandInfo cmd, IGuild guild, IUser user)
{
if (guild == null)
return false;
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
var cmdcds = CmdCdsCommands.CommandCooldowns.GetOrAdd(guild.Id, new ConcurrentHashSet<CommandCooldown>());
CommandCooldown cdRule;
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
{
[Group]
public class CommandCostCommands : ModuleBase
public class CommandCostCommands : NadekoSubmodule
{
private static readonly ConcurrentDictionary<string, int> _commandCosts = new ConcurrentDictionary<string, int>();
public static IReadOnlyDictionary<string, int> CommandCosts => _commandCosts;
@ -29,29 +29,29 @@ namespace NadekoBot.Modules.Permissions
// x => x.Cost));
}
[NadekoCommand, Usage, Description, Aliases]
public async Task CmdCosts(int page = 1)
{
var prices = _commandCosts.ToList();
//[NadekoCommand, Usage, Description, Aliases]
//public async Task CmdCosts(int page = 1)
//{
// var prices = _commandCosts.ToList();
if (!prices.Any())
{
await Context.Channel.SendConfirmAsync("No costs set.").ConfigureAwait(false);
return;
}
// if (!prices.Any())
// {
// await Context.Channel.SendConfirmAsync(GetText("no_costs")).ConfigureAwait(false);
// return;
// }
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => {
var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Command Costs");
var current = prices.Skip((curPage - 1) * 9)
.Take(9);
foreach (var price in current)
{
embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true));
}
return embed;
}, prices.Count / 9).ConfigureAwait(false);
}
// await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => {
// var embed = new EmbedBuilder().WithOkColor()
// .WithTitle(GetText("command_costs"));
// var current = prices.Skip((curPage - 1) * 9)
// .Take(9);
// foreach (var price in current)
// {
// embed.AddField(efb => efb.WithName(price.Key).WithValue(price.Value.ToString()).WithIsInline(true));
// }
// return embed;
// }, prices.Count / 9).ConfigureAwait(false);
//}
//[NadekoCommand, Usage, Description, Aliases]
//public async Task CommandCost(int cost, CommandInfo cmd)

View File

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

View File

@ -15,7 +15,7 @@ using NLog;
namespace NadekoBot.Modules.Permissions
{
[NadekoModule("Permissions", ";")]
public partial class Permissions : NadekoModule
public partial class Permissions : NadekoTopLevelModule
{
public class PermissionCache
{
@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Permissions
static Permissions()
{
var _log = LogManager.GetCurrentClassLogger();
var log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
@ -46,7 +46,7 @@ namespace NadekoBot.Modules.Permissions
}
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
}
[NadekoCommand, Usage, Description, Aliases]
@ -65,8 +65,14 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.Verbose = config.VerbosePermissions; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync(" I will " + (action.Value ? "now" : "no longer") + " show permission warnings.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("verbose_true").ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("verbose_false").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -81,22 +87,20 @@ namespace NadekoBot.Modules.Permissions
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
if (role == null)
{
await Context.Channel.SendConfirmAsync($" Current permission role is **{config.PermissionRole}**.").ConfigureAwait(false);
await ReplyConfirmLocalized("permrole", Format.Bold(config.PermissionRole)).ConfigureAwait(false);
return;
}
else {
config.PermissionRole = role.Name.Trim();
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = Permission.GetDefaultRoot(),
Verbose = config.VerbosePermissions
}, (id, old) => { old.PermRole = role.Name.Trim(); return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
config.PermissionRole = role.Name.Trim();
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = Permission.GetDefaultRoot(),
Verbose = config.VerbosePermissions
}, (id, old) => { old.PermRole = role.Name.Trim(); return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"Users now require **{role.Name}** role in order to edit permissions.").ConfigureAwait(false);
await ReplyConfirmLocalized("permrole_changed", Format.Bold(role.Name)).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -105,12 +109,18 @@ namespace NadekoBot.Modules.Permissions
{
if (page < 1 || page > 4)
return;
string toSend = "";
string toSend;
using (var uow = DbHandler.UnitOfWork())
{
var perms = uow.GuildConfigs.PermissionsFor(Context.Guild.Id).RootPermission;
var i = 1 + 20 * (page - 1);
toSend = Format.Code($"📄 Permissions page {page}") + "\n\n" + String.Join("\n", perms.AsEnumerable().Skip((page - 1) * 20).Take(20).Select(p => $"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild)Context.Guild) + " [uneditable]") : (p.GetCommand((SocketGuild)Context.Guild)))}"));
toSend = Format.Bold(GetText("page", page)) + "\n\n" + string.Join("\n",
perms.AsEnumerable()
.Skip((page - 1) * 20)
.Take(20)
.Select(
p =>
$"`{(i++)}.` {(p.Next == null ? Format.Bold(p.GetCommand((SocketGuild) Context.Guild) + $" [{GetText("uneditable")}]") : (p.GetCommand((SocketGuild) Context.Guild)))}"));
}
await Context.Channel.SendMessageAsync(toSend).ConfigureAwait(false);
@ -132,7 +142,7 @@ namespace NadekoBot.Modules.Permissions
{
return;
}
else if (index == 0)
if (index == 0)
{
p = perms;
config.RootPermission = perms.Next;
@ -155,12 +165,13 @@ namespace NadekoBot.Modules.Permissions
uow2._context.Remove<Permission>(p);
uow2._context.SaveChanges();
}
await Context.Channel.SendConfirmAsync($"✅ {Context.User.Mention} removed permission **{p.GetCommand((SocketGuild)Context.Guild)}** from position #{index + 1}.").ConfigureAwait(false);
await ReplyConfirmLocalized("removed",
index+1,
Format.Code(p.GetCommand((SocketGuild)Context.Guild))).ConfigureAwait(false);
}
catch (ArgumentOutOfRangeException)
catch (IndexOutOfRangeException)
{
await Context.Channel.SendErrorAsync("❗️`No command on that index found.`").ConfigureAwait(false);
await ReplyErrorLocalized("perm_out_of_range").ConfigureAwait(false);
}
}
@ -180,7 +191,6 @@ namespace NadekoBot.Modules.Permissions
{
var config = uow.GuildConfigs.PermissionsFor(Context.Guild.Id);
var perms = config.RootPermission;
var root = perms;
var index = 0;
var fromFound = false;
var toFound = false;
@ -207,13 +217,13 @@ namespace NadekoBot.Modules.Permissions
{
if (!fromFound)
{
await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++from}`").ConfigureAwait(false);
await ReplyErrorLocalized("not_found", ++from).ConfigureAwait(false);
return;
}
if (!toFound)
{
await Context.Channel.SendErrorAsync($"Can't find permission at index `#{++to}`").ConfigureAwait(false);
await ReplyErrorLocalized("not_found", ++to).ConfigureAwait(false);
return;
}
}
@ -230,7 +240,6 @@ namespace NadekoBot.Modules.Permissions
next.Previous = pre;
if (from == 0)
{
root = next;
}
await uow.CompleteAsync().ConfigureAwait(false);
//Inserting
@ -263,14 +272,18 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"`Moved permission:` \"{fromPerm.GetCommand((SocketGuild)Context.Guild)}\" `from #{++from} to #{++to}.`").ConfigureAwait(false);
await ReplyConfirmLocalized("moved_permission",
Format.Code(fromPerm.GetCommand((SocketGuild) Context.Guild)),
++from,
++to)
.ConfigureAwait(false);
return;
}
catch (Exception e) when (e is ArgumentOutOfRangeException || e is IndexOutOfRangeException)
{
}
}
await Context.Channel.SendErrorAsync("`Invalid index(es) specified.`").ConfigureAwait(false);
await ReplyErrorLocalized("perm_out_of_range").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -297,7 +310,19 @@ namespace NadekoBot.Modules.Permissions
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command on this server.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("sx_enable",
Format.Code(command.Aliases.First()),
GetText("of_command")).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("sx_disable",
Format.Code(command.Aliases.First()),
GetText("of_command")).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -323,7 +348,19 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of **`{module.Name}`** module on this server.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("sx_enable",
Format.Code(module.Name),
GetText("of_module")).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("sx_disable",
Format.Code(module.Name),
GetText("of_module")).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -349,7 +386,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{user}` user.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("ux_enable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(user.ToString())).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("ux_disable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(user.ToString())).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -375,7 +426,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{user}` user.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("ux_enable",
Format.Code(module.Name),
GetText("of_module"),
Format.Code(user.ToString())).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("ux_disable",
Format.Code(module.Name),
GetText("of_module"),
Format.Code(user.ToString())).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -404,7 +469,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{role}` role.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("rx_enable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(role.Name)).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("rx_disable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(role.Name)).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -433,39 +512,62 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{role}` role.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("rx_enable",
Format.Code(module.Name),
GetText("of_module"),
Format.Code(role.Name)).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("rx_disable",
Format.Code(module.Name),
GetText("of_module"),
Format.Code(role.Name)).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ChnlCmd(CommandInfo command, PermissionAction action, [Remainder] ITextChannel chnl)
{
try
using (var uow = DbHandler.UnitOfWork())
{
using (var uow = DbHandler.UnitOfWork())
var newPerm = new Permission
{
var newPerm = new Permission
{
PrimaryTarget = PrimaryPermissionType.Channel,
PrimaryTargetId = chnl.Id,
SecondaryTarget = SecondaryPermissionType.Command,
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
Verbose = config.VerbosePermissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
PrimaryTarget = PrimaryPermissionType.Channel,
PrimaryTargetId = chnl.Id,
SecondaryTarget = SecondaryPermissionType.Command,
SecondaryTargetName = command.Aliases.First().ToLowerInvariant(),
State = action.Value,
};
var config = uow.GuildConfigs.SetNewRootPermission(Context.Guild.Id, newPerm);
Cache.AddOrUpdate(Context.Guild.Id, new PermissionCache()
{
PermRole = config.PermissionRole,
RootPermission = config.RootPermission,
Verbose = config.VerbosePermissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
catch (Exception ex) {
_log.Error(ex);
if (action.Value)
{
await ReplyConfirmLocalized("cx_enable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(chnl.Name)).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("cx_disable",
Format.Code(command.Aliases.First()),
GetText("of_command"),
Format.Code(chnl.Name)).ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{command.Aliases.First()}` command for `{chnl}` channel.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -491,7 +593,21 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `{module.Name}` module for `{chnl}` channel.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("cx_enable",
Format.Code(module.Name),
GetText("of_module"),
Format.Code(chnl.Name)).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("cx_disable",
Format.Code(module.Name),
GetText("of_module"),
Format.Code(chnl.Name)).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -517,7 +633,17 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{chnl}` channel.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("acm_enable",
Format.Code(chnl.Name)).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("acm_disable",
Format.Code(chnl.Name)).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -546,7 +672,17 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{role}` role.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("arm_enable",
Format.Code(role.Name)).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("arm_disable",
Format.Code(role.Name)).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -572,7 +708,17 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` for `{user}` user.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("aum_enable",
Format.Code(user.ToString())).ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("aum_disable",
Format.Code(user.ToString())).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
@ -609,7 +755,15 @@ namespace NadekoBot.Modules.Permissions
}, (id, old) => { old.RootPermission = config.RootPermission; return old; });
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync($"{(action.Value ? " Allowed" : "🆗 Denied")} usage of `ALL MODULES` on this server.").ConfigureAwait(false);
if (action.Value)
{
await ReplyConfirmLocalized("asm_enable").ConfigureAwait(false);
}
else
{
await ReplyConfirmLocalized("asm_disable").ConfigureAwait(false);
}
}
}
}

View File

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

View File

@ -1,6 +1,5 @@
using AngleSharp;
using AngleSharp.Dom.Html;
using AngleSharp.Extensions;
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
@ -8,7 +7,6 @@ using NadekoBot.Extensions;
using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NLog;
using System;
using System.Collections.Generic;
using System.Linq;
@ -21,15 +19,13 @@ namespace NadekoBot.Modules.Searches
public partial class Searches
{
[Group]
public class AnimeSearchCommands : ModuleBase
public class AnimeSearchCommands : NadekoSubmodule
{
private static Timer anilistTokenRefresher { get; }
private static Logger _log { get; }
private static readonly Timer anilistTokenRefresher;
private static string anilistToken { get; set; }
static AnimeSearchCommands()
{
_log = LogManager.GetCurrentClassLogger();
anilistTokenRefresher = new Timer(async (state) =>
{
try
@ -49,9 +45,9 @@ namespace NadekoBot.Modules.Searches
anilistToken = JObject.Parse(stringContent)["access_token"].ToString();
}
}
catch (Exception ex)
catch
{
_log.Error(ex);
// ignored
}
}, null, TimeSpan.FromSeconds(0), TimeSpan.FromMinutes(29));
}
@ -75,7 +71,7 @@ namespace NadekoBot.Modules.Searches
var favorites = document.QuerySelectorAll("div.user-favorites > div.di-tc");
var favAnime = "No favorite anime yet";
var favAnime = GetText("anime_no_fav");
if (favorites[0].QuerySelector("p") == null)
favAnime = string.Join("\n", favorites[0].QuerySelectorAll("ul > li > div.di-tc.va-t > a")
.Shuffle()
@ -106,14 +102,14 @@ namespace NadekoBot.Modules.Searches
var embed = new EmbedBuilder()
.WithOkColor()
.WithTitle($"{name}'s MAL profile")
.AddField(efb => efb.WithName("💚 Watching").WithValue(stats[0]).WithIsInline(true))
.AddField(efb => efb.WithName("💙 Completed").WithValue(stats[1]).WithIsInline(true));
.WithTitle(GetText("mal_profile", name))
.AddField(efb => efb.WithName("💚 " + GetText("watching")).WithValue(stats[0]).WithIsInline(true))
.AddField(efb => efb.WithName("💙 " + GetText("completed")).WithValue(stats[1]).WithIsInline(true));
if (info.Count < 3)
embed.AddField(efb => efb.WithName("💛 On-Hold").WithValue(stats[2]).WithIsInline(true));
embed.AddField(efb => efb.WithName("💛 " + GetText("on_hold")).WithValue(stats[2]).WithIsInline(true));
embed
.AddField(efb => efb.WithName("💔 Dropped").WithValue(stats[3]).WithIsInline(true))
.AddField(efb => efb.WithName("⚪ Plan to watch").WithValue(stats[4]).WithIsInline(true))
.AddField(efb => efb.WithName("💔 " + GetText("dropped")).WithValue(stats[3]).WithIsInline(true))
.AddField(efb => efb.WithName("⚪ " + GetText("plan_to_watch")).WithValue(stats[4]).WithIsInline(true))
.AddField(efb => efb.WithName("🕐 " + daysAndMean[0][0]).WithValue(daysAndMean[0][1]).WithIsInline(true))
.AddField(efb => efb.WithName("📊 " + daysAndMean[1][0]).WithValue(daysAndMean[1][1]).WithIsInline(true))
.AddField(efb => efb.WithName(MalInfoToEmoji(info[0].Item1) + " " + info[0].Item1).WithValue(info[0].Item2.TrimTo(20)).WithIsInline(true))
@ -126,7 +122,7 @@ namespace NadekoBot.Modules.Searches
.WithDescription($@"
** https://myanimelist.net/animelist/{ name } **
**Top 3 Favorite Anime:**
**{GetText("top_3_fav_anime")}**
{favAnime}"
//**[Manga List](https://myanimelist.net/mangalist/{name})**
@ -176,7 +172,7 @@ namespace NadekoBot.Modules.Searches
if (animeData == null)
{
await Context.Channel.SendErrorAsync("Failed finding that animu.").ConfigureAwait(false);
await ReplyErrorLocalized("failed_finding_anime").ConfigureAwait(false);
return;
}
@ -185,10 +181,10 @@ namespace NadekoBot.Modules.Searches
.WithTitle(animeData.title_english)
.WithUrl(animeData.Link)
.WithImageUrl(animeData.image_url_lge)
.AddField(efb => efb.WithName("Episodes").WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Status").WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", animeData.Genres)).WithIsInline(true))
.WithFooter(efb => efb.WithText("Score: " + animeData.average_score + " / 100"));
.AddField(efb => efb.WithName(GetText("episodes")).WithValue(animeData.total_episodes.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("status")).WithValue(animeData.AiringStatus.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", animeData.Genres)).WithIsInline(true))
.WithFooter(efb => efb.WithText(GetText("score") + " " + animeData.average_score + " / 100"));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
@ -203,7 +199,7 @@ namespace NadekoBot.Modules.Searches
if (mangaData == null)
{
await Context.Channel.SendErrorAsync("Failed finding that mango.").ConfigureAwait(false);
await ReplyErrorLocalized("failed_finding_manga").ConfigureAwait(false);
return;
}
@ -212,10 +208,10 @@ namespace NadekoBot.Modules.Searches
.WithTitle(mangaData.title_english)
.WithUrl(mangaData.Link)
.WithImageUrl(mangaData.image_url_lge)
.AddField(efb => efb.WithName("Episodes").WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Status").WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Genres").WithValue(String.Join(", ", mangaData.Genres)).WithIsInline(true))
.WithFooter(efb => efb.WithText("Score: " + mangaData.average_score + " / 100"));
.AddField(efb => efb.WithName(GetText("chapters")).WithValue(mangaData.total_chapters.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("status")).WithValue(mangaData.publishing_status.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("genres")).WithValue(String.Join(",\n", mangaData.Genres)).WithIsInline(true))
.WithFooter(efb => efb.WithText(GetText("score") + " " + mangaData.average_score + " / 100"));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}

View File

@ -1,4 +1,5 @@
using Newtonsoft.Json.Linq;
using System;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Net;
@ -13,7 +14,7 @@ namespace NadekoBot.Modules.Searches
public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator());
public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x);
private Dictionary<string, string> _languageDictionary;
private readonly Dictionary<string, string> _languageDictionary;
static GoogleTranslator() { }
private GoogleTranslator() {
@ -153,13 +154,18 @@ namespace NadekoBot.Modules.Searches
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(targetLanguage),
WebUtility.UrlEncode(sourceText));
using (HttpClient http = new HttpClient())
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36");
text = await http.GetStringAsync(url).ConfigureAwait(false);
@ -170,7 +176,7 @@ namespace NadekoBot.Modules.Searches
private string ConvertToLanguageCode(string language)
{
string mode = string.Empty;
string mode;
_languageDictionary.TryGetValue(language, out mode);
return mode;
}

View File

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

View File

@ -33,7 +33,7 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases]
public async Task Lolban()
{
var showCount = 8;
const int showCount = 8;
//http://api.champion.gg/stats/champs/mostBanned?api_key=YOUR_API_TOKEN&page=1&limit=2
try
{
@ -44,19 +44,20 @@ namespace NadekoBot.Modules.Searches
$"limit={showCount}")
.ConfigureAwait(false))["data"] as JArray;
var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList();
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline($"{dataList.Count} most banned champions"));
for (var i = 0; i < dataList.Count; i++)
var eb = new EmbedBuilder().WithOkColor().WithTitle(Format.Underline(GetText("x_most_banned_champs",dataList.Count)));
foreach (var champ in dataList)
{
var champ = dataList[i];
eb.AddField(efb => efb.WithName(champ["name"].ToString()).WithValue(champ["general"]["banRate"] + "%").WithIsInline(true));
var champ1 = champ;
eb.AddField(efb => efb.WithName(champ1["name"].ToString()).WithValue(champ1["general"]["banRate"] + "%").WithIsInline(true));
}
await Context.Channel.EmbedAsync(eb, Format.Italics(trashTalk[new NadekoRandom().Next(0, trashTalk.Length)])).ConfigureAwait(false);
}
}
catch (Exception)
catch (Exception ex)
{
await Context.Channel.SendMessageAsync("Something went wrong.").ConfigureAwait(false);
_log.Warn(ex);
await ReplyErrorLocalized("something_went_wrong").ConfigureAwait(false);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,6 @@ using NadekoBot.Modules.Searches.Models;
using Newtonsoft.Json;
using NLog;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@ -16,7 +15,7 @@ namespace NadekoBot.Modules.Searches
public partial class Searches
{
[Group]
public class PokemonSearchCommands : ModuleBase
public class PokemonSearchCommands : NadekoSubmodule
{
private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>();
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 PokemonListFile = "data/pokemon/pokemon_list7.json";
private static Logger _log { get; }
private new static readonly Logger _log;
static PokemonSearchCommands()
{
@ -57,14 +56,13 @@ namespace NadekoBot.Modules.Searches
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(kvp.Key.ToTitleCase())
.WithDescription(p.BaseStats.ToString())
.AddField(efb => efb.WithName("Types").WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
.AddField(efb => efb.WithName("Height/Weight").WithValue($"{p.HeightM}m/{p.WeightKg}kg").WithIsInline(true))
.AddField(efb => efb.WithName("Abilitities").WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true))
);
.AddField(efb => efb.WithName(GetText("types")).WithValue(string.Join(",\n", p.Types)).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("height_weight")).WithValue(GetText("height_weight_val", p.HeightM, p.WeightKg)).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("abilities")).WithValue(string.Join(",\n", p.Abilities.Select(a => a.Value))).WithIsInline(true)));
return;
}
}
await Context.Channel.SendErrorAsync("No pokemon found.");
await ReplyErrorLocalized("pokemon_none").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -79,13 +77,16 @@ namespace NadekoBot.Modules.Searches
{
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(kvp.Value.Name)
.WithDescription(kvp.Value.Desc)
.AddField(efb => efb.WithName("Rating").WithValue(kvp.Value.Rating.ToString()).WithIsInline(true))
.WithDescription(string.IsNullOrWhiteSpace(kvp.Value.Desc)
? kvp.Value.ShortDesc
: kvp.Value.Desc)
.AddField(efb => efb.WithName(GetText("rating"))
.WithValue(kvp.Value.Rating.ToString(_cultureInfo)).WithIsInline(true))
).ConfigureAwait(false);
return;
}
}
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 Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using NLog;
using NadekoBot.Extensions;
using System.Diagnostics;
namespace NadekoBot.Modules.Searches
{
@ -65,23 +63,19 @@ namespace NadekoBot.Modules.Searches
}
[Group]
public class StreamNotificationCommands : ModuleBase
public class StreamNotificationCommands : NadekoSubmodule
{
private static Timer checkTimer { get; }
private static ConcurrentDictionary<string, StreamStatus> oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static ConcurrentDictionary<string, StreamStatus> cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static Logger _log { get; }
private static readonly Timer _checkTimer;
private static readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>();
private static bool FirstPass { get; set; } = true;
private static bool firstPass { get; set; } = true;
static StreamNotificationCommands()
{
_log = LogManager.GetCurrentClassLogger();
checkTimer = new Timer(async (state) =>
_checkTimer = new Timer(async (state) =>
{
oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(cachedStatuses);
cachedStatuses.Clear();
var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses);
_cachedStatuses.Clear();
IEnumerable<FollowedStream> streams;
using (var uow = DbHandler.UnitOfWork())
{
@ -93,7 +87,7 @@ namespace NadekoBot.Modules.Searches
try
{
var newStatus = await GetStreamStatus(fs).ConfigureAwait(false);
if (FirstPass)
if (firstPass)
{
return;
}
@ -108,7 +102,7 @@ namespace NadekoBot.Modules.Searches
return;
try
{
var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false);
await channel.EmbedAsync(fs.GetEmbed(newStatus, channel.Guild.Id)).ConfigureAwait(false);
}
catch
{
@ -122,7 +116,7 @@ namespace NadekoBot.Modules.Searches
}
}));
FirstPass = false;
firstPass = false;
}, null, TimeSpan.Zero, TimeSpan.FromSeconds(60));
}
@ -134,7 +128,7 @@ namespace NadekoBot.Modules.Searches
{
case FollowedStream.FollowedStreamType.Hitbox:
var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}";
if (checkCache && cachedStatuses.TryGetValue(hitboxUrl, out result))
if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result))
return result;
using (var http = new HttpClient())
{
@ -149,11 +143,11 @@ namespace NadekoBot.Modules.Searches
ApiLink = hitboxUrl,
Views = hbData.Views
};
cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
_cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result);
return result;
case FollowedStream.FollowedStreamType.Twitch:
var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6";
if (checkCache && cachedStatuses.TryGetValue(twitchUrl, out result))
if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result))
return result;
using (var http = new HttpClient())
{
@ -170,11 +164,11 @@ namespace NadekoBot.Modules.Searches
ApiLink = twitchUrl,
Views = twData.Stream?.Viewers.ToString() ?? "0"
};
cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
_cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result);
return result;
case FollowedStream.FollowedStreamType.Beam:
var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}";
if (checkCache && cachedStatuses.TryGetValue(beamUrl, out result))
if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result))
return result;
using (var http = new HttpClient())
{
@ -190,7 +184,7 @@ namespace NadekoBot.Modules.Searches
ApiLink = beamUrl,
Views = bmData.ViewersCurrent.ToString()
};
cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
_cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result);
return result;
default:
break;
@ -234,17 +228,21 @@ namespace NadekoBot.Modules.Searches
if (!streams.Any())
{
await Context.Channel.SendConfirmAsync("You are not following any streams on this server.").ConfigureAwait(false);
await ReplyErrorLocalized("streams_none").ConfigureAwait(false);
return;
}
var text = string.Join("\n", await Task.WhenAll(streams.Select(async snc =>
{
var ch = await Context.Guild.GetTextChannelAsync(snc.ChannelId);
return $"`{snc.Username}`'s stream on **{(ch)?.Name}** channel. 【`{snc.Type.ToString()}`】";
return string.Format("{0}'s stream on {1} channel. 【{2}】",
Format.Code(snc.Username),
Format.Bold(ch?.Name ?? "deleted-channel"),
Format.Code(snc.Type.ToString()));
})));
await Context.Channel.SendConfirmAsync($"You are following **{streams.Count()}** streams on this server.\n\n" + text).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(GetText("streams_following", streams.Count()) + "\n\n" + text)
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -271,10 +269,13 @@ namespace NadekoBot.Modules.Searches
}
if (!removed)
{
await Context.Channel.SendErrorAsync("No such stream.").ConfigureAwait(false);
await ReplyErrorLocalized("stream_no").ConfigureAwait(false);
return;
}
await Context.Channel.SendConfirmAsync($"Removed `{username}`'s stream ({type}) from notifications.").ConfigureAwait(false);
await ReplyConfirmLocalized("stream_removed",
Format.Code(username),
type).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -293,20 +294,24 @@ namespace NadekoBot.Modules.Searches
}));
if (streamStatus.IsLive)
{
await Context.Channel.SendConfirmAsync($"Streamer {username} is online with {streamStatus.Views} viewers.");
await ReplyConfirmLocalized("streamer_online",
username,
streamStatus.Views)
.ConfigureAwait(false);
}
else
{
await Context.Channel.SendConfirmAsync($"Streamer {username} is offline.");
await ReplyConfirmLocalized("streamer_offline",
username).ConfigureAwait(false);
}
}
catch
{
await Context.Channel.SendErrorAsync("No channel found.");
await ReplyErrorLocalized("no_channel_found").ConfigureAwait(false);
}
}
private static async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type)
private async Task TrackStream(ITextChannel channel, string username, FollowedStream.FollowedStreamType type)
{
username = username.ToLowerInvariant().Trim();
var fs = new FollowedStream
@ -324,7 +329,7 @@ namespace NadekoBot.Modules.Searches
}
catch
{
await channel.SendErrorAsync("Stream probably doesn't exist.").ConfigureAwait(false);
await ReplyErrorLocalized("stream_not_exist").ConfigureAwait(false);
return;
}
@ -335,24 +340,24 @@ namespace NadekoBot.Modules.Searches
.Add(fs);
await uow.CompleteAsync().ConfigureAwait(false);
}
await channel.EmbedAsync(fs.GetEmbed(status), $"🆗 I will notify this channel when status changes.").ConfigureAwait(false);
await channel.EmbedAsync(fs.GetEmbed(status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false);
}
}
}
public static class FollowedStreamExtensions
{
public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status)
public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status, ulong guildId)
{
var embed = new EmbedBuilder().WithTitle(fs.Username)
.WithUrl(fs.GetLink())
.AddField(efb => efb.WithName("Status")
.AddField(efb => efb.WithName(fs.GetText("status"))
.WithValue(status.IsLive ? "Online" : "Offline")
.WithIsInline(true))
.AddField(efb => efb.WithName("Viewers")
.AddField(efb => efb.WithName(fs.GetText("viewers"))
.WithValue(status.IsLive ? status.Views : "-")
.WithIsInline(true))
.AddField(efb => efb.WithName("Platform")
.AddField(efb => efb.WithName(fs.GetText("platform"))
.WithValue(fs.Type.ToString())
.WithIsInline(true))
.WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor);
@ -360,15 +365,21 @@ namespace NadekoBot.Modules.Searches
return embed;
}
public static string GetLink(this FollowedStream fs) {
public static string GetText(this FollowedStream fs, string key, params object[] replacements) =>
NadekoTopLevelModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(fs.GuildId),
typeof(Searches).Name.ToLowerInvariant(),
replacements);
public static string GetLink(this FollowedStream fs)
{
if (fs.Type == FollowedStream.FollowedStreamType.Hitbox)
return $"http://www.hitbox.tv/{fs.Username}/";
else if (fs.Type == FollowedStream.FollowedStreamType.Twitch)
if (fs.Type == FollowedStream.FollowedStreamType.Twitch)
return $"http://www.twitch.tv/{fs.Username}/";
else if (fs.Type == FollowedStream.FollowedStreamType.Beam)
if (fs.Type == FollowedStream.FollowedStreamType.Beam)
return $"https://beam.pro/{fs.Username}/";
else
return "??";
return "??";
}
}
}

View File

@ -19,10 +19,10 @@ namespace NadekoBot.Modules.Searches
}
[Group]
public class TranslateCommands : ModuleBase
public class TranslateCommands : NadekoSubmodule
{
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<ulong, bool> translatedChannels { get; } = new ConcurrentDictionary<ulong, bool>();
private static ConcurrentDictionary<UserChannelPair, string> userLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>();
static TranslateCommands()
{
@ -35,7 +35,7 @@ namespace NadekoBot.Modules.Searches
return;
bool autoDelete;
if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
if (!translatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete))
return;
var key = new UserChannelPair()
{
@ -44,10 +44,10 @@ namespace NadekoBot.Modules.Searches
};
string langs;
if (!UserLanguages.TryGetValue(key, out langs))
if (!userLanguages.TryGetValue(key, out langs))
return;
var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore), true)
var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore))
.ConfigureAwait(false);
if (autoDelete)
try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { }
@ -64,21 +64,21 @@ namespace NadekoBot.Modules.Searches
{
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var translation = await TranslateInternal(langs, text);
await Context.Channel.SendConfirmAsync("Translation " + langs, translation).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(GetText("translation") + " " + langs, translation).ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync("Bad input format, or something went wrong...").ConfigureAwait(false);
await ReplyErrorLocalized("bad_input_format").ConfigureAwait(false);
}
}
private static async Task<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('>');
if (langarr.Length != 2)
throw new ArgumentException();
string from = langarr[0];
string to = langarr[1];
var from = langarr[0];
var to = langarr[1];
text = text?.Trim();
if (string.IsNullOrWhiteSpace(text))
throw new ArgumentException();
@ -101,20 +101,20 @@ namespace NadekoBot.Modules.Searches
if (autoDelete == AutoDeleteAutoTranslate.Del)
{
TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel. User messages will be auto-deleted.").ConfigureAwait(false); } catch { }
translatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true);
await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false);
return;
}
bool throwaway;
if (TranslatedChannels.TryRemove(channel.Id, out throwaway))
if (translatedChannels.TryRemove(channel.Id, out throwaway))
{
try { await channel.SendConfirmAsync("Stopped automatic translation of messages on this channel.").ConfigureAwait(false); } catch { }
await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false);
return;
}
else if (TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
if (translatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del))
{
try { await channel.SendConfirmAsync("Started automatic translation of messages on this channel.").ConfigureAwait(false); } catch { }
await ReplyConfirmLocalized("atl_started").ConfigureAwait(false);
}
}
@ -130,8 +130,8 @@ namespace NadekoBot.Modules.Searches
if (string.IsNullOrWhiteSpace(langs))
{
if (UserLanguages.TryRemove(ucp, out langs))
await Context.Channel.SendConfirmAsync($"{Context.User.Mention}'s auto-translate language has been removed.").ConfigureAwait(false);
if (userLanguages.TryRemove(ucp, out langs))
await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false);
return;
}
@ -143,20 +143,20 @@ namespace NadekoBot.Modules.Searches
if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to))
{
try { await Context.Channel.SendErrorAsync("Invalid source and/or target language.").ConfigureAwait(false); } catch { }
await ReplyErrorLocalized("invalid_lang").ConfigureAwait(false);
return;
}
UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
userLanguages.AddOrUpdate(ucp, langs, (key, val) => langs);
await Context.Channel.SendConfirmAsync($"Your auto-translate language has been set to {from}>{to}").ConfigureAwait(false);
await ReplyConfirmLocalized("atl_set", from, to).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Translangs()
{
await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", columns: 3);
await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", 3);
}
}

View File

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

View File

@ -1,5 +1,4 @@
using Discord;
using Discord.Commands;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
@ -8,27 +7,26 @@ using System.Text;
using System.Net.Http;
using NadekoBot.Services;
using System.Threading.Tasks;
using NadekoBot.Attributes;
using System.Text.RegularExpressions;
using System.Net;
using NadekoBot.Modules.Searches.Models;
using System.Collections.Generic;
using ImageSharp;
using NadekoBot.Extensions;
using System.IO;
using NadekoBot.Modules.Searches.Commands.OMDB;
using NadekoBot.Modules.Searches.Commands.Models;
using AngleSharp.Parser.Html;
using AngleSharp;
using AngleSharp.Dom.Html;
using AngleSharp.Dom;
using System.Xml;
using System.Xml.Linq;
using Configuration = AngleSharp.Configuration;
using NadekoBot.Attributes;
using Discord.Commands;
using ImageSharp.Processing.Processors;
namespace NadekoBot.Modules.Searches
{
[NadekoModule("Searches", "~")]
public partial class Searches : NadekoModule
public partial class Searches : NadekoTopLevelModule
{
[NadekoCommand, Usage, Description, Aliases]
public async Task Weather([Remainder] string query)
@ -43,15 +41,15 @@ namespace NadekoBot.Modules.Searches
var data = JsonConvert.DeserializeObject<WeatherData>(response);
var embed = new EmbedBuilder()
.AddField(fb => fb.WithName("🌍 **Location**").WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
.AddField(fb => fb.WithName("📏 **Lat,Long**").WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
.AddField(fb => fb.WithName("☁ **Condition**").WithValue(String.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
.AddField(fb => fb.WithName("😓 **Humidity**").WithValue($"{data.main.humidity}%").WithIsInline(true))
.AddField(fb => fb.WithName("💨 **Wind Speed**").WithValue(data.wind.speed + " km/h").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 **Temperature**").WithValue(data.main.temp + "°C").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 **Min - Max**").WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true))
.AddField(fb => fb.WithName("🌄 **Sunrise (utc)**").WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("🌇 **Sunset (utc)**").WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("🌍 " + GetText("location")).WithValue(data.name + ", " + data.sys.country).WithIsInline(true))
.AddField(fb => fb.WithName("📏 " + GetText("latlong")).WithValue($"{data.coord.lat}, {data.coord.lon}").WithIsInline(true))
.AddField(fb => fb.WithName("☁ " + GetText("condition")).WithValue(string.Join(", ", data.weather.Select(w => w.main))).WithIsInline(true))
.AddField(fb => fb.WithName("😓 " + GetText("humidity")).WithValue($"{data.main.humidity}%").WithIsInline(true))
.AddField(fb => fb.WithName("💨 " + GetText("wind_speed")).WithValue(data.wind.speed + " km/h").WithIsInline(true))
.AddField(fb => fb.WithName("🌡 " + GetText("temperature")).WithValue(data.main.temp + "°C").WithIsInline(true))
.AddField(fb => fb.WithName("🔆 " + GetText("min_max")).WithValue($"{data.main.temp_min}°C - {data.main.temp_max}°C").WithIsInline(true))
.AddField(fb => fb.WithName("🌄 " + GetText("sunrise")).WithValue($"{data.sys.sunrise.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("🌇 " + GetText("sunset")).WithValue($"{data.sys.sunset.ToUnixTimestamp():HH:mm}").WithIsInline(true))
.WithOkColor()
.WithFooter(efb => efb.WithText("Powered by http://openweathermap.org"));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -60,17 +58,15 @@ namespace NadekoBot.Modules.Searches
[NadekoCommand, Usage, Description, Aliases]
public async Task Youtube([Remainder] string query = null)
{
if (!(await ValidateQuery(Context.Channel, query).ConfigureAwait(false))) return;
if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return;
var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault();
if (string.IsNullOrWhiteSpace(result))
{
await Context.Channel.SendErrorAsync("No results found for that query.").ConfigureAwait(false);
await ReplyErrorLocalized("no_results").ConfigureAwait(false);
return;
}
await Context.Channel.SendMessageAsync(result).ConfigureAwait(false);
//await Context.Channel.EmbedAsync(new Discord.API.Embed() { Video = new Discord.API.EmbedVideo() { Url = result.Replace("watch?v=", "embed/") }, Color = NadekoBot.OkColor }).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -82,7 +78,7 @@ namespace NadekoBot.Modules.Searches
var movie = await OmdbProvider.FindMovie(query);
if (movie == null)
{
await Context.Channel.SendErrorAsync("Failed to find that movie.").ConfigureAwait(false);
await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false);
return;
}
await Context.Channel.EmbedAsync(movie.GetEmbed()).ConfigureAwait(false);
@ -122,7 +118,7 @@ namespace NadekoBot.Modules.Searches
var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link)
@ -174,7 +170,7 @@ namespace NadekoBot.Modules.Searches
var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false);
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
.WithUrl("https://www.google.rs/search?q=" + terms + "&source=lnms&tbm=isch")
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithDescription(res.Link)
@ -205,7 +201,7 @@ namespace NadekoBot.Modules.Searches
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Image Search For: " + terms.TrimTo(50))
.WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://s.imgur.com/images/logo-1200-630.jpg?"))
.WithDescription(source)
@ -235,13 +231,14 @@ namespace NadekoBot.Modules.Searches
if (shortened == arg)
{
await Context.Channel.SendErrorAsync("Failed to shorten that url.").ConfigureAwait(false);
await ReplyErrorLocalized("shorten_fail").ConfigureAwait(false);
return;
}
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("Original Url")
.AddField(efb => efb.WithName(GetText("original_url"))
.WithValue($"<{arg}>"))
.AddField(efb => efb.WithName("Short Url")
.AddField(efb => efb.WithName(GetText("short_url"))
.WithValue($"<{shortened}>")))
.ConfigureAwait(false);
}
@ -289,7 +286,7 @@ namespace NadekoBot.Modules.Searches
var embed = new EmbedBuilder()
.WithOkColor()
.WithAuthor(eab => eab.WithName("Search For: " + terms.TrimTo(50))
.WithAuthor(eab => eab.WithName(GetText("search_for") + " " + terms.TrimTo(50))
.WithUrl(fullQueryLink)
.WithIconUrl("http://i.imgur.com/G46fm8J.png"))
.WithTitle(Context.User.ToString())
@ -298,26 +295,22 @@ namespace NadekoBot.Modules.Searches
var desc = await Task.WhenAll(results.Select(async res =>
$"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n"))
.ConfigureAwait(false);
await Context.Channel.EmbedAsync(embed.WithDescription(String.Concat(desc))).ConfigureAwait(false);
await Context.Channel.EmbedAsync(embed.WithDescription(string.Concat(desc))).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task MagicTheGathering([Remainder] string name = null)
public async Task MagicTheGathering([Remainder] string name)
{
var arg = name;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
string response = "";
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}")
.ConfigureAwait(false);
var response = await http.GetStringAsync($"https://api.deckbrew.com/mtg/cards?name={Uri.EscapeUriString(arg)}")
.ConfigureAwait(false);
try
{
var items = JArray.Parse(response).ToArray();
@ -327,50 +320,46 @@ namespace NadekoBot.Modules.Searches
var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString());
var cost = item["cost"].ToString();
var desc = item["text"].ToString();
var types = String.Join(",\n", item["types"].ToObject<string[]>());
var types = string.Join(",\n", item["types"].ToObject<string[]>());
var img = item["editions"][0]["image_url"].ToString();
var embed = new EmbedBuilder().WithOkColor()
.WithTitle(item["name"].ToString())
.WithDescription(desc)
.WithImageUrl(img)
.AddField(efb => efb.WithName("Store Url").WithValue(storeUrl).WithIsInline(true))
.AddField(efb => efb.WithName("Cost").WithValue(cost).WithIsInline(true))
.AddField(efb => efb.WithName("Types").WithValue(types).WithIsInline(true));
.AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("types")).WithValue(types).WithIsInline(true));
//.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync($"Error could not find the card '{arg}'.").ConfigureAwait(false);
await ReplyErrorLocalized("card_not_found").ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hearthstone([Remainder] string name = null)
public async Task Hearthstone([Remainder] string name)
{
var arg = name;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a card name to search for.").ConfigureAwait(false);
return;
}
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
string response = "";
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}")
.ConfigureAwait(false);
var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}")
.ConfigureAwait(false);
try
{
var items = JArray.Parse(response).Shuffle().ToList();
@ -393,17 +382,17 @@ namespace NadekoBot.Modules.Searches
string msg = null;
if (items.Count > 4)
{
msg = "⚠ Found over 4 images. Showing random 4.";
msg = GetText("hs_over_x", 4);
}
var ms = new MemoryStream();
await Task.Run(() => images.AsEnumerable().Merge().SaveAsPng(ms));
await Task.Run(() => images.AsEnumerable().Merge().Save(ms));
ms.Position = 0;
await Context.Channel.SendFileAsync(ms, arg + ".png", msg).ConfigureAwait(false);
}
catch (Exception ex)
{
await Context.Channel.SendErrorAsync($"Error occured.").ConfigureAwait(false);
_log.Error(ex);
await ReplyErrorLocalized("error_occured").ConfigureAwait(false);
}
}
}
@ -413,23 +402,20 @@ namespace NadekoBot.Modules.Searches
{
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
return;
}
var arg = query;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a sentence.").ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(query))
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
http.DefaultRequestHeaders.Add("Accept", "text/plain");
var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(query)}").ConfigureAwait(false);
try
{
var embed = new EmbedBuilder()
@ -441,7 +427,7 @@ namespace NadekoBot.Modules.Searches
}
catch
{
await Context.Channel.SendErrorAsync("Failed to yodify your sentence.").ConfigureAwait(false);
await ReplyErrorLocalized("yodify_error").ConfigureAwait(false);
}
}
}
@ -451,22 +437,19 @@ namespace NadekoBot.Modules.Searches
{
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
return;
}
var arg = query;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(query))
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("Accept", "application/json");
var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(arg)}").ConfigureAwait(false);
var res = await http.GetStringAsync($"http://api.urbandictionary.com/v0/define?term={Uri.EscapeUriString(query)}").ConfigureAwait(false);
try
{
var items = JObject.Parse(res);
@ -482,7 +465,7 @@ namespace NadekoBot.Modules.Searches
}
catch
{
await Context.Channel.SendErrorAsync("Failed finding a definition for that term.").ConfigureAwait(false);
await ReplyErrorLocalized("ud_error").ConfigureAwait(false);
}
}
}
@ -509,39 +492,36 @@ namespace NadekoBot.Modules.Searches
definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Define: " + word)
.WithTitle(GetText("define") + " " + word)
.WithDescription(definition)
.WithFooter(efb => efb.WithText(sense.Gramatical_info?.type));
if (sense.Examples != null)
embed.AddField(efb => efb.WithName("Example").WithValue(sense.Examples.First().text));
embed.AddField(efb => efb.WithName(GetText("example")).WithValue(sense.Examples.First().text));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Hashtag([Remainder] string query = null)
public async Task Hashtag([Remainder] string query)
{
var arg = query;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter a search term.").ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(query))
return;
}
if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey))
{
await Context.Channel.SendErrorAsync("Bot owner didn't specify MashapeApiKey. You can't use this functionality.").ConfigureAwait(false);
await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false);
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
var res = "";
string res;
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey);
res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(arg)}.json").ConfigureAwait(false);
res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(query)}.json").ConfigureAwait(false);
}
try
@ -560,7 +540,7 @@ namespace NadekoBot.Modules.Searches
}
catch
{
await Context.Channel.SendErrorAsync("Failed finding a definition for that tag.").ConfigureAwait(false);
await ReplyErrorLocalized("hashtag_error").ConfigureAwait(false);
}
}
@ -574,7 +554,7 @@ namespace NadekoBot.Modules.Searches
return;
var fact = JObject.Parse(response)["facts"][0].ToString();
await Context.Channel.SendConfirmAsync("🐈fact", fact).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🐈" + GetText("catfact"), fact).ConfigureAwait(false);
}
}
@ -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 data = JsonConvert.DeserializeObject<WikipediaApiModel>(result);
if (data.Query.Pages[0].Missing)
await Context.Channel.SendErrorAsync("That page could not be found.").ConfigureAwait(false);
await ReplyErrorLocalized("wiki_page_not_found").ConfigureAwait(false);
else
await Context.Channel.SendMessageAsync(data.Query.Pages[0].FullUrl).ConfigureAwait(false);
}
@ -625,28 +605,21 @@ namespace NadekoBot.Modules.Searches
return;
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]
public async Task Videocall([Remainder] params IUser[] users)
public async Task Videocall(params IUser[] users)
{
try
var allUsrs = users.Append(Context.User);
var allUsrsArray = allUsrs.ToArray();
var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString()));
str += new NadekoRandom().Next();
foreach (var usr in allUsrsArray)
{
var allUsrs = users.Append(Context.User);
var allUsrsArray = allUsrs.ToArray();
var str = allUsrsArray.Aggregate("http://appear.in/", (current, usr) => current + Uri.EscapeUriString(usr.Username[0].ToString()));
str += new NadekoRandom().Next();
foreach (var usr in allUsrsArray)
{
await (await (usr as IGuildUser).CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false);
}
}
catch (Exception ex)
{
_log.Error(ex);
await (await usr.CreateDMChannelAsync()).SendConfirmAsync(str).ConfigureAwait(false);
}
}
@ -666,11 +639,11 @@ namespace NadekoBot.Modules.Searches
}
[NadekoCommand, Usage, Description, Aliases]
public async Task Wikia(string target, [Remainder] string query = null)
public async Task Wikia(string target, [Remainder] string query)
{
if (string.IsNullOrWhiteSpace(target) || string.IsNullOrWhiteSpace(query))
{
await Context.Channel.SendErrorAsync("Please enter a target wikia, followed by search query.").ConfigureAwait(false);
await ReplyErrorLocalized("wikia_input_error").ConfigureAwait(false);
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
@ -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 items = JObject.Parse(res);
var found = items["items"][0];
var response = $@"`Title:` {found["title"].ToString()}
`Quality:` {found["quality"]}
`URL:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
var response = $@"`{GetText("title")}` {found["title"]}
`{GetText("quality")}` {found["quality"]}
`{GetText("url")}:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}";
await Context.Channel.SendMessageAsync(response).ConfigureAwait(false);
}
catch
{
await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false);
await ReplyErrorLocalized("wikia_error").ConfigureAwait(false);
}
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task MCPing([Remainder] string query = null)
{
var arg = query;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("💢 Please enter a `ip:port`.").ConfigureAwait(false);
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
string ip = arg.Split(':')[0];
string port = arg.Split(':')[1];
var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
try
{
var items = JObject.Parse(res);
var sb = new StringBuilder();
int ping = (int)Math.Ceiling(Double.Parse(items["latency"].ToString()));
sb.AppendLine($"`Server:` {arg}");
sb.AppendLine($"`Version:` {items["version"]["name"].ToString()} / Protocol {items["version"]["protocol"].ToString()}");
sb.AppendLine($"`Description:` {items["description"].ToString()}");
sb.AppendLine($"`Online Players:` {items["players"]["online"].ToString()}/{items["players"]["max"].ToString()}");
sb.Append($"`Latency:` {ping}");
await Context.Channel.SendMessageAsync(sb.ToString());
}
catch
{
await Context.Channel.SendErrorAsync($"Failed finding `{arg}`.").ConfigureAwait(false);
}
}
}
//[NadekoCommand, Usage, Description, Aliases]
//public async Task MCPing([Remainder] string query2 = null)
//{
// var query = query2;
// if (string.IsNullOrWhiteSpace(query))
// return;
// await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
// using (var http = new HttpClient())
// {
// http.DefaultRequestHeaders.Clear();
// var ip = query.Split(':')[0];
// var port = query.Split(':')[1];
// var res = await http.GetStringAsync($"https://api.minetools.eu/ping/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
// try
// {
// var items = JObject.Parse(res);
// var sb = new StringBuilder();
// var ping = (int)Math.Ceiling(double.Parse(items["latency"].ToString()));
// sb.AppendLine($"`Server:` {query}");
// sb.AppendLine($"`Version:` {items["version"]["name"]} / Protocol {items["version"]["protocol"]}");
// sb.AppendLine($"`Description:` {items["description"]}");
// sb.AppendLine($"`Online Players:` {items["players"]["online"]}/{items["players"]["max"]}");
// sb.Append($"`Latency:` {ping}");
// await Context.Channel.SendMessageAsync(sb.ToString());
// }
// catch
// {
// await Context.Channel.SendErrorAsync($"Failed finding `{query}`.").ConfigureAwait(false);
// }
// }
//}
[NadekoCommand, Usage, Description, Aliases]
public async Task MCQ([Remainder] string query = null)
{
var arg = query;
if (string.IsNullOrWhiteSpace(arg))
{
await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
return;
}
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Clear();
try
{
string ip = arg.Split(':')[0];
string port = arg.Split(':')[1];
var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
var items = JObject.Parse(res);
var sb = new StringBuilder();
sb.AppendLine($"`Server:` {arg.ToString()} 〘Status: {items["status"]}〙");
sb.AppendLine($"`Player List (First 5):`");
foreach (var item in items["Playerlist"].Take(5))
{
sb.AppendLine($":rosette: {item}");
}
sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}");
sb.AppendLine($"`Plugins:` {items["Plugins"]}");
sb.Append($"`Version:` {items["Version"]}");
await Context.Channel.SendMessageAsync(sb.ToString());
}
catch
{
await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false);
}
}
}
//[NadekoCommand, Usage, Description, Aliases]
//public async Task MCQ([Remainder] string query = null)
//{
// var arg = query;
// if (string.IsNullOrWhiteSpace(arg))
// {
// await Context.Channel.SendErrorAsync("Please enter `ip:port`.").ConfigureAwait(false);
// return;
// }
// await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
// using (var http = new HttpClient())
// {
// http.DefaultRequestHeaders.Clear();
// try
// {
// var ip = arg.Split(':')[0];
// var port = arg.Split(':')[1];
// var res = await http.GetStringAsync($"https://api.minetools.eu/query/{Uri.EscapeUriString(ip)}/{Uri.EscapeUriString(port)}").ConfigureAwait(false);
// var items = JObject.Parse(res);
// var sb = new StringBuilder();
// sb.AppendLine($"`Server:` {arg} 〘Status: {items["status"]}〙");
// sb.AppendLine("`Player List (First 5):`");
// foreach (var item in items["Playerlist"].Take(5))
// {
// sb.AppendLine($":rosette: {item}");
// }
// sb.AppendLine($"`Online Players:` {items["Players"]} / {items["MaxPlayers"]}");
// sb.AppendLine($"`Plugins:` {items["Plugins"]}");
// sb.Append($"`Version:` {items["Version"]}");
// await Context.Channel.SendMessageAsync(sb.ToString());
// }
// catch
// {
// await Context.Channel.SendErrorAsync($"Failed finding server `{arg}`.").ConfigureAwait(false);
// }
// }
//}
public enum DapiSearchType
{
@ -776,7 +746,7 @@ namespace NadekoBot.Modules.Searches
Yandere
}
public static async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
public async Task InternalDapiCommand(IUserMessage umsg, string tag, DapiSearchType type)
{
var channel = umsg.Channel;
@ -785,7 +755,7 @@ namespace NadekoBot.Modules.Searches
var url = await InternalDapiSearch(tag, type).ConfigureAwait(false);
if (url == null)
await channel.SendErrorAsync(umsg.Author.Mention + " No results.");
await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results"));
else
await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(umsg.Author.Mention + " " + tag)
@ -796,7 +766,7 @@ namespace NadekoBot.Modules.Searches
public static async Task<string> InternalDapiSearch(string tag, DapiSearchType type)
{
tag = tag?.Replace(" ", "_");
string website = "";
var website = "";
switch (type)
{
case DapiSearchType.Safebooru:
@ -841,10 +811,10 @@ namespace NadekoBot.Modules.Searches
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;
await ch.SendErrorAsync("Please specify search parameters.").ConfigureAwait(false);
if (!string.IsNullOrWhiteSpace(query)) return true;
await ch.SendErrorAsync(GetText("specify_search_params")).ConfigureAwait(false);
return false;
}
}

View File

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

View File

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

View File

@ -12,7 +12,7 @@ namespace NadekoBot.Modules.Utility
public partial class Utility
{
[Group]
public class InfoCommands : ModuleBase
public class InfoCommands : NadekoSubmodule
{
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
@ -24,7 +24,7 @@ namespace NadekoBot.Modules.Utility
if (string.IsNullOrWhiteSpace(guildName))
guild = channel.Guild;
else
guild = NadekoBot.Client.GetGuilds().Where(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant()).FirstOrDefault();
guild = NadekoBot.Client.GetGuilds().FirstOrDefault(g => g.Name.ToUpperInvariant() == guildName.ToUpperInvariant());
if (guild == null)
return;
var ownername = await guild.GetUserAsync(guild.OwnerId);
@ -37,22 +37,22 @@ namespace NadekoBot.Modules.Utility
if (string.IsNullOrWhiteSpace(features))
features = "-";
var embed = new EmbedBuilder()
.WithAuthor(eab => eab.WithName("Server Info"))
.WithAuthor(eab => eab.WithName(GetText("server_info")))
.WithTitle(guild.Name)
.AddField(fb => fb.WithName("**ID**").WithValue(guild.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Owner**").WithValue(ownername.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Features**").WithValue(features).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("id")).WithValue(guild.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("owner")).WithValue(ownername.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("members")).WithValue(users.Count.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("text_channels")).WithValue(textchn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("voice_channels")).WithValue(voicechn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("created_at")).WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("region")).WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("roles")).WithValue((guild.Roles.Count - 1).ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("features")).WithValue(features).WithIsInline(true))
.WithImageUrl(guild.IconUrl)
.WithColor(NadekoBot.OkColor);
if (guild.Emojis.Any())
{
embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(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);
}
@ -69,9 +69,9 @@ namespace NadekoBot.Modules.Utility
var embed = new EmbedBuilder()
.WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions())
.AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("id")).WithValue(ch.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("created_at")).WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("users")).WithValue(usercount.ToString()).WithIsInline(true))
.WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
@ -86,15 +86,15 @@ namespace NadekoBot.Modules.Utility
return;
var embed = new EmbedBuilder()
.AddField(fb => fb.WithName("**Name**").WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true));
.AddField(fb => fb.WithName(GetText("name")).WithValue($"**{user.Username}**#{user.Discriminator}").WithIsInline(true));
if (!string.IsNullOrWhiteSpace(user.Nickname))
{
embed.AddField(fb => fb.WithName("**Nickname**").WithValue(user.Nickname).WithIsInline(true));
embed.AddField(fb => fb.WithName(GetText("nickname")).WithValue(user.Nickname).WithIsInline(true));
}
embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
embed.AddField(fb => fb.WithName(GetText("id")).WithValue(user.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName(GetText("joined_server")).WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "?"}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("joined_discord")).WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName(GetText("roles")).WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
.WithColor(NadekoBot.OkColor);
if (user.AvatarId != null)
@ -119,12 +119,17 @@ namespace NadekoBot.Modules.Utility
StringBuilder str = new StringBuilder();
foreach (var kvp in NadekoBot.CommandHandler.UserMessagesSent.OrderByDescending(kvp => kvp.Value).Skip(page*activityPerPage).Take(activityPerPage))
{
str.AppendLine($"`{++startCount}.` **{kvp.Key}** [{kvp.Value/NadekoBot.Stats.GetUptime().TotalSeconds:F2}/s] - {kvp.Value} total");
str.AppendLine(GetText("activity_line",
++startCount,
Format.Bold(kvp.Key.ToString()),
kvp.Value / NadekoBot.Stats.GetUptime().TotalSeconds, kvp.Value));
}
await Context.Channel.EmbedAsync(new EmbedBuilder().WithTitle($"Activity Page #{page}")
await Context.Channel.EmbedAsync(new EmbedBuilder()
.WithTitle(GetText("activity_page", page))
.WithOkColor()
.WithFooter(efb => efb.WithText($"{NadekoBot.CommandHandler.UserMessagesSent.Count} users total."))
.WithFooter(efb => efb.WithText(GetText("activity_users_total",
NadekoBot.CommandHandler.UserMessagesSent.Count)))
.WithDescription(str.ToString()));
}
}

View File

@ -5,12 +5,10 @@ using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
@ -22,14 +20,16 @@ namespace NadekoBot.Modules.Utility
public partial class Utility
{
[Group]
public class RepeatCommands : ModuleBase
public class RepeatCommands : NadekoSubmodule
{
//guildid/RepeatRunners
public static ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> repeaters { get; }
public static ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>> Repeaters { get; set; }
private static bool _ready;
public class RepeatRunner
{
private Logger _log { get; }
private readonly Logger _log;
private CancellationTokenSource source { get; set; }
private CancellationToken token { get; set; }
@ -39,8 +39,16 @@ namespace NadekoBot.Modules.Utility
public RepeatRunner(Repeater repeater, ITextChannel channel = null)
{
_log = LogManager.GetCurrentClassLogger();
this.Repeater = repeater;
this.Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
Repeater = repeater;
//if (channel == null)
//{
// var guild = NadekoBot.Client.GetGuild(repeater.GuildId);
// Channel = guild.GetTextChannel(repeater.ChannelId);
//}
//else
// Channel = channel;
Channel = channel ?? NadekoBot.Client.GetGuild(repeater.GuildId)?.GetTextChannel(repeater.ChannelId);
if (Channel == null)
return;
Task.Run(Run);
@ -101,22 +109,21 @@ namespace NadekoBot.Modules.Utility
public override string ToString()
{
return $"{this.Channel.Mention} | {(int)this.Repeater.Interval.TotalHours}:{this.Repeater.Interval:mm} | {this.Repeater.Message.TrimTo(33)}";
return $"{Channel.Mention} | {(int)Repeater.Interval.TotalHours}:{Repeater.Interval:mm} | {Repeater.Message.TrimTo(33)}";
}
}
static RepeatCommands()
{
var _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(NadekoBot.AllGuildConfigs
.ToDictionary(gc => gc.GuildId,
gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr))
.Where(gr => gr.Channel != null))));
sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
var _ = Task.Run(async () =>
{
await Task.Delay(5000).ConfigureAwait(false);
Repeaters = new ConcurrentDictionary<ulong, ConcurrentQueue<RepeatRunner>>(NadekoBot.AllGuildConfigs
.ToDictionary(gc => gc.GuildId,
gc => new ConcurrentQueue<RepeatRunner>(gc.GuildRepeaters.Select(gr => new RepeatRunner(gr))
.Where(gr => gr.Channel != null))));
_ready = true;
});
}
[NadekoCommand, Usage, Description, Aliases]
@ -124,11 +131,13 @@ namespace NadekoBot.Modules.Utility
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task RepeatInvoke(int index)
{
if (!_ready)
return;
index -= 1;
ConcurrentQueue<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;
}
@ -136,7 +145,7 @@ namespace NadekoBot.Modules.Utility
if (index >= repList.Count)
{
await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false);
await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false);
return;
}
var repeater = repList[index].Repeater;
@ -151,19 +160,21 @@ namespace NadekoBot.Modules.Utility
[Priority(0)]
public async Task RepeatRemove(int index)
{
if (!_ready)
return;
if (index < 1)
return;
index -= 1;
ConcurrentQueue<RepeatRunner> rep;
if (!repeaters.TryGetValue(Context.Guild.Id, out rep))
if (!Repeaters.TryGetValue(Context.Guild.Id, out rep))
return;
var repeaterList = rep.ToList();
if (index >= repeaterList.Count)
{
await Context.Channel.SendErrorAsync("Index out of range.").ConfigureAwait(false);
await ReplyErrorLocalized("index_out_of_range").ConfigureAwait(false);
return;
}
@ -179,8 +190,9 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync().ConfigureAwait(false);
}
if (repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(repeaterList), rep))
await Context.Channel.SendConfirmAsync("Message Repeater",$"#{index+1} stopped.\n\n{repeater.ToString()}").ConfigureAwait(false);
if (Repeaters.TryUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(repeaterList), rep))
await Context.Channel.SendConfirmAsync(GetText("message_repeater"),
GetText("repeater_stopped" , index + 1) + $"\n\n{repeater}").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -189,6 +201,8 @@ namespace NadekoBot.Modules.Utility
[Priority(1)]
public async Task Repeat(int minutes, [Remainder] string message)
{
if (!_ready)
return;
if (minutes < 1 || minutes > 10080)
return;
@ -216,13 +230,18 @@ namespace NadekoBot.Modules.Utility
var rep = new RepeatRunner(toAdd, (ITextChannel)Context.Channel);
repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(new[] { rep }), (key, old) =>
Repeaters.AddOrUpdate(Context.Guild.Id, new ConcurrentQueue<RepeatRunner>(new[] { rep }), (key, old) =>
{
old.Enqueue(rep);
return old;
});
await Context.Channel.SendConfirmAsync($"🔁 Repeating **\"{rep.Repeater.Message}\"** every `{rep.Repeater.Interval.Days} day(s), {rep.Repeater.Interval.Hours} hour(s) and {rep.Repeater.Interval.Minutes} minute(s)`.").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(
"🔁 " + GetText("repeater",
Format.Bold(rep.Repeater.Message),
Format.Bold(rep.Repeater.Interval.Days.ToString()),
Format.Bold(rep.Repeater.Interval.Hours.ToString()),
Format.Bold(rep.Repeater.Interval.Minutes.ToString()))).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -230,27 +249,33 @@ namespace NadekoBot.Modules.Utility
[RequireUserPermission(GuildPermission.ManageMessages)]
public async Task RepeatList()
{
if (!_ready)
return;
ConcurrentQueue<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;
}
var replist = repRunners.ToList();
var sb = new StringBuilder();
for (int i = 0; i < replist.Count; i++)
for (var i = 0; i < replist.Count; i++)
{
var rep = replist[i];
sb.AppendLine($"`{i + 1}.` {rep.ToString()}");
sb.AppendLine($"`{i + 1}.` {rep}");
}
var desc = sb.ToString();
if (string.IsNullOrWhiteSpace(desc))
desc = GetText("no_active_repeaters");
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle("List Of Repeaters")
.WithDescription(sb.ToString()))
.ConfigureAwait(false);
.WithTitle(GetText("list_of_repeaters"))
.WithDescription(desc))
.ConfigureAwait(false);
}
}
}

View File

@ -33,10 +33,11 @@ namespace NadekoBot.Modules.Utility
}
if (quotes.Any())
await Context.Channel.SendConfirmAsync($"💬 **Page {page + 1} of quotes:**\n```xl\n" + String.Join("\n", quotes.Select((q) => $"{q.Keyword,-20} by {q.AuthorName}")) + "\n```")
await Context.Channel.SendConfirmAsync(GetText("quotes_page", page + 1),
string.Join("\n", quotes.Select(q => $"{q.Keyword,-20} by {q.AuthorName}")))
.ConfigureAwait(false);
else
await Context.Channel.SendErrorAsync("No quotes on this page.").ConfigureAwait(false);
await ReplyErrorLocalized("quotes_page_none").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -72,11 +73,11 @@ namespace NadekoBot.Modules.Utility
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireContext(ContextType.Guild)]
public async Task SearchQuote(string keyword, [Remainder] string text)
{
if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text))
return;
if (string.IsNullOrWhiteSpace(keyword) || string.IsNullOrWhiteSpace(text))
return;
keyword = keyword.ToUpperInvariant();
@ -113,7 +114,7 @@ namespace NadekoBot.Modules.Utility
});
await uow.CompleteAsync().ConfigureAwait(false);
}
await Context.Channel.SendConfirmAsync("✅ Quote added.").ConfigureAwait(false);
await ReplyConfirmLocalized("quote_added").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -135,7 +136,7 @@ namespace NadekoBot.Modules.Utility
if (qs == null || !qs.Any())
{
sucess = false;
response = "No quotes found which you can remove.";
response = GetText("quotes_remove_none");
}
else
{
@ -144,7 +145,7 @@ namespace NadekoBot.Modules.Utility
uow.Quotes.Remove(q);
await uow.CompleteAsync().ConfigureAwait(false);
sucess = true;
response = "🗑 **Deleted a random quote.**";
response = GetText("quote_deleted");
}
}
if(sucess)
@ -172,7 +173,7 @@ namespace NadekoBot.Modules.Utility
await uow.CompleteAsync();
}
await Context.Channel.SendConfirmAsync($"🗑 **Deleted all quotes** with **{keyword}** keyword.");
await ReplyConfirmLocalized("quotes_deleted", Format.Bold(keyword)).ConfigureAwait(false);
}
}
}

View File

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

View File

@ -9,7 +9,6 @@ using Newtonsoft.Json;
using NLog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
@ -21,12 +20,12 @@ namespace NadekoBot.Modules.Utility
public partial class Utility
{
[Group]
public class UnitConverterCommands : ModuleBase
public class UnitConverterCommands : NadekoSubmodule
{
public static List<ConvertUnit> Units { get; set; } = new List<ConvertUnit>();
private static Logger _log { get; }
private new static readonly Logger _log;
private static Timer _timer;
private static TimeSpan updateInterval = new TimeSpan(12, 0, 0);
private static readonly TimeSpan _updateInterval = new TimeSpan(12, 0, 0);
static UnitConverterCommands()
{
@ -55,7 +54,7 @@ namespace NadekoBot.Modules.Utility
_log.Warn("Could not load units: " + e.Message);
}
_timer = new Timer(async (obj) => await UpdateCurrency(), null, (int)updateInterval.TotalMilliseconds, (int)updateInterval.TotalMilliseconds);
_timer = new Timer(async (obj) => await UpdateCurrency(), null, _updateInterval, _updateInterval);
}
public static async Task UpdateCurrency()
@ -93,15 +92,34 @@ namespace NadekoBot.Modules.Utility
}
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]
public async Task ConvertList()
{
var res = Units.GroupBy(x => x.UnitType)
.Aggregate(new EmbedBuilder().WithTitle("__Units which can be used by the converter__")
.Aggregate(new EmbedBuilder().WithTitle(GetText("convertlist"))
.WithColor(NadekoBot.OkColor),
(embed, g) => embed.AddField(efb =>
efb.WithName(g.Key.ToTitleCase())
@ -116,12 +134,12 @@ namespace NadekoBot.Modules.Utility
var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant()));
if (originUnit == null || targetUnit == null)
{
await Context.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: units not found", origin, target));
await ReplyErrorLocalized("convert_not_found", Format.Bold(origin), Format.Bold(target)).ConfigureAwait(false);
return;
}
if (originUnit.UnitType != targetUnit.UnitType)
{
await Context.Channel.SendErrorAsync(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First()));
await ReplyErrorLocalized("convert_type_error", Format.Bold(originUnit.Triggers.First()), Format.Bold(targetUnit.Triggers.First())).ConfigureAwait(false);
return;
}
decimal res;
@ -150,8 +168,6 @@ namespace NadekoBot.Modules.Utility
case "F":
res = res * (9m / 5m) - 459.67m;
break;
default:
break;
}
}
else
@ -165,7 +181,7 @@ namespace NadekoBot.Modules.Utility
}
res = Math.Round(res, 4);
await Context.Channel.SendConfirmAsync(string.Format("{0} {1} is equal to {2} {3}", value, (originUnit.Triggers.First() + "s").SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2)));
await Context.Channel.SendConfirmAsync(GetText("convert", value, (originUnit.Triggers.First()).SnPl(value.IsInteger() ? (int)value : 2), res, (targetUnit.Triggers.First() + "s").SnPl(res.IsInteger() ? (int)res : 2)));
}
}

View File

@ -6,7 +6,6 @@ using System.Linq;
using System.Threading.Tasks;
using System.Text;
using NadekoBot.Extensions;
using System.Text.RegularExpressions;
using System.Reflection;
using NadekoBot.Services.Impl;
using System.Net.Http;
@ -21,7 +20,7 @@ using NadekoBot.Services;
namespace NadekoBot.Modules.Utility
{
[NadekoModule("Utility", ".")]
public partial class Utility : NadekoModule
public partial class Utility : NadekoTopLevelModule
{
private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
@ -118,14 +117,14 @@ namespace NadekoBot.Modules.Utility
if (rotatingRoleColors.TryRemove(role.Id, out t))
{
t.Change(Timeout.Infinite, Timeout.Infinite);
await channel.SendConfirmAsync($"Stopped rotating colors for the **{role.Name}** role").ConfigureAwait(false);
await ReplyConfirmLocalized("rrc_stop", Format.Bold(role.Name)).ConfigureAwait(false);
}
return;
}
var hexColors = hexes.Select(hex =>
{
try { return (ImageSharp.Color?)new ImageSharp.Color(hex.Replace("#", "")); } catch { return null; }
try { return (ImageSharp.Color?)ImageSharp.Color.FromHex(hex.Replace("#", "")); } catch { return null; }
})
.Where(c => c != null)
.Select(c => c.Value)
@ -133,7 +132,7 @@ namespace NadekoBot.Modules.Utility
if (!hexColors.Any())
{
await channel.SendMessageAsync("No colors are in the correct format. Use `#00ff00` for example.").ConfigureAwait(false);
await ReplyErrorLocalized("rrc_no_colors").ConfigureAwait(false);
return;
}
@ -163,8 +162,7 @@ namespace NadekoBot.Modules.Utility
old.Change(Timeout.Infinite, Timeout.Infinite);
return t;
});
await channel.SendFileAsync(images, "magicalgirl.jpg", $"Rotating **{role.Name}** role's color.").ConfigureAwait(false);
await channel.SendFileAsync(images, "magicalgirl.jpg", GetText("rrc_start", Format.Bold(role.Name))).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -181,14 +179,14 @@ namespace NadekoBot.Modules.Utility
.WithAuthor(eab => eab.WithIconUrl("https://togethertube.com/assets/img/favicons/favicon-32x32.png")
.WithName("Together Tube")
.WithUrl("https://togethertube.com/"))
.WithDescription($"{Context.User.Mention} Here is your room link:\n{target}"));
.WithDescription(Context.User.Mention + " " + GetText("togtub_room_link") + "\n" + target));
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task WhosPlaying([Remainder] string game = null)
public async Task WhosPlaying([Remainder] string game)
{
game = game.Trim().ToUpperInvariant();
game = game?.Trim().ToUpperInvariant();
if (string.IsNullOrWhiteSpace(game))
return;
@ -208,7 +206,7 @@ namespace NadekoBot.Modules.Utility
int i = 0;
if (arr.Length == 0)
await Context.Channel.SendErrorAsync("Nobody is playing that game.").ConfigureAwait(false);
await ReplyErrorLocalized("nobody_playing_game").ConfigureAwait(false);
else
{
await Context.Channel.SendConfirmAsync("```css\n" + string.Join("\n", arr.GroupBy(item => (i++) / 2)
@ -219,26 +217,24 @@ namespace NadekoBot.Modules.Utility
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task InRole([Remainder] string roles)
public async Task InRole(params IRole[] roles)
{
if (string.IsNullOrWhiteSpace(roles))
if (roles.Length == 0)
return;
var arg = roles.Split(',').Select(r => r.Trim().ToUpperInvariant());
string send = " **Here is a list of users in those roles:**";
foreach (var roleStr in arg.Where(str => !string.IsNullOrWhiteSpace(str) && str != "@EVERYONE" && str != "EVERYONE"))
var send = " " + Format.Bold(GetText("inrole_list"));
var usrs = (await Context.Guild.GetUsersAsync()).ToArray();
foreach (var role in roles.Where(r => r.Id != Context.Guild.Id))
{
var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleStr).FirstOrDefault();
if (role == null) continue;
send += $"```css\n[{role.Name}]\n";
send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString()));
send += $"\n```";
send += string.Join(", ", usrs.Where(u => u.RoleIds.Contains(role.Id)).Select(u => u.ToString()));
send += "\n```";
}
var usr = Context.User as IGuildUser;
var usr = (IGuildUser)Context.User;
while (send.Length > 2000)
{
if (!usr.GetPermissions((ITextChannel)Context.Channel).ManageMessages)
{
await Context.Channel.SendErrorAsync($"⚠️ {usr.Mention} **you are not allowed to use this command on roles with a lot of users in them to prevent abuse.**").ConfigureAwait(false);
await ReplyErrorLocalized("inrole_not_allowed").ConfigureAwait(false);
return;
}
var curstr = send.Substring(0, 2000);
@ -255,15 +251,13 @@ namespace NadekoBot.Modules.Utility
public async Task CheckMyPerms()
{
StringBuilder builder = new StringBuilder("```http\n");
var user = Context.User as IGuildUser;
StringBuilder builder = new StringBuilder();
var user = (IGuildUser) Context.User;
var perms = user.GetPermissions((ITextChannel)Context.Channel);
foreach (var p in perms.GetType().GetProperties().Where(p => !p.GetGetMethod().GetParameters().Any()))
{
builder.AppendLine($"{p.Name} : {p.GetValue(perms, null).ToString()}");
builder.AppendLine($"{p.Name} : {p.GetValue(perms, null)}");
}
builder.Append("```");
await Context.Channel.SendConfirmAsync(builder.ToString());
}
@ -272,20 +266,23 @@ namespace NadekoBot.Modules.Utility
public async Task UserId(IGuildUser target = null)
{
var usr = target ?? Context.User;
await Context.Channel.SendConfirmAsync($"🆔 of the user **{ usr.Username }** is `{ usr.Id }`").ConfigureAwait(false);
await ReplyConfirmLocalized("userid", "🆔", Format.Bold(usr.ToString()),
Format.Code(usr.Id.ToString())).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ChannelId()
{
await Context.Channel.SendConfirmAsync($"🆔 of this channel is `{Context.Channel.Id}`").ConfigureAwait(false);
await ReplyConfirmLocalized("channelidd", "🆔", Format.Code(Context.Channel.Id.ToString()))
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task ServerId()
{
await Context.Channel.SendConfirmAsync($"🆔 of this server is `{Context.Guild.Id}`").ConfigureAwait(false);
await ReplyConfirmLocalized("serverid", "🆔", Format.Code(Context.Guild.Id.ToString()))
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -295,33 +292,36 @@ namespace NadekoBot.Modules.Utility
var channel = (ITextChannel)Context.Channel;
var guild = channel.Guild;
const int RolesPerPage = 20;
const int rolesPerPage = 20;
if (page < 1 || page > 100)
return;
if (target != null)
{
var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
var roles = target.GetRoles().Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * rolesPerPage).Take(rolesPerPage).ToArray();
if (!roles.Any())
{
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false);
}
else
{
await channel.SendConfirmAsync($"⚔ **Page #{page} of roles for {target.Username}**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
await channel.SendConfirmAsync(GetText("roles_page", page, Format.Bold(target.ToString())),
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions()).ConfigureAwait(false);
}
}
else
{
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * RolesPerPage).Take(RolesPerPage);
var roles = guild.Roles.Except(new[] { guild.EveryoneRole }).OrderBy(r => -r.Position).Skip((page - 1) * rolesPerPage).Take(rolesPerPage).ToArray();
if (!roles.Any())
{
await channel.SendErrorAsync("No roles on this page.").ConfigureAwait(false);
await ReplyErrorLocalized("no_roles_on_page").ConfigureAwait(false);
}
else
{
await channel.SendConfirmAsync($"⚔ **Page #{page} of all roles on this server:**", $"```css\n• " + string.Join("\n• ", roles).SanitizeMentions() + "\n```").ConfigureAwait(false);
await channel.SendConfirmAsync(GetText("roles_all_page", page),
"\n• " + string.Join("\n• ", (IEnumerable<IRole>)roles).SanitizeMentions()).ConfigureAwait(false);
}
}
}
@ -340,9 +340,9 @@ namespace NadekoBot.Modules.Utility
var topic = channel.Topic;
if (string.IsNullOrWhiteSpace(topic))
await Context.Channel.SendErrorAsync("No topic set.").ConfigureAwait(false);
await ReplyErrorLocalized("no_topic_set").ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync("Channel topic", topic).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(GetText("channel_topic"), topic).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -363,11 +363,13 @@ namespace NadekoBot.Modules.Utility
return;
var status = string.Join(", ", NadekoBot.Client.Shards.GroupBy(x => x.ConnectionState)
.Select(x => $"{x.Count()} shards {x.Key}")
.Select(x => $"{x.Count()} {x.Key}")
.ToArray());
var allShardStrings = NadekoBot.Client.Shards
.Select(x => $"Shard **#{x.ShardId.ToString()}** is in {Format.Bold(x.ConnectionState.ToString())} state with {Format.Bold(x.Guilds.Count.ToString())} servers")
.Select(x =>
GetText("shard_stats_txt", x.ShardId.ToString(),
Format.Bold(x.ConnectionState.ToString()), Format.Bold(x.Guilds.Count.ToString())))
.ToArray();
@ -378,10 +380,10 @@ namespace NadekoBot.Modules.Utility
var str = string.Join("\n", allShardStrings.Skip(25 * (curPage - 1)).Take(25));
if (string.IsNullOrWhiteSpace(str))
str = "No shards on this page.";
str = GetText("no_shards_on_page");
return new EmbedBuilder()
.WithAuthor(a => a.WithName("Shard Stats"))
.WithAuthor(a => a.WithName(GetText("shard_stats")))
.WithTitle(status)
.WithOkColor()
.WithDescription(str);
@ -393,7 +395,7 @@ namespace NadekoBot.Modules.Utility
{
var shardId = NadekoBot.Client.GetShardIdFor(guildid);
await Context.Channel.SendConfirmAsync($"ShardId for **{guildid}** with {NadekoBot.Client.Shards.Count} total shards", shardId.ToString()).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(shardId.ToString()).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
@ -410,17 +412,21 @@ namespace NadekoBot.Modules.Utility
.WithAuthor(eab => eab.WithName($"NadekoBot v{StatsService.BotVersion}")
.WithUrl("http://nadekobot.readthedocs.io/en/latest/")
.WithIconUrl("https://cdn.discordapp.com/avatars/116275390695079945/b21045e778ef21c96d175400e779f0fb.jpg"))
.AddField(efb => efb.WithName(Format.Bold("Author")).WithValue(stats.Author).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Bot ID")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Shard")).WithValue($"#{shardId}, {NadekoBot.Client.Shards.Count} total").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Commands Ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Owner ID(s)")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
.AddField(efb => efb.WithName(Format.Bold("Presence")).WithValue($"{NadekoBot.Client.GetGuildCount()} Servers\n{stats.TextChannels} Text Channels\n{stats.VoiceChannels} Voice Channels").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("author")).WithValue(stats.Author).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("botid")).WithValue(NadekoBot.Client.CurrentUser.Id.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("shard")).WithValue($"#{shardId} / {NadekoBot.Client.Shards.Count}").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("commands_ran")).WithValue(stats.CommandsRan.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("messages")).WithValue($"{stats.MessageCounter} ({stats.MessagesPerSecond:F2}/sec)").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("memory")).WithValue($"{stats.Heap} MB").WithIsInline(true))
.AddField(efb => efb.WithName(GetText("owner_ids")).WithValue(string.Join("\n", NadekoBot.Credentials.OwnerIds)).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("uptime")).WithValue(stats.GetUptimeString("\n")).WithIsInline(true))
.AddField(efb => efb.WithName(GetText("presence")).WithValue(
GetText("presence_txt",
NadekoBot.Client.GetGuildCount(), stats.TextChannels, stats.VoiceChannels)).WithIsInline(true))
#if !GLOBAL_NADEKO
.WithFooter(efb => efb.WithText($"Playing {Music.Music.MusicPlayers.Where(mp => mp.Value.CurrentSong != null).Count()} songs, {Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count)} queued."))
.WithFooter(efb => efb.WithText(GetText("stats_songs",
Music.Music.MusicPlayers.Count(mp => mp.Value.CurrentSong != null),
Music.Music.MusicPlayers.Sum(mp => mp.Value.Playlist.Count))))
#endif
);
}
@ -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 result = string.Join("\n", tags.Select(m => $"**Name:** {m} **Link:** {m.Url}"));
var result = string.Join("\n", tags.Select(m => GetText("showemojis", m, m.Url)));
if (string.IsNullOrWhiteSpace(result))
await Context.Channel.SendErrorAsync("No special emojis found.");
await ReplyErrorLocalized("emojis_none").ConfigureAwait(false);
else
await Context.Channel.SendMessageAsync(result).ConfigureAwait(false);
}
@ -451,13 +457,15 @@ namespace NadekoBot.Modules.Utility
if (!guilds.Any())
{
await Context.Channel.SendErrorAsync("No servers found on that page.").ConfigureAwait(false);
await ReplyErrorLocalized("listservers_none").ConfigureAwait(false);
return;
}
await Context.Channel.EmbedAsync(guilds.Aggregate(new EmbedBuilder().WithOkColor(),
(embed, g) => embed.AddField(efb => efb.WithName(g.Name)
.WithValue($"```css\nID: {g.Id}\nMembers: {g.Users.Count}\nOwnerID: {g.OwnerId} ```")
.WithValue(
GetText("listservers", g.Id, g.Users.Count,
g.OwnerId))
.WithIsInline(false))))
.ConfigureAwait(false);
}

View File

@ -60,6 +60,9 @@ namespace NadekoBot
OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16));
ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16));
}
//ImageSharp.Configuration.Default.AddImageFormat(new ImageSharp.Formats.PngFormat());
//ImageSharp.Configuration.Default.AddImageFormat(new ImageSharp.Formats.JpegFormat());
}
public async Task RunAsync(params string[] args)

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">
<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_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>
/// 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>
public static string antispam_desc {
get {
@ -717,7 +717,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string autotranslang_desc {
get {
@ -726,7 +726,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string autotranslang_usage {
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>
/// Looks up a localized string similar to delmsgoncmd.
/// </summary>
@ -3579,7 +3552,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string inrole_desc {
get {
@ -3588,7 +3561,7 @@ namespace NadekoBot.Resources {
}
/// <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>
public static string inrole_usage {
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>
/// Looks up a localized string similar to reloadimages.
/// </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>
/// Looks up a localized string similar to send.
/// </summary>

View File

@ -841,10 +841,10 @@
<value>inrole</value>
</data>
<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 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 name="checkmyperms_cmd" xml:space="preserve">
<value>checkmyperms</value>
@ -1151,7 +1151,7 @@
</data>
<data name="searchquote_usage" xml:space="preserve">
<value>`{0}qsearch keyword text`</value>
</data>
</data>
<data name="deletequote_cmd" xml:space="preserve">
<value>deletequote delq</value>
</data>
@ -2443,7 +2443,7 @@
<value>antispam</value>
</data>
<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 name="antispam_usage" xml:space="preserve">
<value>`{0}antispam 3 Mute` or `{0}antispam 4 Kick` or `{0}antispam 6 Ban`</value>
@ -2569,10 +2569,10 @@
<value>autotranslang atl</value>
</data>
<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 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 name="autotranslate_cmd" xml:space="preserve">
<value>autotrans at</value>
@ -3141,4 +3141,13 @@
<data name="languageslist_usage" xml:space="preserve">
<value>`{0}langli`</value>
</data>
</root>
<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>

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]
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>> SlotNumbers { get; }
ImmutableArray<byte> WifeMatrix { get; }
ImmutableArray<byte> RategirlDot { get; }
Task<TimeSpan> Reload();
}
}

View File

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

View File

@ -14,9 +14,9 @@ namespace NadekoBot.Services.Impl
public class StatsService : IStatsService
{
private readonly DiscordShardedClient _client;
private DateTime _started;
private readonly DateTime _started;
public const string BotVersion = "1.1.8-alpha";
public const string BotVersion = "1.2-beta";
public string Author => "Kwoth#2560";
public string Library => "Discord.Net";

View File

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

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.YouTube.v3": "1.20.0.701",
"Google.Apis.Customsearch.v1": "1.20.0.466",
"ImageSharp": "1.0.0-alpha-000079",
"ImageSharp": "1.0.0-alpha2-00090",
"ImageSharp.Processing": "1.0.0-alpha2-00078",
"ImageSharp.Formats.Png": "1.0.0-alpha2-00086",
"ImageSharp.Drawing": "1.0.0-alpha2-00090",
"ImageSharp.Drawing.Paths": "1.0.0-alpha2-00040",
//"ImageSharp.Formats.Jpeg": "1.0.0-alpha2-00090",
"Microsoft.EntityFrameworkCore": "1.1.0",
"Microsoft.EntityFrameworkCore.Design": "1.1.0",
"Microsoft.EntityFrameworkCore.Sqlite": "1.1.0",