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

# Conflicts:
#	src/NadekoBot/Resources/CommandStrings.resx
This commit is contained in:
Shikhir Arora 2017-02-15 07:10:31 -05:00
commit 26e769c6dd
73 changed files with 7286 additions and 1299 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class guildtimezoneandlocale : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(
name: "Locale",
table: "GuildConfigs",
nullable: true,
defaultValue: null);
migrationBuilder.AddColumn<string>(
name: "TimeZoneId",
table: "GuildConfigs",
nullable: true,
defaultValue: null);
migrationBuilder.AddColumn<string>(
name: "Locale",
table: "BotConfig",
nullable: true,
defaultValue: null);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "Locale",
table: "GuildConfigs");
migrationBuilder.DropColumn(
name: "TimeZoneId",
table: "GuildConfigs");
migrationBuilder.DropColumn(
name: "Locale",
table: "BotConfig");
}
}
}

View File

@ -128,6 +128,8 @@ namespace NadekoBot.Migrations
b.Property<string>("HelpString"); b.Property<string>("HelpString");
b.Property<string>("Locale");
b.Property<int>("MigrationVersion"); b.Property<int>("MigrationVersion");
b.Property<int>("MinimumBetAmount"); b.Property<int>("MinimumBetAmount");
@ -469,6 +471,8 @@ namespace NadekoBot.Migrations
b.Property<ulong>("GuildId"); b.Property<ulong>("GuildId");
b.Property<string>("Locale");
b.Property<int?>("LogSettingId"); b.Property<int?>("LogSettingId");
b.Property<string>("MuteRoleName"); b.Property<string>("MuteRoleName");
@ -483,6 +487,8 @@ namespace NadekoBot.Migrations
b.Property<bool>("SendDmGreetMessage"); b.Property<bool>("SendDmGreetMessage");
b.Property<string>("TimeZoneId");
b.Property<bool>("VerbosePermissions"); b.Property<bool>("VerbosePermissions");
b.Property<bool>("VoicePlusTextEnabled"); b.Property<bool>("VoicePlusTextEnabled");

View File

@ -1,18 +1,14 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using Newtonsoft.Json;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Net.Http;
using System.IO;
using static NadekoBot.Modules.Permissions.Permissions; using static NadekoBot.Modules.Permissions.Permissions;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using NLog; using NLog;
@ -20,21 +16,18 @@ using NLog;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
{ {
[NadekoModule("Administration", ".")] [NadekoModule("Administration", ".")]
public partial class Administration : DiscordModule public partial class Administration : NadekoModule
{ {
private static ConcurrentHashSet<ulong> deleteMessagesOnCommand { get; }
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>(); private new static readonly Logger _log;
private static ConcurrentHashSet<ulong> DeleteMessagesOnCommand { get; } = new ConcurrentHashSet<ulong>();
private new static Logger _log { get; }
static Administration() static Administration()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler;
DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); deleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId));
} }
@ -47,7 +40,7 @@ namespace NadekoBot.Modules.Administration
var channel = msg.Channel as SocketTextChannel; var channel = msg.Channel as SocketTextChannel;
if (channel == null) if (channel == null)
return; return;
if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune") if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune")
await msg.DeleteAsync().ConfigureAwait(false); await msg.DeleteAsync().ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
@ -74,17 +67,17 @@ namespace NadekoBot.Modules.Administration
PermRole = config.PermissionRole, PermRole = config.PermissionRole,
Verbose = config.VerbosePermissions, Verbose = config.VerbosePermissions,
}; };
Permissions.Permissions.Cache.AddOrUpdate(channel.Guild.Id, Cache.AddOrUpdate(channel.Guild.Id,
toAdd, (id, old) => toAdd); toAdd, (id, old) => toAdd);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false);
await channel.SendConfirmAsync($"{Context.Message.Author.Mention} 🆗 **Permissions for this server are reset.**");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.Administrator)] [RequireUserPermission(GuildPermission.Administrator)]
[RequireBotPermission(GuildPermission.ManageMessages)]
public async Task Delmsgoncmd() public async Task Delmsgoncmd()
{ {
bool enabled; bool enabled;
@ -97,29 +90,31 @@ namespace NadekoBot.Modules.Administration
} }
if (enabled) if (enabled)
{ {
DeleteMessagesOnCommand.Add(Context.Guild.Id); deleteMessagesOnCommand.Add(Context.Guild.Id);
await Context.Channel.SendConfirmAsync("✅ **Now automatically deleting successful command invokations.**").ConfigureAwait(false); await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false);
} }
else else
{ {
DeleteMessagesOnCommand.TryRemove(Context.Guild.Id); deleteMessagesOnCommand.TryRemove(Context.Guild.Id);
await Context.Channel.SendConfirmAsync("❗**Stopped automatic deletion of successful command invokations.**").ConfigureAwait(false); await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireBotPermission(GuildPermission.ManageRoles)]
public async Task Setrole(IGuildUser usr, [Remainder] IRole role) public async Task Setrole(IGuildUser usr, [Remainder] IRole role)
{ {
try try
{ {
await usr.AddRolesAsync(role).ConfigureAwait(false); await usr.AddRolesAsync(role).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($" Successfully added role **{role.Name}** to user **{usr.Username}**").ConfigureAwait(false); await ReplyConfirmLocalized("setrole", Format.Bold(role.Name), Format.Bold(usr.ToString()))
.ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync("⚠️ Failed to add role. **Bot has insufficient permissions.**\n").ConfigureAwait(false); await ReplyErrorLocalized("setrole_err").ConfigureAwait(false);
Console.WriteLine(ex.ToString()); Console.WriteLine(ex.ToString());
} }
} }
@ -127,95 +122,94 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireBotPermission(GuildPermission.ManageRoles)]
public async Task Removerole(IGuildUser usr, [Remainder] IRole role) public async Task Removerole(IGuildUser usr, [Remainder] IRole role)
{ {
try try
{ {
await usr.RemoveRolesAsync(role).ConfigureAwait(false); await usr.RemoveRolesAsync(role).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($" Successfully removed role **{role.Name}** from user **{usr.Username}**").ConfigureAwait(false); await ReplyConfirmLocalized("remrole", Format.Bold(role.Name), Format.Bold(usr.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ Failed to remove role. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false); await ReplyErrorLocalized("remrole_err").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireBotPermission(GuildPermission.ManageRoles)]
public async Task RenameRole(IRole roleToEdit, string newname) public async Task RenameRole(IRole roleToEdit, string newname)
{ {
try try
{ {
if (roleToEdit.Position > (await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position)) if (roleToEdit.Position > (await Context.Guild.GetCurrentUserAsync().ConfigureAwait(false)).GetRoles().Max(r => r.Position))
{ {
await Context.Channel.SendErrorAsync("🚫 You can't edit roles higher than your highest role.").ConfigureAwait(false); await ReplyErrorLocalized("renrole_perms").ConfigureAwait(false);
return; return;
} }
await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false); await roleToEdit.ModifyAsync(g => g.Name = newname).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ Role renamed.").ConfigureAwait(false); await ReplyConfirmLocalized("renrole").ConfigureAwait(false);
} }
catch (Exception) catch (Exception)
{ {
await Context.Channel.SendErrorAsync("⚠️ Failed to rename role. Probably **insufficient permissions.**").ConfigureAwait(false); await ReplyErrorLocalized("renrole_err").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireBotPermission(GuildPermission.ManageRoles)]
public async Task RemoveAllRoles([Remainder] IGuildUser user) public async Task RemoveAllRoles([Remainder] IGuildUser user)
{ {
try try
{ {
await user.RemoveRolesAsync(user.GetRoles()).ConfigureAwait(false); await user.RemoveRolesAsync(user.GetRoles()).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🗑 Successfully removed **all** roles from user **{user.Username}**").ConfigureAwait(false); await ReplyConfirmLocalized("rar", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ Failed to remove roles. Most likely reason: **Insufficient permissions.**").ConfigureAwait(false); await ReplyErrorLocalized("rar_err").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireBotPermission(GuildPermission.ManageRoles)]
public async Task CreateRole([Remainder] string roleName = null) public async Task CreateRole([Remainder] string roleName = null)
{ {
if (string.IsNullOrWhiteSpace(roleName)) if (string.IsNullOrWhiteSpace(roleName))
return; return;
try
{ var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false);
var r = await Context.Guild.CreateRoleAsync(roleName).ConfigureAwait(false); await ReplyConfirmLocalized("cr", Format.Bold(r.Name)).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"✅ Successfully created role **{r.Name}**.").ConfigureAwait(false);
}
catch (Exception)
{
await Context.Channel.SendErrorAsync("⚠️ Unspecified error.").ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
[RequireBotPermission(GuildPermission.ManageRoles)]
public async Task RoleColor(params string[] args) public async Task RoleColor(params string[] args)
{ {
if (args.Count() != 2 && args.Count() != 4) if (args.Length != 2 && args.Length != 4)
{ {
await Context.Channel.SendErrorAsync("❌ The parameters specified are **invalid.**").ConfigureAwait(false); await ReplyErrorLocalized("rc_params").ConfigureAwait(false);
return; return;
} }
var roleName = args[0].ToUpperInvariant(); var roleName = args[0].ToUpperInvariant();
var role = Context.Guild.Roles.Where(r => r.Name.ToUpperInvariant() == roleName).FirstOrDefault(); var role = Context.Guild.Roles.FirstOrDefault(r => r.Name.ToUpperInvariant() == roleName);
if (role == null) if (role == null)
{ {
await Context.Channel.SendErrorAsync("🚫 That role **does not exist.**").ConfigureAwait(false); await ReplyErrorLocalized("rc_not_exist").ConfigureAwait(false);
return; return;
} }
try try
{ {
var rgb = args.Count() == 4; var rgb = args.Length == 4;
var arg1 = args[1].Replace("#", ""); var arg1 = args[1].Replace("#", "");
var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16)); var red = Convert.ToByte(rgb ? int.Parse(arg1) : Convert.ToInt32(arg1.Substring(0, 2), 16));
@ -223,64 +217,57 @@ namespace NadekoBot.Modules.Administration
var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16)); var blue = Convert.ToByte(rgb ? int.Parse(args[3]) : Convert.ToInt32(arg1.Substring(4, 2), 16));
await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false); await role.ModifyAsync(r => r.Color = new Color(red, green, blue)).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"☑️ Role **{role.Name}'s** color has been changed.").ConfigureAwait(false); await ReplyConfirmLocalized("rc", Format.Bold(role.Name)).ConfigureAwait(false);
} }
catch (Exception) catch (Exception)
{ {
await Context.Channel.SendErrorAsync("⚠️ Error occured, most likely **invalid parameters** or **insufficient permissions.**").ConfigureAwait(false); await ReplyErrorLocalized("rc_perms").ConfigureAwait(false);
} }
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.BanMembers)] [RequireUserPermission(GuildPermission.BanMembers)]
[RequireBotPermission(GuildPermission.BanMembers)]
public async Task Ban(IGuildUser user, [Remainder] string msg = null) public async Task Ban(IGuildUser user, [Remainder] string msg = null)
{ {
if (string.IsNullOrWhiteSpace(msg))
{
msg = "❗No reason provided.";
}
if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())) if (Context.User.Id != user.Guild.OwnerId && (user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()))
{ {
await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy.").ConfigureAwait(false); await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
return; return;
} }
if (!string.IsNullOrWhiteSpace(msg)) if (!string.IsNullOrWhiteSpace(msg))
{ {
try try
{ {
await (await user.CreateDMChannelAsync()).SendErrorAsync($"⛔️ **You have been BANNED from `{Context.Guild.Name}` server.**\n" + await user.SendErrorAsync(GetText("bandm", Format.Bold(Context.Guild.Name), msg));
$"⚖ *Reason:* {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
try
{
await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("⛔️ **Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
} await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
catch .WithTitle("⛔️ " + GetText("banned_user"))
{ .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
await Context.Channel.SendErrorAsync("⚠️ **Error.** Most likely I don't have sufficient permissions.").ConfigureAwait(false); .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
} .ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.KickMembers)] [RequireUserPermission(GuildPermission.KickMembers)]
[RequireUserPermission(GuildPermission.ManageMessages)] [RequireUserPermission(GuildPermission.ManageMessages)]
[RequireBotPermission(GuildPermission.BanMembers)]
public async Task Softban(IGuildUser user, [Remainder] string msg = null) public async Task Softban(IGuildUser user, [Remainder] string msg = null)
{ {
if (string.IsNullOrWhiteSpace(msg))
{
msg = "❗No reason provided.";
}
if (Context.User.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()) if (Context.User.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
{ {
await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
return; return;
} }
@ -288,161 +275,162 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await user.SendErrorAsync($"☣ **You have been SOFT-BANNED from `{Context.Guild.Name}` server.**\n" + await user.SendErrorAsync(GetText("sbdm", Format.Bold(Context.Guild.Name), msg));
$"⚖ *Reason:* {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
try await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false);
{ try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
await Context.Guild.AddBanAsync(user, 7).ConfigureAwait(false); catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
try { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); }
catch { await Context.Guild.RemoveBanAsync(user).ConfigureAwait(false); } await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle("☣ " + GetText("sb_user"))
await Context.Channel.SendConfirmAsync("☣ **Soft-Banned** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
} .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
catch .ConfigureAwait(false);
{
await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.KickMembers)] [RequireUserPermission(GuildPermission.KickMembers)]
[RequireBotPermission(GuildPermission.KickMembers)]
public async Task Kick(IGuildUser user, [Remainder] string msg = null) public async Task Kick(IGuildUser user, [Remainder] string msg = null)
{ {
if (user == null)
{
await Context.Channel.SendErrorAsync("❗User not found.").ConfigureAwait(false);
return;
}
if (Context.Message.Author.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max()) if (Context.Message.Author.Id != user.Guild.OwnerId && user.GetRoles().Select(r => r.Position).Max() >= ((IGuildUser)Context.User).GetRoles().Select(r => r.Position).Max())
{ {
await Context.Channel.SendErrorAsync("⚠️ You can't use this command on users with a role higher or equal to yours in the role hierarchy."); await ReplyErrorLocalized("hierarchy").ConfigureAwait(false);
return; return;
} }
if (!string.IsNullOrWhiteSpace(msg)) if (!string.IsNullOrWhiteSpace(msg))
{ {
try try
{ {
await user.SendErrorAsync($"‼️**You have been KICKED from `{Context.Guild.Name}` server.**\n" + await user.SendErrorAsync(GetText("kickdm", Format.Bold(Context.Guild.Name), msg));
$"⚖ *Reason:* {msg}").ConfigureAwait(false);
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
} }
catch { } catch { }
} }
try
{ await user.KickAsync().ConfigureAwait(false);
await user.KickAsync().ConfigureAwait(false); await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
await Context.Channel.SendConfirmAsync("‼️**Kicked** user **" + user.Username + "** ID: `" + user.Id + "`").ConfigureAwait(false); .WithTitle(GetText("kicked_user"))
} .AddField(efb => efb.WithName(GetText("username")).WithValue(user.ToString()).WithIsInline(true))
catch .AddField(efb => efb.WithName("ID").WithValue(user.Id.ToString()).WithIsInline(true)))
{ .ConfigureAwait(false);
await Context.Channel.SendErrorAsync("⚠️ Error. Most likely I don't have sufficient permissions.").ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.DeafenMembers)] [RequireUserPermission(GuildPermission.DeafenMembers)]
[RequireBotPermission(GuildPermission.DeafenMembers)]
public async Task Deafen(params IGuildUser[] users) public async Task Deafen(params IGuildUser[] users)
{ {
if (!users.Any()) if (!users.Any())
return; return;
try foreach (var u in users)
{ {
foreach (var u in users) try
{ {
await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false); await u.ModifyAsync(usr => usr.Deaf = true).ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("🔇 **Deafen** successful.").ConfigureAwait(false); catch
} {
catch // ignored
{ }
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
} }
await ReplyConfirmLocalized("deafen").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.DeafenMembers)] [RequireUserPermission(GuildPermission.DeafenMembers)]
[RequireBotPermission(GuildPermission.DeafenMembers)]
public async Task UnDeafen(params IGuildUser[] users) public async Task UnDeafen(params IGuildUser[] users)
{ {
if (!users.Any()) if (!users.Any())
return; return;
try
foreach (var u in users)
{ {
foreach (var u in users) try
{ {
await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false); await u.ModifyAsync(usr => usr.Deaf = false).ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("🔊 **Undeafen** successful.").ConfigureAwait(false); catch
} {
catch // ignored
{ }
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false);
} }
await ReplyConfirmLocalized("undeafen").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageChannels)]
public async Task DelVoiChanl([Remainder] IVoiceChannel voiceChannel) public async Task DelVoiChanl([Remainder] IVoiceChannel voiceChannel)
{ {
await voiceChannel.DeleteAsync().ConfigureAwait(false); await voiceChannel.DeleteAsync().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🗑 Removed voice channel **{voiceChannel.Name}** successfully.").ConfigureAwait(false); await ReplyConfirmLocalized("delvoich", Format.Bold(voiceChannel.Name)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageChannels)]
public async Task CreatVoiChanl([Remainder] string channelName) public async Task CreatVoiChanl([Remainder] string channelName)
{ {
var ch = await Context.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false); var ch = await Context.Guild.CreateVoiceChannelAsync(channelName).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"✅ Created voice channel **{ch.Name}**. ID: `{ch.Id}`").ConfigureAwait(false); await ReplyConfirmLocalized("createvoich",Format.Bold(ch.Name)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageChannels)]
public async Task DelTxtChanl([Remainder] ITextChannel toDelete) public async Task DelTxtChanl([Remainder] ITextChannel toDelete)
{ {
await toDelete.DeleteAsync().ConfigureAwait(false); await toDelete.DeleteAsync().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🗑 Removed text channel **{toDelete.Name}**. ID: `{toDelete.Id}`").ConfigureAwait(false); await ReplyConfirmLocalized("deltextchan", Format.Bold(toDelete.Name)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageChannels)]
public async Task CreaTxtChanl([Remainder] string channelName) public async Task CreaTxtChanl([Remainder] string channelName)
{ {
var txtCh = await Context.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false); var txtCh = await Context.Guild.CreateTextChannelAsync(channelName).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"✅ Added text channel **{txtCh.Name}**. ID: `{txtCh.Id}`").ConfigureAwait(false); await ReplyConfirmLocalized("createtextchan", Format.Bold(txtCh.Name)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageChannels)]
public async Task SetTopic([Remainder] string topic = null) public async Task SetTopic([Remainder] string topic = null)
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
topic = topic ?? ""; topic = topic ?? "";
await channel.ModifyAsync(c => c.Topic = topic); await channel.ModifyAsync(c => c.Topic = topic);
await channel.SendConfirmAsync("🆗 **New channel topic set.**").ConfigureAwait(false); await ReplyConfirmLocalized("set_topic").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.ManageChannels)] [RequireUserPermission(GuildPermission.ManageChannels)]
[RequireBotPermission(GuildPermission.ManageChannels)]
public async Task SetChanlName([Remainder] string name) public async Task SetChanlName([Remainder] string name)
{ {
var channel = (ITextChannel)Context.Channel; var channel = (ITextChannel)Context.Channel;
await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false); await channel.ModifyAsync(c => c.Name = name).ConfigureAwait(false);
await channel.SendConfirmAsync("🆗 **New channel name set.**").ConfigureAwait(false); await ReplyConfirmLocalized("set_channel_name").ConfigureAwait(false);
} }
@ -462,6 +450,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(ChannelPermission.ManageMessages)] [RequireUserPermission(ChannelPermission.ManageMessages)]
[RequireBotPermission(GuildPermission.ManageMessages)]
public async Task Prune(int count) public async Task Prune(int count)
{ {
if (count < 1) if (count < 1)
@ -477,6 +466,7 @@ namespace NadekoBot.Modules.Administration
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[RequireUserPermission(ChannelPermission.ManageMessages)] [RequireUserPermission(ChannelPermission.ManageMessages)]
[RequireBotPermission(GuildPermission.ManageMessages)]
public async Task Prune(IGuildUser user, int count = 100) public async Task Prune(IGuildUser user, int count = 100)
{ {
if (count < 1) if (count < 1)
@ -495,11 +485,11 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.MentionEveryone)] [RequireUserPermission(GuildPermission.MentionEveryone)]
public async Task MentionRole(params IRole[] roles) public async Task MentionRole(params IRole[] roles)
{ {
string send = $"❕{Context.User.Mention} has invoked a mention on the following roles ❕"; string send = "❕" +GetText("menrole",Context.User.Mention);
foreach (var role in roles) foreach (var role in roles)
{ {
send += $"\n**{role.Name}**\n"; send += $"\n**{role.Name}**\n";
send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Distinct().Select(u => u.Mention)); send += string.Join(", ", (await Context.Guild.GetUsersAsync()).Where(u => u.GetRoles().Contains(role)).Take(50).Select(u => u.Mention));
} }
while (send.Length > 2000) while (send.Length > 2000)
@ -513,7 +503,7 @@ namespace NadekoBot.Modules.Administration
await Context.Channel.SendMessageAsync(send).ConfigureAwait(false); await Context.Channel.SendMessageAsync(send).ConfigureAwait(false);
} }
IGuild nadekoSupportServer; private IGuild _nadekoSupportServer;
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Donators() public async Task Donators()
{ {
@ -523,18 +513,15 @@ namespace NadekoBot.Modules.Administration
{ {
donatorsOrdered = uow.Donators.GetDonatorsOrdered(); donatorsOrdered = uow.Donators.GetDonatorsOrdered();
} }
await Context.Channel.SendConfirmAsync("Thanks to the people listed below for making this project happen!", string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false);
nadekoSupportServer = nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); _nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850);
if (nadekoSupportServer == null) var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585);
return;
var patreonRole = nadekoSupportServer.GetRole(236667642088259585);
if (patreonRole == null) if (patreonRole == null)
return; return;
var usrs = (await nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u)); var usrs = (await _nadekoSupportServer.GetUsersAsync()).Where(u => u.RoleIds.Contains(236667642088259585u));
await Context.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false); await Context.Channel.SendConfirmAsync("Patreon supporters", string.Join("⭐", usrs.Select(d => d.Username))).ConfigureAwait(false);
} }
@ -549,8 +536,7 @@ namespace NadekoBot.Modules.Administration
don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount); don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await ReplyConfirmLocalized("donadd", don.Amount).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Successfuly added a new donator. Total donated amount from this user: {don.Amount} 👑").ConfigureAwait(false);
} }
//[NadekoCommand, Usage, Description, Aliases] //[NadekoCommand, Usage, Description, Aliases]

View File

@ -1,13 +1,10 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -16,24 +13,23 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class AutoAssignRoleCommands : ModuleBase public class AutoAssignRoleCommands : NadekoSubmodule
{ {
private static Logger _log { get; }
//guildid/roleid //guildid/roleid
private static ConcurrentDictionary<ulong, ulong> AutoAssignedRoles { get; } private static ConcurrentDictionary<ulong, ulong> autoAssignedRoles { get; }
static AutoAssignRoleCommands() static AutoAssignRoleCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); var log = LogManager.GetCurrentClassLogger();
AutoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0) autoAssignedRoles = new ConcurrentDictionary<ulong, ulong>(NadekoBot.AllGuildConfigs.Where(x => x.AutoAssignRoleId != 0)
.ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId)); .ToDictionary(k => k.GuildId, v => v.AutoAssignRoleId));
NadekoBot.Client.UserJoined += async (user) => NadekoBot.Client.UserJoined += async (user) =>
{ {
try try
{ {
ulong roleId = 0; ulong roleId;
AutoAssignedRoles.TryGetValue(user.Guild.Id, out roleId); autoAssignedRoles.TryGetValue(user.Guild.Id, out roleId);
if (roleId == 0) if (roleId == 0)
return; return;
@ -43,7 +39,7 @@ namespace NadekoBot.Modules.Administration
if (role != null) if (role != null)
await user.AddRolesAsync(role).ConfigureAwait(false); await user.AddRolesAsync(role).ConfigureAwait(false);
} }
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { log.Warn(ex); }
}; };
} }
@ -52,20 +48,19 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
public async Task AutoAssignRole([Remainder] IRole role = null) public async Task AutoAssignRole([Remainder] IRole role = null)
{ {
GuildConfig conf;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
if (role == null) if (role == null)
{ {
conf.AutoAssignRoleId = 0; conf.AutoAssignRoleId = 0;
ulong throwaway; ulong throwaway;
AutoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway); autoAssignedRoles.TryRemove(Context.Guild.Id, out throwaway);
} }
else else
{ {
conf.AutoAssignRoleId = role.Id; conf.AutoAssignRoleId = role.Id;
AutoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id); autoAssignedRoles.AddOrUpdate(Context.Guild.Id, role.Id, (key, val) => role.Id);
} }
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
@ -73,11 +68,11 @@ namespace NadekoBot.Modules.Administration
if (role == null) if (role == null)
{ {
await Context.Channel.SendConfirmAsync("🆗 **Auto assign role** on user join is now **disabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("aar_disabled").ConfigureAwait(false);
return; return;
} }
await Context.Channel.SendConfirmAsync("✅ **Auto assign role** on user join is now **enabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("aar_enabled").ConfigureAwait(false);
} }
} }
} }

View File

@ -1,90 +0,0 @@
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NLog;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class DMForwardCommands : ModuleBase
{
private static bool ForwardDMs { get; set; }
private static bool ForwardDMsToAllOwners { get; set; }
private static readonly Logger _log;
static DMForwardCommands()
{
_log = LogManager.GetCurrentClassLogger();
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
ForwardDMs = config.ForwardMessages;
ForwardDMsToAllOwners = config.ForwardToAllOwners;
}
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ForwardMessages()
{
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
ForwardDMs = config.ForwardMessages = !config.ForwardMessages;
uow.Complete();
}
if (ForwardDMs)
await Context.Channel.SendConfirmAsync("✅ **I will forward DMs from now on.**").ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync("🆗 **I will stop forwarding DMs from now on.**").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ForwardToAll()
{
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
ForwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
uow.Complete();
}
if (ForwardDMsToAllOwners)
await Context.Channel.SendConfirmAsync(" **I will forward DMs to all owners.**").ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync(" **I will forward DMs only to the first owner.**").ConfigureAwait(false);
}
public static async Task HandleDMForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
{
if (ForwardDMs && ownerChannels.Any())
{
var title = $"DM from [{msg.Author}]({msg.Author.Id})";
if (ForwardDMsToAllOwners)
{
var msgs = await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
.Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false);
}
else
{
var firstOwnerChannel = ownerChannels.First();
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); } catch { }
}
}
}
}
}
}

View File

