A lot more localization, utility module done.

This commit is contained in:
Kwoth 2017-02-23 03:06:37 +01:00
parent 5cdb41b65f
commit 53140940d7
10 changed files with 787 additions and 148 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3353,6 +3353,42 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Joined.
/// </summary>
public static string utiliity_joined {
get {
return ResourceManager.GetString("utiliity_joined", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}.` {1} [{2:F2}/s] - {3} total.
/// </summary>
public static string utility_activity_line {
get {
return ResourceManager.GetString("utility_activity_line", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Activity Page #{0}.
/// </summary>
public static string utility_activity_page {
get {
return ResourceManager.GetString("utility_activity_page", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} users total..
/// </summary>
public static string utility_activity_users_total {
get {
return ResourceManager.GetString("utility_activity_users_total", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Author.
/// </summary>
@ -3371,6 +3407,15 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to List of functions in {0}calc command.
/// </summary>
public static string utility_calcops {
get {
return ResourceManager.GetString("utility_calcops", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Channel Topic.
/// </summary>
@ -3398,6 +3443,123 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to {0} {1} is equal to {2} {3}.
/// </summary>
public static string utility_convert {
get {
return ResourceManager.GetString("utility_convert", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot convert {0} to {1}: units not found.
/// </summary>
public static string utility_convert_not_found {
get {
return ResourceManager.GetString("utility_convert_not_found", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot convert {0} to {1}: types of unit are not equal.
/// </summary>
public static string utility_convert_type_error {
get {
return ResourceManager.GetString("utility_convert_type_error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Units which can be used by the converter.
/// </summary>
public static string utility_convertlist {
get {
return ResourceManager.GetString("utility_convertlist", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Created At.
/// </summary>
public static string utility_created_at {
get {
return ResourceManager.GetString("utility_created_at", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Joined cross server channel..
/// </summary>
public static string utility_csc_join {
get {
return ResourceManager.GetString("utility_csc_join", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Left cross server channel..
/// </summary>
public static string utility_csc_leave {
get {
return ResourceManager.GetString("utility_csc_leave", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This is your CSC token.
/// </summary>
public static string utility_csc_token {
get {
return ResourceManager.GetString("utility_csc_token", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Custom Emojis.
/// </summary>
public static string utility_custom_emojis {
get {
return ResourceManager.GetString("utility_custom_emojis", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Error.
/// </summary>
public static string utility_error {
get {
return ResourceManager.GetString("utility_error", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Features.
/// </summary>
public static string utility_features {
get {
return ResourceManager.GetString("utility_features", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ID.
/// </summary>
public static string utility_id {
get {
return ResourceManager.GetString("utility_id", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Index out of range..
/// </summary>
public static string utility_index_out_of_range {
get {
return ResourceManager.GetString("utility_index_out_of_range", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Here is a list of users in those roles:.
/// </summary>
@ -3416,6 +3578,42 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Invalid {0} value..
/// </summary>
public static string utility_invalid_value {
get {
return ResourceManager.GetString("utility_invalid_value", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Joined Discord.
/// </summary>
public static string utility_joined_discord {
get {
return ResourceManager.GetString("utility_joined_discord", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Joined Server.
/// </summary>
public static string utility_joined_server {
get {
return ResourceManager.GetString("utility_joined_server", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to List of Repeaters.
/// </summary>
public static string utility_list_of_repeaters {
get {
return ResourceManager.GetString("utility_list_of_repeaters", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to ID: {0}
///Members: {1}
@ -3436,6 +3634,15 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Members.
/// </summary>
public static string utility_members {
get {
return ResourceManager.GetString("utility_members", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Memory.
/// </summary>
@ -3445,6 +3652,15 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Message Repeater.
/// </summary>
public static string utility_message_repeater {
get {
return ResourceManager.GetString("utility_message_repeater", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Messages.
/// </summary>
@ -3454,6 +3670,33 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Name.
/// </summary>
public static string utility_name {
get {
return ResourceManager.GetString("utility_name", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Nickname.
/// </summary>
public static string utility_nickname {
get {
return ResourceManager.GetString("utility_nickname", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No active repeaters..
/// </summary>
public static string utility_no_active_repeaters {
get {
return ResourceManager.GetString("utility_no_active_repeaters", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No roles on this page..
/// </summary>
@ -3490,6 +3733,15 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Owner.
/// </summary>
public static string utility_owner {
get {
return ResourceManager.GetString("utility_owner", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Owner IDs.
/// </summary>
@ -3519,6 +3771,168 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Quote Added.
/// </summary>
public static string utility_quote_added {
get {
return ResourceManager.GetString("utility_quote_added", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Deleted a random quote..
/// </summary>
public static string utility_quote_deleted {
get {
return ResourceManager.GetString("utility_quote_deleted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Deleted all quotes with {0} keyword..
/// </summary>
public static string utility_quotes_deleted {
get {
return ResourceManager.GetString("utility_quotes_deleted", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Page {0} of quotes.
/// </summary>
public static string utility_quotes_page {
get {
return ResourceManager.GetString("utility_quotes_page", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No quotes on this page..
/// </summary>
public static string utility_quotes_page_none {
get {
return ResourceManager.GetString("utility_quotes_page_none", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No quotes found which you can remove..
/// </summary>
public static string utility_quotes_remove_none {
get {
return ResourceManager.GetString("utility_quotes_remove_none", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Region.
/// </summary>
public static string utility_region {
get {
return ResourceManager.GetString("utility_region", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Registered On.
/// </summary>
public static string utility_registered_on {
get {
return ResourceManager.GetString("utility_registered_on", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})`.
/// </summary>
public static string utility_remind {
get {
return ResourceManager.GetString("utility_remind", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Not a valid time format. Check the commandlist..
/// </summary>
public static string utility_remind_invalid_format {
get {
return ResourceManager.GetString("utility_remind_invalid_format", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to New remind template set..
/// </summary>
public static string utility_remind_template {
get {
return ResourceManager.GetString("utility_remind_template", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No repeating messages found on this server..
/// </summary>
public static string utility_repeat_invoke_none {
get {
return ResourceManager.GetString("utility_repeat_invoke_none", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Repeating {0} every {1} day(s), {2} hour(s) and {3} minute(s)..
/// </summary>
public static string utility_repeater {
get {
return ResourceManager.GetString("utility_repeater", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to #{0} stopped..
/// </summary>
public static string utility_repeater_stopped {
get {
return ResourceManager.GetString("utility_repeater_stopped", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to List Of Repeaters.
/// </summary>
public static string utility_repeaters_list {
get {
return ResourceManager.GetString("utility_repeaters_list", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No repeaters running on this server..
/// </summary>
public static string utility_repeaters_none {
get {
return ResourceManager.GetString("utility_repeaters_none", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Result.
/// </summary>
public static string utility_result {
get {
return ResourceManager.GetString("utility_result", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Roles.
/// </summary>
public static string utility_roles {
get {
return ResourceManager.GetString("utility_roles", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Page #{0} of all roles on this server:.
/// </summary>
@ -3564,6 +3978,15 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Server Info.
/// </summary>
public static string utility_server_info {
get {
return ResourceManager.GetString("utility_server_info", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} of this server is {1}.
/// </summary>
@ -3627,6 +4050,15 @@ namespace NadekoBot.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Text Channels.
/// </summary>
public static string utility_text_channels {
get {
return ResourceManager.GetString("utility_text_channels", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Here is your room link:.
/// </summary>
@ -3653,5 +4085,23 @@ namespace NadekoBot.Resources {
return ResourceManager.GetString("utility_userid", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Users.
/// </summary>
public static string utility_users {
get {
return ResourceManager.GetString("utility_users", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Voice Channels.
/// </summary>
public static string utility_voice_channels {
get {
return ResourceManager.GetString("utility_voice_channels", resourceCulture);
}
}
}
}

View File

@ -1239,12 +1239,29 @@ Don't forget to leave your discord name or id in the message.
<data name="games_hangman_types" xml:space="preserve">
<value>List of "{0}hangman" term types:</value>
</data>
<data name="utiliity_joined" xml:space="preserve">
<value>Joined</value>
</data>
<data name="utility_activity_line" xml:space="preserve">
<value>`{0}.` {1} [{2:F2}/s] - {3} total</value>
<comment>/s and total need to be localized to fit the context -
`1.` </comment>
</data>
<data name="utility_activity_page" xml:space="preserve">
<value>Activity Page #{0}</value>
</data>
<data name="utility_activity_users_total" xml:space="preserve">
<value>{0} users total.</value>
</data>
<data name="utility_author" xml:space="preserve">
<value>Author</value>
</data>
<data name="utility_botid" xml:space="preserve">
<value>Bot ID</value>
</data>
<data name="utility_calcops" xml:space="preserve">
<value>List of functions in {0}calc command</value>
</data>
<data name="utility_channelid" xml:space="preserve">
<value>{0} of this channel is {1}</value>
</data>
@ -1254,12 +1271,61 @@ Don't forget to leave your discord name or id in the message.
<data name="utility_commands_ran" xml:space="preserve">
<value>Commands Ran</value>
</data>
<data name="utility_convert" xml:space="preserve">
<value>{0} {1} is equal to {2} {3}</value>
</data>
<data name="utility_convertlist" xml:space="preserve">
<value>Units which can be used by the converter</value>
</data>
<data name="utility_convert_not_found" xml:space="preserve">
<value>Cannot convert {0} to {1}: units not found</value>
</data>
<data name="utility_convert_type_error" xml:space="preserve">
<value>Cannot convert {0} to {1}: types of unit are not equal</value>
</data>
<data name="utility_created_at" xml:space="preserve">
<value>Created At</value>
</data>
<data name="utility_csc_join" xml:space="preserve">
<value>Joined cross server channel.</value>
</data>
<data name="utility_csc_leave" xml:space="preserve">
<value>Left cross server channel.</value>
</data>
<data name="utility_csc_token" xml:space="preserve">
<value>This is your CSC token</value>
</data>
<data name="utility_custom_emojis" xml:space="preserve">
<value>Custom Emojis</value>
</data>
<data name="utility_error" xml:space="preserve">
<value>Error</value>
</data>
<data name="utility_features" xml:space="preserve">
<value>Features</value>
</data>
<data name="utility_id" xml:space="preserve">
<value>ID</value>
</data>
<data name="utility_index_out_of_range" xml:space="preserve">
<value>Index out of range.</value>
</data>
<data name="utility_inrole_list" xml:space="preserve">
<value>Here is a list of users in those roles:</value>
</data>
<data name="utility_inrole_not_allowed" xml:space="preserve">
<value>you are not allowed to use this command on roles with a lot of users in them to prevent abuse.</value>
</data>
<data name="utility_invalid_value" xml:space="preserve">
<value>Invalid {0} value.</value>
<comment>Invalid months value/ Invalid hours value</comment>
</data>
<data name="utility_joined_discord" xml:space="preserve">
<value>Joined Discord</value>
</data>
<data name="utility_joined_server" xml:space="preserve">
<value>Joined Server</value>
</data>
<data name="utility_listservers" xml:space="preserve">
<value>ID: {0}
Members: {1}
@ -1268,15 +1334,33 @@ OwnerID: {2}</value>
<data name="utility_listservers_none" xml:space="preserve">
<value>No servers found on that page.</value>
</data>
<data name="utility_list_of_repeaters" xml:space="preserve">
<value>List of Repeaters</value>
</data>
<data name="utility_members" xml:space="preserve">
<value>Members</value>
</data>
<data name="utility_memory" xml:space="preserve">
<value>Memory</value>
</data>
<data name="utility_messages" xml:space="preserve">
<value>Messages</value>
</data>
<data name="utility_message_repeater" xml:space="preserve">
<value>Message Repeater</value>
</data>
<data name="utility_name" xml:space="preserve">
<value>Name</value>
</data>
<data name="utility_nickname" xml:space="preserve">
<value>Nickname</value>
</data>
<data name="utility_nobody_playing_game" xml:space="preserve">
<value>Nobody is playing that game.</value>
</data>
<data name="utility_no_active_repeaters" xml:space="preserve">
<value>No active repeaters.</value>
</data>
<data name="utility_no_roles_on_page" xml:space="preserve">
<value>No roles on this page.</value>
</data>
@ -1286,6 +1370,9 @@ OwnerID: {2}</value>
<data name="utility_no_topic_set" xml:space="preserve">
<value>No topic set.</value>
</data>
<data name="utility_owner" xml:space="preserve">
<value>Owner</value>
</data>
<data name="utility_owner_ids" xml:space="preserve">
<value>Owner IDs</value>
</data>
@ -1297,6 +1384,60 @@ OwnerID: {2}</value>
{1} Text Channels
{2} Voice Channels</value>
</data>
<data name="utility_quotes_deleted" xml:space="preserve">
<value>Deleted all quotes with {0} keyword.</value>
</data>
<data name="utility_quotes_page" xml:space="preserve">
<value>Page {0} of quotes</value>
</data>
<data name="utility_quotes_page_none" xml:space="preserve">
<value>No quotes on this page.</value>
</data>
<data name="utility_quotes_remove_none" xml:space="preserve">
<value>No quotes found which you can remove.</value>
</data>
<data name="utility_quote_added" xml:space="preserve">
<value>Quote Added</value>
</data>
<data name="utility_quote_deleted" xml:space="preserve">
<value>Deleted a random quote.</value>
</data>
<data name="utility_region" xml:space="preserve">
<value>Region</value>
</data>
<data name="utility_registered_on" xml:space="preserve">
<value>Registered On</value>
</data>
<data name="utility_remind" xml:space="preserve">
<value>I will remind {0} to {1} in {2} `({3:d.M.yyyy.} at {4:HH:mm})`</value>
</data>
<data name="utility_remind_invalid_format" xml:space="preserve">
<value>Not a valid time format. Check the commandlist.</value>
</data>
<data name="utility_remind_template" xml:space="preserve">
<value>New remind template set.</value>
</data>
<data name="utility_repeater" xml:space="preserve">
<value>Repeating {0} every {1} day(s), {2} hour(s) and {3} minute(s).</value>
</data>
<data name="utility_repeaters_list" xml:space="preserve">
<value>List Of Repeaters</value>
</data>
<data name="utility_repeaters_none" xml:space="preserve">
<value>No repeaters running on this server.</value>
</data>
<data name="utility_repeater_stopped" xml:space="preserve">
<value>#{0} stopped.</value>
</data>
<data name="utility_repeat_invoke_none" xml:space="preserve">
<value>No repeating messages found on this server.</value>
</data>
<data name="utility_result" xml:space="preserve">
<value>Result</value>
</data>
<data name="utility_roles" xml:space="preserve">
<value>Roles</value>
</data>
<data name="utility_roles_all_page" xml:space="preserve">
<value>Page #{0} of all roles on this server:</value>
</data>
@ -1315,6 +1456,9 @@ OwnerID: {2}</value>
<data name="utility_serverid" xml:space="preserve">
<value>{0} of this server is {1}</value>
</data>
<data name="utility_server_info" xml:space="preserve">
<value>Server Info</value>
</data>
<data name="utility_shard" xml:space="preserve">
<value>Shard</value>
</data>
@ -1333,6 +1477,9 @@ OwnerID: {2}</value>
<data name="utility_stats_songs" xml:space="preserve">
<value>Playing {0} songs, {1} queued.</value>
</data>
<data name="utility_text_channels" xml:space="preserve">
<value>Text Channels</value>
</data>
<data name="utility_togtub_room_link" xml:space="preserve">
<value>Here is your room link:</value>
</data>
@ -1343,4 +1490,10 @@ OwnerID: {2}</value>
<value>{0} of the user {1} is {2}</value>
<comment>Id of the user kwoth#1234 is 123123123123</comment>
</data>
<data name="utility_users" xml:space="preserve">
<value>Users</value>
</data>
<data name="utility_voice_channels" xml:space="preserve">
<value>Voice Channels</value>
</data>
</root>