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
73 changed files with 7286 additions and 1299 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

@@ -17,7 +17,7 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling
{
[Group]
public class DriceRollCommands : ModuleBase
public class DriceRollCommands : NadekoSubmodule
{
private Regex dndRegex { get; } = new Regex(@"^(?<n1>\d+)d(?<n2>\d+)(?:\+(?<add>\d+))?(?:\-(?<sub>\d+))?$", 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.Extensions;
using NadekoBot.Modules.Gambling.Models;
using NLog;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
@@ -17,17 +15,17 @@ namespace NadekoBot.Modules.Gambling
public partial class Gambling
{
[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]
[RequireContext(ContextType.Guild)]
public async Task Draw(int num = 1)
{
var cards = AllDecks.GetOrAdd(Context.Guild, (s) => new Cards());
var cards = _allDecks.GetOrAdd(Context.Guild, (s) => new Cards());
var images = new List<Image>();
var cardObjects = new List<Cards.Card>();
if (num > 5) num = 5;
@@ -35,12 +33,19 @@ namespace NadekoBot.Modules.Gambling
{
if (cards.CardPool.Count == 0 && i != 0)
{
try { await Context.Channel.SendErrorAsync("No more cards in a deck.").ConfigureAwait(false); } catch { }
try
{
await ReplyErrorLocalized("no_more_cards").ConfigureAwait(false);
}
catch
{
// ignored
}
break;
}
var currentCard = cards.DrawACard();
cardObjects.Add(currentCard);
using (var stream = File.OpenRead(Path.Combine(cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
using (var stream = File.OpenRead(Path.Combine(_cardsPath, currentCard.ToString().ToLowerInvariant()+ ".jpg").Replace(' ','_')))
images.Add(new Image(stream));
}
MemoryStream bitmapStream = new MemoryStream();
@@ -59,7 +64,7 @@ namespace NadekoBot.Modules.Gambling
{
//var channel = (ITextChannel)Context.Channel;
AllDecks.AddOrUpdate(Context.Guild,
_allDecks.AddOrUpdate(Context.Guild,
(g) => new Cards(),
(g, c) =>
{
@@ -67,7 +72,7 @@ namespace NadekoBot.Modules.Gambling
return c;
});
await Context.Channel.SendConfirmAsync("Deck reshuffled.").ConfigureAwait(false);
await ReplyConfirmLocalized("deck_reshuffled").ConfigureAwait(false);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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;
}
else
activeCdsForGuild.Add(new ActiveCooldown()
{
activeCdsForGuild.Add(new ActiveCooldown()
UserId = user.Id,
Command = cmd.Aliases.First().ToLowerInvariant(),
});
var _ = Task.Run(async () =>
{
try
{
UserId = user.Id,
Command = cmd.Aliases.First().ToLowerInvariant(),
});
var t = Task.Run(async () =>
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
}
catch
{
try
{
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Aliases.First().ToLowerInvariant() && ac.UserId == user.Id);
}
catch { }
});
}
// ignored
}
});
}
return false;
}

View File

@@ -42,23 +42,19 @@ namespace NadekoBot.Modules.Permissions
static FilterCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
var guildConfigs = NadekoBot.AllGuildConfigs;
var guildConfigs = NadekoBot.AllGuildConfigs;
InviteFilteringServers = new ConcurrentHashSet<ulong>(guildConfigs.Where(gc => gc.FilterInvites).Select(gc => gc.GuildId));
InviteFilteringChannels = new ConcurrentHashSet<ulong>(guildConfigs.SelectMany(gc => gc.FilterInvitesChannelIds.Select(fci => fci.ChannelId)));
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)));
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);
WordFilteringServers = new ConcurrentHashSet<ulong>(serverFiltering.Select(gc => gc.GuildId));
var serverFiltering = guildConfigs.Where(gc => gc.FilterWords);
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]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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