@ -0,0 +1,234 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Administration
{
public partial class Administration
{
[Group]
public class LocalizationCommands : NadekoSubmodule
{
private ImmutableDictionary<string, string> supportedLocales { get; } = new Dictionary<string, string>()
{
{"en-US", "English, United States" },
{"sr-cyrl-rs", "Serbian, Cyrillic" }
}.ToImmutableDictionary();
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task LanguageSet()
{
var cul = NadekoBot.Localization.GetCultureInfo(Context.Guild);
await ReplyConfirmLocalized("lang_set_show", Format.Bold(cul.ToString()), Format.Bold(cul.NativeName))
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
[RequireUserPermission(GuildPermission.Administrator)]
public async Task LanguageSet(string name)
{
try
{
CultureInfo ci;
if (name.Trim().ToLowerInvariant() == "default")
{
NadekoBot.Localization.RemoveGuildCulture(Context.Guild);
ci = NadekoBot.Localization.DefaultCultureInfo;
}
else
{
ci = new CultureInfo(name);
NadekoBot.Localization.SetGuildCulture(Context.Guild, ci);
}
await ReplyConfirmLocalized("lang_set", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
}
catch(Exception)
{
await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task LanguageSetDefault()
{
var cul = NadekoBot.Localization.DefaultCultureInfo;
await ReplyConfirmLocalized("lang_set_bot_show", cul, cul.NativeName).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task LanguageSetDefault(string name)
{
try
{
CultureInfo ci;
if (name.Trim().ToLowerInvariant() == "default")
{
NadekoBot.Localization.ResetDefaultCulture();
ci = NadekoBot.Localization.DefaultCultureInfo;
}
else
{
ci = new CultureInfo(name);
NadekoBot.Localization.SetDefaultCulture(ci);
}
await ReplyConfirmLocalized("lang_set_bot", Format.Bold(ci.ToString()), Format.Bold(ci.NativeName)).ConfigureAwait(false);
}
catch (Exception)
{
await ReplyErrorLocalized("lang_set_fail").ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task LanguagesList()
{
await ReplyConfirmLocalized("lang_list",
string.Join("\n", supportedLocales.Select(x => $"{Format.Code(x.Key)} => {x.Value}")))
.ConfigureAwait(false);
}
}
}
}
/* list of language codes for reference.
* taken from https://github.com/dotnet/coreclr/blob/ee5862c6a257e60e263537d975ab6c513179d47f/src/mscorlib/src/System/Globalization/CultureData.cs#L192
{ "029", "en-029" },
{ "AE", "ar-AE" },
{ "AF", "prs-AF" },
{ "AL", "sq-AL" },
{ "AM", "hy-AM" },
{ "AR", "es-AR" },
{ "AT", "de-AT" },
{ "AU", "en-AU" },
{ "AZ", "az-Cyrl-AZ" },
{ "BA", "bs-Latn-BA" },
{ "BD", "bn-BD" },
{ "BE", "nl-BE" },
{ "BG", "bg-BG" },
{ "BH", "ar-BH" },
{ "BN", "ms-BN" },
{ "BO", "es-BO" },
{ "BR", "pt-BR" },
{ "BY", "be-BY" },
{ "BZ", "en-BZ" },
{ "CA", "en-CA" },
{ "CH", "it-CH" },
{ "CL", "es-CL" },
{ "CN", "zh-CN" },
{ "CO", "es-CO" },
{ "CR", "es-CR" },
{ "CS", "sr-Cyrl-CS" },
{ "CZ", "cs-CZ" },
{ "DE", "de-DE" },
{ "DK", "da-DK" },
{ "DO", "es-DO" },
{ "DZ", "ar-DZ" },
{ "EC", "es-EC" },
{ "EE", "et-EE" },
{ "EG", "ar-EG" },
{ "ES", "es-ES" },
{ "ET", "am-ET" },
{ "FI", "fi-FI" },
{ "FO", "fo-FO" },
{ "FR", "fr-FR" },
{ "GB", "en-GB" },
{ "GE", "ka-GE" },
{ "GL", "kl-GL" },
{ "GR", "el-GR" },
{ "GT", "es-GT" },
{ "HK", "zh-HK" },
{ "HN", "es-HN" },
{ "HR", "hr-HR" },
{ "HU", "hu-HU" },
{ "ID", "id-ID" },
{ "IE", "en-IE" },
{ "IL", "he-IL" },
{ "IN", "hi-IN" },
{ "IQ", "ar-IQ" },
{ "IR", "fa-IR" },
{ "IS", "is-IS" },
{ "IT", "it-IT" },
{ "IV", "" },
{ "JM", "en-JM" },
{ "JO", "ar-JO" },
{ "JP", "ja-JP" },
{ "KE", "sw-KE" },
{ "KG", "ky-KG" },
{ "KH", "km-KH" },
{ "KR", "ko-KR" },
{ "KW", "ar-KW" },
{ "KZ", "kk-KZ" },
{ "LA", "lo-LA" },
{ "LB", "ar-LB" },
{ "LI", "de-LI" },
{ "LK", "si-LK" },
{ "LT", "lt-LT" },
{ "LU", "lb-LU" },
{ "LV", "lv-LV" },
{ "LY", "ar-LY" },
{ "MA", "ar-MA" },
{ "MC", "fr-MC" },
{ "ME", "sr-Latn-ME" },
{ "MK", "mk-MK" },
{ "MN", "mn-MN" },
{ "MO", "zh-MO" },
{ "MT", "mt-MT" },
{ "MV", "dv-MV" },
{ "MX", "es-MX" },
{ "MY", "ms-MY" },
{ "NG", "ig-NG" },
{ "NI", "es-NI" },
{ "NL", "nl-NL" },
{ "NO", "nn-NO" },
{ "NP", "ne-NP" },
{ "NZ", "en-NZ" },
{ "OM", "ar-OM" },
{ "PA", "es-PA" },
{ "PE", "es-PE" },
{ "PH", "en-PH" },
{ "PK", "ur-PK" },
{ "PL", "pl-PL" },
{ "PR", "es-PR" },
{ "PT", "pt-PT" },
{ "PY", "es-PY" },
{ "QA", "ar-QA" },
{ "RO", "ro-RO" },
{ "RS", "sr-Latn-RS" },
{ "RU", "ru-RU" },
{ "RW", "rw-RW" },
{ "SA", "ar-SA" },
{ "SE", "sv-SE" },
{ "SG", "zh-SG" },
{ "SI", "sl-SI" },
{ "SK", "sk-SK" },
{ "SN", "wo-SN" },
{ "SV", "es-SV" },
{ "SY", "ar-SY" },
{ "TH", "th-TH" },
{ "TJ", "tg-Cyrl-TJ" },
{ "TM", "tk-TM" },
{ "TN", "ar-TN" },
{ "TR", "tr-TR" },
{ "TT", "en-TT" },
{ "TW", "zh-TW" },
{ "UA", "uk-UA" },
{ "US", "en-US" },
{ "UY", "es-UY" },
{ "UZ", "uz-Cyrl-UZ" },
{ "VE", "es-VE" },
{ "VN", "vi-VN" },
{ "YE", "ar-YE" },
{ "ZA", "af-ZA" },
{ "ZW", "en-ZW" }
*/

View File

@ -1,7 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Permissions; using NadekoBot.Modules.Permissions;
@ -21,45 +20,43 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class LogCommands : ModuleBase public class LogCommands : NadekoSubmodule
{ {
private const string clockEmojiUrl = "https://cdn.discordapp.com/attachments/155726317222887425/258309524966866945/clock.png"; private static DiscordShardedClient client { get; }
private new static Logger _log { get; }
private static DiscordShardedClient _client { get; }
private static Logger _log { get; }
private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】"; private static string prettyCurrentTime => $"【{DateTime.Now:HH:mm:ss}】";
private static string currentTime => $"{DateTime.Now:HH:mm:ss}"; private static string currentTime => $"{DateTime.Now:HH:mm:ss}";
public static ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; } public static ConcurrentDictionary<ulong, LogSetting> GuildLogSettings { get; }
private static ConcurrentDictionary<ITextChannel, List<string>> PresenceUpdates { get; } = new ConcurrentDictionary<ITextChannel, List<string>>(); private static ConcurrentDictionary<ITextChannel, List<string>> presenceUpdates { get; } = new ConcurrentDictionary<ITextChannel, List<string>>();
private static Timer timerReference { get; } private static readonly Timer _timerReference;
private IGoogleApiService _google { get; }
static LogCommands() static LogCommands()
{ {
_client = NadekoBot.Client; client = NadekoBot.Client;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
.ToDictionary(g => g.GuildId, g => g.LogSetting));
using (var uow = DbHandler.UnitOfWork()) _timerReference = new Timer(async (state) =>
{
GuildLogSettings = new ConcurrentDictionary<ulong, LogSetting>(NadekoBot.AllGuildConfigs
.ToDictionary(g => g.GuildId, g => g.LogSetting));
}
timerReference = new Timer(async (state) =>
{ {
try try
{ {
var keys = PresenceUpdates.Keys.ToList(); var keys = presenceUpdates.Keys.ToList();
await Task.WhenAll(keys.Select(async key => await Task.WhenAll(keys.Select(async key =>
{ {
List<string> messages; List<string> messages;
if (PresenceUpdates.TryRemove(key, out messages)) if (presenceUpdates.TryRemove(key, out messages))
try { await key.SendConfirmAsync("Presence Updates", string.Join(Environment.NewLine, messages)); } catch { } try { await key.SendConfirmAsync(key.Guild.GetLogText("presence_updates"), string.Join(Environment.NewLine, messages)); }
catch
{
// ignored
}
})); }));
} }
catch (Exception ex) catch (Exception ex)
@ -72,23 +69,22 @@ namespace NadekoBot.Modules.Administration
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");
//_client.MessageReceived += _client_MessageReceived; //_client.MessageReceived += _client_MessageReceived;
_client.MessageUpdated += _client_MessageUpdated; client.MessageUpdated += _client_MessageUpdated;
_client.MessageDeleted += _client_MessageDeleted; client.MessageDeleted += _client_MessageDeleted;
_client.UserBanned += _client_UserBanned; client.UserBanned += _client_UserBanned;
_client.UserUnbanned += _client_UserUnbanned; client.UserUnbanned += _client_UserUnbanned;
_client.UserJoined += _client_UserJoined; client.UserJoined += _client_UserJoined;
_client.UserLeft += _client_UserLeft; client.UserLeft += _client_UserLeft;
_client.UserPresenceUpdated += _client_UserPresenceUpdated; client.UserPresenceUpdated += _client_UserPresenceUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated; client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated;
_client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS; client.UserVoiceStateUpdated += _client_UserVoiceStateUpdated_TTS;
_client.GuildMemberUpdated += _client_GuildUserUpdated; client.GuildMemberUpdated += _client_GuildUserUpdated;
#if !GLOBAL_NADEKO #if !GLOBAL_NADEKO
_client.UserUpdated += _client_UserUpdated; client.UserUpdated += _client_UserUpdated;
#endif #endif
client.ChannelCreated += _client_ChannelCreated;
_client.ChannelCreated += _client_ChannelCreated; client.ChannelDestroyed += _client_ChannelDestroyed;
_client.ChannelDestroyed += _client_ChannelDestroyed; client.ChannelUpdated += _client_ChannelUpdated;
_client.ChannelUpdated += _client_ChannelUpdated;
MuteCommands.UserMuted += MuteCommands_UserMuted; MuteCommands.UserMuted += MuteCommands_UserMuted;
MuteCommands.UserUnmuted += MuteCommands_UserUnmuted; MuteCommands.UserUnmuted += MuteCommands_UserUnmuted;
@ -119,7 +115,7 @@ namespace NadekoBot.Modules.Administration
if (before.Username != after.Username) if (before.Username != after.Username)
{ {
embed.WithTitle("👥 Username Changed") embed.WithTitle("👥 " + g.GetLogText("username_changed"))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true)) .AddField(fb => fb.WithName("Old Name").WithValue($"{before.Username}").WithIsInline(true))
.AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true)) .AddField(fb => fb.WithName("New Name").WithValue($"{after.Username}").WithIsInline(true))
@ -128,7 +124,7 @@ namespace NadekoBot.Modules.Administration
} }
else if (before.AvatarUrl != after.AvatarUrl) else if (before.AvatarUrl != after.AvatarUrl)
{ {
embed.WithTitle("👥 Avatar Changed") embed.WithTitle("👥" + g.GetLogText("avatar_changed"))
.WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}") .WithDescription($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}") .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}")
.WithThumbnailUrl(before.AvatarUrl) .WithThumbnailUrl(before.AvatarUrl)
@ -159,7 +155,9 @@ namespace NadekoBot.Modules.Administration
//} //}
} }
catch catch
{ } {
// ignored
}
} }
private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after) private static async Task _client_UserVoiceStateUpdated_TTS(SocketUser iusr, SocketVoiceState before, SocketVoiceState after)
@ -188,20 +186,23 @@ namespace NadekoBot.Modules.Administration
var str = ""; var str = "";
if (beforeVch?.Guild == afterVch?.Guild) if (beforeVch?.Guild == afterVch?.Guild)
{ {
str = $"{usr.Username} moved from {beforeVch.Name} to {afterVch.Name}"; str = logChannel.Guild.GetLogText("moved", usr.Username, beforeVch?.Name, afterVch?.Name);
} }
else if (beforeVch == null) else if (beforeVch == null)
{ {
str = $"{usr.Username} has joined {afterVch.Name}"; str = logChannel.Guild.GetLogText("joined", usr.Username, afterVch.Name);
} }
else if (afterVch == null) else if (afterVch == null)
{ {
str = $"{usr.Username} has left {beforeVch.Name}"; str = logChannel.Guild.GetLogText("left", usr.Username, beforeVch.Name);
} }
var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false); var toDelete = await logChannel.SendMessageAsync(str, true).ConfigureAwait(false);
toDelete.DeleteAfter(5); toDelete.DeleteAfter(5);
} }
catch { } catch
{
// ignored
}
} }
private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType) private static async void MuteCommands_UserMuted(IGuildUser usr, MuteCommands.MuteType muteType)
@ -216,28 +217,32 @@ namespace NadekoBot.Modules.Administration
ITextChannel logChannel; ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
return; return;
string mutes = ""; var mutes = "";
var mutedLocalized = logChannel.Guild.GetLogText("muted_sn");
switch (muteType) switch (muteType)
{ {
case MuteCommands.MuteType.Voice: case MuteCommands.MuteType.Voice:
mutes = "voice chat"; mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_voice", mutedLocalized);
break; break;
case MuteCommands.MuteType.Chat: case MuteCommands.MuteType.Chat:
mutes = "text chat"; mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text", mutedLocalized);
break; break;
case MuteCommands.MuteType.All: case MuteCommands.MuteType.All:
mutes = "text and voice chat"; mutes = "🔇 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", mutedLocalized);
break; break;
} }
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔇 User Muted from " + mutes)) var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes))
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter(fb => fb.WithText(currentTime)) .WithFooter(fb => fb.WithText(currentTime))
.WithOkColor(); .WithOkColor();
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType) private static async void MuteCommands_UserUnmuted(IGuildUser usr, MuteCommands.MuteType muteType)
@ -253,28 +258,32 @@ namespace NadekoBot.Modules.Administration
if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null) if ((logChannel = await TryGetLogChannel(usr.Guild, logSetting, LogType.UserMuted)) == null)
return; return;
string mutes = ""; var mutes = "";
var unmutedLocalized = logChannel.Guild.GetLogText("unmuted_sn");
switch (muteType) switch (muteType)
{ {
case MuteCommands.MuteType.Voice: case MuteCommands.MuteType.Voice:
mutes = "voice chat"; mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_voice", unmutedLocalized);
break; break;
case MuteCommands.MuteType.Chat: case MuteCommands.MuteType.Chat:
mutes = "text chat"; mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text", unmutedLocalized);
break; break;
case MuteCommands.MuteType.All: case MuteCommands.MuteType.All:
mutes = "text and voice chat"; mutes = "🔊 " + logChannel.Guild.GetLogText("xmuted_text_and_voice", unmutedLocalized);
break; break;
} }
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName("🔊 User Unmuted from " + mutes)) var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName(mutes))
.WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}") .WithTitle($"{usr.Username}#{usr.Discriminator} | {usr.Id}")
.WithFooter(fb => fb.WithText($"{currentTime}")) .WithFooter(fb => fb.WithText($"{currentTime}"))
.WithOkColor(); .WithOkColor();
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection) public static async Task TriggeredAntiProtection(IGuildUser[] users, PunishmentAction action, ProtectionType protection)
@ -293,28 +302,31 @@ namespace NadekoBot.Modules.Administration
return; return;
var punishment = ""; var punishment = "";
if (action == PunishmentAction.Mute) switch (action)
{ {
punishment = "🔇 MUTED"; case PunishmentAction.Mute:
} punishment = "🔇 " + logChannel.Guild.GetLogText("muted_pl").ToUpperInvariant();
else if (action == PunishmentAction.Kick) break;
{ case PunishmentAction.Kick:
punishment = "☣ SOFT-BANNED (KICKED)"; punishment = "☣ " + logChannel.Guild.GetLogText("soft_banned_pl").ToUpperInvariant();
} break;
else if (action == PunishmentAction.Ban) case PunishmentAction.Ban:
{ punishment = "⛔️ " + logChannel.Guild.GetLogText("banned_pl").ToUpperInvariant();
punishment = "⛔️ BANNED"; break;
} }
var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}")) var embed = new EmbedBuilder().WithAuthor(eab => eab.WithName($"🛡 Anti-{protection}"))
.WithTitle($"Users " + punishment) .WithTitle(logChannel.Guild.GetLogText("users") + " " + punishment)
.WithDescription(String.Join("\n", users.Select(u => u.ToString()))) .WithDescription(string.Join("\n", users.Select(u => u.ToString())))
.WithFooter(fb => fb.WithText($"{currentTime}")) .WithFooter(fb => fb.WithText($"{currentTime}"))
.WithOkColor(); .WithOkColor();
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after) private static async Task _client_GuildUserUpdated(SocketGuildUser before, SocketGuildUser after)
@ -333,23 +345,23 @@ namespace NadekoBot.Modules.Administration
.WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}"); .WithTitle($"{before.Username}#{before.Discriminator} | {before.Id}");
if (before.Nickname != after.Nickname) if (before.Nickname != after.Nickname)
{ {
embed.WithAuthor(eab => eab.WithName("👥 Nickname Changed")) embed.WithAuthor(eab => eab.WithName("👥 " + logChannel.Guild.GetLogText("nick_change")))
.AddField(efb => efb.WithName("Old Nickname").WithValue($"{before.Nickname}#{before.Discriminator}")) .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_nick")).WithValue($"{before.Nickname}#{before.Discriminator}"))
.AddField(efb => efb.WithName("New Nickname").WithValue($"{after.Nickname}#{after.Discriminator}")); .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_nick")).WithValue($"{after.Nickname}#{after.Discriminator}"));
} }
else if (!before.RoleIds.SequenceEqual(after.RoleIds)) else if (!before.RoleIds.SequenceEqual(after.RoleIds))
{ {
if (before.RoleIds.Count < after.RoleIds.Count) if (before.RoleIds.Count < after.RoleIds.Count)
{ {
var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); var diffRoles = after.RoleIds.Where(r => !before.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
embed.WithAuthor(eab => eab.WithName("⚔ User's Role Added")) embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_add")))
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
} }
else if (before.RoleIds.Count > after.RoleIds.Count) else if (before.RoleIds.Count > after.RoleIds.Count)
{ {
var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name); var diffRoles = before.RoleIds.Where(r => !after.RoleIds.Contains(r)).Select(r => before.Guild.GetRole(r).Name);
embed.WithAuthor(eab => eab.WithName("⚔ User's Role Removed")) embed.WithAuthor(eab => eab.WithName("⚔ " + logChannel.Guild.GetLogText("user_role_rem")))
.WithDescription(string.Join(", ", diffRoles).SanitizeMentions()); .WithDescription(string.Join(", ", diffRoles).SanitizeMentions());
} }
} }
@ -357,7 +369,10 @@ namespace NadekoBot.Modules.Administration
return; return;
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter) private static async Task _client_ChannelUpdated(IChannel cbefore, IChannel cafter)
@ -386,23 +401,26 @@ namespace NadekoBot.Modules.Administration
if (before.Name != after.Name) if (before.Name != after.Name)
{ {
embed.WithTitle(" Channel Name Changed") embed.WithTitle(" " + logChannel.Guild.GetLogText("ch_name_change"))
.WithDescription($"{after} | {after.Id}") .WithDescription($"{after} | {after.Id}")
.AddField(efb => efb.WithName("Old Name").WithValue(before.Name)); .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("ch_old_name")).WithValue(before.Name));
} }
else if (beforeTextChannel?.Topic != afterTextChannel?.Topic) else if (beforeTextChannel?.Topic != afterTextChannel?.Topic)
{ {
embed.WithTitle(" Channel Topic Changed") embed.WithTitle(" " + logChannel.Guild.GetLogText("ch_topic_change"))
.WithDescription($"{after} | {after.Id}") .WithDescription($"{after} | {after.Id}")
.AddField(efb => efb.WithName("Old Topic").WithValue(beforeTextChannel.Topic)) .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_topic")).WithValue(beforeTextChannel?.Topic ?? "-"))
.AddField(efb => efb.WithName("New Topic").WithValue(afterTextChannel.Topic)); .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_topic")).WithValue(afterTextChannel?.Topic ?? "-"));
} }
else else
return; return;
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async Task _client_ChannelDestroyed(IChannel ich) private static async Task _client_ChannelDestroyed(IChannel ich)
@ -422,14 +440,23 @@ namespace NadekoBot.Modules.Administration
ITextChannel logChannel; ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null) if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelDestroyed)) == null)
return; return;
string title;
if (ch is IVoiceChannel)
{
title = logChannel.Guild.GetLogText("voice_chan_destroyed");
}
else
title = logChannel.Guild.GetLogText("text_chan_destroyed");
await logChannel.EmbedAsync(new EmbedBuilder() await logChannel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Destroyed") .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async Task _client_ChannelCreated(IChannel ich) private static async Task _client_ChannelCreated(IChannel ich)
@ -448,10 +475,16 @@ namespace NadekoBot.Modules.Administration
ITextChannel logChannel; ITextChannel logChannel;
if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null) if ((logChannel = await TryGetLogChannel(ch.Guild, logSetting, LogType.ChannelCreated)) == null)
return; return;
string title;
if (ch is IVoiceChannel)
{
title = logChannel.Guild.GetLogText("voice_chan_created");
}
else
title = logChannel.Guild.GetLogText("text_chan_created");
await logChannel.EmbedAsync(new EmbedBuilder() await logChannel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("🆕 " + (ch is IVoiceChannel ? "Voice" : "Text") + " Channel Created") .WithTitle("🆕 " + title)
.WithDescription($"{ch.Name} | {ch.Id}") .WithDescription($"{ch.Name} | {ch.Id}")
.WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
} }
@ -484,20 +517,29 @@ namespace NadekoBot.Modules.Administration
string str = null; string str = null;
if (beforeVch?.Guild == afterVch?.Guild) if (beforeVch?.Guild == afterVch?.Guild)
{ {
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ moved from **{beforeVch.Name}** to **{afterVch.Name}** voice channel."; str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vmoved",
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(beforeVch?.Name ?? ""), Format.Bold(afterVch?.Name ?? ""));
} }
else if (beforeVch == null) else if (beforeVch == null)
{ {
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has joined **{afterVch.Name}** voice channel."; str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vjoined",
"👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(afterVch.Name ?? ""));
} }
else if (afterVch == null) else if (afterVch == null)
{ {
str = $"🎙`{prettyCurrentTime}`👤__**{usr.Username}#{usr.Discriminator}**__ has left **{beforeVch.Name}** voice channel."; str = "🎙" + Format.Code(prettyCurrentTime) + logChannel.Guild.GetLogText("user_vleft",
"👤" + Format.Code(prettyCurrentTime), "👤" + Format.Bold(usr.Username + "#" + usr.Discriminator),
Format.Bold(beforeVch.Name ?? ""));
} }
if (str != null) if (str != null)
PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; }); presenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
}
catch
{
// ignored
} }
catch { }
} }
private static async Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after) private static async Task _client_UserPresenceUpdated(Optional<SocketGuild> optGuild, SocketUser usr, SocketPresence before, SocketPresence after)
@ -520,7 +562,10 @@ namespace NadekoBot.Modules.Administration
return; return;
string str = ""; string str = "";
if (before.Status != after.Status) if (before.Status != after.Status)
str = $"🎭`{prettyCurrentTime}`👤__**{usr.Username}**__ is now **{after.Status}**."; str = "🎭" + Format.Code(prettyCurrentTime) +
logChannel.Guild.GetLogText("user_status_change",
"👤" + Format.Bold(usr.Username),
Format.Bold(after.Status.ToString()));
//if (before.Game?.Name != after.Game?.Name) //if (before.Game?.Name != after.Game?.Name)
//{ //{
@ -529,9 +574,12 @@ namespace NadekoBot.Modules.Administration
// str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**."; // str += $"👾`{prettyCurrentTime}`👤__**{usr.Username}**__ is now playing **{after.Game?.Name}**.";
//} //}
PresenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; }); presenceUpdates.AddOrUpdate(logChannel, new List<string>() { str }, (id, list) => { list.Add(str); return list; });
}
catch
{
// ignored
} }
catch { }
} }
private static async Task _client_UserLeft(IGuildUser usr) private static async Task _client_UserLeft(IGuildUser usr)
@ -549,13 +597,16 @@ namespace NadekoBot.Modules.Administration
await logChannel.EmbedAsync(new EmbedBuilder() await logChannel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("❌ User Left") .WithTitle("❌ " + logChannel.Guild.GetLogText("user_left"))
.WithThumbnailUrl(usr.AvatarUrl) .WithThumbnailUrl(usr.AvatarUrl)
.WithDescription(usr.ToString()) .WithDescription(usr.ToString())
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
.WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false); .WithFooter(efb => efb.WithText(currentTime))).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async Task _client_UserJoined(IGuildUser usr) private static async Task _client_UserJoined(IGuildUser usr)
@ -573,7 +624,7 @@ namespace NadekoBot.Modules.Administration
await logChannel.EmbedAsync(new EmbedBuilder() await logChannel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("✅ User Joined") .WithTitle("✅ " + logChannel.Guild.GetLogText("user_joined"))
.WithThumbnailUrl(usr.AvatarUrl) .WithThumbnailUrl(usr.AvatarUrl)
.WithDescription($"{usr}") .WithDescription($"{usr}")
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
@ -597,7 +648,7 @@ namespace NadekoBot.Modules.Administration
await logChannel.EmbedAsync(new EmbedBuilder() await logChannel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("♻️ User Unbanned") .WithTitle("♻️ " + logChannel.Guild.GetLogText("user_unbanned"))
.WithThumbnailUrl(usr.AvatarUrl) .WithThumbnailUrl(usr.AvatarUrl)
.WithDescription(usr.ToString()) .WithDescription(usr.ToString())
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
@ -620,7 +671,7 @@ namespace NadekoBot.Modules.Administration
return; return;
await logChannel.EmbedAsync(new EmbedBuilder() await logChannel.EmbedAsync(new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("🚫 User Banned") .WithTitle("🚫 " + logChannel.Guild.GetLogText("user_banned"))
.WithThumbnailUrl(usr.AvatarUrl) .WithThumbnailUrl(usr.AvatarUrl)
.WithDescription(usr.ToString()) .WithDescription(usr.ToString())
.AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString())) .AddField(efb => efb.WithName("Id").WithValue(usr.Id.ToString()))
@ -653,17 +704,20 @@ namespace NadekoBot.Modules.Administration
return; return;
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle($"🗑 Message Deleted in #{((ITextChannel)msg.Channel).Name}") .WithTitle("🗑 " + logChannel.Guild.GetLogText("msg_del", ((ITextChannel)msg.Channel).Name))
.WithDescription($"{msg.Author}") .WithDescription($"{msg.Author}")
.AddField(efb => efb.WithName("Content").WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("content")).WithValue(msg.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(msg.Id.ToString()).WithIsInline(false))
.WithFooter(efb => efb.WithText(currentTime)); .WithFooter(efb => efb.WithText(currentTime));
if (msg.Attachments.Any()) if (msg.Attachments.Any())
embed.AddField(efb => efb.WithName("Attachments").WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false)); embed.AddField(efb => efb.WithName(logChannel.Guild.GetLogText("attachments")).WithValue(string.Join(", ", msg.Attachments.Select(a => a.ProxyUrl))).WithIsInline(false));
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
private static async Task _client_MessageUpdated(Optional<SocketMessage> optmsg, SocketMessage imsg2) private static async Task _client_MessageUpdated(Optional<SocketMessage> optmsg, SocketMessage imsg2)
@ -697,16 +751,19 @@ namespace NadekoBot.Modules.Administration
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle($"📝 Message Updated in #{((ITextChannel)after.Channel).Name}") .WithTitle("📝 " + logChannel.Guild.GetLogText("msg_update", ((ITextChannel)after.Channel).Name))
.WithDescription(after.Author.ToString()) .WithDescription(after.Author.ToString())
.AddField(efb => efb.WithName("Old Message").WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("old_msg")).WithValue(before.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName("New Message").WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false)) .AddField(efb => efb.WithName(logChannel.Guild.GetLogText("new_msg")).WithValue(after.Resolve(userHandling: TagHandling.FullName)).WithIsInline(false))
.AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false)) .AddField(efb => efb.WithName("Id").WithValue(after.Id.ToString()).WithIsInline(false))
.WithFooter(efb => efb.WithText(currentTime)); .WithFooter(efb => efb.WithText(currentTime));
await logChannel.EmbedAsync(embed).ConfigureAwait(false); await logChannel.EmbedAsync(embed).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
public enum LogType public enum LogType
@ -778,8 +835,6 @@ namespace NadekoBot.Modules.Administration
case LogType.UserMuted: case LogType.UserMuted:
id = logSetting.UserMutedId; id = logSetting.UserMutedId;
break; break;
default:
break;
} }
if (!id.HasValue) if (!id.HasValue)
@ -850,8 +905,6 @@ namespace NadekoBot.Modules.Administration
case LogType.VoicePresenceTTS: case LogType.VoicePresenceTTS:
newLogSetting.LogVoicePresenceTTSId = null; newLogSetting.LogVoicePresenceTTSId = null;
break; break;
default:
break;
} }
GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting); GuildLogSettings.AddOrUpdate(guildId, newLogSetting, (gid, old) => newLogSetting);
uow.Complete(); uow.Complete();
@ -895,9 +948,9 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
if (action.Value) if (action.Value)
await channel.SendConfirmAsync("Logging all events in this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("log_all").ConfigureAwait(false);
else else
await channel.SendConfirmAsync("Logging disabled.").ConfigureAwait(false); await ReplyConfirmLocalized("log_disabled").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -924,9 +977,9 @@ namespace NadekoBot.Modules.Administration
} }
if (removed == 0) if (removed == 0)
await channel.SendConfirmAsync($"Logging will IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false); await ReplyConfirmLocalized("log_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
else else
await channel.SendConfirmAsync($"Logging will NOT IGNORE **{channel.Mention} ({channel.Id})**").ConfigureAwait(false); await ReplyConfirmLocalized("log_not_ignore", Format.Bold(channel.Mention + "(" + channel.Id + ")")).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -935,7 +988,7 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task LogEvents() public async Task LogEvents()
{ {
await Context.Channel.SendConfirmAsync("Log events you can subscribe to:", String.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>())); await ReplyConfirmLocalized("log_events", string.Join(", ", Enum.GetNames(typeof(LogType)).Cast<string>())).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -1003,10 +1056,19 @@ namespace NadekoBot.Modules.Administration
} }
if (channelId != null) if (channelId != null)
await channel.SendConfirmAsync($"Logging **{type}** event in this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("log", Format.Bold(type.ToString())).ConfigureAwait(false);
else else
await channel.SendConfirmAsync($"Stopped logging **{type}** event.").ConfigureAwait(false); await ReplyConfirmLocalized("log_stop", Format.Bold(type.ToString())).ConfigureAwait(false);
} }
} }
} }
public static class GuildExtensions
{
public static string GetLogText(this IGuild guild, string key, params object[] replacements)
=> NadekoModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(guild),
typeof(Administration).Name.ToLowerInvariant(),
replacements);
}
} }

View File

@ -20,11 +20,11 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class Migration : ModuleBase public class Migration : NadekoSubmodule
{ {
private const int CURRENT_VERSION = 1; private const int CURRENT_VERSION = 1;
private static Logger _log { get; } private new static readonly Logger _log;
static Migration() static Migration()
{ {
@ -51,12 +51,12 @@ namespace NadekoBot.Modules.Administration
break; break;
} }
} }
await Context.Channel.SendMessageAsync("🆙 **Migration done.**").ConfigureAwait(false); await ReplyConfirmLocalized("migration_done").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
_log.Error(ex); _log.Error(ex);
await Context.Channel.SendMessageAsync("⚠️ **Error while migrating, check `logs` for more informations.**").ConfigureAwait(false); await ReplyErrorLocalized("migration_error").ConfigureAwait(false);
} }
} }
@ -103,11 +103,8 @@ namespace NadekoBot.Modules.Administration
var greetChannel = (ulong)(long)reader["GreetChannelId"]; var greetChannel = (ulong)(long)reader["GreetChannelId"];
var greetMsg = (string)reader["GreetText"]; var greetMsg = (string)reader["GreetText"];
var bye = (long)reader["Bye"] == 1; var bye = (long)reader["Bye"] == 1;
var byeDM = (long)reader["ByePM"] == 1;
var byeChannel = (ulong)(long)reader["ByeChannelId"]; var byeChannel = (ulong)(long)reader["ByeChannelId"];
var byeMsg = (string)reader["ByeText"]; var byeMsg = (string)reader["ByeText"];
var grdel = false;
var byedel = grdel;
var gc = uow.GuildConfigs.For(gid, set => set); var gc = uow.GuildConfigs.For(gid, set => set);
if (greetDM) if (greetDM)
@ -121,7 +118,6 @@ namespace NadekoBot.Modules.Administration
gc.ByeMessageChannelId = byeChannel; gc.ByeMessageChannelId = byeChannel;
gc.ChannelByeMessageText = byeMsg; gc.ChannelByeMessageText = byeMsg;
gc.AutoDeleteGreetMessagesTimer = gc.AutoDeleteByeMessagesTimer = grdel ? 30 : 0;
_log.Info(++i); _log.Info(++i);
} }
} }
@ -129,12 +125,12 @@ namespace NadekoBot.Modules.Administration
_log.Warn("Greet/bye messages won't be migrated"); _log.Warn("Greet/bye messages won't be migrated");
} }
var com2 = db.CreateCommand(); var com2 = db.CreateCommand();
com.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId"; com2.CommandText = "SELECT * FROM CurrencyState GROUP BY UserId";
i = 0; i = 0;
try try
{ {
var reader2 = com.ExecuteReader(); var reader2 = com2.ExecuteReader();
while (reader2.Read()) while (reader2.Read())
{ {
_log.Info(++i); _log.Info(++i);
@ -203,7 +199,6 @@ namespace NadekoBot.Modules.Administration
guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles; guildConfig.ExclusiveSelfAssignedRoles = data.ExclusiveSelfAssignedRoles;
guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key })); guildConfig.GenerateCurrencyChannelIds = new HashSet<GCChannelId>(data.GenerateCurrencyChannels.Select(gc => new GCChannelId() { ChannelId = gc.Key }));
selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray()); selfAssRoles.AddRange(data.ListOfSelfAssignableRoles.Select(r => new SelfAssignedRole() { GuildId = guildConfig.GuildId, RoleId = r }).ToArray());
var logSetting = guildConfig.LogSetting;
guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id })); guildConfig.LogSetting.IgnoredChannels = new HashSet<IgnoredLogChannel>(data.LogserverIgnoreChannels.Select(id => new IgnoredLogChannel() { ChannelId = id }));
guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel; guildConfig.LogSetting.LogUserPresenceId = data.LogPresenceChannel;
@ -249,7 +244,7 @@ namespace NadekoBot.Modules.Administration
private void MigratePermissions0_9(IUnitOfWork uow) private void MigratePermissions0_9(IUnitOfWork uow)
{ {
var PermissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>(); var permissionsDict = new ConcurrentDictionary<ulong, ServerPermissions0_9>();
if (!Directory.Exists("data/permissions/")) if (!Directory.Exists("data/permissions/"))
{ {
_log.Warn("No data from permissions will be migrated."); _log.Warn("No data from permissions will be migrated.");
@ -263,12 +258,15 @@ namespace NadekoBot.Modules.Administration
if (string.IsNullOrWhiteSpace(strippedFileName)) continue; if (string.IsNullOrWhiteSpace(strippedFileName)) continue;
var id = ulong.Parse(strippedFileName); var id = ulong.Parse(strippedFileName);
var data = JsonConvert.DeserializeObject<ServerPermissions0_9>(File.ReadAllText(file)); var data = JsonConvert.DeserializeObject<ServerPermissions0_9>(File.ReadAllText(file));
PermissionsDict.TryAdd(id, data); permissionsDict.TryAdd(id, data);
}
catch
{
// ignored
} }
catch { }
} }
var i = 0; var i = 0;
PermissionsDict permissionsDict
.Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) }) .Select(p => new { data = p.Value, gconfig = uow.GuildConfigs.For(p.Key) })
.AsParallel() .AsParallel()
.ForAll(perms => .ForAll(perms =>

View File

@ -8,8 +8,6 @@ using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -18,11 +16,10 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class MuteCommands : ModuleBase public class MuteCommands : NadekoSubmodule
{ {
private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } = new ConcurrentDictionary<ulong, string>(); private static ConcurrentDictionary<ulong, string> guildMuteRoles { get; }
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> mutedUsers { get; }
private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>();
public static event Action<IGuildUser, MuteType> UserMuted = delegate { }; public static event Action<IGuildUser, MuteType> UserMuted = delegate { };
public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { }; public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { };
@ -36,14 +33,12 @@ namespace NadekoBot.Modules.Administration
static MuteCommands() static MuteCommands()
{ {
var _log = LogManager.GetCurrentClassLogger();
var configs = NadekoBot.AllGuildConfigs; var configs = NadekoBot.AllGuildConfigs;
GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs guildMuteRoles = new ConcurrentDictionary<ulong, string>(configs
.Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName)) .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName))
.ToDictionary(c => c.GuildId, c => c.MuteRoleName)); .ToDictionary(c => c.GuildId, c => c.MuteRoleName));
MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary( mutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary(
k => k.GuildId, k => k.GuildId,
v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId)) v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId))
)); ));
@ -56,16 +51,15 @@ namespace NadekoBot.Modules.Administration
try try
{ {
ConcurrentHashSet<ulong> muted; ConcurrentHashSet<ulong> muted;
MutedUsers.TryGetValue(usr.Guild.Id, out muted); mutedUsers.TryGetValue(usr.Guild.Id, out muted);
if (muted == null || !muted.Contains(usr.Id)) if (muted == null || !muted.Contains(usr.Id))
return; return;
else await MuteUser(usr).ConfigureAwait(false);
await MuteUser(usr).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
_log.Warn(ex); LogManager.GetCurrentClassLogger().Warn(ex);
} }
} }
@ -82,7 +76,7 @@ namespace NadekoBot.Modules.Administration
UserId = usr.Id UserId = usr.Id
}); });
ConcurrentHashSet<ulong> muted; ConcurrentHashSet<ulong> muted;
if (MutedUsers.TryGetValue(usr.Guild.Id, out muted)) if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
muted.Add(usr.Id); muted.Add(usr.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
@ -102,18 +96,18 @@ namespace NadekoBot.Modules.Administration
UserId = usr.Id UserId = usr.Id
}); });
ConcurrentHashSet<ulong> muted; ConcurrentHashSet<ulong> muted;
if (MutedUsers.TryGetValue(usr.Guild.Id, out muted)) if (mutedUsers.TryGetValue(usr.Guild.Id, out muted))
muted.TryRemove(usr.Id); muted.TryRemove(usr.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
UserUnmuted(usr, MuteType.All); UserUnmuted(usr, MuteType.All);
} }
public static async Task<IRole> GetMuteRole(IGuild guild) public static async Task<IRole>GetMuteRole(IGuild guild)
{ {
const string defaultMuteRoleName = "nadeko-mute"; const string defaultMuteRoleName = "nadeko-mute";
var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName); var muteRoleName = guildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName);
var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName); var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName);
if (muteRole == null) if (muteRole == null)
@ -135,7 +129,10 @@ namespace NadekoBot.Modules.Administration
await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny)) await toOverwrite.AddPermissionOverwriteAsync(muteRole, new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
await Task.Delay(200).ConfigureAwait(false); await Task.Delay(200).ConfigureAwait(false);
} }
} }
@ -148,7 +145,6 @@ namespace NadekoBot.Modules.Administration
[Priority(1)] [Priority(1)]
public async Task SetMuteRole([Remainder] string name) public async Task SetMuteRole([Remainder] string name)
{ {
//var channel = (ITextChannel)Context.Channel;
name = name.Trim(); name = name.Trim();
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
return; return;
@ -157,10 +153,10 @@ namespace NadekoBot.Modules.Administration
{ {
var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); var config = uow.GuildConfigs.For(Context.Guild.Id, set => set);
config.MuteRoleName = name; config.MuteRoleName = name;
GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name); guildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("☑️ **New mute role set.**").ConfigureAwait(false); await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -178,12 +174,12 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await MuteUser(user).ConfigureAwait(false); await MuteUser(user).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🔇 **{user}** has been **muted** from text and voice chat.").ConfigureAwait(false); await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
} }
} }
@ -196,11 +192,11 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await UnmuteUser(user).ConfigureAwait(false); await UnmuteUser(user).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"🔉 **{user}** has been **unmuted** from text and voice chat.").ConfigureAwait(false); await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
} }
} }
@ -213,11 +209,11 @@ namespace NadekoBot.Modules.Administration
{ {
await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false); await user.AddRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
UserMuted(user, MuteType.Chat); UserMuted(user, MuteType.Chat);
await Context.Channel.SendConfirmAsync($"✏️🚫 **{user}** has been **muted** from chatting.").ConfigureAwait(false); await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
} }
} }
@ -230,11 +226,11 @@ namespace NadekoBot.Modules.Administration
{ {
await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false); await user.RemoveRolesAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false);
UserUnmuted(user, MuteType.Chat); UserUnmuted(user, MuteType.Chat);
await Context.Channel.SendConfirmAsync($"✏️✅ **{user}** has been **unmuted** from chatting.").ConfigureAwait(false); await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
} }
} }
@ -247,11 +243,11 @@ namespace NadekoBot.Modules.Administration
{ {
await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false); await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false);
UserMuted(user, MuteType.Voice); UserMuted(user, MuteType.Voice);
await Context.Channel.SendConfirmAsync($"🎙🚫 **{user}** has been **voice muted**.").ConfigureAwait(false); await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
} }
} }
@ -264,11 +260,11 @@ namespace NadekoBot.Modules.Administration
{ {
await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false); await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false);
UserUnmuted(user, MuteType.Voice); UserUnmuted(user, MuteType.Voice);
await Context.Channel.SendConfirmAsync($"🎙✅ **{user}** has been **voice unmuted**.").ConfigureAwait(false); await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync("⚠️ I most likely don't have the permission necessary for that.").ConfigureAwait(false); await ReplyErrorLocalized("mute_error").ConfigureAwait(false);
} }
} }
} }

View File

@ -1,5 +1,4 @@
using Discord; using Discord.Commands;
using Discord.Commands;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
@ -8,7 +7,6 @@ using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -18,16 +16,17 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class PlayingRotateCommands : ModuleBase public class PlayingRotateCommands : NadekoSubmodule
{ {
private static Logger _log { get; }
public static List<PlayingStatus> RotatingStatusMessages { get; } public static List<PlayingStatus> RotatingStatusMessages { get; }
public static bool RotatingStatuses { get; private set; } = false; public static volatile bool RotatingStatuses;
private static Timer _t { get; } private readonly object _locker = new object();
private new static Logger _log { get; }
private static readonly Timer _t;
private class TimerState private class TimerState
{ {
public int Index { get; set; } = 0; public int Index { get; set; }
} }
static PlayingRotateCommands() static PlayingRotateCommands()
@ -37,8 +36,6 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages; RotatingStatusMessages = NadekoBot.BotConfig.RotatingStatusMessages;
RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses; RotatingStatuses = NadekoBot.BotConfig.RotatingStatuses;
_t = new Timer(async (objState) => _t = new Timer(async (objState) =>
{ {
try try
@ -46,26 +43,24 @@ namespace NadekoBot.Modules.Administration
var state = (TimerState)objState; var state = (TimerState)objState;
if (!RotatingStatuses) if (!RotatingStatuses)
return; return;
else if (state.Index >= RotatingStatusMessages.Count)
{ state.Index = 0;
if (state.Index >= RotatingStatusMessages.Count)
state.Index = 0;
if (!RotatingStatusMessages.Any()) if (!RotatingStatusMessages.Any())
return; return;
var status = RotatingStatusMessages[state.Index++].Status; var status = RotatingStatusMessages[state.Index++].Status;
if (string.IsNullOrWhiteSpace(status)) if (string.IsNullOrWhiteSpace(status))
return; return;
PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value())); PlayingPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value()));
var shards = NadekoBot.Client.Shards; var shards = NadekoBot.Client.Shards;
for (int i = 0; i < shards.Count; i++) for (int i = 0; i < shards.Count; i++)
{
var curShard = shards.ElementAt(i);
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(curShard)));
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
catch (Exception ex)
{ {
ShardSpecificPlaceholders.ForEach(e => status = status.Replace(e.Key, e.Value(shards.ElementAt(i)))); _log.Warn(ex);
try { await shards.ElementAt(i).SetGameAsync(status).ConfigureAwait(false); }
catch (Exception ex)
{
_log.Warn(ex);
}
} }
} }
} }
@ -107,17 +102,20 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task RotatePlaying() public async Task RotatePlaying()
{ {
using (var uow = DbHandler.UnitOfWork()) lock (_locker)
{ {
var config = uow.BotConfig.GetOrCreate(); using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses; RotatingStatuses = config.RotatingStatuses = !config.RotatingStatuses;
await uow.CompleteAsync(); uow.Complete();
}
} }
if (RotatingStatuses) if (RotatingStatuses)
await Context.Channel.SendConfirmAsync("🆗 **Rotating playing status enabled.**").ConfigureAwait(false); await ReplyConfirmLocalized("ropl_enabled").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync(" **Rotating playing status disabled.**").ConfigureAwait(false); await ReplyConfirmLocalized("ropl_disabled").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -133,7 +131,7 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await Context.Channel.SendConfirmAsync("✅ **Added.**").ConfigureAwait(false); await ReplyConfirmLocalized("ropl_added").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -141,11 +139,13 @@ namespace NadekoBot.Modules.Administration
public async Task ListPlaying() public async Task ListPlaying()
{ {
if (!RotatingStatusMessages.Any()) if (!RotatingStatusMessages.Any())
await Context.Channel.SendErrorAsync("❎ **No rotating playing statuses set.**"); await ReplyErrorLocalized("ropl_not_set").ConfigureAwait(false);
else else
{ {
var i = 1; var i = 1;
await Context.Channel.SendConfirmAsync($" {Context.User.Mention} `Here is a list of rotating statuses:`\n\n\t" + string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}"))); await ReplyConfirmLocalized("ropl_list",
string.Join("\n\t", RotatingStatusMessages.Select(rs => $"`{i++}.` {rs.Status}")))
.ConfigureAwait(false);
} }
} }
@ -156,7 +156,7 @@ namespace NadekoBot.Modules.Administration
{ {
index -= 1; index -= 1;
string msg = ""; string msg;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var config = uow.BotConfig.GetOrCreate(); var config = uow.BotConfig.GetOrCreate();
@ -168,7 +168,7 @@ namespace NadekoBot.Modules.Administration
RotatingStatusMessages.RemoveAt(index); RotatingStatusMessages.RemoveAt(index);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await Context.Channel.SendConfirmAsync($"🗑 **Removed the the playing message:** {msg}").ConfigureAwait(false); await ReplyConfirmLocalized("reprm", msg).ConfigureAwait(false);
} }
} }
} }

View File

