diff --git a/src/NadekoBot/Common/TypeReaders/Rgba32TypeReader.cs b/src/NadekoBot/Common/TypeReaders/Rgba32TypeReader.cs index fd62819e4..28f428884 100644 --- a/src/NadekoBot/Common/TypeReaders/Rgba32TypeReader.cs +++ b/src/NadekoBot/Common/TypeReaders/Rgba32TypeReader.cs @@ -5,18 +5,16 @@ namespace NadekoBot.Common.TypeReaders; public sealed class Rgba32TypeReader : NadekoTypeReader { - public override async ValueTask> ReadAsync(ICommandContext context, string input) + public override ValueTask> ReadAsync(ICommandContext context, string input) { - await Task.Yield(); - input = input.Replace("#", "", StringComparison.InvariantCulture); try { - return TypeReaderResult.FromSuccess(Color.ParseHex(input)); + return ValueTask.FromResult(TypeReaderResult.FromSuccess(Color.ParseHex(input))); } catch { - return TypeReaderResult.FromError(CommandError.ParseFailed, "Parameter is not a valid color hex."); + return ValueTask.FromResult(TypeReaderResult.FromError(CommandError.ParseFailed, "Parameter is not a valid color hex.")); } } } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Administration/VcRole/VcRoleService.cs b/src/NadekoBot/Modules/Administration/VcRole/VcRoleService.cs index 9b4b3feed..3c6f1caff 100644 --- a/src/NadekoBot/Modules/Administration/VcRole/VcRoleService.cs +++ b/src/NadekoBot/Modules/Administration/VcRole/VcRoleService.cs @@ -97,7 +97,6 @@ public class VcRoleService : INService private async Task InitializeVcRole(GuildConfig gconf) { - await Task.Yield(); var g = _client.GetGuild(gconf.GuildId); if (g is null) return; @@ -120,11 +119,12 @@ public class VcRoleService : INService if (missingRoles.Any()) { await using var uow = _db.GetDbContext(); - Log.Warning("Removing {MissingRoleCount} missing roles from {ServiceName}", - missingRoles.Count, - nameof(VcRoleService)); uow.RemoveRange(missingRoles); await uow.SaveChangesAsync(); + + Log.Warning("Removed {MissingRoleCount} missing roles from {ServiceName}", + missingRoles.Count, + nameof(VcRoleService)); } } diff --git a/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs b/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs index 42d3c3f25..58b596e04 100644 --- a/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs +++ b/src/NadekoBot/Modules/Searches/Crypto/CryptoCommands.cs @@ -8,13 +8,15 @@ namespace NadekoBot.Modules.Searches; // todo autoplay/fairplay public partial class Searches { - public partial class CryptoCommands : NadekoSubmodule + public partial class FinanceCommands : NadekoSubmodule { private readonly IStockDataService _stocksService; + private readonly IStockChartDrawingService _stockDrawingService; - public CryptoCommands(IStockDataService stocksService) + public FinanceCommands(IStockDataService stocksService, IStockChartDrawingService stockDrawingService) { _stocksService = stocksService; + _stockDrawingService = stockDrawingService; } [Cmd] @@ -22,9 +24,9 @@ public partial class Searches { using var typing = ctx.Channel.EnterTypingState(); - var stocks = await _stocksService.GetStockDataAsync(query); + var stock = await _stocksService.GetStockDataAsync(query); - if (stocks.Count == 0) + if (stock is null) { var symbols = await _stocksService.SearchSymbolAsync(query); @@ -43,34 +45,18 @@ public partial class Searches return; query = symbol.Symbol; - stocks = await _stocksService.GetStockDataAsync(query); + stock = await _stocksService.GetStockDataAsync(query); - if (stocks.Count == 0) + if (stock is null) { await ReplyErrorLocalizedAsync(strs.not_found); return; } - - } - - // try to find a ticker match - var stock = stocks.Count == 1 - ? stocks.FirstOrDefault() - : stocks.FirstOrDefault(x => x.Ticker == query.ToUpperInvariant()); - - if (stock is null) - { - var ebImprecise = _eb.Create() - .WithOkColor() - .WithTitle(GetText(strs.stocks_multiple_results)) - .WithDescription(stocks.Take(20) - .Select(s => $"{Format.Code(s.Ticker)} {s.Name.TrimTo(50)}") - .Join('\n')); - - await ctx.Channel.EmbedAsync(ebImprecise); - return; } + var candles = await _stocksService.GetCandleDataAsync(query); + var stockImageTask = _stockDrawingService.GenerateCombinedChartAsync(candles); + var localCulture = (CultureInfo)Culture.Clone(); localCulture.NumberFormat.CurrencySymbol = "$"; @@ -85,19 +71,20 @@ public partial class Searches ? "\\🔼" : "\\🔻"; - var change50 = (stock.Change50d / 100).ToString("P1", Culture); + var change50 = (stock.Change50d).ToString("P1", Culture); var sign200 = stock.Change200d >= 0 ? "\\🔼" : "\\🔻"; - var change200 = (stock.Change200d / 100).ToString("P1", Culture); + var change200 = (stock.Change200d).ToString("P1", Culture); var price = stock.Price.ToString("C2", localCulture); var eb = _eb.Create() .WithOkColor() - .WithAuthor(stock.Ticker) + .WithAuthor(stock.Symbol) + .WithUrl($"https://www.tradingview.com/chart/?symbol={stock.Symbol}") .WithTitle(stock.Name) .AddField(GetText(strs.price), $"{sign} **{price}**", true) .AddField(GetText(strs.market_cap), stock.MarketCap.ToString("C0", localCulture), true) @@ -107,7 +94,25 @@ public partial class Searches .AddField("Change 200d", $"{sign200}{change200}", true) .WithFooter(stock.Exchange); - await ctx.Channel.EmbedAsync(eb); + var message = await ctx.Channel.EmbedAsync(eb); + await using var imageData = await stockImageTask; + if (imageData is null) + return; + + var fileName = $"{query}-sparkline.{imageData.Extension}"; + await message.ModifyAsync(mp => + { + mp.Attachments = + new(new[] + { + new FileAttachment( + imageData.FileData, + fileName + ) + }); + + mp.Embed = eb.WithImageUrl($"attachment://{fileName}").Build(); + }); } diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/DefaultStockDataService.cs b/src/NadekoBot/Modules/Searches/Crypto/DefaultStockDataService.cs similarity index 64% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/DefaultStockDataService.cs rename to src/NadekoBot/Modules/Searches/Crypto/DefaultStockDataService.cs index b234ca94b..12c38bf2b 100644 --- a/src/NadekoBot/Modules/Searches/Crypto/_Common/DefaultStockDataService.cs +++ b/src/NadekoBot/Modules/Searches/Crypto/DefaultStockDataService.cs @@ -1,5 +1,4 @@ -using System.Net.Http.Json; -using System.Text.Json; +using System.Text.Json; using YahooFinanceApi; namespace NadekoBot.Modules.Searches; @@ -9,16 +8,14 @@ public sealed class DefaultStockDataService : IStockDataService, INService private readonly IHttpClientFactory _httpClientFactory; public DefaultStockDataService(IHttpClientFactory httpClientFactory) - { - _httpClientFactory = httpClientFactory; - } - - public async Task> GetStockDataAsync(string query) + => _httpClientFactory = httpClientFactory; + + public async Task GetStockDataAsync(string query) { try { if (!query.IsAlphaNumeric()) - return Array.Empty(); + return default; var symbols = await Yahoo.Symbols(query) .Fields(Field.LongName, @@ -31,28 +28,30 @@ public sealed class DefaultStockDataService : IStockDataService, INService Field.AverageDailyVolume10Day, Field.FullExchangeName) .QueryAsync(); + + var symbol = symbols.Values.FirstOrDefault(); + + if (symbol is null) + return default; - return symbols - .Select(static x => x.Value) - .Select(static x => new StockData() - { - Name = x.LongName, - Ticker = x.Symbol, - Price = x.RegularMarketPrice, - Close = x.RegularMarketPreviousClose, - MarketCap = x.MarketCap, - Change50d = x.FiftyDayAverageChangePercent, - Change200d = x.TwoHundredDayAverageChangePercent, - DailyVolume = x.AverageDailyVolume10Day, - Exchange = x.FullExchangeName - }) - .ToList(); + return new() + { + Name = symbol.LongName, + Symbol = symbol.Symbol, + Price = symbol.RegularMarketPrice, + Close = symbol.RegularMarketPreviousClose, + MarketCap = symbol.MarketCap, + Change50d = symbol.FiftyDayAverageChangePercent, + Change200d = symbol.TwoHundredDayAverageChangePercent, + DailyVolume = symbol.AverageDailyVolume10Day, + Exchange = symbol.FullExchangeName + }; } catch (Exception ex) { // what the hell is this api exception Log.Warning(ex, "Error getting stock data: {ErrorMessage}", ex.Message); - return Array.Empty(); + return default; } } @@ -81,10 +80,11 @@ public sealed class DefaultStockDataService : IStockDataService, INService .ToList(); } - // public async Task> GetCandleDataAsync(string query) - // { - // - // } -} + public async Task> GetCandleDataAsync(string query) + { + var candles = await Yahoo.GetHistoricalAsync(query, DateTime.Now.Subtract(30.Days())); -public record CandleData(); \ No newline at end of file + return candles + .Map(static x => new CandleData(x.Open, x.Close, x.High, x.Low, x.Volume)); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/Drawing/CandleDrawingData.cs b/src/NadekoBot/Modules/Searches/Crypto/Drawing/CandleDrawingData.cs new file mode 100644 index 000000000..e27b266a3 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Crypto/Drawing/CandleDrawingData.cs @@ -0,0 +1,12 @@ +using SixLabors.ImageSharp; + +namespace NadekoBot.Modules.Searches; + +/// +/// All data required to draw a candle +/// +/// Whether the candle is green +/// Rectangle for the body +/// High line point +/// Low line point +public record CandleDrawingData(bool IsGreen, RectangleF BodyRect, PointF High, PointF Low); \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/Drawing/IStockChartDrawingService.cs b/src/NadekoBot/Modules/Searches/Crypto/Drawing/IStockChartDrawingService.cs new file mode 100644 index 000000000..c90a7a544 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Crypto/Drawing/IStockChartDrawingService.cs @@ -0,0 +1,8 @@ +namespace NadekoBot.Modules.Searches; + +public interface IStockChartDrawingService +{ + Task GenerateSparklineAsync(IReadOnlyCollection series); + Task GenerateCombinedChartAsync(IReadOnlyCollection series); + Task GenerateCandleChartAsync(IReadOnlyCollection series); +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/Drawing/ImagesharpStockChartDrawingService.cs b/src/NadekoBot/Modules/Searches/Crypto/Drawing/ImagesharpStockChartDrawingService.cs new file mode 100644 index 000000000..d67e25a80 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Crypto/Drawing/ImagesharpStockChartDrawingService.cs @@ -0,0 +1,202 @@ +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Drawing.Processing; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using System.Runtime.CompilerServices; +using Color = SixLabors.ImageSharp.Color; + +namespace NadekoBot.Modules.Searches; + +public sealed class ImagesharpStockChartDrawingService : IStockChartDrawingService, INService +{ + private const int WIDTH = 300; + private const int HEIGHT = 100; + private const decimal MAX_HEIGHT = HEIGHT * 0.8m; + + private static readonly Rgba32 _backgroundColor = Rgba32.ParseHex("17181E"); + private static readonly Rgba32 _lineGuideColor = Rgba32.ParseHex("212125"); + private static readonly Rgba32 _sparklineColor = Rgba32.ParseHex("2961FC"); + private static readonly Rgba32 _greenBrush = Rgba32.ParseHex("26A69A"); + private static readonly Rgba32 _redBrush = Rgba32.ParseHex("EF5350"); + + private static float GetNormalizedPoint(decimal max, decimal point, decimal range) + => (float)((MAX_HEIGHT * ((max - point) / range)) + HeightOffset()); + + private PointF[] GetSparklinePointsInternal(IReadOnlyCollection series) + { + var candleStep = WIDTH / (series.Count + 1); + var max = series.Max(static x => x.High); + var min = series.Min(static x => x.Low); + + var range = max - min; + + var points = new PointF[series.Count]; + + var i = 0; + foreach (var candle in series) + { + var x = candleStep * (i + 1); + + var y = GetNormalizedPoint(max, candle.Close, range); + points[i++] = new(x, y); + } + + return points; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static decimal HeightOffset() + => (HEIGHT - MAX_HEIGHT) / 2m; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Image CreateCanvasInternal() + => new Image(WIDTH, HEIGHT, _backgroundColor); + + private CandleDrawingData[] GetChartDrawingDataInternal(IReadOnlyCollection series) + { + var candleMargin = 2; + var candleStep = (WIDTH - (candleMargin * series.Count)) / (series.Count + 1); + var max = series.Max(static x => x.High); + var min = series.Min(static x => x.Low); + + var range = max - min; + + var drawData = new CandleDrawingData[series.Count]; + + var candleWidth = candleStep; + + var i = 0; + foreach (var candle in series) + { + var offsetX = (i - 1) * candleMargin; + var x = (candleStep * (i + 1)) + offsetX; + var yOpen = GetNormalizedPoint(max, candle.Open, range); + var yClose = GetNormalizedPoint(max, candle.Close, range); + var y = candle.Open > candle.Close + ? yOpen + : yClose; + + var sizeH = Math.Abs(yOpen - yClose); + + var high = GetNormalizedPoint(max, candle.High, range); + var low = GetNormalizedPoint(max, candle.Low, range); + drawData[i] = new(candle.Open < candle.Close, + new(x, y, candleWidth, sizeH), + new(x + (candleStep / 2), high), + new(x + (candleStep / 2), low)); + ++i; + } + + return drawData; + } + + private void DrawChartData(Image image, CandleDrawingData[] drawData) + => image.Mutate(ctx => + { + foreach (var data in drawData) + DrawLineExtensions.DrawLines(ctx, + data.IsGreen + ? _greenBrush + : _redBrush, + 1, + data.High, + data.Low); + + + foreach (var data in drawData) + FillRectangleExtensions.Fill(ctx, + data.IsGreen + ? _greenBrush + : _redBrush, + data.BodyRect); + }); + + private void DrawLineGuides(Image image, IReadOnlyCollection series) + { + var max = series.Max(x => x.High); + var min = series.Min(x => x.Low); + + var step = (max - min) / 5; + + var lines = new float[6]; + + for (var i = 0; i < 6; i++) + { + var y = GetNormalizedPoint(max, min + (step * i), max - min); + lines[i] = y; + } + + image.Mutate(ctx => + { + // draw guides + foreach (var y in lines) + ctx.DrawLines(_lineGuideColor, 1, new PointF(0, y), new PointF(WIDTH, y)); + + // // draw min and max price on the chart + // ctx.DrawText(min.ToString(CultureInfo.InvariantCulture), + // SystemFonts.CreateFont("Arial", 5), + // Color.White, + // new PointF(0, (float)HeightOffset() - 5) + // ); + // + // ctx.DrawText(max.ToString("N1", CultureInfo.InvariantCulture), + // SystemFonts.CreateFont("Arial", 5), + // Color.White, + // new PointF(0, HEIGHT - (float)HeightOffset()) + // ); + }); + } + + public async Task GenerateSparklineAsync(IReadOnlyCollection series) + { + if (series.Count == 0) + return default; + + using var image = CreateCanvasInternal(); + + var points = GetSparklinePointsInternal(series); + + image.Mutate(ctx => + { + ctx.DrawLines(_sparklineColor, 2, points); + }); + + return new("png", image.ToStream()); + } + + public async Task GenerateCombinedChartAsync(IReadOnlyCollection series) + { + if (series.Count == 0) + return default; + + using var image = CreateCanvasInternal(); + + DrawLineGuides(image, series); + + var chartData = GetChartDrawingDataInternal(series); + DrawChartData(image, chartData); + + var points = GetSparklinePointsInternal(series); + image.Mutate(ctx => + { + ctx.DrawLines(Color.ParseHex("00FFFFAA"), 1, points); + }); + + return new("png", image.ToStream()); + } + + public async Task GenerateCandleChartAsync(IReadOnlyCollection series) + { + if (series.Count == 0) + return default; + + using var image = CreateCanvasInternal(); + + DrawLineGuides(image, series); + + var drawData = GetChartDrawingDataInternal(series); + DrawChartData(image, drawData); + + return new("png", image.ToStream()); + } +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/IStockDataService.cs b/src/NadekoBot/Modules/Searches/Crypto/IStockDataService.cs similarity index 52% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/IStockDataService.cs rename to src/NadekoBot/Modules/Searches/Crypto/IStockDataService.cs index 6ecb376c4..93f4a5c92 100644 --- a/src/NadekoBot/Modules/Searches/Crypto/_Common/IStockDataService.cs +++ b/src/NadekoBot/Modules/Searches/Crypto/IStockDataService.cs @@ -2,6 +2,7 @@ public interface IStockDataService { - public Task> GetStockDataAsync(string query); + public Task GetStockDataAsync(string symbol); Task> SearchSymbolAsync(string query); + Task> GetCandleDataAsync(string query); } \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/FinnHubSearchResponse.cs b/src/NadekoBot/Modules/Searches/Crypto/Polygon/FinnHubSearchResponse.cs similarity index 100% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/FinnHubSearchResponse.cs rename to src/NadekoBot/Modules/Searches/Crypto/Polygon/FinnHubSearchResponse.cs diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/FinnHubSearchResult.cs b/src/NadekoBot/Modules/Searches/Crypto/Polygon/FinnHubSearchResult.cs similarity index 100% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/FinnHubSearchResult.cs rename to src/NadekoBot/Modules/Searches/Crypto/Polygon/FinnHubSearchResult.cs diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/PolygonApiClient.cs b/src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonApiClient.cs similarity index 100% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/PolygonApiClient.cs rename to src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonApiClient.cs diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/PolygonStockDataService.cs b/src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonStockDataService.cs similarity index 100% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/PolygonStockDataService.cs rename to src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonStockDataService.cs diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/PolygonTickerData.cs b/src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonTickerData.cs similarity index 100% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/PolygonTickerData.cs rename to src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonTickerData.cs diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/PolygonTickerResponse.cs b/src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonTickerResponse.cs similarity index 100% rename from src/NadekoBot/Modules/Searches/Crypto/_Common/Polygon/PolygonTickerResponse.cs rename to src/NadekoBot/Modules/Searches/Crypto/Polygon/PolygonTickerResponse.cs diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/CandleData.cs b/src/NadekoBot/Modules/Searches/Crypto/_Common/CandleData.cs new file mode 100644 index 000000000..8103a88f2 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Crypto/_Common/CandleData.cs @@ -0,0 +1,8 @@ +namespace NadekoBot.Modules.Searches; + +public record CandleData( + decimal Open, + decimal Close, + decimal High, + decimal Low, + long Volume); \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/ImageData.cs b/src/NadekoBot/Modules/Searches/Crypto/_Common/ImageData.cs new file mode 100644 index 000000000..06e78f378 --- /dev/null +++ b/src/NadekoBot/Modules/Searches/Crypto/_Common/ImageData.cs @@ -0,0 +1,7 @@ +namespace NadekoBot.Modules.Searches; + +public record ImageData(string Extension, Stream FileData) : IAsyncDisposable +{ + public ValueTask DisposeAsync() + => FileData.DisposeAsync(); +} \ No newline at end of file diff --git a/src/NadekoBot/Modules/Searches/Crypto/_Common/StockData.cs b/src/NadekoBot/Modules/Searches/Crypto/_Common/StockData.cs index a69a01031..e18525b21 100644 --- a/src/NadekoBot/Modules/Searches/Crypto/_Common/StockData.cs +++ b/src/NadekoBot/Modules/Searches/Crypto/_Common/StockData.cs @@ -4,7 +4,7 @@ namespace NadekoBot.Modules.Searches; public class StockData { public string Name { get; set; } - public string Ticker { get; set; } + public string Symbol { get; set; } public double Price { get; set; } public long MarketCap { get; set; } public double Close { get; set; } diff --git a/src/NadekoBot/Modules/Utility/CommandMap/CommandMapService.cs b/src/NadekoBot/Modules/Utility/CommandMap/CommandMapService.cs index 54f3bd1c2..ce2397fa6 100644 --- a/src/NadekoBot/Modules/Utility/CommandMap/CommandMapService.cs +++ b/src/NadekoBot/Modules/Utility/CommandMap/CommandMapService.cs @@ -51,43 +51,38 @@ public class CommandMapService : IInputTransformer, INService IUser user, string input) { - await Task.Yield(); - if (guild is null || string.IsNullOrWhiteSpace(input)) return input; - if (guild is not null) + if (AliasMaps.TryGetValue(guild.Id, out var maps)) { - if (AliasMaps.TryGetValue(guild.Id, out var maps)) + var keys = maps.Keys.OrderByDescending(x => x.Length); + + foreach (var k in keys) { - var keys = maps.Keys.OrderByDescending(x => x.Length); + string newInput; + if (input.StartsWith(k + " ", StringComparison.InvariantCultureIgnoreCase)) + newInput = maps[k] + input.Substring(k.Length, input.Length - k.Length); + else if (input.Equals(k, StringComparison.InvariantCultureIgnoreCase)) + newInput = maps[k]; + else + continue; - foreach (var k in keys) + try { - string newInput; - if (input.StartsWith(k + " ", StringComparison.InvariantCultureIgnoreCase)) - newInput = maps[k] + input.Substring(k.Length, input.Length - k.Length); - else if (input.Equals(k, StringComparison.InvariantCultureIgnoreCase)) - newInput = maps[k]; - else - continue; - - try + var toDelete = await channel.SendConfirmAsync(_eb, $"{input} => {newInput}"); + _ = Task.Run(async () => { - var toDelete = await channel.SendConfirmAsync(_eb, $"{input} => {newInput}"); - _ = Task.Run(async () => + await Task.Delay(1500); + await toDelete.DeleteAsync(new() { - await Task.Delay(1500); - await toDelete.DeleteAsync(new() - { - RetryMode = RetryMode.AlwaysRetry - }); + RetryMode = RetryMode.AlwaysRetry }); - } - catch { } - - return newInput; + }); } + catch { } + + return newInput; } } diff --git a/src/NadekoBot/Services/Impl/GoogleApiService.cs b/src/NadekoBot/Services/Impl/GoogleApiService.cs index 3388cae90..2fbeaa87f 100644 --- a/src/NadekoBot/Services/Impl/GoogleApiService.cs +++ b/src/NadekoBot/Services/Impl/GoogleApiService.cs @@ -177,7 +177,6 @@ public class GoogleApiService : IGoogleApiService, INService public async Task> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) { - await Task.Yield(); if (string.IsNullOrWhiteSpace(keywords)) throw new ArgumentNullException(nameof(keywords)); diff --git a/src/NadekoBot/data/strings/commands/commands.en-US.yml b/src/NadekoBot/data/strings/commands/commands.en-US.yml index 2cbb0b1ac..1d7b6e490 100644 --- a/src/NadekoBot/data/strings/commands/commands.en-US.yml +++ b/src/NadekoBot/data/strings/commands/commands.en-US.yml @@ -2034,9 +2034,10 @@ crypto: - "btc" - "bitcoin" stock: - desc: "Shows basic information about a stock. Only symbols are supported. Full company names are not supported atm." + desc: "Shows basic information about a stock. You can use a symbol or company name" args: - "tsla" + - "advanced micro devices" - "amd" rolelevelreq: desc: "Set a level requirement on a self-assignable role." diff --git a/src/NadekoBot/data/strings/responses/responses.en-US.json b/src/NadekoBot/data/strings/responses/responses.en-US.json index 06422db6e..468b18011 100644 --- a/src/NadekoBot/data/strings/responses/responses.en-US.json +++ b/src/NadekoBot/data/strings/responses/responses.en-US.json @@ -888,7 +888,6 @@ "volume_24h": "Volume (24h)", "change_7d_24h": "Change (7d / 24h)", "crypto_not_found": "CryptoCurrency with that name was not found.", - "stocks_multiple_results": "Multiple results found, please use a symbol", "did_you_mean": "Did you mean {0}?", "self_assign_level_req": "Self assignable role {0} now requires at least server level {1}.", "self_assign_not_level": "That self-assignable role requires at least server level {0}.",