diff --git a/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs b/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs new file mode 100644 index 00000000..e3ad166b --- /dev/null +++ b/src/NadekoBot/Modules/Utility/Commands/CalcCommand.cs @@ -0,0 +1,67 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Modules.Utility +{ + [Group] + public partial class Utility + { + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public static async Task Calculate(IUserMessage msg, [Remainder] string expression) + { + try + { + var expr = new NCalc.Expression(expression, NCalc.EvaluateOptions.IgnoreCase); + expr.EvaluateParameter += Expr_EvaluateParameter; + var result = expr.Evaluate(); + await msg.Reply(string.Format("Your expression evaluated to: {0}", expr.Error ?? result)); + } + catch (Exception e) + { + await msg.Reply($"Your expression failed to evaluate: {e.Message} "); + } + } + + private static void Expr_EvaluateParameter(string name, NCalc.ParameterArgs args) + { + switch (name.ToLowerInvariant()) { + case "pi": args.Result= Math.PI; + break; + case "e": args.Result = Math.E; + break; + } + } + + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task CalcOps(IUserMessage msg) + { + StringBuilder builder = new StringBuilder(); + var selection = typeof(Math).GetTypeInfo().GetMethods().Except(typeof(object).GetTypeInfo().GetMethods()).Select(x => + { + var name = x.Name; + if (x.GetParameters().Any()) + { + name += " (" + string.Join(", ", x.GetParameters().Select(y => y.IsOptional ? $"[{y.ParameterType.Name + " " + y.Name }]" : y.ParameterType.Name + " " + y.Name)) + ")"; + } + return name; + }); + foreach (var method in selection) builder.AppendLine(method); + await msg.ReplyLong(builder.ToString()); + } + } + class ExpressionContext + { + public double Pi { get; set; } = Math.PI; + } + +} diff --git a/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs new file mode 100644 index 00000000..459466bb --- /dev/null +++ b/src/NadekoBot/Modules/Utility/Commands/UnitConversion.cs @@ -0,0 +1,180 @@ +using Discord; +using Discord.Commands; +using NadekoBot.Attributes; +using NadekoBot.Extensions; +using NadekoBot.Modules.Utility.Commands.Models; +using NadekoBot.Services; +using NadekoBot.Services.Database.Models; +using Newtonsoft.Json; +using NLog; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace NadekoBot.Modules.Utility +{ + public partial class Utility + { + [Group] + public class UnitConverterCommands + { + private Logger _log; + private static Timer _timer; + public static TimeSpan Span = new TimeSpan(12, 0, 0); + public UnitConverterCommands() + { + _log = LogManager.GetCurrentClassLogger(); + + try + { + using (var uow = DbHandler.UnitOfWork()) + { + //need to do this the first time + if (uow.ConverterUnits.Empty()) + { + var content = JsonConvert.DeserializeObject>(File.ReadAllText("units.json")).Select(u => new ConvertUnit() + { + Modifier = u.Modifier, + UnitType = u.UnitType, + InternalTrigger = string.Join("|", u.Triggers) + }); + + uow.ConverterUnits.AddRange(content.ToArray()); + uow.Complete(); + } + Units = uow.ConverterUnits.GetAll().ToList(); + } + } + catch (Exception e) + { + _log.Warn("Could not load units: " + e.Message); + } + + + + _timer = new Timer(new TimerCallback(UpdateCurrency), null, 0,(int)Span.TotalMilliseconds); + + } + + public void UpdateCurrency(object stateInfo) + { + var currencyRates = UpdateCurrencyRates().Result; + var unitTypeString = "currency"; + using (var uow = DbHandler.UnitOfWork()) + { + var toRemove = Units.Where(u => u.UnitType == unitTypeString); + Units.RemoveAll(u => u.UnitType == unitTypeString); + uow.ConverterUnits.RemoveRange(toRemove.ToArray()); + var baseType = new ConvertUnit() + { + Triggers = new[] { currencyRates.Base }, + Modifier = decimal.One, + UnitType = unitTypeString + }; + uow.ConverterUnits.Add(baseType); + Units.Add(baseType); + var range = currencyRates.ConversionRates.Select(u => new ConvertUnit() + { + InternalTrigger = u.Key, + Modifier = u.Value, + UnitType = unitTypeString + }).ToArray(); + uow.ConverterUnits.AddRange(range); + Units.AddRange(range); + + uow.Complete(); + } + _log.Info("Updated Currency"); + } + + public List Units { get; set; } + [NadekoCommand, Usage, Description, Aliases] + [RequireContext(ContextType.Guild)] + public async Task ConvertList(IUserMessage msg) + { + var sb = new StringBuilder("Units that can be used by the converter: \n"); + var res = Units.GroupBy(x => x.UnitType); + foreach (var group in res) + { + sb.AppendLine($"{group.Key}: ```xl"); + sb.AppendLine(string.Join(",", group.Select(x => x.Triggers.FirstOrDefault()).OrderBy(x => x))); + sb.AppendLine("```"); + } + await msg.ReplyLong(sb.ToString(), breakOn: new[] { "```xl\n", "\n" }); + } + [NadekoCommand, Usage, Description, Aliases] + public async Task Convert(IUserMessage msg, string origin, string target, decimal value) + { + var originUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(origin.ToLowerInvariant())); + var targetUnit = Units.Find(x => x.Triggers.Select(y => y.ToLowerInvariant()).Contains(target.ToLowerInvariant())); + if (originUnit == null || targetUnit == null) + { + await msg.Reply(string.Format("Cannot convert {0} to {1}: units not found", origin, target)); + return; + } + if (originUnit.UnitType != targetUnit.UnitType) + { + await msg.Reply(string.Format("Cannot convert {0} to {1}: types of unit are not equal", originUnit.Triggers.First(), targetUnit.Triggers.First())); + return; + } + decimal res; + if (originUnit.Triggers == targetUnit.Triggers) res = value; + else if (originUnit.UnitType == "temperature") + { + //don't really care too much about efficiency, so just convert to Kelvin, then to target + switch (originUnit.Triggers.First().ToUpperInvariant()) + { + case "C": + res = value + 273.15m; //celcius! + break; + case "F": + res = (value + 459.67m) * (5 / 9); + break; + default: + res = value; + break; + } + //from Kelvin to target + switch (targetUnit.Triggers.First()) + { + case "C": + res = value - 273.15m; //celcius! + break; + case "F": + res = res * (9 / 5) - 458.67m; + break; + default: + break; + } + } + else + { + if (originUnit.UnitType == "currency") + { + res = (value * targetUnit.Modifier) / originUnit.Modifier; + } + else + res = (value * originUnit.Modifier) / targetUnit.Modifier; + } + res = Math.Round(res, 4); + + await msg.Reply(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))); + } + } + + public static async Task UpdateCurrencyRates() + { + using (var http = new HttpClient()) + { + var res = await http.GetStringAsync("http://api.fixer.io/latest").ConfigureAwait(false); + return JsonConvert.DeserializeObject(res); + } + } + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Utility/Models/MeasurementUnit.cs b/src/NadekoBot/Modules/Utility/Models/MeasurementUnit.cs new file mode 100644 index 00000000..017ea8b4 --- /dev/null +++ b/src/NadekoBot/Modules/Utility/Models/MeasurementUnit.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; + +namespace NadekoBot.Modules.Utility.Commands.Models +{ + public class MeasurementUnit + { + public List Triggers { get; set; } + public string UnitType { get; set; } + public decimal Modifier { get; set; } + } +} diff --git a/src/NadekoBot/Modules/Utility/Models/Rates.cs b/src/NadekoBot/Modules/Utility/Models/Rates.cs new file mode 100644 index 00000000..b460a3c5 --- /dev/null +++ b/src/NadekoBot/Modules/Utility/Models/Rates.cs @@ -0,0 +1,14 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; + +namespace NadekoBot.Modules.Utility.Commands.Models +{ + public class Rates + { + public string Base { get; set; } + public DateTime Date { get; set; } + [JsonProperty("rates")] + public Dictionary ConversionRates { get; set; } + } +} diff --git a/src/NadekoBot/Resources/CommandStrings.Designer.cs b/src/NadekoBot/Resources/CommandStrings.Designer.cs index 7e59fb43..45b92683 100644 --- a/src/NadekoBot/Resources/CommandStrings.Designer.cs +++ b/src/NadekoBot/Resources/CommandStrings.Designer.cs @@ -1122,7 +1122,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Toggles automatic deleting of invites posted in the channel. Does not negate the .srvrfilterinv enabled setting.. + /// Looks up a localized string similar to Toggles automatic deleting of invites posted in the channel. Does not negate the .srvrfilterinv enabled setting. Does not affect Bot Owner.. /// public static string chnlfilterinv_desc { get { @@ -1149,7 +1149,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Toggles automatic deleting of messages containing banned words on the channel. Does not negate the .srvrfilterwords enabled setting.. + /// Looks up a localized string similar to Toggles automatic deleting of messages containing banned words on the channel. Does not negate the .srvrfilterwords enabled setting. Does not affect bot owner.. /// public static string chnlfilterwords_desc { get { @@ -2310,7 +2310,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Adds or removes (if it exists) a word from the list of filtered words.. + /// Looks up a localized string similar to Adds or removes (if it exists) a word from the list of filtered words. Use` ;sfw` or `;cfw` to toggle filtering.. /// public static string filterword_desc { get { @@ -6387,7 +6387,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Toggles automatic deleting of invites posted in the server.. + /// Looks up a localized string similar to Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner.. /// public static string srvrfilterinv_desc { get { @@ -6414,7 +6414,7 @@ namespace NadekoBot.Resources { } /// - /// Looks up a localized string similar to Toggles automatic deleting of messages containing forbidden words on the server.. + /// Looks up a localized string similar to Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner.. /// public static string srvrfilterwords_desc { get { diff --git a/src/NadekoBot/Resources/CommandStrings.resx b/src/NadekoBot/Resources/CommandStrings.resx index 730b3399..8af562f3 100644 --- a/src/NadekoBot/Resources/CommandStrings.resx +++ b/src/NadekoBot/Resources/CommandStrings.resx @@ -958,7 +958,7 @@ chnlfilterinv cfi - Toggles automatic deleting of invites posted in the channel. Does not negate the .srvrfilterinv enabled setting. + Toggles automatic deleting of invites posted in the channel. Does not negate the .srvrfilterinv enabled setting. Does not affect Bot Owner. `;cfi` @@ -967,7 +967,7 @@ srvrfilterinv sfi - Toggles automatic deleting of invites posted in the server. + Toggles automatic deleting of invites posted in the server. Does not affect Bot Owner. `;sfi` @@ -976,7 +976,7 @@ chnlfilterwords cfw - Toggles automatic deleting of messages containing banned words on the channel. Does not negate the .srvrfilterwords enabled setting. + Toggles automatic deleting of messages containing banned words on the channel. Does not negate the .srvrfilterwords enabled setting. Does not affect bot owner. `;cfw` @@ -985,7 +985,7 @@ fw - Adds or removes (if it exists) a word from the list of filtered words. + Adds or removes (if it exists) a word from the list of filtered words. Use` ;sfw` or `;cfw` to toggle filtering. `;fw poop` @@ -1003,7 +1003,7 @@ srvrfilterwords sfw - Toggles automatic deleting of messages containing forbidden words on the server. + Toggles automatic deleting of messages containing forbidden words on the server. Does not affect Bot Owner. `;sfw`