@ -4,12 +4,10 @@ using Microsoft.EntityFrameworkCore;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -26,12 +24,8 @@ namespace NadekoBot.Modules.Administration
public class AntiRaidStats public class AntiRaidStats
{ {
public AntiRaidSetting AntiRaidSettings { get; set; } public AntiRaidSetting AntiRaidSettings { get; set; }
public int UsersCount { get; set; } = 0; public int UsersCount { get; set; }
public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>(); public ConcurrentHashSet<IGuildUser> RaidUsers { get; set; } = new ConcurrentHashSet<IGuildUser>();
public override string ToString() =>
$"If **{AntiRaidSettings.UserThreshold}** or more users join within **{AntiRaidSettings.Seconds}** seconds," +
$" I will **{AntiRaidSettings.Action}** them.";
} }
public class AntiSpamStats public class AntiSpamStats
@ -39,16 +33,6 @@ namespace NadekoBot.Modules.Administration
public AntiSpamSetting AntiSpamSettings { get; set; } public AntiSpamSetting AntiSpamSettings { get; set; }
public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; } public ConcurrentDictionary<ulong, UserSpamStats> UserStats { get; set; }
= new ConcurrentDictionary<ulong, UserSpamStats>(); = new ConcurrentDictionary<ulong, UserSpamStats>();
public override string ToString()
{
var ignoredString = string.Join(", ", AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
if (string.IsNullOrWhiteSpace(ignoredString))
ignoredString = "none";
return $"If a user posts **{AntiSpamSettings.MessageThreshold}** same messages in a row, I will **{AntiSpamSettings.Action}** them."
+ $"\n\t__IgnoredChannels__: {ignoredString}";
}
} }
public class UserSpamStats public class UserSpamStats
@ -76,15 +60,15 @@ namespace NadekoBot.Modules.Administration
} }
[Group] [Group]
public class ProtectionCommands : ModuleBase public class ProtectionCommands : NadekoSubmodule
{ {
private static ConcurrentDictionary<ulong, AntiRaidStats> antiRaidGuilds = private static readonly ConcurrentDictionary<ulong, AntiRaidStats> _antiRaidGuilds =
new ConcurrentDictionary<ulong, AntiRaidStats>(); new ConcurrentDictionary<ulong, AntiRaidStats>();
// guildId | (userId|messages) // guildId | (userId|messages)
private static ConcurrentDictionary<ulong, AntiSpamStats> antiSpamGuilds = private static readonly ConcurrentDictionary<ulong, AntiSpamStats> _antiSpamGuilds =
new ConcurrentDictionary<ulong, AntiSpamStats>(); new ConcurrentDictionary<ulong, AntiSpamStats>();
private static Logger _log { get; } private new static readonly Logger _log;
static ProtectionCommands() static ProtectionCommands()
{ {
@ -98,11 +82,11 @@ namespace NadekoBot.Modules.Administration
if (raid != null) if (raid != null)
{ {
var raidStats = new AntiRaidStats() { AntiRaidSettings = raid }; var raidStats = new AntiRaidStats() { AntiRaidSettings = raid };
antiRaidGuilds.TryAdd(gc.GuildId, raidStats); _antiRaidGuilds.TryAdd(gc.GuildId, raidStats);
} }
if (spam != null) if (spam != null)
antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam }); _antiSpamGuilds.TryAdd(gc.GuildId, new AntiSpamStats() { AntiSpamSettings = spam });
} }
NadekoBot.Client.MessageReceived += (imsg) => NadekoBot.Client.MessageReceived += (imsg) =>
@ -119,7 +103,7 @@ namespace NadekoBot.Modules.Administration
try try
{ {
AntiSpamStats spamSettings; AntiSpamStats spamSettings;
if (!antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) || if (!_antiSpamGuilds.TryGetValue(channel.Guild.Id, out spamSettings) ||
spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore() spamSettings.AntiSpamSettings.IgnoredChannels.Contains(new AntiSpamIgnore()
{ {
ChannelId = channel.Id ChannelId = channel.Id
@ -141,7 +125,10 @@ namespace NadekoBot.Modules.Administration
} }
} }
} }
catch { } catch
{
// ignored
}
}); });
return Task.CompletedTask; return Task.CompletedTask;
}; };
@ -151,7 +138,7 @@ namespace NadekoBot.Modules.Administration
if (usr.IsBot) if (usr.IsBot)
return Task.CompletedTask; return Task.CompletedTask;
AntiRaidStats settings; AntiRaidStats settings;
if (!antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings)) if (!_antiRaidGuilds.TryGetValue(usr.Guild.Id, out settings))
return Task.CompletedTask; return Task.CompletedTask;
if (!settings.RaidUsers.Add(usr)) if (!settings.RaidUsers.Add(usr))
return Task.CompletedTask; return Task.CompletedTask;
@ -175,7 +162,10 @@ namespace NadekoBot.Modules.Administration
--settings.UsersCount; --settings.UsersCount;
} }
catch { } catch
{
// ignored
}
}); });
return Task.CompletedTask; return Task.CompletedTask;
}; };
@ -219,13 +209,27 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); } catch (Exception ex) { _log.Warn(ex, "I can't apply punishment"); }
break; break;
default:
break;
} }
} }
await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false); await LogCommands.TriggeredAntiProtection(gus, action, pt).ConfigureAwait(false);
} }
private string GetAntiSpamString(AntiSpamStats stats)
{
var ignoredString = string.Join(", ", stats.AntiSpamSettings.IgnoredChannels.Select(c => $"<#{c.ChannelId}>"));
if (string.IsNullOrWhiteSpace(ignoredString))
ignoredString = "none";
return GetText("spam_stats",
Format.Bold(stats.AntiSpamSettings.MessageThreshold.ToString()),
Format.Bold(stats.AntiSpamSettings.Action.ToString()),
ignoredString);
}
private string GetAntiRaidString(AntiRaidStats stats) => GetText("raid_stats",
Format.Bold(stats.AntiRaidSettings.UserThreshold.ToString()),
Format.Bold(stats.AntiRaidSettings.Seconds.ToString()),
Format.Bold(stats.AntiRaidSettings.Action.ToString()));
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
@ -234,18 +238,18 @@ namespace NadekoBot.Modules.Administration
{ {
if (userThreshold < 2 || userThreshold > 30) if (userThreshold < 2 || userThreshold > 30)
{ {
await Context.Channel.SendErrorAsync("❗User threshold must be between **2** and **30**.").ConfigureAwait(false); await ReplyErrorLocalized("raid_cnt", 2, 30).ConfigureAwait(false);
return; return;
} }
if (seconds < 2 || seconds > 300) if (seconds < 2 || seconds > 300)
{ {
await Context.Channel.SendErrorAsync("❗Time must be between **2** and **300** seconds.").ConfigureAwait(false); await ReplyErrorLocalized("raid_time", 2, 300).ConfigureAwait(false);
return; return;
} }
AntiRaidStats throwaway; AntiRaidStats throwaway;
if (antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway)) if (_antiRaidGuilds.TryRemove(Context.Guild.Id, out throwaway))
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -254,7 +258,7 @@ namespace NadekoBot.Modules.Administration
gc.AntiRaidSetting = null; gc.AntiRaidSetting = null;
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("**Anti-Raid** feature has been **disabled** on this server.").ConfigureAwait(false); await ReplyConfirmLocalized("prot_disable", "Anti-Raid").ConfigureAwait(false);
return; return;
} }
@ -264,10 +268,8 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendConfirmAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
"or create 'nadeko-mute' role with disabled SendMessages and try again.")
.ConfigureAwait(false);
_log.Warn(ex); _log.Warn(ex);
await ReplyErrorLocalized("prot_error").ConfigureAwait(false);
return; return;
} }
@ -281,7 +283,7 @@ namespace NadekoBot.Modules.Administration
} }
}; };
antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); _antiRaidGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -291,7 +293,7 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("Anti-Raid Enabled", $"{Context.User.Mention} {stats.ToString()}") await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Raid"), $"{Context.User.Mention} {GetAntiRaidString(stats)}")
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -304,7 +306,7 @@ namespace NadekoBot.Modules.Administration
return; return;
AntiSpamStats throwaway; AntiSpamStats throwaway;
if (antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway)) if (_antiSpamGuilds.TryRemove(Context.Guild.Id, out throwaway))
{ {
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -314,7 +316,7 @@ namespace NadekoBot.Modules.Administration
gc.AntiSpamSetting = null; gc.AntiSpamSetting = null;
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("**Anti-Spam** has been **disabled** on this server.").ConfigureAwait(false); await ReplyConfirmLocalized("prot_disable", "Anti-Spam").ConfigureAwait(false);
return; return;
} }
@ -324,10 +326,8 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync("⚠️ Failed creating a mute role. Give me ManageRoles permission" +
"or create 'nadeko-mute' role with disabled SendMessages and try again.")
.ConfigureAwait(false);
_log.Warn(ex); _log.Warn(ex);
await ReplyErrorLocalized("prot_error").ConfigureAwait(false);
return; return;
} }
@ -340,7 +340,7 @@ namespace NadekoBot.Modules.Administration
} }
}; };
antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats); _antiSpamGuilds.AddOrUpdate(Context.Guild.Id, stats, (key, old) => stats);
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -350,7 +350,7 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("Anti-Spam Enabled", $"{Context.User.Mention} {stats.ToString()}").ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("prot_enable", "Anti-Spam"), $"{Context.User.Mention} {GetAntiSpamString(stats)}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -376,7 +376,7 @@ namespace NadekoBot.Modules.Administration
if (spam.IgnoredChannels.Add(obj)) if (spam.IgnoredChannels.Add(obj))
{ {
AntiSpamStats temp; AntiSpamStats temp;
if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
temp.AntiSpamSettings.IgnoredChannels.Add(obj); temp.AntiSpamSettings.IgnoredChannels.Add(obj);
added = true; added = true;
} }
@ -384,7 +384,7 @@ namespace NadekoBot.Modules.Administration
{ {
spam.IgnoredChannels.Remove(obj); spam.IgnoredChannels.Remove(obj);
AntiSpamStats temp; AntiSpamStats temp;
if (antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp)) if (_antiSpamGuilds.TryGetValue(Context.Guild.Id, out temp))
temp.AntiSpamSettings.IgnoredChannels.Remove(obj); temp.AntiSpamSettings.IgnoredChannels.Remove(obj);
added = false; added = false;
} }
@ -392,9 +392,9 @@ namespace NadekoBot.Modules.Administration
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
if (added) if (added)
await Context.Channel.SendConfirmAsync("Anti-Spam will ignore this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("spam_ignore", "Anti-Spam").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync("Anti-Spam will no longer ignore this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("spam_not_ignore", "Anti-Spam").ConfigureAwait(false);
} }
@ -402,31 +402,29 @@ namespace NadekoBot.Modules.Administration
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task AntiList() public async Task AntiList()
{ {
var channel = (ITextChannel)Context.Channel;
AntiSpamStats spam; AntiSpamStats spam;
antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam); _antiSpamGuilds.TryGetValue(Context.Guild.Id, out spam);
AntiRaidStats raid; AntiRaidStats raid;
antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid); _antiRaidGuilds.TryGetValue(Context.Guild.Id, out raid);
if (spam == null && raid == null) if (spam == null && raid == null)
{ {
await Context.Channel.SendConfirmAsync("No protections enabled."); await ReplyConfirmLocalized("prot_none").ConfigureAwait(false);
return; return;
} }
var embed = new EmbedBuilder().WithOkColor() var embed = new EmbedBuilder().WithOkColor()
.WithTitle("Protections Enabled"); .WithTitle(GetText("prot_active"));
if (spam != null) if (spam != null)
embed.AddField(efb => efb.WithName("Anti-Spam") embed.AddField(efb => efb.WithName("Anti-Spam")
.WithValue(spam.ToString()) .WithValue(GetAntiSpamString(spam))
.WithIsInline(true)); .WithIsInline(true));
if (raid != null) if (raid != null)
embed.AddField(efb => efb.WithName("Anti-Raid") embed.AddField(efb => efb.WithName("Anti-Raid")
.WithValue(raid.ToString()) .WithValue(GetAntiRaidString(raid))
.WithIsInline(true)); .WithIsInline(true));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);

View File

@ -13,10 +13,10 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class RatelimitCommand : ModuleBase public class RatelimitCommand : NadekoSubmodule
{ {
public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>(); public static ConcurrentDictionary<ulong, Ratelimiter> RatelimitingChannels = new ConcurrentDictionary<ulong, Ratelimiter>();
private static Logger _log { get; } private new static readonly Logger _log;
public class Ratelimiter public class Ratelimiter
{ {
@ -37,26 +37,22 @@ namespace NadekoBot.Modules.Administration
public bool CheckUserRatelimit(ulong id) public bool CheckUserRatelimit(ulong id)
{ {
RatelimitedUser usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id }); var usr = Users.GetOrAdd(id, (key) => new RatelimitedUser() { UserId = id });
if (usr.MessageCount == MaxMessages) if (usr.MessageCount == MaxMessages)
{ {
return true; return true;
} }
else usr.MessageCount++;
var _ = Task.Run(async () =>
{ {
usr.MessageCount++; try
var t = Task.Run(async () =>
{ {
try await Task.Delay(PerSeconds * 1000, cancelSource.Token);
{ }
await Task.Delay(PerSeconds * 1000, cancelSource.Token); catch (OperationCanceledException) { }
} usr.MessageCount--;
catch (OperationCanceledException) { } });
usr.MessageCount--; return false;
});
return false;
}
} }
} }
@ -69,9 +65,7 @@ namespace NadekoBot.Modules.Administration
try try
{ {
var usrMsg = umsg as IUserMessage; var usrMsg = umsg as IUserMessage;
if (usrMsg == null) var channel = usrMsg?.Channel as ITextChannel;
return;
var channel = usrMsg.Channel as ITextChannel;
if (channel == null || usrMsg.IsAuthor()) if (channel == null || usrMsg.IsAuthor())
return; return;
@ -95,8 +89,7 @@ namespace NadekoBot.Modules.Administration
if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway)) if (RatelimitingChannels.TryRemove(Context.Channel.Id, out throwaway))
{ {
throwaway.cancelSource.Cancel(); throwaway.cancelSource.Cancel();
await Context.Channel.SendConfirmAsync(" Slow mode disabled.").ConfigureAwait(false); await ReplyConfirmLocalized("slowmode_disabled").ConfigureAwait(false);
return;
} }
} }
@ -109,7 +102,7 @@ namespace NadekoBot.Modules.Administration
if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600) if (msg < 1 || perSec < 1 || msg > 100 || perSec > 3600)
{ {
await Context.Channel.SendErrorAsync("⚠️ Invalid parameters."); await ReplyErrorLocalized("invalid_params").ConfigureAwait(false);
return; return;
} }
var toAdd = new Ratelimiter() var toAdd = new Ratelimiter()
@ -120,8 +113,8 @@ namespace NadekoBot.Modules.Administration
}; };
if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd)) if(RatelimitingChannels.TryAdd(Context.Channel.Id, toAdd))
{ {
await Context.Channel.SendConfirmAsync("Slow mode initiated", await Context.Channel.SendConfirmAsync(GetText("slowmode_init"),
$"Users can't send more than `{toAdd.MaxMessages} message(s)` every `{toAdd.PerSeconds} second(s)`.") GetText("slowmode_desc", Format.Bold(toAdd.MaxMessages.ToString()), Format.Bold(toAdd.PerSeconds.ToString())))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
} }

View File

@ -16,7 +16,7 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class SelfAssignedRolesCommands : ModuleBase public class SelfAssignedRolesCommands : NadekoSubmodule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -44,25 +44,30 @@ namespace NadekoBot.Modules.Administration
IEnumerable<SelfAssignedRole> roles; IEnumerable<SelfAssignedRole> roles;
string msg; string msg;
var error = false;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id); roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id)) if (roles.Any(s => s.RoleId == role.Id && s.GuildId == role.Guild.Id))
{ {
await Context.Channel.SendMessageAsync($"💢 Role **{role.Name}** is already in the list.").ConfigureAwait(false); msg = GetText("role_in_list", Format.Bold(role.Name));
return; error = true;
} }
else else
{ {
uow.SelfAssignedRoles.Add(new SelfAssignedRole { uow.SelfAssignedRoles.Add(new SelfAssignedRole
{
RoleId = role.Id, RoleId = role.Id,
GuildId = role.Guild.Id GuildId = role.Guild.Id
}); });
await uow.CompleteAsync(); await uow.CompleteAsync();
msg = $"🆗 Role **{role.Name}** added to the list."; msg = GetText("role_added", Format.Bold(role.Name));
} }
} }
await Context.Channel.SendConfirmAsync(msg.ToString()).ConfigureAwait(false); if (error)
await Context.Channel.SendErrorAsync(msg).ConfigureAwait(false);
else
await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -70,8 +75,6 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
public async Task Rsar([Remainder] IRole role) public async Task Rsar([Remainder] IRole role)
{ {
//var channel = (ITextChannel)Context.Channel;
bool success; bool success;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -80,18 +83,16 @@ namespace NadekoBot.Modules.Administration
} }
if (!success) if (!success)
{ {
await Context.Channel.SendErrorAsync("❎ That role is not self-assignable.").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
return; return;
} }
await Context.Channel.SendConfirmAsync($"🗑 **{role.Name}** has been removed from the list of self-assignable roles.").ConfigureAwait(false); await ReplyConfirmLocalized("self_assign_rem", Format.Bold(role.Name)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Lsar() public async Task Lsar()
{ {
//var channel = (ITextChannel)Context.Channel;
var toRemove = new ConcurrentHashSet<SelfAssignedRole>(); var toRemove = new ConcurrentHashSet<SelfAssignedRole>();
var removeMsg = new StringBuilder(); var removeMsg = new StringBuilder();
var msg = new StringBuilder(); var msg = new StringBuilder();
@ -116,11 +117,11 @@ namespace NadekoBot.Modules.Administration
} }
foreach (var role in toRemove) foreach (var role in toRemove)
{ {
removeMsg.AppendLine($"`{role.RoleId} not found. Cleaned up.`"); removeMsg.AppendLine(GetText("role_clean", role.RoleId));
} }
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
await Context.Channel.SendConfirmAsync($" There are `{roleCnt}` self assignable roles:", msg.ToString() + "\n\n" + removeMsg.ToString()).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("self_assign_list", roleCnt), msg + "\n\n" + removeMsg).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -128,8 +129,6 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.ManageRoles)] [RequireUserPermission(GuildPermission.ManageRoles)]
public async Task Tesar() public async Task Tesar()
{ {
//var channel = (ITextChannel)Context.Channel;
bool areExclusive; bool areExclusive;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
@ -138,15 +137,16 @@ namespace NadekoBot.Modules.Administration
areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles; areExclusive = config.ExclusiveSelfAssignedRoles = !config.ExclusiveSelfAssignedRoles;
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
string exl = areExclusive ? "**exclusive**." : "**not exclusive**."; if(areExclusive)
await Context.Channel.SendConfirmAsync(" Self assigned roles are now " + exl); await ReplyConfirmLocalized("self_assign_excl").ConfigureAwait(false);
else
await ReplyConfirmLocalized("self_assign_no_excl").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Iam([Remainder] IRole role) public async Task Iam([Remainder] IRole role)
{ {
//var channel = (ITextChannel)Context.Channel;
var guildUser = (IGuildUser)Context.User; var guildUser = (IGuildUser)Context.User;
GuildConfig conf; GuildConfig conf;
@ -156,25 +156,24 @@ namespace NadekoBot.Modules.Administration
conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); conf = uow.GuildConfigs.For(Context.Guild.Id, set => set);
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id); roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
} }
SelfAssignedRole roleModel; if (roles.FirstOrDefault(r=>r.RoleId == role.Id) == null)
if ((roleModel = roles.FirstOrDefault(r=>r.RoleId == role.Id)) == null)
{ {
await Context.Channel.SendErrorAsync("That role is not self-assignable.").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
return; return;
} }
if (guildUser.RoleIds.Contains(role.Id)) if (guildUser.RoleIds.Contains(role.Id))
{ {
await Context.Channel.SendErrorAsync($"You already have **{role.Name}** role.").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_already", Format.Bold(role.Name)).ConfigureAwait(false);
return; return;
} }
if (conf.ExclusiveSelfAssignedRoles) if (conf.ExclusiveSelfAssignedRoles)
{ {
var sameRoleId = guildUser.RoleIds.Where(r => roles.Select(sar => sar.RoleId).Contains(r)).FirstOrDefault(); var sameRoleId = guildUser.RoleIds.FirstOrDefault(r => roles.Select(sar => sar.RoleId).Contains(r));
var sameRole = Context.Guild.GetRole(sameRoleId); var sameRole = Context.Guild.GetRole(sameRoleId);
if (sameRoleId != default(ulong)) if (sameRoleId != default(ulong))
{ {
await Context.Channel.SendErrorAsync($"You already have **{sameRole?.Name}** `exclusive self-assigned` role.").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_already_excl", Format.Bold(sameRole?.Name)).ConfigureAwait(false);
return; return;
} }
} }
@ -184,11 +183,11 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false);
Console.WriteLine(ex); Console.WriteLine(ex);
return; return;
} }
var msg = await Context.Channel.SendConfirmAsync($"🆗 You now have **{role.Name}** role.").ConfigureAwait(false); var msg = await ReplyConfirmLocalized("self_assign_success",Format.Bold(role.Name)).ConfigureAwait(false);
if (conf.AutoDeleteSelfAssignedRoleMessages) if (conf.AutoDeleteSelfAssignedRoleMessages)
{ {
@ -210,15 +209,14 @@ namespace NadekoBot.Modules.Administration
autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages; autoDeleteSelfAssignedRoleMessages = uow.GuildConfigs.For(Context.Guild.Id, set => set).AutoDeleteSelfAssignedRoleMessages;
roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id); roles = uow.SelfAssignedRoles.GetFromGuild(Context.Guild.Id);
} }
SelfAssignedRole roleModel; if (roles.FirstOrDefault(r => r.RoleId == role.Id) == null)
if ((roleModel = roles.FirstOrDefault(r => r.RoleId == role.Id)) == null)
{ {
await Context.Channel.SendErrorAsync("💢 That role is not self-assignable.").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_not").ConfigureAwait(false);
return; return;
} }
if (!guildUser.RoleIds.Contains(role.Id)) if (!guildUser.RoleIds.Contains(role.Id))
{ {
await Context.Channel.SendErrorAsync($"❎ You don't have **{role.Name}** role.").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_not_have",Format.Bold(role.Name)).ConfigureAwait(false);
return; return;
} }
try try
@ -227,10 +225,10 @@ namespace NadekoBot.Modules.Administration
} }
catch (Exception) catch (Exception)
{ {
await Context.Channel.SendErrorAsync($"⚠️ I am unable to add that role to you. `I can't remove roles to owners or other roles higher than my role in the role hierarchy.`").ConfigureAwait(false); await ReplyErrorLocalized("self_assign_perms").ConfigureAwait(false);
return; return;
} }
var msg = await Context.Channel.SendConfirmAsync($"🆗 You no longer have **{role.Name}** role.").ConfigureAwait(false); var msg = await ReplyConfirmLocalized("self_assign_remove", Format.Bold(role.Name)).ConfigureAwait(false);
if (autoDeleteSelfAssignedRoleMessages) if (autoDeleteSelfAssignedRoleMessages)
{ {

View File

@ -3,19 +3,97 @@ using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System; using System;
using System.Diagnostics; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.WebSocket;
using NadekoBot.Services;
namespace NadekoBot.Modules.Administration namespace NadekoBot.Modules.Administration
{ {
public partial class Administration public partial class Administration
{ {
[Group] [Group]
class SelfCommands : ModuleBase public class SelfCommands : NadekoSubmodule
{ {
private static volatile bool _forwardDMs;
private static volatile bool _forwardDMsToAllOwners;
private static readonly object _locker = new object();
static SelfCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
_forwardDMs = config.ForwardMessages;
_forwardDMsToAllOwners = config.ForwardToAllOwners;
}
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ForwardMessages()
{
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
lock (_locker)
_forwardDMs = config.ForwardMessages = !config.ForwardMessages;
uow.Complete();
}
if (_forwardDMs)
await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false);
else
await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[OwnerOnly]
public async Task ForwardToAll()
{
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.BotConfig.GetOrCreate();
lock (_locker)
_forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners;
uow.Complete();
}
if (_forwardDMsToAllOwners)
await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false);
else
await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false);
}
public static async Task HandleDmForwarding(SocketMessage msg, List<IDMChannel> ownerChannels)
{
if (_forwardDMs && ownerChannels.Any())
{
var title =
GetTextStatic("dm_from", NadekoBot.Localization.DefaultCultureInfo,
typeof(Administration).Name.ToLowerInvariant()) + $" [{msg.Author}]({msg.Author.Id})";
if (_forwardDMsToAllOwners)
{
await Task.WhenAll(ownerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)
.Select(ch => ch.SendConfirmAsync(title, msg.Content))).ConfigureAwait(false);
}
else
{
var firstOwnerChannel = ownerChannels.First();
if (firstOwnerChannel.Recipient.Id != msg.Author.Id)
try { await firstOwnerChannel.SendConfirmAsync(title, msg.Content).ConfigureAwait(false); }
catch
{
// ignored
}
}
}
}
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[OwnerOnly] [OwnerOnly]
public async Task ConnectShard(int shardid) public async Task ConnectShard(int shardid)
@ -24,14 +102,14 @@ namespace NadekoBot.Modules.Administration
if (shard == null) if (shard == null)
{ {
await Context.Channel.SendErrorAsync("No shard by that id found.").ConfigureAwait(false); await ReplyErrorLocalized("no_shard_id").ConfigureAwait(false);
return; return;
} }
try try
{ {
await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnecting.").ConfigureAwait(false); await ReplyConfirmLocalized("shard_reconnecting", Format.Bold("#" + shardid)).ConfigureAwait(false);
await shard.ConnectAsync().ConfigureAwait(false); await shard.ConnectAsync().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Shard **#{shardid}** reconnected.").ConfigureAwait(false); await ReplyConfirmLocalized("shard_reconnected", Format.Bold("#" + shardid)).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -49,18 +127,18 @@ namespace NadekoBot.Modules.Administration
if (server == null) if (server == null)
{ {
await Context.Channel.SendErrorAsync("⚠️ Cannot find that server").ConfigureAwait(false); await ReplyErrorLocalized("no_server").ConfigureAwait(false);
return; return;
} }
if (server.OwnerId != NadekoBot.Client.CurrentUser.Id) if (server.OwnerId != NadekoBot.Client.CurrentUser.Id)
{ {
await server.LeaveAsync().ConfigureAwait(false); await server.LeaveAsync().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("✅ Left server " + server.Name).ConfigureAwait(false); await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false);
} }
else else
{ {
await server.DeleteAsync().ConfigureAwait(false); await server.DeleteAsync().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("Deleted server " + server.Name).ConfigureAwait(false); await ReplyConfirmLocalized("deleted_server",Format.Bold(server.Name)).ConfigureAwait(false);
} }
} }
@ -69,7 +147,14 @@ namespace NadekoBot.Modules.Administration
[OwnerOnly] [OwnerOnly]
public async Task Die() public async Task Die()
{ {
try { await Context.Channel.SendConfirmAsync(" **Shutting down.**").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } try
{
await ReplyConfirmLocalized("shutting_down").ConfigureAwait(false);
}
catch
{
// ignored
}
await Task.Delay(2000).ConfigureAwait(false); await Task.Delay(2000).ConfigureAwait(false);
Environment.Exit(0); Environment.Exit(0);
} }
@ -83,7 +168,7 @@ namespace NadekoBot.Modules.Administration
await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false); await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Bot name changed to **{newName}**").ConfigureAwait(false); await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -92,7 +177,7 @@ namespace NadekoBot.Modules.Administration
{ {
await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false); await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Bot status changed to **{status}**").ConfigureAwait(false); await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -114,7 +199,7 @@ namespace NadekoBot.Modules.Administration
} }
} }
await Context.Channel.SendConfirmAsync("🆒 **New avatar set.**").ConfigureAwait(false); await ReplyConfirmLocalized("set_avatar").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -123,7 +208,7 @@ namespace NadekoBot.Modules.Administration
{ {
await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false); await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("👾 **New game set.**").ConfigureAwait(false); await ReplyConfirmLocalized("set_game").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -134,7 +219,7 @@ namespace NadekoBot.Modules.Administration
await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false); await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync(" **New stream set.**").ConfigureAwait(false); await ReplyConfirmLocalized("set_stream").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -148,7 +233,7 @@ namespace NadekoBot.Modules.Administration
if (ids.Length != 2) if (ids.Length != 2)
return; return;
var sid = ulong.Parse(ids[0]); var sid = ulong.Parse(ids[0]);
var server = NadekoBot.Client.GetGuilds().Where(s => s.Id == sid).FirstOrDefault(); var server = NadekoBot.Client.GetGuilds().FirstOrDefault(s => s.Id == sid);
if (server == null) if (server == null)
return; return;
@ -156,7 +241,7 @@ namespace NadekoBot.Modules.Administration
if (ids[1].ToUpperInvariant().StartsWith("C:")) if (ids[1].ToUpperInvariant().StartsWith("C:"))
{ {
var cid = ulong.Parse(ids[1].Substring(2)); var cid = ulong.Parse(ids[1].Substring(2));
var ch = server.TextChannels.Where(c => c.Id == cid).FirstOrDefault(); var ch = server.TextChannels.FirstOrDefault(c => c.Id == cid);
if (ch == null) if (ch == null)
{ {
return; return;
@ -166,7 +251,7 @@ namespace NadekoBot.Modules.Administration
else if (ids[1].ToUpperInvariant().StartsWith("U:")) else if (ids[1].ToUpperInvariant().StartsWith("U:"))
{ {
var uid = ulong.Parse(ids[1].Substring(2)); var uid = ulong.Parse(ids[1].Substring(2));
var user = server.Users.Where(u => u.Id == uid).FirstOrDefault(); var user = server.Users.FirstOrDefault(u => u.Id == uid);
if (user == null) if (user == null)
{ {
return; return;
@ -175,8 +260,10 @@ namespace NadekoBot.Modules.Administration
} }
else else
{ {
await Context.Channel.SendErrorAsync("⚠️ Invalid format.").ConfigureAwait(false); await ReplyErrorLocalized("invalid_format").ConfigureAwait(false);
return;
} }
await ReplyConfirmLocalized("message_sent").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -186,10 +273,10 @@ namespace NadekoBot.Modules.Administration
var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray(); var channels = NadekoBot.Client.GetGuilds().Select(g => g.DefaultChannel).ToArray();
if (channels == null) if (channels == null)
return; return;
await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync($"🆕 Message from {Context.User} `[Bot Owner]`:", message))) await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message)))
.ConfigureAwait(false); .ConfigureAwait(false);
await Context.Channel.SendConfirmAsync("🆗").ConfigureAwait(false); await ReplyConfirmLocalized("message_sent").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -197,7 +284,7 @@ namespace NadekoBot.Modules.Administration
public async Task ReloadImages() public async Task ReloadImages()
{ {
var time = await NadekoBot.Images.Reload().ConfigureAwait(false); var time = await NadekoBot.Images.Reload().ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Images loaded after {time.TotalSeconds:F3}s!").ConfigureAwait(false); await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false);
} }
private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus) private static UserStatus SettableUserStatusToUserStatus(SettableUserStatus sus)

View File

@ -1,11 +1,9 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.DataStructures; using NadekoBot.DataStructures;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using NLog; using NLog;
using System; using System;
@ -18,10 +16,10 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class ServerGreetCommands : ModuleBase public class ServerGreetCommands : NadekoSubmodule
{ {
//make this to a field in the guildconfig table //make this to a field in the guildconfig table
class GreetSettings private class GreetSettings
{ {
public int AutoDeleteGreetMessagesTimer { get; set; } public int AutoDeleteGreetMessagesTimer { get; set; }
public int AutoDeleteByeMessagesTimer { get; set; } public int AutoDeleteByeMessagesTimer { get; set; }
@ -53,9 +51,9 @@ namespace NadekoBot.Modules.Administration
}; };
} }
private static Logger _log { get; } private new static Logger _log { get; }
private static ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache { get; } = new ConcurrentDictionary<ulong, GreetSettings>(); private static ConcurrentDictionary<ulong, GreetSettings> guildConfigsCache { get; }
static ServerGreetCommands() static ServerGreetCommands()
{ {
@ -63,13 +61,13 @@ namespace NadekoBot.Modules.Administration
NadekoBot.Client.UserLeft += UserLeft; NadekoBot.Client.UserLeft += UserLeft;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, (g) => GreetSettings.Create(g))); guildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(NadekoBot.AllGuildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create));
} }
private static GreetSettings GetOrAddSettingsForGuild(ulong guildId) private static GreetSettings GetOrAddSettingsForGuild(ulong guildId)
{ {
GreetSettings settings; GreetSettings settings;
GuildConfigsCache.TryGetValue(guildId, out settings); guildConfigsCache.TryGetValue(guildId, out settings);
if (settings != null) if (settings != null)
return settings; return settings;
@ -80,7 +78,7 @@ namespace NadekoBot.Modules.Administration
settings = GreetSettings.Create(gc); settings = GreetSettings.Create(gc);
} }
GuildConfigsCache.TryAdd(guildId, settings); guildConfigsCache.TryAdd(guildId, settings);
return settings; return settings;
} }
@ -129,7 +127,10 @@ namespace NadekoBot.Modules.Administration
catch (Exception ex) { _log.Warn(ex); } catch (Exception ex) { _log.Warn(ex); }
} }
} }
catch { } catch
{
// ignored
}
}); });
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -212,7 +213,10 @@ namespace NadekoBot.Modules.Administration
} }
} }
} }
catch { } catch
{
// ignored
}
}); });
return Task.CompletedTask; return Task.CompletedTask;
} }
@ -222,16 +226,15 @@ namespace NadekoBot.Modules.Administration
[RequireUserPermission(GuildPermission.ManageGuild)] [RequireUserPermission(GuildPermission.ManageGuild)]
public async Task GreetDel(int timer = 30) public async Task GreetDel(int timer = 30)
{ {
var channel = (ITextChannel)Context.Channel;
if (timer < 0 || timer > 600) if (timer < 0 || timer > 600)
return; return;
await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false); await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0) if (timer > 0)
await Context.Channel.SendConfirmAsync($"🆗 Greet messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false); await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync(" Automatic deletion of greet messages has been **disabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false);
} }
private static async Task SetGreetDel(ulong id, int timer) private static async Task SetGreetDel(ulong id, int timer)
@ -245,7 +248,7 @@ namespace NadekoBot.Modules.Administration
conf.AutoDeleteGreetMessagesTimer = timer; conf.AutoDeleteGreetMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -259,9 +262,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); var enabled = await ServerGreetCommands.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
if (enabled) if (enabled)
await Context.Channel.SendConfirmAsync("✅ Greeting messages **enabled** on this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("greet_on").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync(" Greeting messages **disabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("greet_off").ConfigureAwait(false);
} }
private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null) private static async Task<bool> SetGreet(ulong guildId, ulong channelId, bool? value = null)
@ -274,7 +277,7 @@ namespace NadekoBot.Modules.Administration
conf.GreetMessageChannelId = channelId; conf.GreetMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -293,15 +296,15 @@ namespace NadekoBot.Modules.Administration
{ {
channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText; channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText;
} }
await Context.Channel.SendConfirmAsync("Current greet message: ", channelGreetMessageText?.SanitizeMentions()); await ReplyConfirmLocalized("greetmsg_cur", channelGreetMessageText?.SanitizeMentions()).ConfigureAwait(false);
return; return;
} }
var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text); var sendGreetEnabled = ServerGreetCommands.SetGreetMessage(Context.Guild.Id, ref text);
await Context.Channel.SendConfirmAsync("🆗 New greet message **set**.").ConfigureAwait(false); await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false);
if (!sendGreetEnabled) if (!sendGreetEnabled)
await Context.Channel.SendConfirmAsync(" Enable greet messsages by typing `.greet`").ConfigureAwait(false); await ReplyConfirmLocalized("greetmsg_enable", $"`{Prefix}greet`").ConfigureAwait(false);
} }
public static bool SetGreetMessage(ulong guildId, ref string message) public static bool SetGreetMessage(ulong guildId, ref string message)
@ -319,7 +322,7 @@ namespace NadekoBot.Modules.Administration
greetMsgEnabled = conf.SendChannelGreetMessage; greetMsgEnabled = conf.SendChannelGreetMessage;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
@ -334,9 +337,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false); var enabled = await ServerGreetCommands.SetGreetDm(Context.Guild.Id).ConfigureAwait(false);
if (enabled) if (enabled)
await Context.Channel.SendConfirmAsync("🆗 DM Greet announcements **enabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync(" Greet announcements **disabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("greetdm_off").ConfigureAwait(false);
} }
private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null) private static async Task<bool> SetGreetDm(ulong guildId, bool? value = null)
@ -348,7 +351,7 @@ namespace NadekoBot.Modules.Administration
enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage; enabled = conf.SendDmGreetMessage = value ?? !conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
@ -367,15 +370,15 @@ namespace NadekoBot.Modules.Administration
{ {
config = uow.GuildConfigs.For(Context.Guild.Id); config = uow.GuildConfigs.For(Context.Guild.Id);
} }
await Context.Channel.SendConfirmAsync(" Current **DM greet** message: `" + config.DmGreetMessageText?.SanitizeMentions() + "`"); await ReplyConfirmLocalized("greetdmmsg_cur", config.DmGreetMessageText?.SanitizeMentions()).ConfigureAwait(false);
return; return;
} }
var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text); var sendGreetEnabled = ServerGreetCommands.SetGreetDmMessage(Context.Guild.Id, ref text);
await Context.Channel.SendConfirmAsync("🆗 New DM greet message **set**.").ConfigureAwait(false); await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false);
if (!sendGreetEnabled) if (!sendGreetEnabled)
await Context.Channel.SendConfirmAsync($" Enable DM greet messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}greetdm`").ConfigureAwait(false); await ReplyConfirmLocalized("greetdmmsg_enable", $"`{Prefix}greetdm`").ConfigureAwait(false);
} }
public static bool SetGreetDmMessage(ulong guildId, ref string message) public static bool SetGreetDmMessage(ulong guildId, ref string message)
@ -393,7 +396,7 @@ namespace NadekoBot.Modules.Administration
greetMsgEnabled = conf.SendDmGreetMessage; greetMsgEnabled = conf.SendDmGreetMessage;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
@ -408,9 +411,9 @@ namespace NadekoBot.Modules.Administration
var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); var enabled = await ServerGreetCommands.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false);
if (enabled) if (enabled)
await Context.Channel.SendConfirmAsync("✅ Bye announcements **enabled** on this channel.").ConfigureAwait(false); await ReplyConfirmLocalized("bye_on").ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync(" Bye announcements **disabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("bye_off").ConfigureAwait(false);
} }
private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null) private static async Task<bool> SetBye(ulong guildId, ulong channelId, bool? value = null)
@ -423,7 +426,7 @@ namespace NadekoBot.Modules.Administration
conf.ByeMessageChannelId = channelId; conf.ByeMessageChannelId = channelId;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync(); await uow.CompleteAsync();
} }
@ -442,15 +445,15 @@ namespace NadekoBot.Modules.Administration
{ {
byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText; byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText;
} }
await Context.Channel.SendConfirmAsync(" Current **bye** message: `" + byeMessageText?.SanitizeMentions() + "`"); await ReplyConfirmLocalized("byemsg_cur", byeMessageText?.SanitizeMentions()).ConfigureAwait(false);
return; return;
} }
var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text); var sendByeEnabled = ServerGreetCommands.SetByeMessage(Context.Guild.Id, ref text);
await Context.Channel.SendConfirmAsync("🆗 New bye message **set**.").ConfigureAwait(false); await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false);
if (!sendByeEnabled) if (!sendByeEnabled)
await Context.Channel.SendConfirmAsync($" Enable bye messsages by typing `{NadekoBot.ModulePrefixes[typeof(Administration).Name]}bye`").ConfigureAwait(false); await ReplyConfirmLocalized("byemsg_enable", $"`{Prefix}bye`").ConfigureAwait(false);
} }
public static bool SetByeMessage(ulong guildId, ref string message) public static bool SetByeMessage(ulong guildId, ref string message)
@ -468,7 +471,7 @@ namespace NadekoBot.Modules.Administration
byeMsgEnabled = conf.SendChannelByeMessage; byeMsgEnabled = conf.SendChannelByeMessage;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
uow.Complete(); uow.Complete();
} }
@ -483,9 +486,9 @@ namespace NadekoBot.Modules.Administration
await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false); await ServerGreetCommands.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false);
if (timer > 0) if (timer > 0)
await Context.Channel.SendConfirmAsync($"🆗 Bye messages **will be deleted** after `{timer} seconds`.").ConfigureAwait(false); await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false);
else else
await Context.Channel.SendConfirmAsync(" Automatic deletion of bye messages has been **disabled**.").ConfigureAwait(false); await ReplyConfirmLocalized("byedel_off").ConfigureAwait(false);
} }
private static async Task SetByeDel(ulong guildId, int timer) private static async Task SetByeDel(ulong guildId, int timer)
@ -499,7 +502,7 @@ namespace NadekoBot.Modules.Administration
conf.AutoDeleteByeMessagesTimer = timer; conf.AutoDeleteByeMessagesTimer = timer;
var toAdd = GreetSettings.Create(conf); var toAdd = GreetSettings.Create(conf);
GuildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd); guildConfigsCache.AddOrUpdate(guildId, toAdd, (key, old) => toAdd);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }

View File

@ -19,21 +19,20 @@ namespace NadekoBot.Modules.Administration
public partial class Administration public partial class Administration
{ {
[Group] [Group]
public class VoicePlusTextCommands : ModuleBase public class VoicePlusTextCommands : NadekoSubmodule
{ {
private static Regex channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); private new static readonly Logger _log;
private static ConcurrentHashSet<ulong> voicePlusTextCache { get; } private static readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled);
private static ConcurrentDictionary<ulong, SemaphoreSlim> guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>(); private static readonly ConcurrentHashSet<ulong> _voicePlusTextCache;
private static readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>();
static VoicePlusTextCommands() static VoicePlusTextCommands()
{ {
var _log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{ _voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId));
}
NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler; NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler;
sw.Stop(); sw.Stop();
@ -53,7 +52,7 @@ namespace NadekoBot.Modules.Administration
if (before.VoiceChannel == after.VoiceChannel) if (before.VoiceChannel == after.VoiceChannel)
return Task.CompletedTask; return Task.CompletedTask;
if (!voicePlusTextCache.Contains(guild.Id)) if (!_voicePlusTextCache.Contains(guild.Id))
return Task.CompletedTask; return Task.CompletedTask;
var _ = Task.Run(async () => var _ = Task.Run(async () =>
@ -66,20 +65,25 @@ namespace NadekoBot.Modules.Administration
try try
{ {
await guild.Owner.SendErrorAsync( await guild.Owner.SendErrorAsync(
"⚠️ I don't have **manage server** and/or **manage channels** permission," + GetTextStatic("vt_exit",
$" so I cannot run `voice+text` on **{guild.Name}** server.").ConfigureAwait(false); NadekoBot.Localization.GetCultureInfo(guild),
typeof(Administration).Name.ToLowerInvariant(),
Format.Bold(guild.Name))).ConfigureAwait(false);
}
catch
{
// ignored
} }
catch { }
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false;
voicePlusTextCache.TryRemove(guild.Id); _voicePlusTextCache.TryRemove(guild.Id);
await uow.CompleteAsync().ConfigureAwait(false); await uow.CompleteAsync().ConfigureAwait(false);
} }
return; return;
} }
var semaphore = guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1)); var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1));
try try
{ {
@ -106,18 +110,16 @@ namespace NadekoBot.Modules.Administration
if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id) if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id)
{ {
var roleName = GetRoleName(afterVch); var roleName = GetRoleName(afterVch);
IRole roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName); var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ??
if (roleToAdd == null) (IRole) await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
roleToAdd = await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false);
ITextChannel textChannel = guild.TextChannels ITextChannel textChannel = guild.TextChannels
.Where(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()) .FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant());
.FirstOrDefault();
if (textChannel == null) if (textChannel == null)
{ {
var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false));
try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch { } try { await guild.CurrentUser.AddRolesAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/}
await Task.Delay(50).ConfigureAwait(false); await Task.Delay(50).ConfigureAwait(false);
await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions( await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions(
readMessages: PermValue.Allow, readMessages: PermValue.Allow,
@ -148,7 +150,7 @@ namespace NadekoBot.Modules.Administration
} }
private static string GetChannelName(string voiceName) => private static string GetChannelName(string voiceName) =>
channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; _channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice";
private static string GetRoleName(IVoiceChannel ch) => private static string GetRoleName(IVoiceChannel ch) =>
"nvoice-" + ch.Id; "nvoice-" + ch.Id;
@ -164,7 +166,7 @@ namespace NadekoBot.Modules.Administration
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false); var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels) if (!botUser.GuildPermissions.ManageRoles || !botUser.GuildPermissions.ManageChannels)
{ {
await Context.Channel.SendErrorAsync("I require atleast **manage roles** and **manage channels permissions** to enable this feature. `(preffered Administration permission)`"); await ReplyErrorLocalized("vt_no_perms").ConfigureAwait(false);
return; return;
} }
@ -172,10 +174,12 @@ namespace NadekoBot.Modules.Administration
{ {
try try
{ {
await Context.Channel.SendErrorAsync("⚠️ You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. " + await ReplyErrorLocalized("vt_no_admin").ConfigureAwait(false);
"`This may cause some issues, and you will have to clean up text channels yourself afterwards.`"); }
catch
{
// ignored
} }
catch { }
} }
try try
{ {
@ -188,7 +192,7 @@ namespace NadekoBot.Modules.Administration
} }
if (!isEnabled) if (!isEnabled)
{ {
voicePlusTextCache.TryRemove(guild.Id); _voicePlusTextCache.TryRemove(guild.Id);
foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice"))) foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice")))
{ {
try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { }
@ -200,11 +204,11 @@ namespace NadekoBot.Modules.Administration
try { await role.DeleteAsync().ConfigureAwait(false); } catch { } try { await role.DeleteAsync().ConfigureAwait(false); } catch { }
await Task.Delay(500).ConfigureAwait(false); await Task.Delay(500).ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync(" Successfuly **removed** voice + text feature.").ConfigureAwait(false); await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false);
return; return;
} }
voicePlusTextCache.Add(guild.Id); _voicePlusTextCache.Add(guild.Id);
await Context.Channel.SendConfirmAsync("🆗 Successfuly **enabled** voice + text feature.").ConfigureAwait(false); await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
@ -224,7 +228,7 @@ namespace NadekoBot.Modules.Administration
var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false); var botUser = await guild.GetCurrentUserAsync().ConfigureAwait(false);
if (!botUser.GuildPermissions.Administrator) if (!botUser.GuildPermissions.Administrator)
{ {
await Context.Channel.SendErrorAsync("I need **Administrator permission** to do that.").ConfigureAwait(false); await ReplyErrorLocalized("need_admin").ConfigureAwait(false);
return; return;
} }
@ -251,7 +255,7 @@ namespace NadekoBot.Modules.Administration
await Task.Delay(500).ConfigureAwait(false); await Task.Delay(500).ConfigureAwait(false);
} }
await Context.Channel.SendConfirmAsync("Cleaned v+t.").ConfigureAwait(false); await ReplyConfirmLocalized("cleaned_up").ConfigureAwait(false);
} }
} }
} }

View File

@ -17,18 +17,14 @@ using NLog;
namespace NadekoBot.Modules.ClashOfClans namespace NadekoBot.Modules.ClashOfClans
{ {
[NadekoModule("ClashOfClans", ",")] [NadekoModule("ClashOfClans", ",")]
public class ClashOfClans : DiscordModule public class ClashOfClans : NadekoModule
{ {
public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>(); public static ConcurrentDictionary<ulong, List<ClashWar>> ClashWars { get; set; } = new ConcurrentDictionary<ulong, List<ClashWar>>();
private static Timer checkWarTimer { get; } private static Timer checkWarTimer { get; }
private static new readonly Logger _log;
static ClashOfClans() static ClashOfClans()
{ {
_log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>( ClashWars = new ConcurrentDictionary<ulong, List<ClashWar>>(
@ -73,7 +69,11 @@ namespace NadekoBot.Modules.ClashOfClans
try try
{ {
SaveWar(war); SaveWar(war);
await war.Channel.SendErrorAsync($"❗🔰**Claim from @{Bases[i].CallUser} for a war against {war.ShortPrint()} has expired.**").ConfigureAwait(false); await war.Channel.SendErrorAsync(GetTextStatic("claim_expired",
NadekoBot.Localization.GetCultureInfo(war.Channel.GuildId),
typeof(ClashOfClans).Name.ToLowerInvariant(),
Format.Bold(Bases[i].CallUser),
war.ShortPrint()));
} }
catch { } catch { }
} }
@ -92,7 +92,7 @@ namespace NadekoBot.Modules.ClashOfClans
if (size < 10 || size > 50 || size % 5 != 0) if (size < 10 || size > 50 || size % 5 != 0)
{ {
await Context.Channel.SendErrorAsync("🔰 Not a Valid war size").ConfigureAwait(false); await ReplyErrorLocalized("invalid_size").ConfigureAwait(false);
return; return;
} }
List<ClashWar> wars; List<ClashWar> wars;
@ -107,7 +107,7 @@ namespace NadekoBot.Modules.ClashOfClans
var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id); var cw = await CreateWar(enemyClan, size, Context.Guild.Id, Context.Channel.Id);
wars.Add(cw); wars.Add(cw);
await Context.Channel.SendConfirmAsync($"❗🔰**CREATED CLAN WAR AGAINST {cw.ShortPrint()}**").ConfigureAwait(false); await ReplyErrorLocalized("war_created", cw.ShortPrint()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -120,18 +120,18 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(Context.Guild, num); var warsInfo = GetWarInfo(Context.Guild, num);
if (warsInfo == null) if (warsInfo == null)
{ {
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
return; return;
} }
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
try try
{ {
war.Start(); war.Start();
await Context.Channel.SendConfirmAsync($"🔰**STARTED WAR AGAINST {war.ShortPrint()}**").ConfigureAwait(false); await ReplyConfirmLocalized("war_started", war.ShortPrint()).ConfigureAwait(false);
} }
catch catch
{ {
await Context.Channel.SendErrorAsync($"🔰**WAR AGAINST {war.ShortPrint()} HAS ALREADY STARTED**").ConfigureAwait(false); await ReplyErrorLocalized("war_already_started", war.ShortPrint()).ConfigureAwait(false);
} }
SaveWar(war); SaveWar(war);
} }
@ -149,22 +149,20 @@ namespace NadekoBot.Modules.ClashOfClans
ClashWars.TryGetValue(Context.Guild.Id, out wars); ClashWars.TryGetValue(Context.Guild.Id, out wars);
if (wars == null || wars.Count == 0) if (wars == null || wars.Count == 0)
{ {
await Context.Channel.SendErrorAsync("🔰 **No active wars.**").ConfigureAwait(false); await ReplyErrorLocalized("no_active_wars").ConfigureAwait(false);
return; return;
} }
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine("🔰 **LIST OF ACTIVE WARS**");
sb.AppendLine("**-------------------------**"); sb.AppendLine("**-------------------------**");
for (var i = 0; i < wars.Count; i++) for (var i = 0; i < wars.Count; i++)
{ {
sb.AppendLine($"**#{i + 1}.** `Enemy:` **{wars[i].EnemyClan}**"); sb.AppendLine($"**#{i + 1}.** `{GetText("enemy")}:` **{wars[i].EnemyClan}**");
sb.AppendLine($"\t\t`Size:` **{wars[i].Size} v {wars[i].Size}**"); sb.AppendLine($"\t\t`{GetText("size")}:` **{wars[i].Size} v {wars[i].Size}**");
sb.AppendLine("**-------------------------**"); sb.AppendLine("**-------------------------**");
} }
await Context.Channel.SendConfirmAsync(sb.ToString()).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(GetText("list_active_wars"), sb.ToString()).ConfigureAwait(false);
return; return;
} }
var num = 0; var num = 0;
int.TryParse(number, out num); int.TryParse(number, out num);
@ -172,10 +170,11 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(Context.Guild, num); var warsInfo = GetWarInfo(Context.Guild, num);
if (warsInfo == null) if (warsInfo == null)
{ {
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
return; return;
} }
await Context.Channel.SendConfirmAsync(warsInfo.Item1[warsInfo.Item2].ToPrettyString()).ConfigureAwait(false); var war = warsInfo.Item1[warsInfo.Item2];
await Context.Channel.SendConfirmAsync(war.Localize("info_about_war", $"`{war.EnemyClan}` ({war.Size} v {war.Size})"), war.ToPrettyString()).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -185,7 +184,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(Context.Guild, number); var warsInfo = GetWarInfo(Context.Guild, number);
if (warsInfo == null || warsInfo.Item1.Count == 0) if (warsInfo == null || warsInfo.Item1.Count == 0)
{ {
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
return; return;
} }
var usr = var usr =
@ -197,11 +196,11 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
war.Call(usr, baseNumber - 1); war.Call(usr, baseNumber - 1);
SaveWar(war); SaveWar(war);
await Context.Channel.SendConfirmAsync($"🔰**{usr}** claimed a base #{baseNumber} for a war against {war.ShortPrint()}").ConfigureAwait(false); await ConfirmLocalized("claimed_base", Format.Bold(usr.ToString()), baseNumber, war.ShortPrint()).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false); await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
} }
} }
@ -233,15 +232,14 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(Context.Guild, number); var warsInfo = GetWarInfo(Context.Guild, number);
if (warsInfo == null) if (warsInfo == null)
{ {
await Context.Channel.SendErrorAsync("🔰 That war does not exist.").ConfigureAwait(false); await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
return; return;
} }
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
war.End(); war.End();
SaveWar(war); SaveWar(war);
await Context.Channel.SendConfirmAsync($"❗🔰**War against {warsInfo.Item1[warsInfo.Item2].ShortPrint()} ended.**").ConfigureAwait(false); await ReplyConfirmLocalized("war_ended", warsInfo.Item1[warsInfo.Item2].ShortPrint()).ConfigureAwait(false);
var size = warsInfo.Item1[warsInfo.Item2].Size;
warsInfo.Item1.RemoveAt(warsInfo.Item2); warsInfo.Item1.RemoveAt(warsInfo.Item2);
} }
@ -252,7 +250,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warsInfo = GetWarInfo(Context.Guild, number); var warsInfo = GetWarInfo(Context.Guild, number);
if (warsInfo == null || warsInfo.Item1.Count == 0) if (warsInfo == null || warsInfo.Item1.Count == 0)
{ {
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
return; return;
} }
var usr = var usr =
@ -264,11 +262,11 @@ namespace NadekoBot.Modules.ClashOfClans
var war = warsInfo.Item1[warsInfo.Item2]; var war = warsInfo.Item1[warsInfo.Item2];
var baseNumber = war.Uncall(usr); var baseNumber = war.Uncall(usr);
SaveWar(war); SaveWar(war);
await Context.Channel.SendConfirmAsync($"🔰 @{usr} has **UNCLAIMED** a base #{baseNumber + 1} from a war against {war.ShortPrint()}").ConfigureAwait(false); await ReplyConfirmLocalized("base_unclaimed", usr, baseNumber + 1, war.ShortPrint()).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
await Context.Channel.SendErrorAsync($"🔰 {ex.Message}").ConfigureAwait(false); await Context.Channel.SendErrorAsync(ex.Message).ConfigureAwait(false);
} }
} }
@ -277,7 +275,7 @@ namespace NadekoBot.Modules.ClashOfClans
var warInfo = GetWarInfo(Context.Guild, number); var warInfo = GetWarInfo(Context.Guild, number);
if (warInfo == null || warInfo.Item1.Count == 0) if (warInfo == null || warInfo.Item1.Count == 0)
{ {
await Context.Channel.SendErrorAsync("🔰 **That war does not exist.**").ConfigureAwait(false); await ReplyErrorLocalized("war_not_exist").ConfigureAwait(false);
return; return;
} }
var war = warInfo.Item1[warInfo.Item2]; var war = warInfo.Item1[warInfo.Item2];
@ -292,7 +290,7 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
war.FinishClaim(baseNumber, stars); war.FinishClaim(baseNumber, stars);
} }
await Context.Channel.SendConfirmAsync($"❗🔰{Context.User.Mention} **DESTROYED** a base #{baseNumber + 1} in a war against {war.ShortPrint()}").ConfigureAwait(false); await ReplyConfirmLocalized("base_destroyed", baseNumber +1, war.ShortPrint()).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -27,13 +27,13 @@ namespace NadekoBot.Modules.ClashOfClans
public static void Call(this ClashWar cw, string u, int baseNumber) public static void Call(this ClashWar cw, string u, int baseNumber)
{ {
if (baseNumber < 0 || baseNumber >= cw.Bases.Count) if (baseNumber < 0 || baseNumber >= cw.Bases.Count)
throw new ArgumentException("Invalid base number"); throw new ArgumentException(cw.Localize("invalid_base_number"));
if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3) if (cw.Bases[baseNumber].CallUser != null && cw.Bases[baseNumber].Stars == 3)
throw new ArgumentException("That base is already destroyed."); throw new ArgumentException(cw.Localize("base_already_claimed"));
for (var i = 0; i < cw.Bases.Count; i++) for (var i = 0; i < cw.Bases.Count; i++)
{ {
if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u) if (cw.Bases[i]?.BaseDestroyed == false && cw.Bases[i]?.CallUser == u)
throw new ArgumentException($"@{u} You already claimed base #{i + 1}. You can't claim a new one."); throw new ArgumentException(cw.Localize("claimed_other", u, i + 1));
} }
var cc = cw.Bases[baseNumber]; var cc = cw.Bases[baseNumber];
@ -45,7 +45,7 @@ namespace NadekoBot.Modules.ClashOfClans
public static void Start(this ClashWar cw) public static void Start(this ClashWar cw)
{ {
if (cw.WarState == StateOfWar.Started) if (cw.WarState == StateOfWar.Started)
throw new InvalidOperationException("War already started"); throw new InvalidOperationException("war_already_started");
//if (Started) //if (Started)
// throw new InvalidOperationException(); // throw new InvalidOperationException();
//Started = true; //Started = true;
@ -66,7 +66,7 @@ namespace NadekoBot.Modules.ClashOfClans
cw.Bases[i].CallUser = null; cw.Bases[i].CallUser = null;
return i; return i;
} }
throw new InvalidOperationException("You are not participating in that war."); throw new InvalidOperationException(cw.Localize("not_partic"));
} }
public static string ShortPrint(this ClashWar cw) => public static string ShortPrint(this ClashWar cw) =>
@ -75,8 +75,7 @@ namespace NadekoBot.Modules.ClashOfClans
public static string ToPrettyString(this ClashWar cw) public static string ToPrettyString(this ClashWar cw)
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendLine($"🔰**WAR AGAINST `{cw.EnemyClan}` ({cw.Size} v {cw.Size}) INFO:**");
if (cw.WarState == StateOfWar.Created) if (cw.WarState == StateOfWar.Created)
sb.AppendLine("`not started`"); sb.AppendLine("`not started`");
var twoHours = new TimeSpan(2, 0, 0); var twoHours = new TimeSpan(2, 0, 0);
@ -84,7 +83,7 @@ namespace NadekoBot.Modules.ClashOfClans
{ {
if (cw.Bases[i].CallUser == null) if (cw.Bases[i].CallUser == null)
{ {
sb.AppendLine($"`{i + 1}.` ❌*unclaimed*"); sb.AppendLine($"`{i + 1}.` ❌*{cw.Localize("not_claimed")}*");
} }
else else
{ {
@ -120,7 +119,7 @@ namespace NadekoBot.Modules.ClashOfClans
cw.Bases[i].Stars = stars; cw.Bases[i].Stars = stars;
return i; return i;
} }
throw new InvalidOperationException($"@{user} You are either not participating in that war, or you already destroyed a base."); throw new InvalidOperationException(cw.Localize("not_partic_or_destroyed", user));
} }
public static void FinishClaim(this ClashWar cw, int index, int stars = 3) public static void FinishClaim(this ClashWar cw, int index, int stars = 3)
@ -128,10 +127,22 @@ namespace NadekoBot.Modules.ClashOfClans
if (index < 0 || index > cw.Bases.Count) if (index < 0 || index > cw.Bases.Count)
throw new ArgumentOutOfRangeException(nameof(index)); throw new ArgumentOutOfRangeException(nameof(index));
var toFinish = cw.Bases[index]; var toFinish = cw.Bases[index];
if (toFinish.BaseDestroyed != false) throw new InvalidOperationException("That base is already destroyed."); if (toFinish.BaseDestroyed != false) throw new InvalidOperationException(cw.Localize("base_already_destroyed"));
if (toFinish.CallUser == null) throw new InvalidOperationException("That base is unclaimed."); if (toFinish.CallUser == null) throw new InvalidOperationException(cw.Localize("base_already_unclaimed"));
toFinish.BaseDestroyed = true; toFinish.BaseDestroyed = true;
toFinish.Stars = stars; toFinish.Stars = stars;
} }
public static string Localize(this ClashWar cw, string key)
{
return NadekoModule.GetTextStatic(key,
NadekoBot.Localization.GetCultureInfo(cw.Channel?.GuildId),
typeof(ClashOfClans).Name.ToLowerInvariant());
}
public static string Localize(this ClashWar cw, string key, params object[] replacements)
{
return string.Format(cw.Localize(key), replacements);
}
} }
} }

View File

@ -17,7 +17,7 @@ using NadekoBot.DataStructures;
namespace NadekoBot.Modules.CustomReactions namespace NadekoBot.Modules.CustomReactions
{ {
[NadekoModule("CustomReactions", ".")] [NadekoModule("CustomReactions", ".")]
public class CustomReactions : DiscordModule public class CustomReactions : NadekoModule
{ {
private static CustomReaction[] _globalReactions = new CustomReaction[] { }; private static CustomReaction[] _globalReactions = new CustomReaction[] { };
public static CustomReaction[] GlobalReactions => _globalReactions; public static CustomReaction[] GlobalReactions => _globalReactions;
@ -139,7 +139,7 @@ namespace NadekoBot.Modules.CustomReactions
if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
{ {
try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { } await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
return; return;
} }
@ -165,8 +165,8 @@ namespace NadekoBot.Modules.CustomReactions
} }
else else
{ {
var reactions = GuildReactions.AddOrUpdate(Context.Guild.Id, GuildReactions.AddOrUpdate(Context.Guild.Id,
Array.Empty<CustomReaction>(), new CustomReaction[] { cr },
(k, old) => (k, old) =>
{ {
Array.Resize(ref old, old.Length + 1); Array.Resize(ref old, old.Length + 1);
@ -176,10 +176,10 @@ namespace NadekoBot.Modules.CustomReactions
} }
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle("New Custom Reaction") .WithTitle(GetText("new_cust_react"))
.WithDescription($"#{cr.Id}") .WithDescription($"#{cr.Id}")
.AddField(efb => efb.WithName("Trigger").WithValue(key)) .AddField(efb => efb.WithName(GetText("trigger")).WithValue(key))
.AddField(efb => efb.WithName("Response").WithValue(message)) .AddField(efb => efb.WithName(GetText("response")).WithValue(message))
).ConfigureAwait(false); ).ConfigureAwait(false);
} }
@ -196,19 +196,20 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray(); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, Array.Empty<CustomReaction>()).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else
{ {
var lastPage = customReactions.Length / 20; await ReplyErrorLocalized("no_found").ConfigureAwait(false);
await Context.Channel.SendPaginatedConfirmAsync(page, curPage => return;
new EmbedBuilder().WithOkColor()
.WithTitle("Custom reactions")
.WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
.Skip((curPage - 1) * 20)
.Take(20)
.Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}"))), lastPage)
.ConfigureAwait(false);
} }
var lastPage = customReactions.Length / 20;
await Context.Channel.SendPaginatedConfirmAsync(page, curPage =>
new EmbedBuilder().WithOkColor()
.WithTitle(GetText("name"))
.WithDescription(string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
.Skip((curPage - 1) * 20)
.Take(20)
.Select(cr => $"`#{cr.Id}` `{GetText("trigger")}:` {cr.Trigger}"))), lastPage)
.ConfigureAwait(false);
} }
public enum All public enum All
@ -227,20 +228,22 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else
{ {
var txtStream = await customReactions.GroupBy(cr => cr.Trigger) await ReplyErrorLocalized("no_found").ConfigureAwait(false);
.OrderBy(cr => cr.Key) return;
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
.ToJson()
.ToStream()
.ConfigureAwait(false);
if (Context.Guild == null) // its a private one, just send back
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
else
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
} }
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key)
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => new { id = y.Id, text = y.Response }).ToList() })
.ToJson()
.ToStream()
.ConfigureAwait(false);
if (Context.Guild == null) // its a private one, just send back
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
else
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", GetText("list_all")).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -255,7 +258,9 @@ namespace NadekoBot.Modules.CustomReactions
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray(); customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new CustomReaction[]{ }).Where(cr => cr != null).ToArray();
if (customReactions == null || !customReactions.Any()) if (customReactions == null || !customReactions.Any())
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false); {
await ReplyErrorLocalized("no_found").ConfigureAwait(false);
}
else else
{ {
var ordered = customReactions var ordered = customReactions
@ -266,7 +271,7 @@ namespace NadekoBot.Modules.CustomReactions
var lastPage = ordered.Count / 20; var lastPage = ordered.Count / 20;
await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) => await Context.Channel.SendPaginatedConfirmAsync(page, (curPage) =>
new EmbedBuilder().WithOkColor() new EmbedBuilder().WithOkColor()
.WithTitle($"Custom Reactions (grouped)") .WithTitle(GetText("name"))
.WithDescription(string.Join("\r\n", ordered .WithDescription(string.Join("\r\n", ordered
.Skip((curPage - 1) * 20) .Skip((curPage - 1) * 20)
.Take(20) .Take(20)
@ -287,13 +292,16 @@ namespace NadekoBot.Modules.CustomReactions
var found = customReactions.FirstOrDefault(cr => cr?.Id == id); var found = customReactions.FirstOrDefault(cr => cr?.Id == id);
if (found == null) if (found == null)
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false); {
await ReplyErrorLocalized("no_found_id").ConfigureAwait(false);
return;
}
else else
{ {
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription($"#{id}") .WithDescription($"#{id}")
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger)) .AddField(efb => efb.WithName(GetText("trigger")).WithValue(found.Trigger))
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```")) .AddField(efb => efb.WithName(GetText("response")).WithValue(found.Response + "\n```css\n" + found.Response + "```"))
).ConfigureAwait(false); ).ConfigureAwait(false);
} }
} }
@ -303,7 +311,7 @@ namespace NadekoBot.Modules.CustomReactions
{ {
if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator)) if ((Context.Guild == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (Context.Guild != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
{ {
try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { } await ReplyErrorLocalized("insuff_perms").ConfigureAwait(false);
return; return;
} }
@ -313,32 +321,42 @@ namespace NadekoBot.Modules.CustomReactions
{ {
toDelete = uow.CustomReactions.Get(id); toDelete = uow.CustomReactions.Get(id);
if (toDelete == null) //not found if (toDelete == null) //not found
return; success = false;
else
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
{ {
uow.CustomReactions.Remove(toDelete); if ((toDelete.GuildId == null || toDelete.GuildId == 0) && Context.Guild == null)
//todo i can dramatically improve performance of this, if Ids are ordered.
_globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
success = true;
}
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
{
uow.CustomReactions.Remove(toDelete);
GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
{ {
return old.Where(cr => cr?.Id != toDelete.Id).ToArray(); uow.CustomReactions.Remove(toDelete);
}); //todo i can dramatically improve performance of this, if Ids are ordered.
success = true; _globalReactions = GlobalReactions.Where(cr => cr?.Id != toDelete.Id).ToArray();
success = true;
}
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && Context.Guild.Id == toDelete.GuildId)
{
uow.CustomReactions.Remove(toDelete);
GuildReactions.AddOrUpdate(Context.Guild.Id, new CustomReaction[] { }, (key, old) =>
{
return old.Where(cr => cr?.Id != toDelete.Id).ToArray();
});
success = true;
}
if (success)
await uow.CompleteAsync().ConfigureAwait(false);
} }
if (success)
await uow.CompleteAsync().ConfigureAwait(false);
} }
if (success) if (success)
await Context.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false); {
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithTitle(GetText("deleted"))
.WithDescription("#" + toDelete.Id)
.AddField(efb => efb.WithName(GetText("trigger")).WithValue(toDelete.Trigger))
.AddField(efb => efb.WithName(GetText("response")).WithValue(toDelete.Response)));
}
else else
await Context.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false); {
await ReplyErrorLocalized("no_found_id").ConfigureAwait(false);
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -348,18 +366,18 @@ namespace NadekoBot.Modules.CustomReactions
if (string.IsNullOrWhiteSpace(trigger)) if (string.IsNullOrWhiteSpace(trigger))
{ {
ClearStats(); ClearStats();
await Context.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false); await ReplyConfirmLocalized("all_stats_cleared").ConfigureAwait(false);
} }
else else
{ {
uint throwaway; uint throwaway;
if (ReactionStats.TryRemove(trigger, out throwaway)) if (ReactionStats.TryRemove(trigger, out throwaway))
{ {
await Context.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false); await ReplyErrorLocalized("stats_cleared", Format.Bold(trigger)).ConfigureAwait(false);
} }
else else
{ {
await Context.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false); await ReplyErrorLocalized("stats_not_found").ConfigureAwait(false);
} }
} }
} }
@ -376,7 +394,7 @@ namespace NadekoBot.Modules.CustomReactions
await Context.Channel.SendPaginatedConfirmAsync(page, await Context.Channel.SendPaginatedConfirmAsync(page,
(curPage) => ordered.Skip((curPage - 1) * 9) (curPage) => ordered.Skip((curPage - 1) * 9)
.Take(9) .Take(9)
.Aggregate(new EmbedBuilder().WithOkColor().WithTitle($"Custom Reaction Stats"), .Aggregate(new EmbedBuilder().WithOkColor().WithTitle(GetText("stats")),
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage) (agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true))), lastPage)
.ConfigureAwait(false); .ConfigureAwait(false);
} }

View File

@ -1,22 +0,0 @@
using Discord.Commands;
using NLog;
namespace NadekoBot.Modules
{
public abstract class DiscordModule : ModuleBase
{
protected Logger _log { get; }
protected string _prefix { get; }
public DiscordModule()
{
string prefix;
if (NadekoBot.ModulePrefixes.TryGetValue(this.GetType().Name, out prefix))
_prefix = prefix;
else
_prefix = "?missing_prefix?";
_log = LogManager.GetCurrentClassLogger();
}
}
}

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling public partial class Gambling
{ {
[Group] [Group]
public class AnimalRacing : ModuleBase public class AnimalRacing : NadekoSubmodule
{ {
public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>(); public static ConcurrentDictionary<ulong, AnimalRace> AnimalRaces { get; } = new ConcurrentDictionary<ulong, AnimalRace>();
@ -25,7 +25,7 @@ namespace NadekoBot.Modules.Gambling
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Race() public async Task Race()
{ {
var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel); var ar = new AnimalRace(Context.Guild.Id, (ITextChannel)Context.Channel, Prefix);
if (ar.Fail) if (ar.Fail)
await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false); await Context.Channel.SendErrorAsync("🏁 `Failed starting a race. Another race is probably running.`").ConfigureAwait(false);
@ -59,13 +59,16 @@ namespace NadekoBot.Modules.Gambling
public List<Participant> participants = new List<Participant>(); public List<Participant> participants = new List<Participant>();
private ulong serverId; private ulong serverId;
private int messagesSinceGameStarted = 0; private int messagesSinceGameStarted = 0;
private readonly string _prefix;
private Logger _log { get; } private Logger _log { get; }
public ITextChannel raceChannel { get; set; } public ITextChannel raceChannel { get; set; }
public bool Started { get; private set; } = false; public bool Started { get; private set; } = false;
public AnimalRace(ulong serverId, ITextChannel ch) public AnimalRace(ulong serverId, ITextChannel ch, string prefix)
{ {
this._prefix = prefix;
this._log = LogManager.GetCurrentClassLogger(); this._log = LogManager.GetCurrentClassLogger();
this.serverId = serverId; this.serverId = serverId;
this.raceChannel = ch; this.raceChannel = ch;
@ -74,11 +77,8 @@ namespace NadekoBot.Modules.Gambling
Fail = true; Fail = true;
return; return;
} }
using (var uow = DbHandler.UnitOfWork()) animals = new ConcurrentQueue<string>(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle());
{
animals = new ConcurrentQueue<string>(NadekoBot.BotConfig.RaceAnimals.Select(ra => ra.Icon).Shuffle());
}
var cancelSource = new CancellationTokenSource(); var cancelSource = new CancellationTokenSource();
@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Gambling
try try
{ {
await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.", await raceChannel.SendConfirmAsync("Animal Race", $"Starting in 20 seconds or when the room is full.",
footer: $"Type {NadekoBot.ModulePrefixes[typeof(Gambling).Name]}jr to join the race."); footer: $"Type {_prefix}jr to join the race.");
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -280,9 +280,7 @@ namespace NadekoBot.Modules.Gambling
public override bool Equals(object obj) public override bool Equals(object obj)
{ {
var p = obj as Participant; var p = obj as Participant;
return p == null ? return p != null && p.User == User;
false :
p.User == User;
} }
public override string ToString() public override string ToString()

View File

@ -5,12 +5,9 @@ using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Discord.WebSocket; using Discord.WebSocket;
using NadekoBot.Services.Database;
using System.Threading; using System.Threading;
using NLog; using NLog;
@ -19,7 +16,7 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling public partial class Gambling
{ {
[Group] [Group]
public class CurrencyEvents : ModuleBase public class CurrencyEvents : NadekoSubmodule
{ {
public enum CurrencyEvent public enum CurrencyEvent
{ {
@ -27,7 +24,7 @@ namespace NadekoBot.Modules.Gambling
SneakyGameStatus SneakyGameStatus
} }
//flower reaction event //flower reaction event
public static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>(); private static readonly ConcurrentHashSet<ulong> _sneakyGameAwardedUsers = new ConcurrentHashSet<ulong>();
private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10) private static readonly char[] _sneakyGameStatusChars = Enumerable.Range(48, 10)
@ -36,32 +33,25 @@ namespace NadekoBot.Modules.Gambling
.Select(x => (char)x) .Select(x => (char)x)
.ToArray(); .ToArray();
private static string _secretCode = String.Empty; private static string _secretCode = string.Empty;
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
[OwnerOnly] [OwnerOnly]
public async Task StartEvent(CurrencyEvent e, int arg = -1) public async Task StartEvent(CurrencyEvent e, int arg = -1)
{ {
var channel = (ITextChannel)Context.Channel; switch (e)
try
{ {
switch (e) case CurrencyEvent.FlowerReaction:
{ await FlowerReactionEvent(Context).ConfigureAwait(false);
case CurrencyEvent.FlowerReaction: break;
await FlowerReactionEvent(Context).ConfigureAwait(false); case CurrencyEvent.SneakyGameStatus:
break; await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
case CurrencyEvent.SneakyGameStatus: break;
await SneakyGameStatusEvent(Context, arg).ConfigureAwait(false);
break;
default:
break;
}
} }
catch { }
} }
public static async Task SneakyGameStatusEvent(CommandContext Context, int? arg) public async Task SneakyGameStatusEvent(CommandContext context, int? arg)
{ {
int num; int num;
if (arg == null || arg < 5) if (arg == null || arg < 5)
@ -69,26 +59,27 @@ namespace NadekoBot.Modules.Gambling
else else
num = arg.Value; num = arg.Value;
if (_secretCode != String.Empty) if (_secretCode != string.Empty)
return; return;
var rng = new NadekoRandom(); var rng = new NadekoRandom();
for (int i = 0; i < 5; i++) for (var i = 0; i < 5; i++)
{ {
_secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)]; _secretCode += _sneakyGameStatusChars[rng.Next(0, _sneakyGameStatusChars.Length)];
} }
var game = NadekoBot.Client.Game?.Name;
await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName) await NadekoBot.Client.SetGameAsync($"type {_secretCode} for " + NadekoBot.BotConfig.CurrencyPluralName)
.ConfigureAwait(false); .ConfigureAwait(false);
try try
{ {
await Context.Channel.SendConfirmAsync($"SneakyGameStatus event started", var title = GetText("sneakygamestatus_title");
$"Users must type a secret code to get 100 currency.\n" + var desc = GetText("sneakygamestatus_desc", Format.Bold(100.ToString()) + CurrencySign, Format.Bold(num.ToString()));
$"Lasts {num} seconds. Don't tell anyone. Shhh.") await context.Channel.SendConfirmAsync(title, desc).ConfigureAwait(false);
.ConfigureAwait(false); }
catch
{
// ignored
} }
catch { }
NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler; NadekoBot.Client.MessageReceived += SneakyGameMessageReceivedEventHandler;
@ -97,9 +88,9 @@ namespace NadekoBot.Modules.Gambling
var cnt = _sneakyGameAwardedUsers.Count; var cnt = _sneakyGameAwardedUsers.Count;
_sneakyGameAwardedUsers.Clear(); _sneakyGameAwardedUsers.Clear();
_secretCode = String.Empty; _secretCode = string.Empty;
await NadekoBot.Client.SetGameAsync($"SneakyGame event ended. {cnt} users received a reward.") await NadekoBot.Client.SetGameAsync(GetText("sneakygamestatus_end", cnt))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -114,29 +105,41 @@ namespace NadekoBot.Modules.Gambling
.ConfigureAwait(false); .ConfigureAwait(false);
try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); } try { await arg.DeleteAsync(new RequestOptions() { RetryMode = RetryMode.AlwaysFail }).ConfigureAwait(false); }
catch { } catch
{
// ignored
}
}); });
} }
return Task.Delay(0); return Task.Delay(0);
} }
public static Task FlowerReactionEvent(CommandContext Context) => public async Task FlowerReactionEvent(CommandContext context)
new FlowerReactionEvent().Start(Context); {
var title = GetText("flowerreaction_title");
var desc = GetText("flowerreaction_desc", "🌸", Format.Bold(100.ToString()) + CurrencySign);
var footer = GetText("flowerreaction_footer", 24);
var msg = await context.Channel.SendConfirmAsync(title,
desc, footer: footer)
.ConfigureAwait(false);
await new FlowerReactionEvent().Start(msg, context);
}
} }
} }
public abstract class CurrencyEvent public abstract class CurrencyEvent
{ {
public abstract Task Start(CommandContext channel); public abstract Task Start(IUserMessage msg, CommandContext channel);
} }
public class FlowerReactionEvent : CurrencyEvent public class FlowerReactionEvent : CurrencyEvent
{ {
public readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> _flowerReactionAwardedUsers = new ConcurrentHashSet<ulong>();
private readonly Logger _log; private readonly Logger _log;
private IUserMessage msg { get; set; } = null; private IUserMessage msg { get; set; }
private CancellationTokenSource source { get; } private CancellationTokenSource source { get; }
private CancellationToken cancelToken { get; } private CancellationToken cancelToken { get; }
@ -163,19 +166,15 @@ namespace NadekoBot.Modules.Gambling
if (msg?.Id == id) if (msg?.Id == id)
{ {
_log.Warn("Stopping flower reaction event because message is deleted."); _log.Warn("Stopping flower reaction event because message is deleted.");
Task.Run(() => End()); var __ = Task.Run(End);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
public override async Task Start(CommandContext context) public override async Task Start(IUserMessage umsg, CommandContext context)
{ {
msg = await context.Channel.SendConfirmAsync("Flower reaction event started!", msg = umsg;
"Add 🌸 reaction to this message to get 100" + NadekoBot.BotConfig.CurrencySign,
footer: "This event is active for up to 24 hours.")
.ConfigureAwait(false);
NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler; NadekoBot.Client.MessageDeleted += MessageDeletedEventHandler;
try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); } try { await msg.AddReactionAsync("🌸").ConfigureAwait(false); }
@ -194,10 +193,14 @@ namespace NadekoBot.Modules.Gambling
{ {
if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id)) if (r.Emoji.Name == "🌸" && r.User.IsSpecified && ((DateTime.UtcNow - r.User.Value.CreatedAt).TotalDays > 5) && _flowerReactionAwardedUsers.Add(r.User.Value.Id))
{ {
try { await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false).ConfigureAwait(false); } catch { } await CurrencyHandler.AddCurrencyAsync(r.User.Value, "Flower Reaction Event", 100, false)
.ConfigureAwait(false);
} }
} }
catch { } catch
{
// ignored
}
})) }))
{ {
try try

View File

@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling public partial class Gambling
{ {
[Group] [Group]
public class DriceRollCommands : ModuleBase public class DriceRollCommands : NadekoSubmodule
{ {
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled); private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", RegexOptions.Compiled);
private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled); private Regex fudgeRegex { get; } = new Regex(@"^(?<n1>\d+)d(?:F|f)$", RegexOptions.Compiled);

View File

@ -4,8 +4,6 @@ using ImageSharp;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Modules.Gambling.Models; using NadekoBot.Modules.Gambling.Models;
using NLog;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -17,17 +15,17 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling public partial class Gambling
{ {
[Group] [Group]
public class DrawCommands : ModuleBase public class DrawCommands : NadekoSubmodule
{ {
private static readonly ConcurrentDictionary<IGuild, Cards> AllDecks = new ConcurrentDictionary<IGuild, Cards>(); private static readonly ConcurrentDictionary<IGuild, Cards> _allDecks = new ConcurrentDictionary<IGuild, Cards>();
private const string cardsPath = "data/images/cards"; private const string _cardsPath = "data/images/cards";
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Draw(int num = 1) public async Task Draw(int num = 1)
{ {
var cards = AllDecks.GetOrAdd(Context.Guild, (s) => new Cards()); var cards = _allDecks.GetOrAdd(Context.Guild, (s) => new Cards());
var images = new List<Image>(); var images = new List<Image>();
var cardObjects = new List<Cards.Card>(); var cardObjects = new List<Cards.Card>();
if (num > 5) num = 5; if (num > 5) num = 5;
@ -35,12 +33,19 @@ namespace NadekoBot.Modules.Gambling
{ {
if (cards.CardPool.Count == 0 && i != 0) if (cards.CardPool.Count == 0 && i != 0)
{ {
try { await Context.Channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch { } try
{
await ReplyErrorLocalized("no_more_cards").ConfigureAwait(false);
}
catch
{
// ignored
}
break; break;
} }
var currentCard = cards.DrawACard(); var currentCard = cards.DrawACard();
cardObjects.Add(currentCard); cardObjects.Add(currentCard);
using (var stream = File.OpenRead(Path.Combine(cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_'))) using (var stream = File.OpenRead(Path.Combine(_cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
images.Add(new Image(stream)); images.Add(new Image(stream));
} }
MemoryStream bitmapStream = new MemoryStream(); MemoryStream bitmapStream = new MemoryStream();
@ -59,7 +64,7 @@ namespace NadekoBot.Modules.Gambling
{ {
//var channel = (ITextChannel)Context.Channel; //var channel = (ITextChannel)Context.Channel;
AllDecks.AddOrUpdate(Context.Guild, _allDecks.AddOrUpdate(Context.Guild,
(g) => new Cards(), (g) => new Cards(),
(g, c) => (g, c) =>
{ {
@ -67,7 +72,7 @@ namespace NadekoBot.Modules.Gambling
return c; return c;
}); });
await Context.Channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false); await ReplyConfirmLocalized("deck_reshuffled").ConfigureAwait(false);
} }
} }
} }

View File

@ -1,12 +1,10 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using ImageSharp;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Image = ImageSharp.Image; using Image = ImageSharp.Image;
@ -15,7 +13,7 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling public partial class Gambling
{ {
[Group] [Group]
public class FlipCoinCommands : ModuleBase public class FlipCoinCommands : NadekoSubmodule
{ {
private readonly IImagesService _images; private readonly IImagesService _images;
@ -24,7 +22,7 @@ namespace NadekoBot.Modules.Gambling
public FlipCoinCommands() public FlipCoinCommands()
{ {
//todo DI in the future, can't atm //todo DI in the future, can't atm
this._images = NadekoBot.Images; _images = NadekoBot.Images;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -36,21 +34,21 @@ namespace NadekoBot.Modules.Gambling
{ {
using (var heads = _images.Heads.ToStream()) using (var heads = _images.Heads.ToStream())
{ {
await Context.Channel.SendFileAsync(heads, "heads.jpg", $"{Context.User.Mention} flipped " + Format.Code("Heads") + ".").ConfigureAwait(false); await Context.Channel.SendFileAsync(heads, "heads.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("heads"))) + ".").ConfigureAwait(false);
} }
} }
else else
{ {
using (var tails = _images.Tails.ToStream()) using (var tails = _images.Tails.ToStream())
{ {
await Context.Channel.SendFileAsync(tails, "tails.jpg", $"{Context.User.Mention} flipped " + Format.Code("Tails") + ".").ConfigureAwait(false); await Context.Channel.SendFileAsync(tails, "tails.jpg", Context.User.Mention + " " + GetText("flipped", Format.Bold(GetText("tails"))) + ".").ConfigureAwait(false);
} }
} }
return; return;
} }
if (count > 10 || count < 1) if (count > 10 || count < 1)
{ {
await Context.Channel.SendErrorAsync("`Invalid number specified. You can flip 1 to 10 coins.`").ConfigureAwait(false); await ReplyErrorLocalized("flip_invalid", 10).ConfigureAwait(false);
return; return;
} }
var imgs = new Image[count]; var imgs = new Image[count];
@ -76,14 +74,13 @@ namespace NadekoBot.Modules.Gambling
if (amount < NadekoBot.BotConfig.MinimumBetAmount) if (amount < NadekoBot.BotConfig.MinimumBetAmount)
{ {
await Context.Channel.SendErrorAsync($"You can't bet less than {NadekoBot.BotConfig.MinimumBetAmount}{CurrencySign}.") await ReplyErrorLocalized("min_bet_limit", NadekoBot.BotConfig.MinimumBetAmount + CurrencySign).ConfigureAwait(false);
.ConfigureAwait(false);
return; return;
} }
var removed = await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false); var removed = await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betflip Gamble", amount, false).ConfigureAwait(false);
if (!removed) if (!removed)
{ {
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false); await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
return; return;
} }
//heads = true //heads = true
@ -91,7 +88,7 @@ namespace NadekoBot.Modules.Gambling
//todo this seems stinky, no time to look at it right now //todo this seems stinky, no time to look at it right now
var isHeads = guessStr == "HEADS" || guessStr == "H"; var isHeads = guessStr == "HEADS" || guessStr == "H";
bool result = false; var result = false;
IEnumerable<byte> imageToSend; IEnumerable<byte> imageToSend;
if (rng.Next(0, 2) == 1) if (rng.Next(0, 2) == 1)
{ {
@ -107,12 +104,12 @@ namespace NadekoBot.Modules.Gambling
if (isHeads == result) if (isHeads == result)
{ {
var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier); var toWin = (int)Math.Round(amount * NadekoBot.BotConfig.BetflipMultiplier);
str = $"{Context.User.Mention}`You guessed it!` You won {toWin}{CurrencySign}"; str = Context.User.Mention + " " + GetText("flip_guess", toWin + CurrencySign);
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betflip Gamble", toWin, false).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(Context.User, GetText("betflip_gamble"), toWin, false).ConfigureAwait(false);
} }
else else
{ {
str = $"{Context.User.Mention}`Better luck next time.`"; str = Context.User.Mention + " " + GetText("better_luck");
} }
using (var toSend = imageToSend.ToStream()) using (var toSend = imageToSend.ToStream())
{ {

View File

@ -5,7 +5,6 @@ using NadekoBot.Extensions;
using NadekoBot.Services; using NadekoBot.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -16,12 +15,12 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling public partial class Gambling
{ {
[Group] [Group]
public class Slots : ModuleBase public class Slots : NadekoSubmodule
{ {
private static int totalBet = 0; private static int _totalBet;
private static int totalPaidOut = 0; private static int _totalPaidOut;
const int alphaCutOut = byte.MaxValue / 3; private const int _alphaCutOut = byte.MaxValue / 3;
//here is a payout chart //here is a payout chart
//https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg //https://lh6.googleusercontent.com/-i1hjAJy_kN4/UswKxmhrbPI/AAAAAAAAB1U/82wq_4ZZc-Y/DE6B0895-6FC1-48BE-AC4F-14D1B91AB75B.jpg
@ -31,14 +30,14 @@ namespace NadekoBot.Modules.Gambling
public Slots() public Slots()
{ {
this._images = NadekoBot.Images; _images = NadekoBot.Images;
} }
public class SlotMachine public class SlotMachine
{ {
public const int MaxValue = 5; public const int MaxValue = 5;
static readonly List<Func<int[], int>> winningCombos = new List<Func<int[], int>>() static readonly List<Func<int[], int>> _winningCombos = new List<Func<int[], int>>()
{ {
//three flowers //three flowers
(arr) => arr.All(a=>a==MaxValue) ? 30 : 0, (arr) => arr.All(a=>a==MaxValue) ? 30 : 0,
@ -53,14 +52,14 @@ namespace NadekoBot.Modules.Gambling
public static SlotResult Pull() public static SlotResult Pull()
{ {
var numbers = new int[3]; var numbers = new int[3];
for (int i = 0; i < numbers.Length; i++) for (var i = 0; i < numbers.Length; i++)
{ {
numbers[i] = new NadekoRandom().Next(0, MaxValue + 1); numbers[i] = new NadekoRandom().Next(0, MaxValue + 1);
} }
int multi = 0; var multi = 0;
for (int i = 0; i < winningCombos.Count; i++) foreach (var t in _winningCombos)
{ {
multi = winningCombos[i](numbers); multi = t(numbers);
if (multi != 0) if (multi != 0)
break; break;
} }
@ -74,8 +73,8 @@ namespace NadekoBot.Modules.Gambling
public int Multiplier { get; } public int Multiplier { get; }
public SlotResult(int[] nums, int multi) public SlotResult(int[] nums, int multi)
{ {
this.Numbers = nums; Numbers = nums;
this.Multiplier = multi; Multiplier = multi;
} }
} }
} }
@ -85,8 +84,8 @@ namespace NadekoBot.Modules.Gambling
public async Task SlotStats() public async Task SlotStats()
{ {
//i remembered to not be a moron //i remembered to not be a moron
var paid = totalPaidOut; var paid = _totalPaidOut;
var bet = totalBet; var bet = _totalBet;
if (bet <= 0) if (bet <= 0)
bet = 1; bet = 1;
@ -130,33 +129,34 @@ namespace NadekoBot.Modules.Gambling
footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%"); footer: $"Total Bet: {tests * bet} | Payout: {payout * bet} | {payout * 1.0f / tests * 100}%");
} }
static HashSet<ulong> runningUsers = new HashSet<ulong>(); private static readonly HashSet<ulong> _runningUsers = new HashSet<ulong>();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Slot(int amount = 0) public async Task Slot(int amount = 0)
{ {
if (!runningUsers.Add(Context.User.Id)) if (!_runningUsers.Add(Context.User.Id))
return; return;
try try
{ {
if (amount < 1) if (amount < 1)
{ {
await Context.Channel.SendErrorAsync($"You can't bet less than 1{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); await ReplyErrorLocalized("min_bet_limit", 1 + CurrencySign).ConfigureAwait(false);
return; return;
} }
if (amount > 999) if (amount > 999)
{ {
await Context.Channel.SendErrorAsync($"You can't bet more than 999{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); GetText("slot_maxbet", 999 + CurrencySign);
await ReplyErrorLocalized("max_bet_limit", 999 + CurrencySign).ConfigureAwait(false);
return; return;
} }
if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false)) if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Slot Machine", amount, false))
{ {
await Context.Channel.SendErrorAsync($"You don't have enough {NadekoBot.BotConfig.CurrencySign}.").ConfigureAwait(false); await ReplyErrorLocalized("not_enough", CurrencySign).ConfigureAwait(false);
return; return;
} }
Interlocked.Add(ref totalBet, amount); Interlocked.Add(ref _totalBet, amount);
using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream()) using (var bgFileStream = NadekoBot.Images.SlotBackground.ToStream())
{ {
var bgImage = new ImageSharp.Image(bgFileStream); var bgImage = new ImageSharp.Image(bgFileStream);
@ -179,7 +179,7 @@ namespace NadekoBot.Modules.Gambling
var x = 95 + 142 * i + j; var x = 95 + 142 * i + j;
int y = 330 + k; int y = 330 + k;
var toSet = toAdd[j, k]; var toSet = toAdd[j, k];
if (toSet.A < alphaCutOut) if (toSet.A < _alphaCutOut)
continue; continue;
bgPixels[x, y] = toAdd[j, k]; bgPixels[x, y] = toAdd[j, k];
} }
@ -203,7 +203,7 @@ namespace NadekoBot.Modules.Gambling
{ {
for (int j = 0; j < pixels.Height; j++) for (int j = 0; j < pixels.Height; j++)
{ {
if (pixels[i, j].A < alphaCutOut) if (pixels[i, j].A < _alphaCutOut)
continue; continue;
var x = 230 - n * 16 + i; var x = 230 - n * 16 + i;
bgPixels[x, 462 + j] = pixels[i, j]; bgPixels[x, 462 + j] = pixels[i, j];
@ -228,7 +228,7 @@ namespace NadekoBot.Modules.Gambling
{ {
for (int j = 0; j < pixels.Height; j++) for (int j = 0; j < pixels.Height; j++)
{ {
if (pixels[i, j].A < alphaCutOut) if (pixels[i, j].A < _alphaCutOut)
continue; continue;
var x = 395 - n * 16 + i; var x = 395 - n * 16 + i;
bgPixels[x, 462 + j] = pixels[i, j]; bgPixels[x, 462 + j] = pixels[i, j];
@ -240,30 +240,30 @@ namespace NadekoBot.Modules.Gambling
} while ((printAmount /= 10) != 0); } while ((printAmount /= 10) != 0);
} }
var msg = "Better luck next time ^_^"; var msg = GetText("better_luck");
if (result.Multiplier != 0) if (result.Multiplier != 0)
{ {
await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false); await CurrencyHandler.AddCurrencyAsync(Context.User, $"Slot Machine x{result.Multiplier}", amount * result.Multiplier, false);
Interlocked.Add(ref totalPaidOut, amount * result.Multiplier); Interlocked.Add(ref _totalPaidOut, amount * result.Multiplier);
if (result.Multiplier == 1) if (result.Multiplier == 1)
msg = $"A single {NadekoBot.BotConfig.CurrencySign}, x1 - Try again!"; msg = GetText("slot_single", CurrencySign, 1);
else if (result.Multiplier == 4) else if (result.Multiplier == 4)
msg = $"Good job! Two {NadekoBot.BotConfig.CurrencySign} - bet x4"; msg = GetText("slot_two", CurrencySign, 4);
else if (result.Multiplier == 10) else if (result.Multiplier == 10)
msg = "Wow! Lucky! Three of a kind! x10"; msg = GetText("slot_three", 10);
else if (result.Multiplier == 30) else if (result.Multiplier == 30)
msg = "WOAAHHHHHH!!! Congratulations!!! x30"; msg = GetText("slot_jackpot", 30);
} }
await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`Bet:`{amount} `Won:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); await Context.Channel.SendFileAsync(bgImage.ToStream(), "result.png", Context.User.Mention + " " + msg + $"\n`{GetText("slot_bet")}:`{amount} `{GetText("slot_won")}:` {amount * result.Multiplier}{NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
} }
} }
finally finally
{ {
var t = Task.Run(async () => var _ = Task.Run(async () =>
{ {
await Task.Delay(2000); await Task.Delay(2000);
runningUsers.Remove(Context.User.Id); _runningUsers.Remove(Context.User.Id);
}); });
} }
} }

View File

@ -47,7 +47,7 @@ namespace NadekoBot.Modules.Gambling
} }
[Group] [Group]
public class WaifuClaimCommands : ModuleBase public class WaifuClaimCommands : NadekoSubmodule
{ {
private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>(); private static ConcurrentDictionary<ulong, DateTime> _divorceCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>(); private static ConcurrentDictionary<ulong, DateTime> _affinityCooldowns { get; } = new ConcurrentDictionary<ulong, DateTime>();
@ -197,8 +197,6 @@ namespace NadekoBot.Modules.Gambling
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Divorce([Remainder]IUser target) public async Task Divorce([Remainder]IUser target)
{ {
var channel = (ITextChannel)Context.Channel;
if (target.Id == Context.User.Id) if (target.Id == Context.User.Id)
return; return;
@ -423,7 +421,7 @@ namespace NadekoBot.Modules.Gambling
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle("Waifu " + w.Waifu.ToString() + " - \"the " + claimInfo.Title + "\"") .WithTitle("Waifu " + w.Waifu + " - \"the " + claimInfo.Title + "\"")
.AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true)) .AddField(efb => efb.WithName("Price").WithValue(w.Price.ToString()).WithIsInline(true))
.AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true)) .AddField(efb => efb.WithName("Claimed by").WithValue(w.Claimer?.ToString() ?? "No one").WithIsInline(true))
.AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true)) .AddField(efb => efb.WithName("Likes").WithValue(w.Affinity?.ToString() ?? "Nobody").WithIsInline(true))

View File

@ -1,9 +1,9 @@
using Discord; using System;
using Discord;
using Discord.Commands; using Discord.Commands;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using NadekoBot.Extensions; using NadekoBot.Extensions;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using NadekoBot.Services; using NadekoBot.Services;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
@ -12,7 +12,7 @@ using System.Collections.Generic;
namespace NadekoBot.Modules.Gambling namespace NadekoBot.Modules.Gambling
{ {
[NadekoModule("Gambling", "$")] [NadekoModule("Gambling", "$")]
public partial class Gambling : DiscordModule public partial class Gambling : NadekoModule
{ {
public static string CurrencyName { get; set; } public static string CurrencyName { get; set; }
public static string CurrencyPluralName { get; set; } public static string CurrencyPluralName { get; set; }
@ -42,7 +42,7 @@ namespace NadekoBot.Modules.Gambling
var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown); var members = role.Members().Where(u => u.Status != UserStatus.Offline && u.Status != UserStatus.Unknown);
var membersArray = members as IUser[] ?? members.ToArray(); var membersArray = members as IUser[] ?? members.ToArray();
var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)]; var usr = membersArray[new NadekoRandom().Next(0, membersArray.Length)];
await Context.Channel.SendConfirmAsync("🎟 Raffled user", $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false); await Context.Channel.SendConfirmAsync("🎟 "+ GetText("raffled_user"), $"**{usr.Username}#{usr.Discriminator}**", footer: $"ID: {usr.Id}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -50,15 +50,14 @@ namespace NadekoBot.Modules.Gambling
public async Task Cash([Remainder] IUser user = null) public async Task Cash([Remainder] IUser user = null)
{ {
user = user ?? Context.User; user = user ?? Context.User;
await ReplyConfirmLocalized("has", Format.Bold(user.ToString()), $"{GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{user.Username} has {GetCurrency(user.Id)} {CurrencySign}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[Priority(1)] [Priority(1)]
public async Task Cash(ulong userId) public async Task Cash(ulong userId)
{ {
await Context.Channel.SendConfirmAsync($"`{userId}` has {GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false); await ReplyConfirmLocalized("has", Format.Code(userId.ToString()), $"{GetCurrency(userId)} {CurrencySign}").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -70,11 +69,12 @@ namespace NadekoBot.Modules.Gambling
var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false); var success = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Gift to {receiver.Username} ({receiver.Id}).", amount, false).ConfigureAwait(false);
if (!success) if (!success)
{ {
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}.").ConfigureAwait(false); await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
return; return;
} }
await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(receiver, $"Gift from {Context.User.Username} ({Context.User.Id}).", amount, true).ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} gifted {amount}{CurrencySign} to {Format.Bold(receiver.ToString())}!").ConfigureAwait(false); await ReplyConfirmLocalized("gifted", amount + CurrencySign, Format.Bold(receiver.ToString()))
.ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -93,8 +93,7 @@ namespace NadekoBot.Modules.Gambling
return; return;
await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false); await CurrencyHandler.AddCurrencyAsync(usrId, $"Awarded by bot owner. ({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false);
await ReplyConfirmLocalized("awarded", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} awarded {amount}{CurrencySign} to <@{usrId}>!").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -103,7 +102,6 @@ namespace NadekoBot.Modules.Gambling
[Priority(0)] [Priority(0)]
public async Task Award(int amount, [Remainder] IRole role) public async Task Award(int amount, [Remainder] IRole role)
{ {
var channel = (ITextChannel)Context.Channel;
var users = (await Context.Guild.GetUsersAsync()) var users = (await Context.Guild.GetUsersAsync())
.Where(u => u.GetRoles().Contains(role)) .Where(u => u.GetRoles().Contains(role))
.ToList(); .ToList();
@ -112,9 +110,10 @@ namespace NadekoBot.Modules.Gambling
amount))) amount)))
.ConfigureAwait(false); .ConfigureAwait(false);
await Context.Channel.SendConfirmAsync($"Awarded `{amount}` {CurrencyPluralName} to `{users.Count}` users from `{role.Name}` role.") await ReplyConfirmLocalized("mass_award",
.ConfigureAwait(false); amount + CurrencySign,
Format.Bold(users.Count.ToString()),
Format.Bold(role.Name)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -126,9 +125,9 @@ namespace NadekoBot.Modules.Gambling
return; return;
if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false)) if (await CurrencyHandler.RemoveCurrencyAsync(user, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount, true).ConfigureAwait(false))
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user}!").ConfigureAwait(false); await ReplyConfirmLocalized("take", amount+CurrencySign, Format.Bold(user.ToString())).ConfigureAwait(false);
else else
await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from {user} because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false); await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Bold(user.ToString()), CurrencyPluralName).ConfigureAwait(false);
} }
@ -140,9 +139,9 @@ namespace NadekoBot.Modules.Gambling
return; return;
if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false)) if (await CurrencyHandler.RemoveCurrencyAsync(usrId, $"Taken by bot owner.({Context.User.Username}/{Context.User.Id})", amount).ConfigureAwait(false))
await Context.Channel.SendConfirmAsync($"{Context.User.Mention} successfully took {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from <@{usrId}>!").ConfigureAwait(false); await ReplyConfirmLocalized("take", amount + CurrencySign, $"<@{usrId}>").ConfigureAwait(false);
else else
await Context.Channel.SendErrorAsync($"{Context.User.Mention} was unable to take {amount} {(amount == 1 ? CurrencyName : CurrencyPluralName)} from `{usrId}` because the user doesn't have that much {CurrencyPluralName}!").ConfigureAwait(false); await ReplyErrorLocalized("take_fail", amount + CurrencySign, Format.Code(usrId.ToString()), CurrencyPluralName).ConfigureAwait(false);
} }
//[NadekoCommand, Usage, Description, Aliases] //[NadekoCommand, Usage, Description, Aliases]
@ -206,49 +205,48 @@ namespace NadekoBot.Modules.Gambling
if (amount < 1) if (amount < 1)
return; return;
long userFlowers; if (!await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false))
using (var uow = DbHandler.UnitOfWork())
{ {
userFlowers = uow.Currency.GetOrCreate(Context.User.Id).Amount; await ReplyErrorLocalized("not_enough", CurrencyPluralName).ConfigureAwait(false);
}
if (userFlowers < amount)
{
await Context.Channel.SendErrorAsync($"{Context.User.Mention} You don't have enough {CurrencyPluralName}. You only have {userFlowers}{CurrencySign}.").ConfigureAwait(false);
return; return;
} }
await CurrencyHandler.RemoveCurrencyAsync(Context.User, "Betroll Gamble", amount, false).ConfigureAwait(false); var rnd = new NadekoRandom().Next(0, 101);
var str = Context.User.Mention + Format.Code(GetText("roll", rnd));
var rng = new NadekoRandom().Next(0, 101); if (rnd < 67)
var str = $"{Context.User.Mention} `You rolled {rng}.` ";
if (rng < 67)
{ {
str += "Better luck next time."; str += GetText("better_luck");
}
else if (rng < 91)
{
str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll67Multiplier}{CurrencySign} for rolling above 66";
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false);
}
else if (rng < 100)
{
str += $"Congratulations! You won {amount * NadekoBot.BotConfig.Betroll91Multiplier}{CurrencySign} for rolling above 90.";
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false);
} }
else else
{ {
str += $"👑 Congratulations! You won {amount * NadekoBot.BotConfig.Betroll100Multiplier}{CurrencySign} for rolling **100**. 👑"; if (rnd < 91)
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble", (int)(amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false); {
str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll67Multiplier) + CurrencySign, 66);
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
(int) (amount * NadekoBot.BotConfig.Betroll67Multiplier), false).ConfigureAwait(false);
}
else if (rnd < 100)
{
str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll91Multiplier) + CurrencySign, 90);
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
(int) (amount * NadekoBot.BotConfig.Betroll91Multiplier), false).ConfigureAwait(false);
}
else
{
str += GetText("br_win", (amount * NadekoBot.BotConfig.Betroll100Multiplier) + CurrencySign, 100) + " 👑";
await CurrencyHandler.AddCurrencyAsync(Context.User, "Betroll Gamble",
(int) (amount * NadekoBot.BotConfig.Betroll100Multiplier), false).ConfigureAwait(false);
}
} }
Console.WriteLine("started sending");
await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(str).ConfigureAwait(false);
Console.WriteLine("done sending");
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Leaderboard() public async Task Leaderboard()
{ {
var richest = new List<Currency>(); List<Currency> richest;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
richest = uow.Currency.GetTopRichest(9).ToList(); richest = uow.Currency.GetTopRichest(9).ToList();
@ -256,22 +254,22 @@ namespace NadekoBot.Modules.Gambling
if (!richest.Any()) if (!richest.Any())
return; return;
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.WithOkColor() .WithOkColor()
.WithTitle(NadekoBot.BotConfig.CurrencySign + " Leaderboard"); .WithTitle(NadekoBot.BotConfig.CurrencySign + " " + GetText("leaderboard"));
for (var i = 0; i < richest.Count; i++) for (var i = 0; i < richest.Count; i++)
{ {
var x = richest[i]; var x = richest[i];
var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false); var usr = await Context.Guild.GetUserAsync(x.UserId).ConfigureAwait(false);
var usrStr = ""; var usrStr = usr == null
if (usr == null) ? x.UserId.ToString()
usrStr = x.UserId.ToString(); : usr.Username?.TrimTo(20, true);
else
usrStr = usr.Username?.TrimTo(20, true);
embed.AddField(efb => efb.WithName("#" + (i + 1) + " " + usrStr).WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign).WithIsInline(true)); var j = i;
embed.AddField(efb => efb.WithName("#" + (j + 1) + " " + usrStr)
.WithValue(x.Amount.ToString() + " " + NadekoBot.BotConfig.CurrencySign)
.WithIsInline(true));
} }
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);

View File

@ -21,12 +21,6 @@ namespace NadekoBot.Modules.Games
{ {
private static Logger _log { get; } private static Logger _log { get; }
class CleverAnswer
{
public string Status { get; set; }
public string Response { get; set; }
}
public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(); public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>();
static CleverBotCommands() static CleverBotCommands()
@ -34,14 +28,12 @@ namespace NadekoBot.Modules.Games
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
var sw = Stopwatch.StartNew(); var sw = Stopwatch.StartNew();
using (var uow = DbHandler.UnitOfWork())
{ var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT);
var bot = ChatterBotFactory.Create(ChatterBotType.CLEVERBOT); CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>(
CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>( NadekoBot.AllGuildConfigs
NadekoBot.AllGuildConfigs .Where(gc => gc.CleverbotEnabled)
.Where(gc => gc.CleverbotEnabled) .ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
.ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => bot.CreateSession(), true)));
}
sw.Stop(); sw.Stop();
_log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s");

View File

@ -23,7 +23,8 @@ namespace NadekoBot.Modules.Games
static HangmanCommands() static HangmanCommands()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
typesStr = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Games).Name]}hangman\" term types:`\n" + String.Join(", ", HangmanTermPool.data.Keys); typesStr =
string.Format("`List of \"{0}hangman\" term types:`\n", NadekoBot.ModulePrefixes[typeof(Games).Name]) + String.Join(", ", HangmanTermPool.data.Keys);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Games
/// https://discord.gg/0TYNJfCU4De7YIk8 /// https://discord.gg/0TYNJfCU4De7YIk8
/// </summary> /// </summary>
[Group] [Group]
public class PlantPickCommands : ModuleBase public class PlantPickCommands : NadekoSubmodule
{ {
private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>(); private static ConcurrentHashSet<ulong> generationChannels { get; } = new ConcurrentHashSet<ulong>();
//channelid/message //channelid/message
@ -37,13 +37,8 @@ namespace NadekoBot.Modules.Games
//channelId/last generation //channelId/last generation
private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>(); private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>();
private static ConcurrentHashSet<ulong> usersRecentlyPicked { get; } = new ConcurrentHashSet<ulong>();
private static Logger _log { get; }
static PlantPickCommands() static PlantPickCommands()
{ {
_log = LogManager.GetCurrentClassLogger();
#if !GLOBAL_NADEKO #if !GLOBAL_NADEKO
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
@ -103,7 +98,8 @@ namespace NadekoBot.Modules.Games
var sent = await channel.SendFileAsync( var sent = await channel.SendFileAsync(
fileStream, fileStream,
file.Key, file.Key,
$"❗ {firstPart} Pick it up by typing `{NadekoBot.ModulePrefixes[typeof(Games).Name]}pick`") string.Format("❗ {0} Pick it up by typing `{1}pick`", firstPart,
NadekoBot.ModulePrefixes[typeof(Games).Name]))
.ConfigureAwait(false); .ConfigureAwait(false);
msgs[0] = sent; msgs[0] = sent;
@ -115,7 +111,7 @@ namespace NadekoBot.Modules.Games
} }
catch (Exception ex) catch (Exception ex)
{ {
_log.Warn(ex); LogManager.GetCurrentClassLogger().Warn(ex);
} }
}); });
return Task.CompletedTask; return Task.CompletedTask;
@ -129,32 +125,18 @@ namespace NadekoBot.Modules.Games
if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages) if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages)
return; return;
#if GLOBAL_NADEKO
if (!usersRecentlyPicked.Add(Context.User.Id)) List<IUserMessage> msgs;
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { }
if (!plantedFlowers.TryRemove(channel.Id, out msgs))
return; return;
#endif
try
{
List<IUserMessage> msgs; await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { } await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
if (!plantedFlowers.TryRemove(channel.Id, out msgs)) var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false);
return; msg.DeleteAfter(10);
await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false);
await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false);
var msg = await channel.SendConfirmAsync($"**{Context.User}** picked {msgs.Count}{NadekoBot.BotConfig.CurrencySign}!").ConfigureAwait(false);
msg.DeleteAfter(10);
}
finally
{
#if GLOBAL_NADEKO
await Task.Delay(60000);
usersRecentlyPicked.TryRemove(Context.User.Id);
#endif
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -174,7 +156,7 @@ namespace NadekoBot.Modules.Games
var imgData = GetRandomCurrencyImage(); var imgData = GetRandomCurrencyImage();
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]); var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.BotConfig.CurrencyName[0]);
var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {NadekoBot.ModulePrefixes[typeof(Games).Name]}pick"; var msgToSend = $"Oh how Nice! **{Context.User.Username}** planted {(amount == 1 ? (vowelFirst ? "an" : "a") : amount.ToString())} {(amount > 1 ? NadekoBot.BotConfig.CurrencyPluralName : NadekoBot.BotConfig.CurrencyName)}. Pick it using {Prefix}pick";
IUserMessage msg; IUserMessage msg;
using (var toSend = imgData.Value.ToStream()) using (var toSend = imgData.Value.ToStream())
@ -236,17 +218,6 @@ namespace NadekoBot.Modules.Games
return images[rng.Next(0, images.Length)]; return images[rng.Next(0, images.Length)];
} }
int GetRandomNumber()
{
using (var rg = RandomNumberGenerator.Create())
{
byte[] rno = new byte[4];
rg.GetBytes(rno);
int randomvalue = BitConverter.ToInt32(rno, 0);
return randomvalue;
}
}
} }
} }
} }

View File

@ -307,7 +307,7 @@ namespace NadekoBot.Modules.Games
var del2 = previousMessage?.DeleteAsync(); var del2 = previousMessage?.DeleteAsync();
try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { } try { previousMessage = await _channel.EmbedAsync(GetEmbed(reason)); } catch { }
try { await del1; } catch { } try { await del1; } catch { }
try { await del2; } catch { } try { if (del2 != null) await del2; } catch { }
}); });
curUserIndex ^= 1; curUserIndex ^= 1;

View File

@ -11,7 +11,7 @@ using NadekoBot.Extensions;
namespace NadekoBot.Modules.Games namespace NadekoBot.Modules.Games
{ {
[NadekoModule("Games", ">")] [NadekoModule("Games", ">")]
public partial class Games : DiscordModule public partial class Games : NadekoModule
{ {
private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray(); private static string[] _8BallResponses { get; } = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToArray();
@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Games
{ {
if (string.IsNullOrWhiteSpace(question)) if (string.IsNullOrWhiteSpace(question))
return; return;
var rng = new NadekoRandom();
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
.AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false)) .AddField(efb => efb.WithName("❓ Question").WithValue(question).WithIsInline(false))

View File

@ -13,19 +13,26 @@ using System.Collections.Generic;
namespace NadekoBot.Modules.Help namespace NadekoBot.Modules.Help
{ {
[NadekoModule("Help", "-")] [NadekoModule("Help", "-")]
public partial class Help : DiscordModule public class Help : NadekoModule
{ {
private static string helpString { get; } = NadekoBot.BotConfig.HelpString; private static string helpString { get; } = NadekoBot.BotConfig.HelpString;
public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]); public static string HelpString => String.Format(helpString, NadekoBot.Credentials.ClientId, NadekoBot.ModulePrefixes[typeof(Help).Name]);
public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString; public static string DMHelpString { get; } = NadekoBot.BotConfig.DMHelpString;
public const string PatreonUrl = "https://patreon.com/nadekobot";
public const string PaypalUrl = "https://paypal.me/Kwoth";
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Modules() public async Task Modules()
{ {
var embed = new EmbedBuilder().WithOkColor()
var embed = new EmbedBuilder().WithOkColor().WithFooter(efb => efb.WithText($" Type `-cmds ModuleName` to get a list of commands in that module. eg `-cmds games`")) .WithFooter(efb => efb.WithText("" + GetText("modules_footer", Prefix)))
.WithTitle("📜 List Of Modules").WithDescription("\n• " + string.Join("\n• ", NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule()).Select(m => m.Key.Name).OrderBy(s => s))); .WithTitle(GetText("list_of_modules"))
.WithDescription(string.Join("\n",
NadekoBot.CommandService.Modules.GroupBy(m => m.GetTopLevelModule())
.Select(m => "• " + m.Key.Name)
.OrderBy(s => s)));
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
} }
@ -45,18 +52,13 @@ namespace NadekoBot.Modules.Help
var cmdsArray = cmds as CommandInfo[] ?? cmds.ToArray(); var cmdsArray = cmds as CommandInfo[] ?? cmds.ToArray();
if (!cmdsArray.Any()) if (!cmdsArray.Any())
{ {
await channel.SendErrorAsync("That module does not exist.").ConfigureAwait(false); await ReplyErrorLocalized("module_not_found").ConfigureAwait(false);
return; return;
} }
if (module != "customreactions" && module != "conversations")
{ await channel.SendTableAsync($"📃 **{GetText("list_of_commands")}**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
await channel.SendTableAsync("📃 **List Of Commands:**\n", cmdsArray, el => $"{el.Aliases.First(),-15} {"["+el.Aliases.Skip(1).FirstOrDefault()+"]",-8}").ConfigureAwait(false);
} await ConfirmLocalized("commands_instr", Prefix).ConfigureAwait(false);
else
{
await channel.SendMessageAsync("📃 **List Of Commands:**\n• " + string.Join("\n• ", cmdsArray.Select(c => $"{c.Aliases.First()}")));
}
await channel.SendConfirmAsync($" **Type** `\"{NadekoBot.ModulePrefixes[typeof(Help).Name]}h CommandName\"` **to see the help for that specified command.** ***e.g.*** `-h >8ball`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -75,32 +77,33 @@ namespace NadekoBot.Modules.Help
if (com == null) if (com == null)
{ {
await channel.SendErrorAsync("I can't find that command. Please check the **command** and **command prefix** before trying again."); await ReplyErrorLocalized("command_not_found").ConfigureAwait(false);
return; return;
} }
var str = $"**`{com.Aliases.First()}`**"; var str = string.Format("**`{0}`**", com.Aliases.First());
var alias = com.Aliases.Skip(1).FirstOrDefault(); var alias = com.Aliases.Skip(1).FirstOrDefault();
if (alias != null) if (alias != null)
str += $" **/ `{alias}`**"; str += string.Format(" **/ `{0}`**", alias);
var embed = new EmbedBuilder() var embed = new EmbedBuilder()
.AddField(fb => fb.WithName(str).WithValue($"{ string.Format(com.Summary, com.Module.Aliases.First())} { GetCommandRequirements(com)}").WithIsInline(true)) .AddField(fb => fb.WithName(str).WithValue($"{string.Format(com.Summary, com.Module.Aliases.First())} {GetCommandRequirements(com)}").WithIsInline(true))
.AddField(fb => fb.WithName("**Usage**").WithValue($"{string.Format(com.Remarks, com.Module.Aliases.First())}").WithIsInline(false)) .AddField(fb => fb.WithName(GetText("usage")).WithValue(string.Format(com.Remarks, com.Module.Aliases.First())).WithIsInline(false))
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
await channel.EmbedAsync(embed).ConfigureAwait(false); await channel.EmbedAsync(embed).ConfigureAwait(false);
} }
private string GetCommandRequirements(CommandInfo cmd) => private string GetCommandRequirements(CommandInfo cmd) =>
String.Join(" ", cmd.Preconditions string.Join(" ", cmd.Preconditions
.Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute) .Where(ca => ca is OwnerOnlyAttribute || ca is RequireUserPermissionAttribute)
.Select(ca => .Select(ca =>
{ {
if (ca is OwnerOnlyAttribute) if (ca is OwnerOnlyAttribute)
return "**Bot Owner only.**"; return Format.Bold(GetText("bot_owner_only"));
var cau = (RequireUserPermissionAttribute)ca; var cau = (RequireUserPermissionAttribute)ca;
if (cau.GuildPermission != null) if (cau.GuildPermission != null)
return $"**Requires {cau.GuildPermission} server permission.**".Replace("Guild", "Server"); return Format.Bold(GetText("server_permission", cau.GuildPermission))
else .Replace("Guild", "Server");
return $"**Requires {cau.ChannelPermission} channel permission.**".Replace("Guild", "Server"); return Format.Bold(GetText("channel_permission", cau.ChannelPermission))
.Replace("Guild", "Server");
})); }));
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -109,14 +112,14 @@ namespace NadekoBot.Modules.Help
public async Task Hgit() public async Task Hgit()
{ {
var helpstr = new StringBuilder(); var helpstr = new StringBuilder();
helpstr.AppendLine("You can support the project on patreon: <https://patreon.com/nadekobot> or paypal: <https://www.paypal.me/Kwoth>\n"); helpstr.AppendLine(GetText("cmdlist_donate", PatreonUrl, PaypalUrl) + "\n");
helpstr.AppendLine("##Table Of Contents"); helpstr.AppendLine("##"+ GetText("table_of_contents"));
helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help") helpstr.AppendLine(string.Join("\n", NadekoBot.CommandService.Modules.Where(m => m.GetTopLevelModule().Name.ToLowerInvariant() != "help")
.Select(m => m.GetTopLevelModule().Name) .Select(m => m.GetTopLevelModule().Name)
.Distinct() .Distinct()
.OrderBy(m => m) .OrderBy(m => m)
.Prepend("Help") .Prepend("Help")
.Select(m => $"- [{m}](#{m.ToLowerInvariant()})"))); .Select(m => string.Format("- [{0}](#{1})", m, m.ToLowerInvariant()))));
helpstr.AppendLine(); helpstr.AppendLine();
string lastModule = null; string lastModule = null;
foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First())) foreach (var com in NadekoBot.CommandService.Commands.OrderBy(com => com.Module.GetTopLevelModule().Name).GroupBy(c => c.Aliases.First()).Select(g => g.First()))
@ -127,44 +130,35 @@ namespace NadekoBot.Modules.Help
if (lastModule != null) if (lastModule != null)
{ {
helpstr.AppendLine(); helpstr.AppendLine();
helpstr.AppendLine("###### [Back to TOC](#table-of-contents)"); helpstr.AppendLine($"###### [{GetText("back_to_toc")}](#{GetText("table_of_contents").ToLowerInvariant().Replace(' ', '-')})");
} }
helpstr.AppendLine(); helpstr.AppendLine();
helpstr.AppendLine("### " + module.Name + " "); helpstr.AppendLine("### " + module.Name + " ");
helpstr.AppendLine("Command and aliases | Description | Usage"); helpstr.AppendLine($"{GetText("cmd_and_alias")} | {GetText("desc")} | {GetText("usage")}");
helpstr.AppendLine("----------------|--------------|-------"); helpstr.AppendLine("----------------|--------------|-------");
lastModule = module.Name; lastModule = module.Name;
} }
helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} | {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} | {string.Format(com.Remarks, com.Module.GetPrefix())}"); helpstr.AppendLine($"{string.Join(" ", com.Aliases.Select(a => "`" + a + "`"))} |" +
$" {string.Format(com.Summary, com.Module.GetPrefix())} {GetCommandRequirements(com)} |" +
$" {string.Format(com.Remarks, com.Module.GetPrefix())}");
} }
helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName"); helpstr = helpstr.Replace(NadekoBot.Client.CurrentUser.Username , "@BotName");
File.WriteAllText("../../docs/Commands List.md", helpstr.ToString()); File.WriteAllText("../../docs/Commands List.md", helpstr.ToString());
await Context.Channel.SendConfirmAsync("Commandlist Regenerated").ConfigureAwait(false); await ReplyConfirmLocalized("commandlist_regen").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Guide() public async Task Guide()
{ {
var channel = (ITextChannel)Context.Channel; await ConfirmLocalized("guide",
"http://nadekobot.readthedocs.io/en/latest/Commands%20List/",
await channel.SendConfirmAsync( "http://nadekobot.readthedocs.io/en/latest/").ConfigureAwait(false);
@"**LIST OF COMMANDS**: <http://nadekobot.readthedocs.io/en/latest/Commands%20List/>
**Hosting Guides and docs can be found here**: <http://nadekobot.readthedocs.io/en/latest/>").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task Donate() public async Task Donate()
{ {
var channel = (ITextChannel)Context.Channel; await ReplyConfirmLocalized("donate", PatreonUrl, PaypalUrl).ConfigureAwait(false);
await channel.SendConfirmAsync(
$@"You can support the NadekoBot project on patreon. <https://patreon.com/nadekobot> or
Paypal <https://paypal.me/Kwoth>
Don't forget to leave your discord name or id in the message.
**Thank you** ").ConfigureAwait(false);
} }
} }

View File

@ -21,7 +21,7 @@ namespace NadekoBot.Modules.Music
{ {
[NadekoModule("Music", "!!")] [NadekoModule("Music", "!!")]
[DontAutoLoad] [DontAutoLoad]
public partial class Music : DiscordModule public partial class Music : NadekoModule
{ {
public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>(); public static ConcurrentDictionary<ulong, MusicPlayer> MusicPlayers { get; } = new ConcurrentDictionary<ulong, MusicPlayer>();
@ -415,27 +415,26 @@ namespace NadekoBot.Modules.Music
var arg = directory; var arg = directory;
if (string.IsNullOrWhiteSpace(arg)) if (string.IsNullOrWhiteSpace(arg))
return; return;
try var dir = new DirectoryInfo(arg);
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
var gusr = (IGuildUser)Context.User;
foreach (var file in fileEnum)
{ {
var dir = new DirectoryInfo(arg); try
var fileEnum = dir.GetFiles("*", SearchOption.AllDirectories)
.Where(x => !x.Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System));
var gusr = (IGuildUser)Context.User;
foreach (var file in fileEnum)
{ {
try await QueueSong(gusr, (ITextChannel)Context.Channel, gusr.VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false);
{ }
await QueueSong(((IGuildUser)Context.User), (ITextChannel)Context.Channel, ((IGuildUser)Context.User).VoiceChannel, file.FullName, true, MusicType.Local).ConfigureAwait(false); catch (PlaylistFullException)
} {
catch (PlaylistFullException) break;
{ }
break; catch
} {
catch { } // ignored
} }
await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
} }
catch { } await Context.Channel.SendConfirmAsync("🎵 Directory queue complete.").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -17,11 +17,11 @@ using System.Collections.Concurrent;
namespace NadekoBot.Modules.NSFW namespace NadekoBot.Modules.NSFW
{ {
[NadekoModule("NSFW", "~")] [NadekoModule("NSFW", "~")]
public class NSFW : DiscordModule public class NSFW : NadekoModule
{ {
private static ConcurrentDictionary<ulong, Timer> AutoHentaiTimers { get; } = new ConcurrentDictionary<ulong, Timer>(); private static readonly ConcurrentDictionary<ulong, Timer> AutoHentaiTimers = new ConcurrentDictionary<ulong, Timer>();
private static ConcurrentHashSet<ulong> _hentaiBombBlacklist { get; } = new ConcurrentHashSet<ulong>(); private static readonly ConcurrentHashSet<ulong> HentaiBombBlacklist = new ConcurrentHashSet<ulong>();
private async Task InternalHentai(IMessageChannel channel, string tag, bool noError) private async Task InternalHentai(IMessageChannel channel, string tag, bool noError)
{ {
@ -30,7 +30,7 @@ namespace NadekoBot.Modules.NSFW
tag = "rating%3Aexplicit+" + tag; tag = "rating%3Aexplicit+" + tag;
var rng = new NadekoRandom(); var rng = new NadekoRandom();
Task<string> provider = Task.FromResult(""); var provider = Task.FromResult("");
switch (rng.Next(0, 4)) switch (rng.Next(0, 4))
{ {
case 0: case 0:
@ -45,20 +45,18 @@ namespace NadekoBot.Modules.NSFW
case 3: case 3:
provider = GetYandereImageLink(tag); provider = GetYandereImageLink(tag);
break; break;
default:
break;
} }
var link = await provider.ConfigureAwait(false); var link = await provider.ConfigureAwait(false);
if (string.IsNullOrWhiteSpace(link)) if (string.IsNullOrWhiteSpace(link))
{ {
if (!noError) if (!noError)
await channel.SendErrorAsync("No results found.").ConfigureAwait(false); await ReplyErrorLocalized("not_found").ConfigureAwait(false);
return; return;
} }
await channel.EmbedAsync(new EmbedBuilder().WithOkColor() await channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithImageUrl(link) .WithImageUrl(link)
.WithDescription("Tag: " + tag)) .WithDescription($"{GetText("tag")}: " + tag))
.ConfigureAwait(false); .ConfigureAwait(false);
} }
@ -74,11 +72,10 @@ namespace NadekoBot.Modules.NSFW
if (interval == 0) if (interval == 0)
{ {
if (AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) if (!AutoHentaiTimers.TryRemove(Context.Channel.Id, out t)) return;
{
t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer t.Change(Timeout.Infinite, Timeout.Infinite); //proper way to disable the timer
await Context.Channel.SendConfirmAsync("Autohentai stopped.").ConfigureAwait(false); await ReplyConfirmLocalized("autohentai_stopped").ConfigureAwait(false);
}
return; return;
} }
@ -96,7 +93,10 @@ namespace NadekoBot.Modules.NSFW
else else
await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false); await InternalHentai(Context.Channel, tagsArr[new NadekoRandom().Next(0, tagsArr.Length)], true).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
}, null, interval * 1000, interval * 1000); }, null, interval * 1000, interval * 1000);
AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) => AutoHentaiTimers.AddOrUpdate(Context.Channel.Id, t, (key, old) =>
@ -105,15 +105,16 @@ namespace NadekoBot.Modules.NSFW
return t; return t;
}); });
await Context.Channel.SendConfirmAsync($"Autohentai started. Reposting every {interval}s with one of the following tags:\n{string.Join(", ", tagsArr)}") await ReplyConfirmLocalized("autohentai_started",
.ConfigureAwait(false); interval,
string.Join(", ", tagsArr)).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task HentaiBomb([Remainder] string tag = null) public async Task HentaiBomb([Remainder] string tag = null)
{ {
if (!_hentaiBombBlacklist.Add(Context.User.Id)) if (!HentaiBombBlacklist.Add(Context.User.Id))
return; return;
try try
{ {
@ -125,19 +126,19 @@ namespace NadekoBot.Modules.NSFW
GetKonachanImageLink(tag), GetKonachanImageLink(tag),
GetYandereImageLink(tag)).ConfigureAwait(false); GetYandereImageLink(tag)).ConfigureAwait(false);
var linksEnum = links?.Where(l => l != null); var linksEnum = links?.Where(l => l != null).ToArray();
if (links == null || !linksEnum.Any()) if (links == null || !linksEnum.Any())
{ {
await Context.Channel.SendErrorAsync("No results found.").ConfigureAwait(false); await ReplyErrorLocalized("not_found").ConfigureAwait(false);
return; return;
} }
await Context.Channel.SendMessageAsync(String.Join("\n\n", linksEnum)).ConfigureAwait(false); await Context.Channel.SendMessageAsync(string.Join("\n\n", linksEnum)).ConfigureAwait(false);
} }
finally finally
{ {
await Task.Delay(5000).ConfigureAwait(false); await Task.Delay(5000).ConfigureAwait(false);
_hentaiBombBlacklist.TryRemove(Context.User.Id); HentaiBombBlacklist.TryRemove(Context.User.Id);
} }
} }
#endif #endif
@ -157,7 +158,7 @@ namespace NadekoBot.Modules.NSFW
var url = await GetE621ImageLink(tag).ConfigureAwait(false); var url = await GetE621ImageLink(tag).ConfigureAwait(false);
if (url == null) if (url == null)
await Context.Channel.SendErrorAsync(Context.User.Mention + " No results."); await ReplyErrorLocalized("not_found").ConfigureAwait(false);
else else
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(Context.User.Mention + " " + tag) .WithDescription(Context.User.Mention + " " + tag)
@ -178,7 +179,7 @@ namespace NadekoBot.Modules.NSFW
var url = await GetDanbooruImageLink(tag).ConfigureAwait(false); var url = await GetDanbooruImageLink(tag).ConfigureAwait(false);
if (url == null) if (url == null)
await Context.Channel.SendErrorAsync(Context.User.Mention + " No results.").ConfigureAwait(false); await ReplyErrorLocalized("not_found").ConfigureAwait(false);
else else
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()
.WithDescription(Context.User.Mention + " " + tag) .WithDescription(Context.User.Mention + " " + tag)
@ -227,9 +228,9 @@ namespace NadekoBot.Modules.NSFW
JToken obj; JToken obj;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{ new NadekoRandom().Next(0, 10330) }").ConfigureAwait(false))[0]; obj = JArray.Parse(await http.GetStringAsync($"http://api.oboobs.ru/boobs/{new NadekoRandom().Next(0, 10330)}").ConfigureAwait(false))[0];
} }
await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); await Context.Channel.SendMessageAsync($"http://media.oboobs.ru/{obj["preview"]}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -245,9 +246,9 @@ namespace NadekoBot.Modules.NSFW
JToken obj; JToken obj;
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{ new NadekoRandom().Next(0, 4335) }").ConfigureAwait(false))[0]; obj = JArray.Parse(await http.GetStringAsync($"http://api.obutts.ru/butts/{new NadekoRandom().Next(0, 4335)}").ConfigureAwait(false))[0];
} }
await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{ obj["preview"].ToString() }").ConfigureAwait(false); await Context.Channel.SendMessageAsync($"http://media.obutts.ru/{obj["preview"]}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -0,0 +1,129 @@
using Discord;
using Discord.Commands;
using NadekoBot.Extensions;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.Threading.Tasks;
namespace NadekoBot.Modules
{
public abstract class NadekoModule : ModuleBase
{
protected readonly Logger _log;
protected CultureInfo _cultureInfo;
public readonly string Prefix;
public readonly string ModuleTypeName;
public readonly string LowerModuleTypeName;
protected NadekoModule(bool isTopLevelModule = true)
{
//if it's top level module
ModuleTypeName = isTopLevelModule ? this.GetType().Name : this.GetType().DeclaringType.Name;
LowerModuleTypeName = ModuleTypeName.ToLowerInvariant();
if (!NadekoBot.ModulePrefixes.TryGetValue(ModuleTypeName, out Prefix))
Prefix = "?err?";
_log = LogManager.GetCurrentClassLogger();
}
protected override void BeforeExecute()
{
_cultureInfo = NadekoBot.Localization.GetCultureInfo(Context.Guild?.Id);
_log.Warn("Culture info is {0}", _cultureInfo);
}
//public Task<IUserMessage> ReplyConfirmLocalized(string titleKey, string textKey, string url = null, string footer = null)
//{
// var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo);
// var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
// return Context.Channel.SendConfirmAsync(title, text, url, footer);
//}
//public Task<IUserMessage> ReplyConfirmLocalized(string textKey)
//{
// var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
// return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + textKey);
//}
//public Task<IUserMessage> ReplyErrorLocalized(string titleKey, string textKey, string url = null, string footer = null)
//{
// var title = NadekoBot.ResponsesResourceManager.GetString(titleKey, cultureInfo);
// var text = NadekoBot.ResponsesResourceManager.GetString(textKey, cultureInfo);
// return Context.Channel.SendErrorAsync(title, text, url, footer);
//}
/// <summary>
/// Used as failsafe in case response key doesn't exist in the selected or default language.
/// </summary>
private static readonly CultureInfo _usCultureInfo = new CultureInfo("en-US");
public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName)
{
var text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, cultureInfo);
if (string.IsNullOrWhiteSpace(text))
{
LogManager.GetCurrentClassLogger().Warn(lowerModuleTypeName + "_" + key + " key is missing from " + cultureInfo + " response strings. PLEASE REPORT THIS.");
text = NadekoBot.ResponsesResourceManager.GetString(lowerModuleTypeName + "_" + key, _usCultureInfo) ?? $"Error: dkey {lowerModuleTypeName + "_" + key} not found!";
if (string.IsNullOrWhiteSpace(text))
return "I cant tell if you command is executed, because there was an error printing out the response. Key '" +
lowerModuleTypeName + "_" + key + "' " + "is missing from resources. Please report this.";
}
return text;
}
public static string GetTextStatic(string key, CultureInfo cultureInfo, string lowerModuleTypeName,
params object[] replacements)
{
try
{
return string.Format(GetTextStatic(key, cultureInfo, lowerModuleTypeName), replacements);
}
catch (FormatException)
{
return "I cant tell if you command is executed, because there was an error printing out the response. Key '" +
lowerModuleTypeName + "_" + key + "' " + "is not properly formatted. Please report this.";
}
}
protected string GetText(string key) =>
GetTextStatic(key, _cultureInfo, LowerModuleTypeName);
protected string GetText(string key, params object[] replacements) =>
GetTextStatic(key, _cultureInfo, LowerModuleTypeName, replacements);
public Task<IUserMessage> ErrorLocalized(string textKey, params object[] replacements)
{
var text = GetText(textKey, replacements);
return Context.Channel.SendErrorAsync(text);
}
public Task<IUserMessage> ReplyErrorLocalized(string textKey, params object[] replacements)
{
var text = GetText(textKey, replacements);
return Context.Channel.SendErrorAsync(Context.User.Mention + " " + text);
}
public Task<IUserMessage> ConfirmLocalized(string textKey, params object[] replacements)
{
var text = GetText(textKey, replacements);
return Context.Channel.SendConfirmAsync(text);
}
public Task<IUserMessage> ReplyConfirmLocalized(string textKey, params object[] replacements)
{
var text = GetText(textKey, replacements);
return Context.Channel.SendConfirmAsync(Context.User.Mention + " " + text);
}
}
public abstract class NadekoSubmodule : NadekoModule
{
protected NadekoSubmodule() : base(false)
{
}
}
}

View File

@ -0,0 +1,15 @@
using Discord;
using NadekoBot.Extensions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules
{
public static class NadekoModuleExtensions
{
}
}

View File

@ -101,23 +101,23 @@ namespace NadekoBot.Modules.Permissions
{ {
return true; return true;
} }
else activeCdsForGuild.Add(new ActiveCooldown()
{ {
activeCdsForGuild.Add(new ActiveCooldown() UserId = user.Id,
Command = cmd.Aliases.First().ToLowerInvariant(),
});
var _ = Task.Run(async () =>
{
try
{ {
UserId = user.Id, await Task.Delay(cdRule.Seconds * 1000);
Command = cmd.Aliases.First().ToLowerInvariant(), activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
}); }
var t = Task.Run(async () => catch
{ {
try // ignored
{ }
await Task.Delay(cdRule.Seconds * 1000); });
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
}
catch { }
});
}
} }
return false; return false;
} }

View File

@ -42,23 +42,19 @@ namespace NadekoBot.Modules.Permissions
static FilterCommands() static FilterCommands()
{ {
using (var uow = DbHandler.UnitOfWork()) var guildConfigs = NadekoBot.AllGuildConfigs;
{
var guildConfigs = NadekoBot.AllGuildConfigs;
InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId)); InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId))); InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word))); var dict = guildConfigs.ToDictionary(gc => gc.GuildId, gc => new ConcurrentHashSet<string>(gc.FilteredWords.Select(fw => fw.Word)));
ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict); ServerFilteredWords = new ConcurrentDictionary<ulong, ConcurrentHashSet<string>>(dict);
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords); var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId)); WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId))); WordFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterWordsChannelIds.Select(fwci => fwci.ChannelId)));
}
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -15,7 +15,7 @@ using NLog;
namespace NadekoBot.Modules.Permissions namespace NadekoBot.Modules.Permissions
{ {
[NadekoModule("Permissions", ";")] [NadekoModule("Permissions", ";")]
public partial class Permissions : DiscordModule public partial class Permissions : NadekoModule
{ {
public class PermissionCache public class PermissionCache
{ {

View File

@ -16,21 +16,21 @@ using System.Collections.Concurrent;
namespace NadekoBot.Modules.Pokemon namespace NadekoBot.Modules.Pokemon
{ {
[NadekoModule("Pokemon", ">")] [NadekoModule("Pokemon", ">")]
public partial class Pokemon : DiscordModule public class Pokemon : NadekoModule
{ {
private static List<PokemonType> PokemonTypes = new List<PokemonType>(); private static readonly List<PokemonType> _pokemonTypes = new List<PokemonType>();
private static ConcurrentDictionary<ulong, PokeStats> Stats = new ConcurrentDictionary<ulong, PokeStats>(); private static readonly ConcurrentDictionary<ulong, PokeStats> _stats = new ConcurrentDictionary<ulong, PokeStats>();
public const string PokemonTypesFile = "data/pokemon_types.json"; public const string PokemonTypesFile = "data/pokemon_types.json";
private static new Logger _log { get; } private new static Logger _log { get; }
static Pokemon() static Pokemon()
{ {
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
if (File.Exists(PokemonTypesFile)) if (File.Exists(PokemonTypesFile))
{ {
PokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile)); _pokemonTypes = JsonConvert.DeserializeObject<List<PokemonType>>(File.ReadAllText(PokemonTypesFile));
} }
else else
{ {
@ -42,21 +42,18 @@ namespace NadekoBot.Modules.Pokemon
private int GetDamage(PokemonType usertype, PokemonType targetType) private int GetDamage(PokemonType usertype, PokemonType targetType)
{ {
var rng = new Random(); var rng = new Random();
int damage = rng.Next(40, 60); var damage = rng.Next(40, 60);
foreach (PokemonMultiplier Multiplier in usertype.Multipliers) foreach (var multiplierObj in usertype.Multipliers)
{ {
if (Multiplier.Type == targetType.Name) if (multiplierObj.Type != targetType.Name) continue;
{ damage = (int)(damage * multiplierObj.Multiplication);
var multiplier = Multiplier.Multiplication;
damage = (int)(damage * multiplier);
}
} }
return damage; return damage;
} }
private PokemonType GetPokeType(ulong id) private static PokemonType GetPokeType(ulong id)
{ {
Dictionary<ulong, string> setTypes; Dictionary<ulong, string> setTypes;
@ -69,20 +66,18 @@ namespace NadekoBot.Modules.Pokemon
{ {
return StringToPokemonType(setTypes[id]); return StringToPokemonType(setTypes[id]);
} }
int count = PokemonTypes.Count; var count = _pokemonTypes.Count;
int remainder = Math.Abs((int)(id % (ulong)count)); var remainder = Math.Abs((int)(id % (ulong)count));
return PokemonTypes[remainder]; return _pokemonTypes[remainder];
} }
private static PokemonType StringToPokemonType(string v)
private PokemonType StringToPokemonType(string v)
{ {
var str = v?.ToUpperInvariant(); var str = v?.ToUpperInvariant();
var list = PokemonTypes; var list = _pokemonTypes;
foreach (PokemonType p in list) foreach (var p in list)
{ {
if (str == p.Name) if (str == p.Name)
{ {
@ -91,8 +86,7 @@ namespace NadekoBot.Modules.Pokemon
} }
return null; return null;
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Attack(string move, IGuildUser targetUser = null) public async Task Attack(string move, IGuildUser targetUser = null)
@ -105,46 +99,44 @@ namespace NadekoBot.Modules.Pokemon
if (targetUser == null) if (targetUser == null)
{ {
await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false); await ReplyErrorLocalized("user_not_found").ConfigureAwait(false);
return; return;
} }
else if (targetUser == user) if (targetUser == user)
{ {
await Context.Channel.SendMessageAsync("You can't attack yourself.").ConfigureAwait(false); await ReplyErrorLocalized("cant_attack_yourself").ConfigureAwait(false);
return; return;
} }
// Checking stats first, then move // Checking stats first, then move
//Set up the userstats //Set up the userstats
PokeStats userStats; var userStats = _stats.GetOrAdd(user.Id, new PokeStats());
userStats = Stats.GetOrAdd(user.Id, new PokeStats());
//Check if able to move //Check if able to move
//User not able if HP < 0, has made more than 4 attacks //User not able if HP < 0, has made more than 4 attacks
if (userStats.Hp < 0) if (userStats.Hp < 0)
{ {
await Context.Channel.SendMessageAsync($"{user.Mention} has fainted and was not able to move!").ConfigureAwait(false); await ReplyErrorLocalized("you_fainted").ConfigureAwait(false);
return; return;
} }
if (userStats.MovesMade >= 5) if (userStats.MovesMade >= 5)
{ {
await Context.Channel.SendMessageAsync($"{user.Mention} has used too many moves in a row and was not able to move!").ConfigureAwait(false); await ReplyErrorLocalized("too_many_moves").ConfigureAwait(false);
return; return;
} }
if (userStats.LastAttacked.Contains(targetUser.Id)) if (userStats.LastAttacked.Contains(targetUser.Id))
{ {
await Context.Channel.SendMessageAsync($"{user.Mention} can't attack again without retaliation!").ConfigureAwait(false); await ReplyErrorLocalized("cant_attack_again").ConfigureAwait(false);
return; return;
} }
//get target stats //get target stats
PokeStats targetStats; var targetStats = _stats.GetOrAdd(targetUser.Id, new PokeStats());
targetStats = Stats.GetOrAdd(targetUser.Id, new PokeStats());
//If target's HP is below 0, no use attacking //If target's HP is below 0, no use attacking
if (targetStats.Hp <= 0) if (targetStats.Hp <= 0)
{ {
await Context.Channel.SendMessageAsync($"{targetUser.Mention} has already fainted!").ConfigureAwait(false); await ReplyErrorLocalized("too_many_moves", targetUser).ConfigureAwait(false);
return; return;
} }
@ -154,7 +146,7 @@ namespace NadekoBot.Modules.Pokemon
var enabledMoves = userType.Moves; var enabledMoves = userType.Moves;
if (!enabledMoves.Contains(move.ToLowerInvariant())) if (!enabledMoves.Contains(move.ToLowerInvariant()))
{ {
await Context.Channel.SendMessageAsync($"{user.Mention} is not able to use **{move}**. Type {NadekoBot.ModulePrefixes[typeof(Pokemon).Name]}ml to see moves").ConfigureAwait(false); await ReplyErrorLocalized("invalid_move", Format.Bold(move), Prefix).ConfigureAwait(false);
return; return;
} }
@ -164,32 +156,32 @@ namespace NadekoBot.Modules.Pokemon
int damage = GetDamage(userType, targetType); int damage = GetDamage(userType, targetType);
//apply damage to target //apply damage to target
targetStats.Hp -= damage; targetStats.Hp -= damage;
var response = $"{user.Mention} used **{move}**{userType.Icon} on {targetUser.Mention}{targetType.Icon} for **{damage}** damage"; var response = GetText("attack", Format.Bold(move), userType.Icon, Format.Bold(targetUser.ToString()), targetType.Icon, Format.Bold(damage.ToString()));
//Damage type //Damage type
if (damage < 40) if (damage < 40)
{ {
response += "\nIt's not effective.."; response += "\n" + GetText("not_effective");
} }
else if (damage > 60) else if (damage > 60)
{ {
response += "\nIt's super effective!"; response += "\n" + GetText("super_effective");
} }
else else
{ {
response += "\nIt's somewhat effective"; response += "\n" + GetText("somewhat_effective");
} }
//check fainted //check fainted
if (targetStats.Hp <= 0) if (targetStats.Hp <= 0)
{ {
response += $"\n**{targetUser.Mention}** has fainted!"; response += "\n" + GetText("fainted", Format.Bold(targetUser.ToString()));
} }
else else
{ {
response += $"\n**{targetUser.Mention}** has {targetStats.Hp} HP remaining"; response += "\n" + GetText("hp_remaining", Format.Bold(targetUser.ToString()), targetStats.Hp);
} }
//update other stats //update other stats
@ -203,10 +195,10 @@ namespace NadekoBot.Modules.Pokemon
//update dictionary //update dictionary
//This can stay the same right? //This can stay the same right?
Stats[user.Id] = userStats; _stats[user.Id] = userStats;
Stats[targetUser.Id] = targetStats; _stats[targetUser.Id] = targetStats;
await Context.Channel.SendMessageAsync(response).ConfigureAwait(false); await Context.Channel.SendConfirmAsync(Context.User.Mention + " " + response).ConfigureAwait(false);
} }
@ -218,12 +210,10 @@ namespace NadekoBot.Modules.Pokemon
var userType = GetPokeType(user.Id); var userType = GetPokeType(user.Id);
var movesList = userType.Moves; var movesList = userType.Moves;
var str = $"**Moves for `{userType.Name}` type.**"; var embed = new EmbedBuilder().WithOkColor()
foreach (string m in movesList) .WithTitle(GetText("moves", userType))
{ .WithDescription(string.Join("\n", movesList.Select(m => userType.Icon + " " + m)));
str += $"\n{userType.Icon}{m}"; await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
}
await Context.Channel.SendMessageAsync(str).ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
@ -232,17 +222,18 @@ namespace NadekoBot.Modules.Pokemon
{ {
IGuildUser user = (IGuildUser)Context.User; IGuildUser user = (IGuildUser)Context.User;
if (targetUser == null) { if (targetUser == null)
await Context.Channel.SendMessageAsync("No such person.").ConfigureAwait(false); {
await ReplyErrorLocalized("user_not_found").ConfigureAwait(false);
return; return;
} }
if (Stats.ContainsKey(targetUser.Id)) if (_stats.ContainsKey(targetUser.Id))
{ {
var targetStats = Stats[targetUser.Id]; var targetStats = _stats[targetUser.Id];
if (targetStats.Hp == targetStats.MaxHp) if (targetStats.Hp == targetStats.MaxHp)
{ {
await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false); await ReplyErrorLocalized("already_full", Format.Bold(targetUser.ToString())).ConfigureAwait(false);
return; return;
} }
//Payment~ //Payment~
@ -251,11 +242,11 @@ namespace NadekoBot.Modules.Pokemon
var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention; var target = (targetUser.Id == user.Id) ? "yourself" : targetUser.Mention;
if (amount > 0) if (amount > 0)
{ {
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false)) if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"Poke-Heal {target}", amount, true).ConfigureAwait(false))
{ {
try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { } await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
return; return;
} }
} }
//healing //healing
@ -263,23 +254,20 @@ namespace NadekoBot.Modules.Pokemon
if (targetStats.Hp < 0) if (targetStats.Hp < 0)
{ {
//Could heal only for half HP? //Could heal only for half HP?
Stats[targetUser.Id].Hp = (targetStats.MaxHp / 2); _stats[targetUser.Id].Hp = (targetStats.MaxHp / 2);
if (target == "yourself") if (target == "yourself")
{ {
await Context.Channel.SendMessageAsync($"You revived yourself with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); await ReplyConfirmLocalized("revive_yourself", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
return;
} }
else
{ await ReplyConfirmLocalized("revive_other", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
await Context.Channel.SendMessageAsync($"{user.Mention} revived {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false);
}
return;
} }
await Context.Channel.SendMessageAsync($"{user.Mention} healed {targetUser.Mention} with one {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); await ReplyConfirmLocalized("healed", Format.Bold(targetUser.ToString()), NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
return;
} }
else else
{ {
await Context.Channel.SendMessageAsync($"{targetUser.Mention} already has full HP!").ConfigureAwait(false); await ErrorLocalized("already_full", Format.Bold(targetUser.ToString()));
} }
} }
@ -288,15 +276,9 @@ namespace NadekoBot.Modules.Pokemon
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Type(IGuildUser targetUser = null) public async Task Type(IGuildUser targetUser = null)
{ {
IGuildUser user = (IGuildUser)Context.User; targetUser = targetUser ?? (IGuildUser)Context.User;
if (targetUser == null)
{
return;
}
var pType = GetPokeType(targetUser.Id); var pType = GetPokeType(targetUser.Id);
await Context.Channel.SendMessageAsync($"Type of {targetUser.Mention} is **{pType.Name.ToLowerInvariant()}**{pType.Icon}").ConfigureAwait(false); await ReplyConfirmLocalized("type_of_user", Format.Bold(targetUser.ToString()), pType).ConfigureAwait(false);
} }
@ -309,7 +291,7 @@ namespace NadekoBot.Modules.Pokemon
var targetType = StringToPokemonType(typeTargeted); var targetType = StringToPokemonType(typeTargeted);
if (targetType == null) if (targetType == null)
{ {
await Context.Channel.EmbedAsync(PokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"), await Context.Channel.EmbedAsync(_pokemonTypes.Aggregate(new EmbedBuilder().WithDescription("List of the available types:"),
(eb, pt) => eb.AddField(efb => efb.WithName(pt.Name) (eb, pt) => eb.AddField(efb => efb.WithName(pt.Name)
.WithValue(pt.Icon) .WithValue(pt.Icon)
.WithIsInline(true))) .WithIsInline(true)))
@ -318,7 +300,7 @@ namespace NadekoBot.Modules.Pokemon
} }
if (targetType == GetPokeType(user.Id)) if (targetType == GetPokeType(user.Id))
{ {
await Context.Channel.SendMessageAsync($"Your type is already {targetType.Name.ToLowerInvariant()}{targetType.Icon}").ConfigureAwait(false); await ReplyErrorLocalized("already_that_type", targetType).ConfigureAwait(false);
return; return;
} }
@ -326,20 +308,19 @@ namespace NadekoBot.Modules.Pokemon
var amount = 1; var amount = 1;
if (amount > 0) if (amount > 0)
{ {
if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user.Mention} change type to {typeTargeted}", amount, true).ConfigureAwait(false)) if (!await CurrencyHandler.RemoveCurrencyAsync(user, $"{user} change type to {typeTargeted}", amount, true).ConfigureAwait(false))
{ {
try { await Context.Channel.SendMessageAsync($"{user.Mention} You don't have enough {NadekoBot.BotConfig.CurrencyName}s.").ConfigureAwait(false); } catch { } await ReplyErrorLocalized("no_currency", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
return; return;
} }
} }
//Actually changing the type here //Actually changing the type here
Dictionary<ulong, string> setTypes;
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
var pokeUsers = uow.PokeGame.GetAll(); var pokeUsers = uow.PokeGame.GetAll().ToArray();
setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type); var setTypes = pokeUsers.ToDictionary(x => x.UserId, y => y.type);
var pt = new UserPokeTypes var pt = new UserPokeTypes
{ {
UserId = user.Id, UserId = user.Id,
@ -353,7 +334,7 @@ namespace NadekoBot.Modules.Pokemon
else else
{ {
//update user in db //update user in db
var pokeUserCmd = pokeUsers.Where(p => p.UserId == user.Id).FirstOrDefault(); var pokeUserCmd = pokeUsers.FirstOrDefault(p => p.UserId == user.Id);
pokeUserCmd.type = targetType.Name; pokeUserCmd.type = targetType.Name;
uow.PokeGame.Update(pokeUserCmd); uow.PokeGame.Update(pokeUserCmd);
} }
@ -361,9 +342,10 @@ namespace NadekoBot.Modules.Pokemon
} }
//Now for the response //Now for the response
await Context.Channel.SendMessageAsync($"Set type of {user.Mention} to {typeTargeted}{targetType.Icon} for a {NadekoBot.BotConfig.CurrencySign}").ConfigureAwait(false); await ReplyConfirmLocalized("settype_success",
targetType,
NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false);
} }
} }
} }

View File

@ -15,6 +15,9 @@ namespace NadekoBot.Modules.Pokemon
public List<PokemonMultiplier> Multipliers { get; set; } public List<PokemonMultiplier> Multipliers { get; set; }
public string Icon { get; set; } public string Icon { get; set; }
public string[] Moves { get; set; } public string[] Moves { get; set; }
public override string ToString() =>
Icon + "**" + Name.ToLowerInvariant() + "**" + Icon;
} }
public class PokemonMultiplier public class PokemonMultiplier
{ {

View File

@ -230,7 +230,7 @@ namespace NadekoBot.Modules.Searches
var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query); var link = "http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query);
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
var res = await http.GetStringAsync("http://anilist.co/api/anime/search/" + Uri.EscapeUriString(query) + $"?access_token={anilistToken}").ConfigureAwait(false); var res = await http.GetStringAsync(link + $"?access_token={anilistToken}").ConfigureAwait(false);
var smallObj = JArray.Parse(res)[0]; var smallObj = JArray.Parse(res)[0];
var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false); var aniData = await http.GetStringAsync("http://anilist.co/api/anime/" + smallObj["id"] + $"?access_token={anilistToken}").ConfigureAwait(false);

View File

@ -36,7 +36,7 @@ namespace NadekoBot.Modules.Searches
var rankimg = $"{model.Competitive.rank_img}"; var rankimg = $"{model.Competitive.rank_img}";
var rank = $"{model.Competitive.rank}"; var rank = $"{model.Competitive.rank}";
var competitiveplay = $"{model.Games.Competitive.played}"; //var competitiveplay = $"{model.Games.Competitive.played}";
if (string.IsNullOrWhiteSpace(rank)) if (string.IsNullOrWhiteSpace(rank))
{ {
var embed = new EmbedBuilder() var embed = new EmbedBuilder()

View File

@ -12,7 +12,8 @@ namespace NadekoBot.Modules.Searches
[Group] [Group]
public class PlaceCommands : ModuleBase public class PlaceCommands : ModuleBase
{ {
private static string typesStr { get; } = $"`List of \"{NadekoBot.ModulePrefixes[typeof(Searches).Name]}place\" tags:`\n" + String.Join(", ", Enum.GetNames(typeof(PlaceType))); private static string typesStr { get; } =
string.Format("`List of \"{0}place\" tags:`\n", NadekoBot.ModulePrefixes[typeof(Searches).Name]) + String.Join(", ", Enum.GetNames(typeof(PlaceType)));
public enum PlaceType public enum PlaceType
{ {

View File

@ -103,19 +103,23 @@ namespace NadekoBot.Modules.Searches
oldStatus.IsLive != newStatus.IsLive) oldStatus.IsLive != newStatus.IsLive)
{ {
var server = NadekoBot.Client.GetGuild(fs.GuildId); var server = NadekoBot.Client.GetGuild(fs.GuildId);
if (server == null) var channel = server?.GetTextChannel(fs.ChannelId);
return;
var channel = server.GetTextChannel(fs.ChannelId);
if (channel == null) if (channel == null)
return; return;
try try
{ {
var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false); var msg = await channel.EmbedAsync(fs.GetEmbed(newStatus)).ConfigureAwait(false);
} }
catch { } catch
{
// ignored
}
} }
} }
catch { } catch
{
// ignored
}
})); }));
FirstPass = false; FirstPass = false;

View File

@ -28,7 +28,7 @@ using System.Xml.Linq;
namespace NadekoBot.Modules.Searches namespace NadekoBot.Modules.Searches
{ {
[NadekoModule("Searches", "~")] [NadekoModule("Searches", "~")]
public partial class Searches : DiscordModule public partial class Searches : NadekoModule
{ {
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
public async Task Weather([Remainder] string query) public async Task Weather([Remainder] string query)
@ -499,12 +499,12 @@ namespace NadekoBot.Modules.Searches
var data = JsonConvert.DeserializeObject<DefineModel>(res); var data = JsonConvert.DeserializeObject<DefineModel>(res);
var sense = data.Results.Where(x => x.Senses != null && x.Senses[0].Definition != null).FirstOrDefault()?.Senses[0]; var sense = data.Results.FirstOrDefault(x => x.Senses?[0].Definition != null)?.Senses[0];
if (sense?.Definition == null) if (sense?.Definition == null)
return; return;
string definition = sense.Definition.ToString(); var definition = sense.Definition.ToString();
if (!(sense.Definition is string)) if (!(sense.Definition is string))
definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString(); definition = ((JArray)JToken.Parse(sense.Definition.ToString())).First.ToString();
@ -536,7 +536,7 @@ namespace NadekoBot.Modules.Searches
} }
await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); await Context.Channel.TriggerTypingAsync().ConfigureAwait(false);
string res = ""; var res = "";
using (var http = new HttpClient()) using (var http = new HttpClient())
{ {
http.DefaultRequestHeaders.Clear(); http.DefaultRequestHeaders.Clear();
@ -548,7 +548,7 @@ namespace NadekoBot.Modules.Searches
{ {
var items = JObject.Parse(res); var items = JObject.Parse(res);
var item = items["defs"]["def"]; var item = items["defs"]["def"];
var hashtag = item["hashtag"].ToString(); //var hashtag = item["hashtag"].ToString();
var link = item["uri"].ToString(); var link = item["uri"].ToString();
var desc = item["text"].ToString(); var desc = item["text"].ToString();
await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor()

View File

@ -32,7 +32,6 @@ namespace NadekoBot.Modules.Utility
var voicechn = (await guild.GetVoiceChannelsAsync()).Count(); var voicechn = (await guild.GetVoiceChannelsAsync()).Count();
var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22); var createdAt = new DateTime(2015, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds(guild.Id >> 22);
var sb = new StringBuilder();
var users = await guild.GetUsersAsync().ConfigureAwait(false); var users = await guild.GetUsersAsync().ConfigureAwait(false);
var features = string.Join("\n", guild.Features); var features = string.Join("\n", guild.Features);
if (string.IsNullOrWhiteSpace(features)) if (string.IsNullOrWhiteSpace(features))
@ -45,13 +44,13 @@ namespace NadekoBot.Modules.Utility
.AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Members**").WithValue(users.Count.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Text Channels**").WithValue(textchn.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("**Voice Channels**").WithValue(voicechn.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Region**").WithValue(guild.VoiceRegionId.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue((guild.Roles.Count - 1).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("**Features**").WithValue(features).WithIsInline(true))
.WithImageUrl(guild.IconUrl) .WithImageUrl(guild.IconUrl)
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
if (guild.Emojis.Count() > 0) if (guild.Emojis.Any())
{ {
embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>")))); embed.AddField(fb => fb.WithName($"**Custom Emojis ({guild.Emojis.Count})**").WithValue(string.Join(" ", guild.Emojis.Shuffle().Take(25).Select(e => $"{e.Name} <:{e.Name}:{e.Id}>"))));
} }
@ -71,7 +70,7 @@ namespace NadekoBot.Modules.Utility
.WithTitle(ch.Name) .WithTitle(ch.Name)
.WithDescription(ch.Topic?.SanitizeMentions()) .WithDescription(ch.Topic?.SanitizeMentions())
.AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**ID**").WithValue(ch.Id.ToString()).WithIsInline(true))
.AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Created At**").WithValue($"{createdAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true)) .AddField(fb => fb.WithName("**Users**").WithValue(usercount.ToString()).WithIsInline(true))
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);
await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); await Context.Channel.EmbedAsync(embed).ConfigureAwait(false);
@ -81,7 +80,6 @@ namespace NadekoBot.Modules.Utility
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task UserInfo(IGuildUser usr = null) public async Task UserInfo(IGuildUser usr = null)
{ {
var channel = (ITextChannel)Context.Channel;
var user = usr ?? Context.User as IGuildUser; var user = usr ?? Context.User as IGuildUser;
if (user == null) if (user == null)
@ -95,7 +93,7 @@ namespace NadekoBot.Modules.Utility
} }
embed.AddField(fb => fb.WithName("**ID**").WithValue(user.Id.ToString()).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 Server**").WithValue($"{user.JoinedAt?.ToString("dd.MM.yyyy HH:mm") ?? "unavail."}").WithIsInline(true))
.AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt.ToString("dd.MM.yyyy HH:mm")}").WithIsInline(true)) .AddField(fb => fb.WithName("**Joined Discord**").WithValue($"{user.CreatedAt:dd.MM.yyyy HH:mm}").WithIsInline(true))
.AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true)) .AddField(fb => fb.WithName("**Roles**").WithValue($"**({user.RoleIds.Count - 1})** - {string.Join("\n", user.GetRoles().Take(10).Where(r => r.Id != r.Guild.EveryoneRole.Id).Select(r => r.Name)).SanitizeMentions()}").WithIsInline(true))
.WithColor(NadekoBot.OkColor); .WithColor(NadekoBot.OkColor);

View File

@ -91,7 +91,7 @@ namespace NadekoBot.Modules.Utility
public void Reset() public void Reset()
{ {
source.Cancel(); source.Cancel();
var t = Task.Run(Run); var _ = Task.Run(Run);
} }
public void Stop() public void Stop()

View File

@ -45,15 +45,15 @@ namespace NadekoBot.Modules.Utility
foreach (var r in reminders) foreach (var r in reminders)
{ {
try { var t = StartReminder(r); } catch (Exception ex) { _log.Warn(ex); } Task.Run(() => StartReminder(r));
} }
} }
private static async Task StartReminder(Reminder r) private static async Task StartReminder(Reminder r)
{ {
var now = DateTime.Now; var now = DateTime.Now;
var twoMins = new TimeSpan(0, 2, 0);
TimeSpan time = r.When - now; var time = r.When - now;
if (time.TotalMilliseconds > int.MaxValue) if (time.TotalMilliseconds > int.MaxValue)
return; return;

View File

@ -21,7 +21,7 @@ using NadekoBot.Services;
namespace NadekoBot.Modules.Utility namespace NadekoBot.Modules.Utility
{ {
[NadekoModule("Utility", ".")] [NadekoModule("Utility", ".")]
public partial class Utility : DiscordModule public partial class Utility : NadekoModule
{ {
private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>(); private static ConcurrentDictionary<ulong, Timer> rotatingRoleColors = new ConcurrentDictionary<ulong, Timer>();
@ -468,7 +468,6 @@ namespace NadekoBot.Modules.Utility
[OwnerOnly] [OwnerOnly]
public async Task SaveChat(int cnt) public async Task SaveChat(int cnt)
{ {
var sb = new StringBuilder();
var msgs = new List<IMessage>(cnt); var msgs = new List<IMessage>(cnt);
await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false); await Context.Channel.GetMessagesAsync(cnt).ForEachAsync(dled => msgs.AddRange(dled)).ConfigureAwait(false);

View File

@ -14,8 +14,12 @@ using System.Collections.Generic;
using NadekoBot.Modules.Permissions; using NadekoBot.Modules.Permissions;
using NadekoBot.TypeReaders; using NadekoBot.TypeReaders;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Immutable;
using System.Diagnostics;
using NadekoBot.Modules.Music; using NadekoBot.Modules.Music;
using NadekoBot.Services.Database.Models; using NadekoBot.Services.Database.Models;
using System.Resources;
using NadekoBot.Resources;
namespace NadekoBot namespace NadekoBot
{ {
@ -29,7 +33,10 @@ namespace NadekoBot
public static CommandService CommandService { get; private set; } public static CommandService CommandService { get; private set; }
public static CommandHandler CommandHandler { get; private set; } public static CommandHandler CommandHandler { get; private set; }
public static DiscordShardedClient Client { get; private set; } public static DiscordShardedClient Client { get; private set; }
public static BotCredentials Credentials { get; private set; } public static BotCredentials Credentials { get; }
public static Localization Localization { get; private set; }
public static ResourceManager ResponsesResourceManager { get; } = new ResourceManager(typeof(ResponseStrings));
public static GoogleApiService Google { get; private set; } public static GoogleApiService Google { get; private set; }
public static StatsService Stats { get; private set; } public static StatsService Stats { get; private set; }
@ -38,7 +45,7 @@ namespace NadekoBot
public static ConcurrentDictionary<string, string> ModulePrefixes { get; private set; } public static ConcurrentDictionary<string, string> ModulePrefixes { get; private set; }
public static bool Ready { get; private set; } public static bool Ready { get; private set; }
public static IEnumerable<GuildConfig> AllGuildConfigs { get; } public static ImmutableArray<GuildConfig> AllGuildConfigs { get; }
public static BotConfig BotConfig { get; } public static BotConfig BotConfig { get; }
static NadekoBot() static NadekoBot()
@ -48,7 +55,7 @@ namespace NadekoBot
using (var uow = DbHandler.UnitOfWork()) using (var uow = DbHandler.UnitOfWork())
{ {
AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs(); AllGuildConfigs = uow.GuildConfigs.GetAllGuildConfigs().ToImmutableArray();
BotConfig = uow.BotConfig.GetOrCreate(); BotConfig = uow.BotConfig.GetOrCreate();
OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16)); OkColor = new Color(Convert.ToUInt32(BotConfig.OkColor, 16));
ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16)); ErrorColor = new Color(Convert.ToUInt32(BotConfig.ErrorColor, 16));
@ -79,6 +86,7 @@ namespace NadekoBot
#endif #endif
//initialize Services //initialize Services
Localization = new Localization(NadekoBot.BotConfig.Locale, NadekoBot.AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale));
CommandService = new CommandService(new CommandServiceConfig() { CommandService = new CommandService(new CommandServiceConfig() {
CaseSensitiveCommands = false, CaseSensitiveCommands = false,
DefaultRunMode = RunMode.Sync DefaultRunMode = RunMode.Sync
@ -102,13 +110,16 @@ namespace NadekoBot
CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader()); CommandService.AddTypeReader<ModuleInfo>(new ModuleTypeReader());
CommandService.AddTypeReader<IGuild>(new GuildTypeReader()); CommandService.AddTypeReader<IGuild>(new GuildTypeReader());
var sw = Stopwatch.StartNew();
//connect //connect
await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false); await Client.LoginAsync(TokenType.Bot, Credentials.Token).ConfigureAwait(false);
await Client.ConnectAsync().ConfigureAwait(false); await Client.ConnectAsync().ConfigureAwait(false);
//await Client.DownloadAllUsersAsync().ConfigureAwait(false); //await Client.DownloadAllUsersAsync().ConfigureAwait(false);
Stats.Initialize(); Stats.Initialize();
_log.Info("Connected"); sw.Stop();
_log.Info("Connected in " + sw.Elapsed.TotalSeconds.ToString("F2"));
//load commands and prefixes //load commands and prefixes

View File

@ -0,0 +1,3 @@
<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></wpf:ResourceDictionary>

View File

@ -3704,6 +3704,87 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to languageset langset.
/// </summary>
public static string languageset_cmd {
get {
return ResourceManager.GetString("languageset_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sets this server&apos;s response language If bot&apos;s response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language..
/// </summary>
public static string languageset_desc {
get {
return ResourceManager.GetString("languageset_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}langset de-DE ` or `{0}langset default`.
/// </summary>
public static string languageset_usage {
get {
return ResourceManager.GetString("languageset_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to langsetdefault langsetd.
/// </summary>
public static string languagesetdefault_cmd {
get {
return ResourceManager.GetString("languagesetdefault_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Sets the bot&apos;s default response language. All servers which use a default locale will use this one. Setting to `default` will use the host&apos;s current culture. Provide no arguments to see currently set language..
/// </summary>
public static string languagesetdefault_desc {
get {
return ResourceManager.GetString("languagesetdefault_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}langsetd en-US` or `{0}langsetd default`.
/// </summary>
public static string languagesetdefault_usage {
get {
return ResourceManager.GetString("languagesetdefault_usage", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to languageslist langli.
/// </summary>
public static string languageslist_cmd {
get {
return ResourceManager.GetString("languageslist_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to List of languages for which translation (or part of it) exist atm..
/// </summary>
public static string languageslist_desc {
get {
return ResourceManager.GetString("languageslist_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}langli`.
/// </summary>
public static string languageslist_usage {
get {
return ResourceManager.GetString("languageslist_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to lcsc. /// Looks up a localized string similar to lcsc.
/// </summary> /// </summary>
@ -8988,7 +9069,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to {0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`. /// Looks up a localized string similar to `{0}yoda my feelings hurt`.
/// </summary> /// </summary>
public static string yodify_usage { public static string yodify_usage {
get { get {

View File

@ -2698,7 +2698,7 @@
<value>Translates your normal sentences into Yoda styled sentences!</value> <value>Translates your normal sentences into Yoda styled sentences!</value>
</data> </data>
<data name="yodify_usage" xml:space="preserve"> <data name="yodify_usage" xml:space="preserve">
<value>{0}yodify I was once an adventurer like you` or `{0}yoda my feelings hurt`</value> <value>`{0}yoda my feelings hurt`</value>
</data> </data>
<data name="attack_cmd" xml:space="preserve"> <data name="attack_cmd" xml:space="preserve">
<value>attack</value> <value>attack</value>
@ -3114,4 +3114,35 @@
<data name="timezone_usage" xml:space="preserve"> <data name="timezone_usage" xml:space="preserve">
<value>`{0}timezone`</value> <value>`{0}timezone`</value>
</data> </data>
<<<<<<< HEAD
</root> </root>
=======
<data name="languagesetdefault_cmd" xml:space="preserve">
<value>langsetdefault langsetd</value>
</data>
<data name="languagesetdefault_desc" xml:space="preserve">
<value>Sets the bot's default response language. All servers which use a default locale will use this one. Setting to `default` will use the host's current culture. Provide no arguments to see currently set language.</value>
</data>
<data name="languagesetdefault_usage" xml:space="preserve">
<value>`{0}langsetd en-US` or `{0}langsetd default`</value>
</data>
<data name="languageset_cmd" xml:space="preserve">
<value>languageset langset</value>
</data>
<data name="languageset_desc" xml:space="preserve">
<value>Sets this server's response language If bot's response strings have been translated to that language, bot will use that language in this server. Reset by using `default` as the locale name. Provide no arguments to see currently set language.</value>
</data>
<data name="languageset_usage" xml:space="preserve">
<value>`{0}langset de-DE ` or `{0}langset default`</value>
</data>
<data name="languageslist_cmd" xml:space="preserve">
<value>languageslist langli</value>
</data>
<data name="languageslist_desc" xml:space="preserve">
<value>List of languages for which translation (or part of it) exist atm.</value>
</data>
<data name="languageslist_usage" xml:space="preserve">
<value>`{0}langli`</value>
</data>
</root>
>>>>>>> Kwoth/dev

File diff suppressed because it is too large Load Diff

View File

@ -117,4 +117,930 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="clashofclans_base_already_claimed" xml:space="preserve">
<value>That base is already claimed or destroyed.</value>
</data>
<data name="clashofclans_base_already_destroyed" xml:space="preserve">
<value>That base is already destroyed.</value>
</data>
<data name="clashofclans_base_already_unclaimed" xml:space="preserve">
<value>That base is not claimed.</value>
</data>
<data name="clashofclans_base_destroyed" xml:space="preserve">
<value>**DESTROYED** base #{0} in a war against {1}</value>
</data>
<data name="clashofclans_base_unclaimed" xml:space="preserve">
<value>{0} has **UNCLAIMED** base #{1} in a war against {2}</value>
</data>
<data name="clashofclans_claimed_base" xml:space="preserve">
<value>{0} claimed a base #{1} in a war against {2}</value>
</data>
<data name="clashofclans_claimed_other" xml:space="preserve">
<value>@{0} You already claimed base #{1}. You can't claim a new one.</value>
</data>
<data name="clashofclans_claim_expired" xml:space="preserve">
<value>Claim from @{0} for a war against {1} has expired.</value>
</data>
<data name="clashofclans_enemy" xml:space="preserve">
<value>Enemy</value>
</data>
<data name="clashofclans_info_about_war" xml:space="preserve">
<value>Info about war against {0}</value>
</data>
<data name="clashofclans_invalid_base_number" xml:space="preserve">
<value>Invalid base number.</value>
</data>
<data name="clashofclans_invalid_size" xml:space="preserve">
<value>Not a Valid war size.</value>
</data>
<data name="clashofclans_list_active_wars" xml:space="preserve">
<value>List Of Active Wars</value>
</data>
<data name="clashofclans_not_claimed" xml:space="preserve">
<value>not claimed</value>
</data>
<data name="clashofclans_not_partic" xml:space="preserve">
<value>You are not participating in that war.</value>
</data>
<data name="clashofclans_not_partic_or_destroyed" xml:space="preserve">
<value>@{0} You are either not participating in that war, or that base is already destroyed.</value>
</data>
<data name="clashofclans_no_active_wars" xml:space="preserve">
<value>No active wars.</value>
</data>
<data name="clashofclans_size" xml:space="preserve">
<value>Size</value>
</data>
<data name="clashofclans_war_already_started" xml:space="preserve">
<value>War against {0} has already started.</value>
</data>
<data name="clashofclans_war_created" xml:space="preserve">
<value>War against {0} created.</value>
</data>
<data name="clashofclans_war_ended" xml:space="preserve">
<value>War against {0} ended.</value>
</data>
<data name="clashofclans_war_not_exist" xml:space="preserve">
<value>That war does not exist.</value>
</data>
<data name="clashofclans_war_started" xml:space="preserve">
<value>War against {0} started!</value>
</data>
<data name="customreactions_all_stats_cleared" xml:space="preserve">
<value>All custom reaction stats cleared.</value>
</data>
<data name="customreactions_deleted" xml:space="preserve">
<value>Custom Reaction deleted</value>
</data>
<data name="customreactions_insuff_perms" xml:space="preserve">
<value>Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for server custom reactions.</value>
</data>
<data name="customreactions_list_all" xml:space="preserve">
<value>List of all custom reactions</value>
</data>
<data name="customreactions_name" xml:space="preserve">
<value>Custom Reactions</value>
</data>
<data name="customreactions_new_cust_react" xml:space="preserve">
<value>New Custom Reaction</value>
</data>
<data name="customreactions_no_found" xml:space="preserve">
<value>No custom reactions found.</value>
</data>
<data name="customreactions_no_found_id" xml:space="preserve">
<value>No custom reaction found with that id.</value>
</data>
<data name="customreactions_response" xml:space="preserve">
<value>Response</value>
</data>
<data name="customreactions_stats" xml:space="preserve">
<value>Custom Reaction Stats</value>
</data>
<data name="customreactions_stats_cleared" xml:space="preserve">
<value>Stats cleared for {0} custom reaction.</value>
</data>
<data name="customreactions_stats_not_found" xml:space="preserve">
<value>No stats for that trigger found, no action taken.</value>
</data>
<data name="customreactions_trigger" xml:space="preserve">
<value>Trigger</value>
</data>
<data name="nsfw_autohentai_stopped" xml:space="preserve">
<value>Autohentai stopped.</value>
</data>
<data name="nsfw_not_found" xml:space="preserve">
<value>No results found.</value>
</data>
<data name="pokemon_already_fainted" xml:space="preserve">
<value>{0} has already fainted.</value>
</data>
<data name="pokemon_already_full" xml:space="preserve">
<value>{0} already has full HP.</value>
</data>
<data name="pokemon_already_that_type" xml:space="preserve">
<value>Your type is already {0}</value>
</data>
<data name="pokemon_attack" xml:space="preserve">
<value>used {0}{1} on {2}{3} for {4} damage.</value>
<comment>Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage.</comment>
</data>
<data name="pokemon_cant_attack_again" xml:space="preserve">
<value>You can't attack again without retaliation!</value>
</data>
<data name="pokemon_cant_attack_yourself" xml:space="preserve">
<value>You can't attack yourself.</value>
</data>
<data name="pokemon_fainted" xml:space="preserve">
<value>{0} has fainted!</value>
</data>
<data name="pokemon_healed" xml:space="preserve">
<value>healed {0} with one {1}</value>
</data>
<data name="pokemon_hp_remaining" xml:space="preserve">
<value>{0} has {1} HP remaining.</value>
</data>
<data name="pokemon_invalid_move" xml:space="preserve">
<value>You can't use {0}. Type `{1}ml` to see a list of moves you can use.</value>
</data>
<data name="pokemon_moves" xml:space="preserve">
<value>Movelist for {0} type</value>
</data>
<data name="pokemon_not_effective" xml:space="preserve">
<value>It's not effective.</value>
</data>
<data name="pokemon_no_currency" xml:space="preserve">
<value>You don't have enough {0}</value>
</data>
<data name="pokemon_revive_other" xml:space="preserve">
<value>revived {0} with one {1}</value>
</data>
<data name="pokemon_revive_yourself" xml:space="preserve">
<value>You revived yourself with one {0}</value>
</data>
<data name="pokemon_settype_success" xml:space="preserve">
<value>Your type has been changed to {0} for a {1}</value>
</data>
<data name="pokemon_somewhat_effective" xml:space="preserve">
<value>It's somewhat effective.</value>
</data>
<data name="pokemon_super_effective" xml:space="preserve">
<value>It's super effective!</value>
</data>
<data name="pokemon_too_many_moves" xml:space="preserve">
<value>You used too many moves in a row, so you can't move!</value>
</data>
<data name="pokemon_type_of_user" xml:space="preserve">
<value>Type of {0} is {1}</value>
</data>
<data name="pokemon_user_not_found" xml:space="preserve">
<value>User not found.</value>
</data>
<data name="pokemon_you_fainted" xml:space="preserve">
<value>You fainted, so you are not able to move!</value>
</data>
<data name="administration_aar_disabled" xml:space="preserve">
<value>**Auto assign role** on user join is now **disabled**.</value>
</data>
<data name="administration_aar_enabled" xml:space="preserve">
<value>**Auto assign role** on user join is now **enabled**.</value>
</data>
<data name="administration_attachments" xml:space="preserve">
<value>Attachments</value>
</data>
<data name="administration_avatar_changed" xml:space="preserve">
<value>Avatar Changed</value>
</data>
<data name="administration_bandm" xml:space="preserve">
<value>You have been banned from {0} server.
Reason: {1}</value>
</data>
<data name="administration_banned_pl" xml:space="preserve">
<value>banned</value>
<comment>PLURAL</comment>
</data>
<data name="administration_banned_user" xml:space="preserve">
<value>User Banned</value>
</data>
<data name="administration_bot_name" xml:space="preserve">
<value>Bot name changed to {0}</value>
</data>
<data name="administration_bot_status" xml:space="preserve">
<value>Bot status changed to {0}</value>
</data>
<data name="administration_byedel_off" xml:space="preserve">
<value>Automatic deletion of bye messages has been disabled.</value>
</data>
<data name="administration_byedel_on" xml:space="preserve">
<value>Bye messages will be deleted after {0} seconds.</value>
</data>
<data name="administration_byemsg_cur" xml:space="preserve">
<value>Current bye message: {0}</value>
</data>
<data name="administration_byemsg_enable" xml:space="preserve">
<value>Enable bye messages by typing {0}</value>
</data>
<data name="administration_byemsg_new" xml:space="preserve">
<value>New bye message set.</value>
</data>
<data name="administration_bye_off" xml:space="preserve">
<value>Bye announcements disabled.</value>
</data>
<data name="administration_bye_on" xml:space="preserve">
<value>Bye announcements enabled on this channel.</value>
</data>
<data name="administration_ch_name_change" xml:space="preserve">
<value>Channel Name Changed</value>
</data>
<data name="administration_ch_old_name" xml:space="preserve">
<value>Old Name</value>
</data>
<data name="administration_ch_topic_change" xml:space="preserve">
<value>Channel Topic Changed</value>
</data>
<data name="administration_cleaned_up" xml:space="preserve">
<value>Cleaned up.</value>
</data>
<data name="administration_content" xml:space="preserve">
<value>Content</value>
</data>
<data name="administration_cr" xml:space="preserve">
<value>Sucessfully created role {0}</value>
</data>
<data name="administration_createtextchan" xml:space="preserve">
<value>Text channel {0} created.</value>
</data>
<data name="administration_createvoich" xml:space="preserve">
<value>Voice channel {0} created.</value>
</data>
<data name="administration_deafen" xml:space="preserve">
<value>Deafen successful.</value>
</data>
<data name="administration_deleted_server" xml:space="preserve">
<value>Deleted server {0}</value>
</data>
<data name="administration_delmsg_off" xml:space="preserve">
<value>Stopped automatic deletion of successful command invokations.</value>
</data>
<data name="administration_delmsg_on" xml:space="preserve">
<value>Now automatically deleting sucessful command invokations.</value>
</data>
<data name="administration_deltextchan" xml:space="preserve">
<value>Text channel {0} deleted.</value>
</data>
<data name="administration_delvoich" xml:space="preserve">
<value>Voice channel {0} deleted.</value>
</data>
<data name="administration_dm_from" xml:space="preserve">
<value>DM from</value>
</data>
<data name="administration_donadd" xml:space="preserve">
<value>Sucessfully added a new donator.Total donated amount from this user: {0} 👑</value>
</data>
<data name="administration_donators" xml:space="preserve">
<value>Thanks to the people listed below for making this project hjappen!</value>
</data>
<data name="administration_fwall_start" xml:space="preserve">
<value>I will forward DMs to all owners.</value>
</data>
<data name="administration_fwall_stop" xml:space="preserve">
<value>I will forward DMs only to the first owner.</value>
</data>
<data name="administration_fwdm_start" xml:space="preserve">
<value>I will forward DMs from now on.</value>
</data>
<data name="administration_fwdm_stop" xml:space="preserve">
<value>I will stop forwarding DMs from now on.</value>
</data>
<data name="administration_greetdel_off" xml:space="preserve">
<value>Automatic deletion of greet messages has been disabled.</value>
</data>
<data name="administration_greetdel_on" xml:space="preserve">
<value>Greet messages will be deleted after {0} seconds.</value>
</data>
<data name="administration_greetdmmsg_cur" xml:space="preserve">
<value>Current DM greet message: {0}</value>
</data>
<data name="administration_greetdmmsg_enable" xml:space="preserve">
<value>Enable DM greet messages by typing {0}</value>
</data>
<data name="administration_greetdmmsg_new" xml:space="preserve">
<value>New DM greet message set.</value>
</data>
<data name="administration_greetdm_off" xml:space="preserve">
<value>DM greet announcements disabled.</value>
</data>
<data name="administration_greetdm_on" xml:space="preserve">
<value>DM greet announcements enabled.</value>
</data>
<data name="administration_greetmsg_cur" xml:space="preserve">
<value>Current greet message: {0}</value>
</data>
<data name="administration_greetmsg_enable" xml:space="preserve">
<value>Enable greet messages by typing {0}</value>
</data>
<data name="administration_greetmsg_new" xml:space="preserve">
<value>New greet message set.</value>
</data>
<data name="administration_greet_off" xml:space="preserve">
<value>Greet announcements disabled.</value>
</data>
<data name="administration_greet_on" xml:space="preserve">
<value>Greet announcements enabled on this channel.</value>
</data>
<data name="administration_hierarchy" xml:space="preserve">
<value>You can't use this command on users with a role higher or equal to yours in the role hierarchy.</value>
</data>
<data name="administration_images_loaded" xml:space="preserve">
<value>Images loaded after {0} seconds!</value>
</data>
<data name="administration_invalid_format" xml:space="preserve">
<value>Invalid input format.</value>
</data>
<data name="administration_invalid_params" xml:space="preserve">
<value>Invalid parameters.</value>
</data>
<data name="administration_joined" xml:space="preserve">
<value>{0} has joined {1}</value>
</data>
<data name="administration_kickdm" xml:space="preserve">
<value>You have been kicked from {0} server.
Reason: {1}</value>
</data>
<data name="administration_kicked_user" xml:space="preserve">
<value>User Kicked</value>
</data>
<data name="administration_lang_list" xml:space="preserve">
<value>List Of Languages
{0}</value>
</data>
<data name="administration_lang_set" xml:space="preserve">
<value>Your server's locale is now {0} - {1}</value>
</data>
<data name="administration_lang_set_bot" xml:space="preserve">
<value>Bot's default locale is now {0} - {1}</value>
</data>
<data name="administration_lang_set_bot_show" xml:space="preserve">
<value>Bot's language is set to {0} - {0}</value>
</data>
<data name="administration_lang_set_fail" xml:space="preserve">
<value>Failed setting locale. Revisit this command's help.</value>
</data>
<data name="administration_lang_set_show" xml:space="preserve">
<value>This server's language is set to {0} - {0}</value>
</data>
<data name="administration_left" xml:space="preserve">
<value>{0} has left {1}</value>
</data>
<data name="administration_left_server" xml:space="preserve">
<value>Left server {0}</value>
</data>
<data name="administration_log" xml:space="preserve">
<value>Logging {0} event in this channel.</value>
</data>
<data name="administration_log_all" xml:space="preserve">
<value>Logging all events in this channel.</value>
</data>
<data name="administration_log_disabled" xml:space="preserve">
<value>Logging disabled.</value>
</data>
<data name="administration_log_events" xml:space="preserve">
<value>Log events you can subscribe to:</value>
</data>
<data name="administration_log_ignore" xml:space="preserve">
<value>Logging will ignore {0}</value>
</data>
<data name="administration_log_not_ignore" xml:space="preserve">
<value>Logging will not ignore {0}</value>
</data>
<data name="administration_log_stop" xml:space="preserve">
<value>Stopped logging {0} event.</value>
</data>
<data name="administration_menrole" xml:space="preserve">
<value>{0} has invoked a mention on the following roles</value>
</data>
<data name="administration_message_from_bo" xml:space="preserve">
<value>Message from {0} `[Bot Owner]`:</value>
</data>
<data name="administration_message_sent" xml:space="preserve">
<value>Message sent.</value>
</data>
<data name="administration_moved" xml:space="preserve">
<value>{0} moved from {1} to {2}</value>
</data>
<data name="administration_msg_del" xml:space="preserve">
<value>Message Deleted in #{0}</value>
</data>
<data name="administration_msg_update" xml:space="preserve">
<value>Message Updated in #{0}</value>
</data>
<data name="administration_muted_pl" xml:space="preserve">
<value>Muted</value>
<comment>PLURAL (users have been muted)</comment>
</data>
<data name="administration_muted_sn" xml:space="preserve">
<value>Muted</value>
<comment>singular "User muted."</comment>
</data>
<data name="administration_mute_error" xml:space="preserve">
<value>I don't have the permission necessary for that most likely.</value>
</data>
<data name="administration_mute_role_set" xml:space="preserve">
<value>New mute role set.</value>
</data>
<data name="administration_need_admin" xml:space="preserve">
<value>I need **Administration** permission to do that.</value>
</data>
<data name="administration_new_msg" xml:space="preserve">
<value>New Message</value>
</data>
<data name="administration_new_nick" xml:space="preserve">
<value>New Nickname</value>
</data>
<data name="administration_new_topic" xml:space="preserve">
<value>New Topic</value>
</data>
<data name="administration_nick_change" xml:space="preserve">
<value>Nickname Changed</value>
</data>
<data name="administration_no_server" xml:space="preserve">
<value>Can't find that server</value>
</data>
<data name="administration_no_shard_id" xml:space="preserve">
<value>No shard with that ID found.</value>
</data>
<data name="administration_old_msg" xml:space="preserve">
<value>Old Message</value>
</data>
<data name="administration_old_nick" xml:space="preserve">
<value>Old Nickname</value>
</data>
<data name="administration_old_topic" xml:space="preserve">
<value>Old Topic</value>
</data>
<data name="administration_perms" xml:space="preserve">
<value>Error. Most likely I don't have sufficient permissions.</value>
</data>
<data name="administration_perms_reset" xml:space="preserve">
<value>Permissions for this server are reset.</value>
</data>
<data name="administration_prot_active" xml:space="preserve">
<value>Active Protections</value>
</data>
<data name="administration_prot_disable" xml:space="preserve">
<value>{0} has been **disabled** on this server.</value>
</data>
<data name="administration_prot_enable" xml:space="preserve">
<value>{0} Enabled</value>
</data>
<data name="administration_prot_error" xml:space="preserve">
<value>Error. I need ManageRoles permission</value>
</data>
<data name="administration_prot_none" xml:space="preserve">
<value>No protections enabled.</value>
</data>
<data name="administration_raid_cnt" xml:space="preserve">
<value>User threshold must be between {0} and {1}.</value>
</data>
<data name="administration_raid_stats" xml:space="preserve">
<value>If {0} or more users join within {1} seconds, I will {2} them.</value>
</data>
<data name="administration_raid_time" xml:space="preserve">
<value>Time must be between {0} and {1} seconds.</value>
</data>
<data name="administration_rar" xml:space="preserve">
<value>Successfully removed all roles from user {0}</value>
</data>
<data name="administration_rar_err" xml:space="preserve">
<value>Failed to remove roles. I have insufficient permissions.</value>
</data>
<data name="administration_rc" xml:space="preserve">
<value>Color of {0} role has been changed.</value>
</data>
<data name="administration_rc_not_exist" xml:space="preserve">
<value>That role does not exist.</value>
</data>
<data name="administration_rc_params" xml:space="preserve">
<value>The parameters specified are invalid.</value>
</data>
<data name="administration_rc_perms" xml:space="preserve">
<value>Error occured due to invalid color or insufficient permissions.</value>
</data>
<data name="administration_remrole" xml:space="preserve">
<value>Successfully removed role {0} from user {1}</value>
</data>
<data name="administration_remrole_err" xml:space="preserve">
<value>Failed to remove role. I have insufficient permissions.</value>
</data>
<data name="administration_renrole" xml:space="preserve">
<value>Role renamed.</value>
</data>
<data name="administration_renrole_err" xml:space="preserve">
<value>Failed to rename role. I have insufficient permissions.</value>
</data>
<data name="administration_renrole_perms" xml:space="preserve">
<value>You can't edit roles higher than your highest role.</value>
</data>
<data name="administration_reprm" xml:space="preserve">
<value>Removed the playing message: {0}</value>
</data>
<data name="administration_role_added" xml:space="preserve">
<value>Role {0} as been added to the list.</value>
</data>
<data name="administration_role_clean" xml:space="preserve">
<value>{0} not found.Cleaned up.</value>
</data>
<data name="administration_role_in_list" xml:space="preserve">
<value>Role {0} is already in the list.</value>
</data>
<data name="administration_ropl_added" xml:space="preserve">
<value>Added.</value>
</data>
<data name="administration_ropl_disabled" xml:space="preserve">
<value>Rotating playing status disabled.</value>
</data>
<data name="administration_ropl_enabled" xml:space="preserve">
<value>Rotating playing status enabled.</value>
</data>
<data name="administration_ropl_list" xml:space="preserve">
<value>Here is a list of rotating statuses:
{0}</value>
</data>
<data name="administration_ropl_not_set" xml:space="preserve">
<value>No rotating playing statuses set.</value>
</data>
<data name="administration_self_assign_already" xml:space="preserve">
<value>You already have {0} role.</value>
</data>
<data name="administration_self_assign_already_excl" xml:space="preserve">
<value>You already have {0} exclusive self-assigned role.</value>
</data>
<data name="administration_self_assign_excl" xml:space="preserve">
<value>Self assigned roles are now exclusive!</value>
</data>
<data name="administration_self_assign_list" xml:space="preserve">
<value>There are {0} self assignable roles</value>
</data>
<data name="administration_self_assign_not" xml:space="preserve">
<value>That role is not self-assignable.</value>
</data>
<data name="administration_self_assign_not_have" xml:space="preserve">
<value>You don't have {0} role.</value>
</data>
<data name="administration_self_assign_no_excl" xml:space="preserve">
<value>Self assigned roles are now not exclusive!</value>
</data>
<data name="administration_self_assign_perms" xml:space="preserve">
<value>I am unable to add that role to you. `I can't add roles to owners or other roles higher than my role in the role hierarchy.`</value>
</data>
<data name="administration_self_assign_rem" xml:space="preserve">
<value>{0} has been removed from the list of self-assignable roles.</value>
</data>
<data name="administration_self_assign_remove" xml:space="preserve">
<value>You no longer have {0} role.</value>
</data>
<data name="administration_self_assign_sucess" xml:space="preserve">
<value>You now have {0} role.</value>
</data>
<data name="administration_setrole" xml:space="preserve">
<value>Sucessfully added role {0} to user {1}</value>
</data>
<data name="administration_setrole_err" xml:space="preserve">
<value>Failed to add role. I have insufficient permissions.</value>
</data>
<data name="administration_set_avatar" xml:space="preserve">
<value>New avatar set!</value>
</data>
<data name="administration_set_channel_name" xml:space="preserve">
<value>New channel name set.</value>
</data>
<data name="administration_set_game" xml:space="preserve">
<value>New game set!</value>
</data>
<data name="administration_set_stream" xml:space="preserve">
<value>New stream set!</value>
</data>
<data name="administration_set_topic" xml:space="preserve">
<value>New channel topic set.</value>
</data>
<data name="administration_shard_reconnected" xml:space="preserve">
<value>Shard {0} reconnected.</value>
</data>
<data name="administration_shard_reconnecting" xml:space="preserve">
<value>Shard {0} reconnecting.</value>
</data>
<data name="administration_shutting_down" xml:space="preserve">
<value>Shutting down</value>
</data>
<data name="administration_slowmode_desc" xml:space="preserve">
<value>Users can't send more than {0} messages every {1} seconds.</value>
</data>
<data name="administration_slowmode_disabled" xml:space="preserve">
<value>Slow mode disabled.</value>
</data>
<data name="administration_slowmode_init" xml:space="preserve">
<value>Slow mode initiated</value>
</data>
<data name="administration_soft_banned_pl" xml:space="preserve">
<value>soft-banned (kicked)</value>
<comment>PLURAL</comment>
</data>
<data name="administration_spam_ignore" xml:space="preserve">
<value>{0} will ignore this channel.</value>
</data>
<data name="administration_spam_not_ignore" xml:space="preserve">
<value>{0} will no longer ignore this channel.</value>
</data>
<data name="administration_spam_stats" xml:space="preserve">
<value>If a user posts {0} same messages in a row, I will {1} them.
__IgnoredChannels__: {2}</value>
</data>
<data name="administration_text_chan_created" xml:space="preserve">
<value>Text Channel Destroyed </value>
</data>
<data name="administration_text_chan_destroyed" xml:space="preserve">
<value>Text Channel Destroyed </value>
</data>
<data name="administration_undeafen" xml:space="preserve">
<value>Undeafen successful.</value>
</data>
<data name="administration_unmuted_sn" xml:space="preserve">
<value>Unmuted</value>
<comment>singular</comment>
</data>
<data name="administration_username" xml:space="preserve">
<value>Username</value>
</data>
<data name="administration_username_changed" xml:space="preserve">
<value>Username Changed</value>
</data>
<data name="administration_users" xml:space="preserve">
<value>Users</value>
</data>
<data name="administration_user_banned" xml:space="preserve">
<value>User Banned</value>
</data>
<data name="administration_user_chat_mute" xml:space="preserve">
<value>{0} has been **muted** from chatting.</value>
</data>
<data name="administration_user_chat_unmute" xml:space="preserve">
<value>{0} has been **unmuted** from chatting.</value>
</data>
<data name="administration_user_joined" xml:space="preserve">
<value>User Joined</value>
</data>
<data name="administration_user_left" xml:space="preserve">
<value>User Left</value>
</data>
<data name="administration_user_muted" xml:space="preserve">
<value>{0} has been **muted** from text and voice chat.</value>
</data>
<data name="administration_user_role_add" xml:space="preserve">
<value>User's Role Added</value>
</data>
<data name="administration_user_role_rem" xml:space="preserve">
<value>User's Role Removed</value>
</data>
<data name="administration_user_status_change" xml:space="preserve">
<value>{0} is now {1}</value>
</data>
<data name="administration_user_unmuted" xml:space="preserve">
<value>{0} has been **unmuted** from text and voice chat.</value>
</data>
<data name="administration_user_vjoined" xml:space="preserve">
<value>{0} has joined {1} voice channel.</value>
</data>
<data name="administration_user_vleft" xml:space="preserve">
<value>{0} has left {1} voice channel.</value>
</data>
<data name="administration_user_vmoved" xml:space="preserve">
<value>{0} moved from {1} to {2} voice channel.</value>
</data>
<data name="administration_user_voice_mute" xml:space="preserve">
<value>{0} has been **voice muted**.</value>
</data>
<data name="administration_user_voice_unmute" xml:space="preserve">
<value>{0} has been **voice unmuted**.</value>
</data>
<data name="administration_voice_chan_created" xml:space="preserve">
<value>Voice Channel Destroyed</value>
</data>
<data name="administration_voice_chan_destroyed" xml:space="preserve">
<value>Voice Channel Destroyed</value>
</data>
<data name="administration_vt_disabled" xml:space="preserve">
<value>Disabled voice + text feature.</value>
</data>
<data name="administration_vt_enabled" xml:space="preserve">
<value>Enabled voice + text feature.</value>
</data>
<data name="administration_vt_exit" xml:space="preserve">
<value>I don't have **manage roles** and/or **manage channels** permission, so I cannot run `voice+text` on {0} server.</value>
</data>
<data name="administration_vt_no_admin" xml:space="preserve">
<value>You are enabling/disabling this feature and **I do not have ADMINISTRATOR permissions**. This may cause some issues, and you will have to clean up text channels yourself afterwards.</value>
</data>
<data name="administration_vt_perms" xml:space="preserve">
<value>I require atleast **manage roles** and **manage channels** permissions to enable this feature. (preffered Administration permission)</value>
</data>
<data name="administration_xmuted_text" xml:space="preserve">
<value>User {0} from text chat</value>
</data>
<data name="administration_xmuted_text_and_voice" xml:space="preserve">
<value>User {0} from text and voice chat</value>
</data>
<data name="administration_xmuted_voice" xml:space="preserve">
<value>User {0} from voice chat</value>
</data>
<data name="administraton_sbdm" xml:space="preserve">
<value>You have been soft-banned from {0} server.
Reason: {1}</value>
</data>
<data name="administraton_user_unbanned" xml:space="preserve">
<value>User Unbanned</value>
</data>
<data name="adminsitration_migration_done" xml:space="preserve">
<value>Migration done!</value>
</data>
<data name="adminsitration_migration_error" xml:space="preserve">
<value>Error while migrating, check bot's console for more information.</value>
</data>
<data name="adminsitration_presence_updates" xml:space="preserve">
<value>Presence Updates</value>
</data>
<data name="adminsitration_sb_user" xml:space="preserve">
<value>User Soft-Banned</value>
</data>
<data name="gambling_awarded" xml:space="preserve">
<value>has awarded {0} to {1}</value>
</data>
<data name="gambling_betflip_gamble" xml:space="preserve">
<value>Betflip Gamble</value>
</data>
<data name="gambling_better_luck" xml:space="preserve">
<value>Better luck next time ^_^</value>
</data>
<data name="gambling_br_win" xml:space="preserve">
<value>Congratulations! You won {0} for rolling above {1}</value>
</data>
<data name="gambling_deck_reshuffled" xml:space="preserve">
<value>Deck reshuffled.</value>
</data>
<data name="gambling_flipped" xml:space="preserve">
<value>flipped {0}.</value>
<comment>User flipped tails.</comment>
</data>
<data name="gambling_flip_guess" xml:space="preserve">
<value>You guessed it! You won {0}</value>
</data>
<data name="gambling_flip_invalid" xml:space="preserve">
<value>Invalid number specified. You can flip 1 to {0} coins.</value>
</data>
<data name="gambling_flowerreaction_desc" xml:space="preserve">
<value>Add {0} reaction to this message to get {1} </value>
</data>
<data name="gambling_flowerreaction_footer" xml:space="preserve">
<value>This event is active for up to {0} hours.</value>
</data>
<data name="gambling_flowerreaction_title" xml:space="preserve">
<value>Flower reaction event started!</value>
</data>
<data name="gambling_gifted" xml:space="preserve">
<value>has gifted {0} to {1}</value>
<comment>X has gifted 15 flowers to Y</comment>
</data>
<data name="gambling_has" xml:space="preserve">
<value>{0} has {1}</value>
<comment>X has Y flowers</comment>
</data>
<data name="gambling_heads" xml:space="preserve">
<value>Heads</value>
</data>
<data name="gambling_leaderboard" xml:space="preserve">
<value>Leaderboard</value>
</data>
<data name="gambling_mass_award" xml:space="preserve">
<value>Awarded {0} to {1} users from {2} role.</value>
</data>
<data name="gambling_max_bet_limit" xml:space="preserve">
<value>You can't bet more than {0}</value>
</data>
<data name="gambling_min_bet_limit" xml:space="preserve">
<value>You can't bet less than {0}</value>
</data>
<data name="gambling_not_enough" xml:space="preserve">
<value>You don't have enough {0}</value>
</data>
<data name="gambling_no_more_cards" xml:space="preserve">
<value>No more cards in the deck.</value>
</data>
<data name="gambling_raffled_user" xml:space="preserve">
<value>Raffled User</value>
</data>
<data name="gambling_roll" xml:space="preserve">
<value>You rolled {0}.</value>
</data>
<data name="gambling_slot_bet" xml:space="preserve">
<value>Bet</value>
</data>
<data name="gambling_slot_jackpot" xml:space="preserve">
<value>WOAAHHHHHH!!! Congratulations!!! x{0}</value>
</data>
<data name="gambling_slot_single" xml:space="preserve">
<value>A single {0}, x{1}</value>
</data>
<data name="gambling_slot_three" xml:space="preserve">
<value>Wow! Lucky! Three of a kind! x{0}</value>
</data>
<data name="gambling_slot_two" xml:space="preserve">
<value>Good job! Two {0} - bet x{1}</value>
</data>
<data name="gambling_slot_won" xml:space="preserve">
<value>Won</value>
</data>
<data name="gambling_sneakygamestatus_desc" xml:space="preserve">
<value>Users must type a secret code to get {0}.
Lasts {1} seconds. Don't tell anyone. Shhh.</value>
</data>
<data name="gambling_sneakygamestatus_end" xml:space="preserve">
<value>SneakyGame event ended. {0} users received the reward.</value>
</data>
<data name="gambling_sneakygamestatus_title" xml:space="preserve">
<value>SneakyGameStatus event started</value>
</data>
<data name="gambling_tails" xml:space="preserve">
<value>Tails</value>
</data>
<data name="gambling_take" xml:space="preserve">
<value>successfully took {0} from {1}</value>
</data>
<data name="gambling_take_fail" xml:space="preserve">
<value>was unable to take {0} from{1} because the user doesn't have that much {2}!</value>
</data>
<data name="help_back_to_toc" xml:space="preserve">
<value>Back to ToC</value>
</data>
<data name="help_bot_owner_only" xml:space="preserve">
<value>Bot Owner Only</value>
</data>
<data name="help_channel_permission" xml:space="preserve">
<value>Requires {0} channel permission.</value>
</data>
<data name="help_cmdlist_donate" xml:space="preserve">
<value>You can support the project on patreon: &lt;{0}&gt; or paypal: &lt;{1}&gt;</value>
</data>
<data name="help_cmd_and_alias" xml:space="preserve">
<value>Command and aliases</value>
</data>
<data name="help_commandlist_regen" xml:space="preserve">
<value>Commandlist Regenerated.</value>
</data>
<data name="help_commands_instr" xml:space="preserve">
<value>Type `{0}h CommandName` to see the help for that specified command. e.g. `{0}h &gt;8ball`</value>
</data>
<data name="help_command_not_found" xml:space="preserve">
<value>I can't find that command. Please verify that the command exists before trying again.</value>
</data>
<data name="help_desc" xml:space="preserve">
<value>Description</value>
</data>
<data name="help_donate" xml:space="preserve">
<value>You can support the NadekoBot project on
Patreon &lt;{0}&gt; or
Paypal &lt;{1}&gt;
Don't forget to leave your discord name or id in the message.
**Thank you** ♥️</value>
</data>
<data name="help_guide" xml:space="preserve">
<value>**List of Commands**: &lt;{0}&gt;
**Hosting Guides and docs can be found here**: &lt;{1}&gt;</value>
</data>
<data name="help_list_of_commands" xml:space="preserve">
<value>List Of Commands</value>
</data>
<data name="help_list_of_modules" xml:space="preserve">
<value>List Of Modules</value>
</data>
<data name="help_modules_footer" xml:space="preserve">
<value>Type `{0}cmds ModuleName` to get a list of commands in that module. eg `{0}cmds games`</value>
</data>
<data name="help_module_not_found" xml:space="preserve">
<value>That module does not exist.</value>
</data>
<data name="help_server_permission" xml:space="preserve">
<value>Requires {0} server permission.</value>
</data>
<data name="help_table_of_contents" xml:space="preserve">
<value>Table Of Contents</value>
</data>
<data name="help_usage" xml:space="preserve">
<value>Usage</value>
</data>
<data name="nsfw_autohentai_started" xml:space="preserve">
<value>Autohentai started. Reposting every {0}s with one of the following tags:
{1}</value>
</data>
<data name="nsfw_tag" xml:space="preserve">
<value>Tag</value>
</data>
</root> </root>

View File

@ -0,0 +1,260 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace NadekoBot.Resources {
using System;
using System.Reflection;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResponseStrings_sr_SP {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
internal ResponseStrings_sr_SP() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NadekoBot.Resources.ResponseStrings-sr-SP", typeof(ResponseStrings_sr_SP).GetTypeInfo().Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to {0} has already fainted..
/// </summary>
public static string pokemon_already_fainted {
get {
return ResourceManager.GetString("pokemon_already_fainted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} already has full HP..
/// </summary>
public static string pokemon_already_full {
get {
return ResourceManager.GetString("pokemon_already_full", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Your type is already {0}.
/// </summary>
public static string pokemon_already_that_type {
get {
return ResourceManager.GetString("pokemon_already_that_type", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to used {0}{1} on {2}{3} for {4} damage..
/// </summary>
public static string pokemon_attack {
get {
return ResourceManager.GetString("pokemon_attack", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You can&apos;t attack again without retaliation!.
/// </summary>
public static string pokemon_cant_attack_again {
get {
return ResourceManager.GetString("pokemon_cant_attack_again", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You can&apos;t attack yourself..
/// </summary>
public static string pokemon_cant_attack_yourself {
get {
return ResourceManager.GetString("pokemon_cant_attack_yourself", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} has fainted!.
/// </summary>
public static string pokemon_fainted {
get {
return ResourceManager.GetString("pokemon_fainted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to healed {0} with one {1}.
/// </summary>
public static string pokemon_healed {
get {
return ResourceManager.GetString("pokemon_healed", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} has {1} HP remaining..
/// </summary>
public static string pokemon_hp_remaining {
get {
return ResourceManager.GetString("pokemon_hp_remaining", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You can&apos;t use {0}. Type `{1}ml` to see a list of moves you can use..
/// </summary>
public static string pokemon_invalid_move {
get {
return ResourceManager.GetString("pokemon_invalid_move", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Movelist for {0} type.
/// </summary>
public static string pokemon_moves {
get {
return ResourceManager.GetString("pokemon_moves", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You don&apos;t have enough {0}.
/// </summary>
public static string pokemon_no_currency {
get {
return ResourceManager.GetString("pokemon_no_currency", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to It&apos;s not effective..
/// </summary>
public static string pokemon_not_effective {
get {
return ResourceManager.GetString("pokemon_not_effective", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to revived {0} with one {1}.
/// </summary>
public static string pokemon_revive_other {
get {
return ResourceManager.GetString("pokemon_revive_other", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You revived yourself with one {0}.
/// </summary>
public static string pokemon_revive_yourself {
get {
return ResourceManager.GetString("pokemon_revive_yourself", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Your type has been changed to {0} for a {1}.
/// </summary>
public static string pokemon_settype_success {
get {
return ResourceManager.GetString("pokemon_settype_success", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to It&apos;s somewhat effective..
/// </summary>
public static string pokemon_somewhat_effective {
get {
return ResourceManager.GetString("pokemon_somewhat_effective", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to It&apos;s super effective!.
/// </summary>
public static string pokemon_super_effective {
get {
return ResourceManager.GetString("pokemon_super_effective", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You used too many moves in a row, so you can&apos;t move!.
/// </summary>
public static string pokemon_too_many_moves {
get {
return ResourceManager.GetString("pokemon_too_many_moves", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Type of {0} is {1}.
/// </summary>
public static string pokemon_type_of_user {
get {
return ResourceManager.GetString("pokemon_type_of_user", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to User not found..
/// </summary>
public static string pokemon_user_not_found {
get {
return ResourceManager.GetString("pokemon_user_not_found", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You fainted, so you are not able to move!.
/// </summary>
public static string pokemon_you_fainted {
get {
return ResourceManager.GetString("pokemon_you_fainted", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,368 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="pokemon_already_fainted" xml:space="preserve">
<value>{0} је већ онесвешћен.</value>
</data>
<data name="pokemon_already_full" xml:space="preserve">
<value>{0} је већ потпуно здрав.</value>
</data>
<data name="pokemon_already_that_type" xml:space="preserve">
<value>Твој тип већ јесте {0}</value>
</data>
<data name="pokemon_attack" xml:space="preserve">
<value>је искористио {0}{1} на {2}{3} и нанео {4} штете.</value>
<comment>Kwoth used punch:type_icon: on Sanity:type_icon: for 50 damage.</comment>
</data>
<data name="pokemon_cant_attack_again" xml:space="preserve">
<value>Не можеш напасти пре узвратног ударца.</value>
</data>
<data name="pokemon_cant_attack_yourself" xml:space="preserve">
<value>Не можеш напасти себе.</value>
</data>
<data name="pokemon_fainted" xml:space="preserve">
<value>{0} се онесвестио.</value>
</data>
<data name="pokemon_healed" xml:space="preserve">
<value>је излечио {0} са једним {1}</value>
</data>
<data name="pokemon_hp_remaining" xml:space="preserve">
<value>{0} има {1} преосталог здравља.</value>
</data>
<data name="pokemon_invalid_move" xml:space="preserve">
<value>Не можеш искористити {0}. Укуцај `{1}ml` да би видео листу удараца које можеш користити.</value>
</data>
<data name="pokemon_moves" xml:space="preserve">
<value>Листа покрета за {0} тип</value>
</data>
<data name="pokemon_not_effective" xml:space="preserve">
<value>Није ефективно.</value>
</data>
<data name="pokemon_no_currency" xml:space="preserve">
<value>Немаш довољно {0}</value>
</data>
<data name="pokemon_revive_other" xml:space="preserve">
<value>је оживео {0} са једним {1}</value>
</data>
<data name="pokemon_revive_yourself" xml:space="preserve">
<value>Оживео си себе са једним {0}</value>
</data>
<data name="pokemon_settype_success" xml:space="preserve">
<value>Твој тип је промењен на {0} за један {1}</value>
</data>
<data name="pokemon_somewhat_effective" xml:space="preserve">
<value>Има неког ефекта!</value>
</data>
<data name="pokemon_super_effective" xml:space="preserve">
<value>Супер ефективно!</value>
</data>
<data name="pokemon_too_many_moves" xml:space="preserve">
<value>Користио си превише напада узастопно, не можеш да се крећеш!</value>
</data>
<data name="pokemon_type_of_user" xml:space="preserve">
<value>{0}-ов тип је {1}</value>
</data>
<data name="pokemon_user_not_found" xml:space="preserve">
<value>Корисник није нађен.</value>
</data>
<data name="pokemon_you_fainted" xml:space="preserve">
<value>Онесвешћен си, не можеш да се крећеш.</value>
</data>
<data name="clashofclans_base_already_claimed" xml:space="preserve">
<value>Та база је већ под захетвом или је уништена.</value>
</data>
<data name="clashofclans_base_already_destroyed" xml:space="preserve">
<value>Та база је већ под захтевом.</value>
</data>
<data name="clashofclans_base_already_unclaimed" xml:space="preserve">
<value>Та база није под захтевом.</value>
</data>
<data name="clashofclans_base_unclaimed" xml:space="preserve">
<value>{0} је **ПОНИШТИО ЗАХТЕВ** за базу #{1} у рату против {2}</value>
</data>
<data name="clashofclans_claimed_base" xml:space="preserve">
<value>{0} захтева базу #{1} у рату против {2}</value>
</data>
<data name="clashofclans_claimed_other" xml:space="preserve">
<value>@{0} Ти већ захтеваш базу #{1}. Не можеш захтевати још једну.</value>
</data>
<data name="clashofclans_claim_expired" xml:space="preserve">
<value>Захтев од @{0} за рат против {1} је истекао.</value>
</data>
<data name="clashofclans_enemy" xml:space="preserve">
<value>Противник</value>
</data>
<data name="clashofclans_info_about_war" xml:space="preserve">
<value>Подаци о рату против {0}</value>
</data>
<data name="clashofclans_invalid_base_number" xml:space="preserve">
<value>Број базе није валидан.</value>
</data>
<data name="clashofclans_invalid_size" xml:space="preserve">
<value>Величина рата није валидна.</value>
</data>
<data name="clashofclans_list_active_wars" xml:space="preserve">
<value>Листа Ратова У Току</value>
</data>
<data name="clashofclans_not_claimed" xml:space="preserve">
<value>нема захтева</value>
</data>
<data name="clashofclans_not_partic" xml:space="preserve">
<value>Ти не учествујеш у том рату.</value>
</data>
<data name="clashofclans_not_partic_or_destroyed" xml:space="preserve">
<value>@{0} Ти или не учествујеш у рату или је та база већ уништена.</value>
</data>
<data name="clashofclans_no_active_wars" xml:space="preserve">
<value>Нема ратова у току.</value>
</data>
<data name="clashofclans_size" xml:space="preserve">
<value>Величина</value>
</data>
<data name="clashofclans_war_already_started" xml:space="preserve">
<value>Рат против {0} је већ почео.</value>
</data>
<data name="clashofclans_war_created" xml:space="preserve">
<value>Рат против {0} је направљен.</value>
</data>
<data name="clashofclans_war_ended" xml:space="preserve">
<value>Рат против {0} је завршен.</value>
</data>
<data name="clashofclans_war_not_exist" xml:space="preserve">
<value>Тај рат не постоји.</value>
</data>
<data name="clashofclans_war_started" xml:space="preserve">
<value>Рат против {0} је започет!</value>
</data>
<data name="clasofclans_base_destroyed" xml:space="preserve">
<value>је **УНИШТИО** базу #{0} у рату против {1}</value>
</data>
<data name="customreactions_all_stats_cleared" xml:space="preserve">
<value>Сва статистика Реакција по Избору је обрисана.</value>
</data>
<data name="customreactions_deleted" xml:space="preserve">
<value>Реакција по Избору обрисана</value>
</data>
<data name="customreactions_insuff_perms" xml:space="preserve">
<value>Немате дозволу. Потребно је поседовати бота за глобалне Реакције по Избору, а Администратор за серверске Реакције по Избору.</value>
</data>
<data name="customreactions_list_all" xml:space="preserve">
<value>Листа свих реакција по избору</value>
</data>
<data name="customreactions_name" xml:space="preserve">
<value>Реакције по Избору</value>
</data>
<data name="customreactions_new_cust_react" xml:space="preserve">
<value>Нова Реакција по Избору</value>
</data>
<data name="customreactions_no_found" xml:space="preserve">
<value>Реакција по избору није нађена.</value>
</data>
<data name="customreactions_no_found_id" xml:space="preserve">
<value>Није нађена реакција са тим идентификатором.</value>
</data>
<data name="customreactions_response" xml:space="preserve">
<value>Одговор</value>
</data>
<data name="customreactions_stats" xml:space="preserve">
<value>Статистика Реакција по Избору</value>
</data>
<data name="customreactions_stats_cleared" xml:space="preserve">
<value>Статистика је обрисана за {0} реакцију по избору.</value>
</data>
<data name="customreactions_stats_not_found" xml:space="preserve">
<value>Није нађена статистика за тај окидач. Нема промене.</value>
</data>
<data name="customreactions_trigger" xml:space="preserve">
<value>Окидач</value>
</data>
<data name="help_back_to_toc" xml:space="preserve">
<value>Назад на Садржај</value>
</data>
<data name="help_bot_owner_only" xml:space="preserve">
<value>Само Власник Бота</value>
</data>
<data name="help_channel_permission" xml:space="preserve">
<value>Захтева {0} дозволу на каналу.</value>
</data>
<data name="help_cmdlist_donate" xml:space="preserve">
<value>Можете подржати пројекат на Пејтриону: &lt;{0}&gt; или Пејпалу: &lt;{1}&gt;</value>
</data>
<data name="help_cmd_and_alias" xml:space="preserve">
<value>Команде и псеудоними</value>
</data>
<data name="help_commandlist_regen" xml:space="preserve">
<value>Листа команди је обновљена.</value>
</data>
<data name="help_commands_instr" xml:space="preserve">
<value>Укуцај `{0}h ИмеКоманде` да видиш помоћ за ту команду. нпр. `{0}h &gt;8ball`</value>
</data>
<data name="help_command_not_found" xml:space="preserve">
<value>Не могу да нађем ту команду. Проверите да ли команда постоји, па покушајте поново.</value>
</data>
<data name="help_desc" xml:space="preserve">
<value>Опис</value>
</data>
<data name="help_donate" xml:space="preserve">
<value>Можете подржати НадекоБот пројекат на
Пејтриону &lt;{0}&gt; или
Пејпалу &lt;{1}&gt;
Не заборавите да оставите своје корисничко име или ИД.
**Хвала** ♥️</value>
</data>
<data name="help_guide" xml:space="preserve">
<value>**List of Commands**: &lt;{0}&gt;
**Hosting Guides and docs can be found here**: &lt;{1}&gt;</value>
</data>
<data name="help_list_of_commands" xml:space="preserve">
<value>Листа Команди</value>
</data>
<data name="help_list_of_modules" xml:space="preserve">
<value>Листа Модула</value>
</data>
<data name="help_modules_footer" xml:space="preserve">
<value>Укуцај `{0}cmds ИмеМодула` да видиш листу свих команди у том модулу. нпр `{0}cmds games`</value>
</data>
<data name="help_module_not_found" xml:space="preserve">
<value>Тај модул не постоји.</value>
</data>
<data name="help_server_permission" xml:space="preserve">
<value>Захтева {0} дозволу на серверу.</value>
</data>
<data name="help_table_of_contents" xml:space="preserve">
<value>Садржај</value>
</data>
<data name="help_usage" xml:space="preserve">
<value>Упутство</value>
</data>
<data name="nsfw_autohentai_started" xml:space="preserve">
<value>Аутохентаи започет. Постоваћу сваких {0} сек. користећи један од следећих тагова:
{1}</value>
</data>
<data name="nsfw_autohentai_stopped" xml:space="preserve">
<value>АутоХентаи заустављен.</value>
</data>
<data name="nsfw_no_results" xml:space="preserve">
<value>Нема резултата.</value>
</data>
<data name="nsfw_tag" xml:space="preserve">
<value>Таг</value>
</data>
</root>

View File

@ -30,9 +30,9 @@ namespace Services.CleverBotApi
public static ChatterBot Create(ChatterBotType type, object arg) public static ChatterBot Create(ChatterBotType type, object arg)
{ {
#if GLOBAL_NADEKO #if GLOBAL_NADEKO
var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=nadekobot"; var url = "http://www.cleverbot.com/webservicemin?uc=777&botapi=nadekobot";
#else #else
var url = "http://www.cleverbot.com/webservicemin?uc=3210&botapi=chatterbotapi"; var url = "http://www.cleverbot.com/webservicemin?uc=777&botapi=chatterbotapi";
#endif #endif
switch (type) switch (type)

View File

@ -73,7 +73,7 @@ namespace NadekoBot.Services
if (!ownerChannels.Any()) if (!ownerChannels.Any())
_log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file."); _log.Warn("No owner channels created! Make sure you've specified correct OwnerId in the credentials.json file.");
else else
_log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Length} owner message channels."); _log.Info($"Created {ownerChannels.Count} out of {NadekoBot.Credentials.OwnerIds.Count} owner message channels.");
_client.MessageReceived += MessageReceivedHandler; _client.MessageReceived += MessageReceivedHandler;
} }
@ -273,7 +273,7 @@ namespace NadekoBot.Services
await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels).ConfigureAwait(false); await SelfCommands.HandleDmForwarding(msg, ownerChannels).ConfigureAwait(false);
} }
} }
} }

View File

@ -60,6 +60,7 @@ Nadeko Support Server: https://discord.gg/nadekobot";
public string OkColor { get; set; } = "71cd40"; public string OkColor { get; set; } = "71cd40";
public string ErrorColor { get; set; } = "ee281f"; public string ErrorColor { get; set; } = "ee281f";
public string Locale { get; set; } = null;
} }
public class PlayingStatus :DbEntity public class PlayingStatus :DbEntity

View File

@ -12,7 +12,6 @@ namespace NadekoBot.Services.Database.Models
public string Trigger { get; set; } public string Trigger { get; set; }
public bool IsRegex { get; set; } public bool IsRegex { get; set; }
public bool OwnerOnly { get; set; } public bool OwnerOnly { get; set; }
public override string ToString() => $"`#{Id}` `Trigger:` {Trigger}\n `Response:` {Response}";
} }
public class ReactionResponse : DbEntity public class ReactionResponse : DbEntity

View File

@ -64,6 +64,9 @@ namespace NadekoBot.Services.Database.Models
public AntiRaidSetting AntiRaidSetting { get; set; } public AntiRaidSetting AntiRaidSetting { get; set; }
public AntiSpamSetting AntiSpamSetting { get; set; } public AntiSpamSetting AntiSpamSetting { get; set; }
public string Locale { get; set; } = null;
public string TimeZoneId { get; set; } = null;
//public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>(); //public List<ProtectionIgnoredChannel> ProtectionIgnoredChannels { get; set; } = new List<ProtectionIgnoredChannel>();
} }

View File

@ -132,7 +132,7 @@ namespace NadekoBot.Services.Database
{ {
#region QUOTES #region QUOTES
var quoteEntity = modelBuilder.Entity<Quote>(); //var quoteEntity = modelBuilder.Entity<Quote>();
#endregion #endregion
@ -166,7 +166,7 @@ namespace NadekoBot.Services.Database
#endregion #endregion
#region BotConfig #region BotConfig
var botConfigEntity = modelBuilder.Entity<BotConfig>(); //var botConfigEntity = modelBuilder.Entity<BotConfig>();
//botConfigEntity //botConfigEntity
// .HasMany(c => c.ModulePrefixes) // .HasMany(c => c.ModulePrefixes)
// .WithOne(mp => mp.BotConfig) // .WithOne(mp => mp.BotConfig)

View File

@ -1,4 +1,6 @@
using Discord; using Discord;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace NadekoBot.Services namespace NadekoBot.Services
{ {
@ -9,7 +11,7 @@ namespace NadekoBot.Services
string Token { get; } string Token { get; }
string GoogleApiKey { get; } string GoogleApiKey { get; }
ulong[] OwnerIds { get; } ImmutableHashSet<ulong> OwnerIds { get; }
string MashapeKey { get; } string MashapeKey { get; }
string LoLApiKey { get; } string LoLApiKey { get; }

View File

@ -5,6 +5,8 @@ using Discord;
using System.Linq; using System.Linq;
using NLog; using NLog;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace NadekoBot.Services.Impl namespace NadekoBot.Services.Impl
{ {
@ -21,7 +23,7 @@ namespace NadekoBot.Services.Impl
public string Token { get; } public string Token { get; }
public ulong[] OwnerIds { get; } public ImmutableHashSet<ulong> OwnerIds { get; }
public string LoLApiKey { get; } public string LoLApiKey { get; }
public string OsuApiKey { get; } public string OsuApiKey { get; }
@ -61,7 +63,7 @@ namespace NadekoBot.Services.Impl
Token = data[nameof(Token)]; Token = data[nameof(Token)];
if (string.IsNullOrWhiteSpace(Token)) if (string.IsNullOrWhiteSpace(Token))
throw new ArgumentNullException(nameof(Token), "Token is missing from credentials.json or Environment varibles."); throw new ArgumentNullException(nameof(Token), "Token is missing from credentials.json or Environment varibles.");
OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToArray(); OwnerIds = data.GetSection("OwnerIds").GetChildren().Select(c => ulong.Parse(c.Value)).ToImmutableHashSet();
LoLApiKey = data[nameof(LoLApiKey)]; LoLApiKey = data[nameof(LoLApiKey)];
GoogleApiKey = data[nameof(GoogleApiKey)]; GoogleApiKey = data[nameof(GoogleApiKey)];
MashapeKey = data[nameof(MashapeKey)]; MashapeKey = data[nameof(MashapeKey)];

View File

@ -1,45 +1,122 @@
namespace NadekoBot.Services using Discord;
using Discord.Commands;
using NadekoBot.Extensions;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using System;
using NadekoBot.Services.Database;
using NLog;
namespace NadekoBot.Services
{ {
public class Localization public class Localization
{ {
public string this[string key] => LoadCommandString(key); private readonly Logger _log;
public ConcurrentDictionary<ulong, CultureInfo> GuildCultureInfos { get; }
public CultureInfo DefaultCultureInfo { get; private set; } = CultureInfo.CurrentCulture;
private Localization() { }
public Localization(string defaultCulture, IDictionary<ulong, string> cultureInfoNames)
{
_log = LogManager.GetCurrentClassLogger();
if (string.IsNullOrWhiteSpace(defaultCulture))
DefaultCultureInfo = new CultureInfo("en-US");
else
{
try
{
DefaultCultureInfo = new CultureInfo(defaultCulture);
}
catch
{
_log.Warn("Unable to load default bot's locale/language. Using en-US.");
DefaultCultureInfo = new CultureInfo("en-US");
}
}
GuildCultureInfos = new ConcurrentDictionary<ulong, CultureInfo>(cultureInfoNames.ToDictionary(x => x.Key, x =>
{
CultureInfo cultureInfo = null;
try
{
cultureInfo = new CultureInfo(x.Value);
}
catch { }
return cultureInfo;
}).Where(x => x.Value != null));
}
public void SetGuildCulture(IGuild guild, CultureInfo ci) =>
SetGuildCulture(guild.Id, ci);
public void SetGuildCulture(ulong guildId, CultureInfo ci)
{
if (ci == DefaultCultureInfo)
{
RemoveGuildCulture(guildId);
return;
}
using (var uow = DbHandler.UnitOfWork())
{
var gc = uow.GuildConfigs.For(guildId, set => set);
gc.Locale = ci.Name;
uow.Complete();
}
GuildCultureInfos.AddOrUpdate(guildId, ci, (id, old) => ci);
}
public void RemoveGuildCulture(IGuild guild) =>
RemoveGuildCulture(guild.Id);
public void RemoveGuildCulture(ulong guildId) {
CultureInfo throwaway;
if (GuildCultureInfos.TryRemove(guildId, out throwaway))
{
using (var uow = DbHandler.UnitOfWork())
{
var gc = uow.GuildConfigs.For(guildId, set => set);
gc.Locale = null;
uow.Complete();
}
}
}
public void SetDefaultCulture(CultureInfo ci)
{
using (var uow = DbHandler.UnitOfWork())
{
var bc = uow.BotConfig.GetOrCreate();
bc.Locale = ci.Name;
uow.Complete();
}
DefaultCultureInfo = ci;
}
public void ResetDefaultCulture() =>
SetDefaultCulture(CultureInfo.CurrentCulture);
public CultureInfo GetCultureInfo(IGuild guild) =>
GetCultureInfo(guild?.Id);
public CultureInfo GetCultureInfo(ulong? guildId)
{
if (guildId == null)
return DefaultCultureInfo;
CultureInfo info = null;
GuildCultureInfos.TryGetValue(guildId.Value, out info);
return info ?? DefaultCultureInfo;
}
public static string LoadCommandString(string key) public static string LoadCommandString(string key)
{ {
string toReturn = Resources.CommandStrings.ResourceManager.GetString(key); string toReturn = Resources.CommandStrings.ResourceManager.GetString(key);
return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn; return string.IsNullOrWhiteSpace(toReturn) ? key : toReturn;
} }
//private static string GetCommandString(string key)
//{
// return key;
//var resx = new List<DictionaryEntry>();
//var fs = new StreamReader(File.OpenRead("./Strings.resx"));
//Console.WriteLine(fs.ReadToEnd());
//using (var reader = new ResourceReader(fs.BaseStream))
//{
// List<DictionaryEntry> existing = new List<DictionaryEntry>();
// foreach (DictionaryEntry item in reader)
// {
// existing.Add(item);
// }
// var existingResource = resx.Where(r => r.Key.ToString() == key).FirstOrDefault();
// if (existingResource.Key == null)
// {
// resx.Add(new DictionaryEntry() { Key = key, Value = key });
// }
// else
// return existingResource.Value.ToString();
//}
//using (var writer = new ResourceWriter(new FileStream("./Strings.resx", FileMode.OpenOrCreate)))
//{
// resx.ForEach(r =>
// {
// writer.AddResource(r.Key.ToString(), r.Value.ToString());
// });
// writer.Generate();
//}
//return key;
//}
} }
} }

View File

@ -15,7 +15,7 @@ namespace NadekoBot.Services.Impl
private DiscordShardedClient client; private DiscordShardedClient client;
private DateTime started; private DateTime started;
public const string BotVersion = "1.1.6"; public const string BotVersion = "1.1.8-alpha";
public string Author => "Kwoth#2560"; public string Author => "Kwoth#2560";
public string Library => "Discord.Net"; public string Library => "Discord.Net";
@ -96,18 +96,21 @@ namespace NadekoBot.Services.Impl
content.Headers.Clear(); content.Headers.Clear();
content.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); content.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
var res = await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false); await http.PostAsync("https://www.carbonitex.net/discord/data/botdata.php", content).ConfigureAwait(false);
} }
}; };
} }
catch { } catch
{
// ignored
}
}, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); }, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1));
} }
public void Initialize() public void Initialize()
{ {
var guilds = this.client.GetGuilds(); var guilds = this.client.GetGuilds().ToArray();
_textChannels = guilds.Sum(g => g.Channels.Where(cx => cx is ITextChannel).Count()); _textChannels = guilds.Sum(g => g.Channels.Count(cx => cx is ITextChannel));
_voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels; _voiceChannels = guilds.Sum(g => g.Channels.Count) - _textChannels;
} }

View File

@ -41,7 +41,6 @@ namespace NadekoBot.Services
return minValue; return minValue;
var bytes = new byte[sizeof(int)]; var bytes = new byte[sizeof(int)];
rng.GetBytes(bytes); rng.GetBytes(bytes);
var num = BitConverter.ToInt32(bytes, 0);
var sign = Math.Sign(BitConverter.ToInt32(bytes, 0)); var sign = Math.Sign(BitConverter.ToInt32(bytes, 0));
return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue; return (sign * BitConverter.ToInt32(bytes, 0)) % (maxValue - minValue) + minValue;
} }

View File

@ -18,7 +18,7 @@
}, },
"dependencies": { "dependencies": {
"AngleSharp": "0.9.9", "AngleSharp": "0.9.9",
"libvideo": "1.0.0", "libvideo": "1.0.1",
"CoreCLR-NCalc": "2.1.2", "CoreCLR-NCalc": "2.1.2",
"Google.Apis.Urlshortener.v1": "1.19.0.138", "Google.Apis.Urlshortener.v1": "1.19.0.138",
"Google.Apis.YouTube.v3": "1.20.0.701", "Google.Apis.YouTube.v3": "1.20.0.701",