mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 09:18:27 -04:00
Added a .stock command
This commit is contained in:
@@ -81,8 +81,8 @@ csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_indexers = true:suggestion
|
||||
csharp_style_expression_bodied_lambdas = true:suggestion
|
||||
csharp_style_expression_bodied_local_functions = true:suggestion
|
||||
csharp_style_expression_bodied_methods = when_on_single_line:warning
|
||||
csharp_style_expression_bodied_operators = when_on_single_line:warning
|
||||
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
|
||||
csharp_style_expression_bodied_properties = true:suggestion
|
||||
|
||||
# Pattern matching preferences
|
||||
|
@@ -63,6 +63,10 @@ Used only for .time command")]
|
||||
[Comment(@"https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
|
||||
Used for cryptocurrency related commands.")]
|
||||
public string CoinmarketcapApiKey { get; set; }
|
||||
|
||||
// [Comment(@"https://polygon.io/dashboard/api-keys api key. Free plan allows for 5 queries per minute.
|
||||
// Used for stocks related commands.")]
|
||||
// public string PolygonIoApiKey { get; set; }
|
||||
|
||||
[Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")]
|
||||
public string OsuApiKey { get; set; }
|
||||
|
@@ -8,6 +8,110 @@ public partial class Searches
|
||||
{
|
||||
public partial class CryptoCommands : NadekoSubmodule<CryptoService>
|
||||
{
|
||||
private readonly IStockDataService _stocksService;
|
||||
|
||||
public CryptoCommands(IStockDataService stocksService)
|
||||
{
|
||||
_stocksService = stocksService;
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async partial Task Stock([Leftover]string query)
|
||||
{
|
||||
if (!query.IsAlphaNumeric())
|
||||
return;
|
||||
|
||||
using var typing = ctx.Channel.EnterTypingState();
|
||||
|
||||
var stocks = await _stocksService.GetStockDataAsync(query);
|
||||
|
||||
if (stocks.Count == 0)
|
||||
{
|
||||
var symbols = await _stocksService.SearchSymbolAsync(query);
|
||||
|
||||
if (symbols.Count == 0)
|
||||
{
|
||||
await ReplyErrorLocalizedAsync(strs.not_found);
|
||||
return;
|
||||
}
|
||||
|
||||
var symbol = symbols.First();
|
||||
var promptEmbed = _eb.Create()
|
||||
.WithDescription(symbol.Description)
|
||||
.WithTitle(GetText(strs.did_you_mean(symbol.Symbol)));
|
||||
|
||||
if (!await PromptUserConfirmAsync(promptEmbed))
|
||||
return;
|
||||
|
||||
query = symbol.Symbol;
|
||||
stocks = await _stocksService.GetStockDataAsync(query);
|
||||
|
||||
if (stocks.Count == 0)
|
||||
{
|
||||
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 localCulture = (CultureInfo)Culture.Clone();
|
||||
localCulture.NumberFormat.CurrencySymbol = "$";
|
||||
|
||||
var sign = stock.Price >= stock.Close
|
||||
? "\\🔼"
|
||||
: "\\🔻";
|
||||
|
||||
var change = (stock.Price - stock.Close).ToString("N2", Culture);
|
||||
var changePercent = (1 - (stock.Close / stock.Price)).ToString("P1", Culture);
|
||||
|
||||
var sign50 = stock.Change50d >= 0
|
||||
? "\\🔼"
|
||||
: "\\🔻";
|
||||
|
||||
var change50 = (stock.Change50d / 100).ToString("P1", Culture);
|
||||
|
||||
var sign200 = stock.Change200d >= 0
|
||||
? "\\🔼"
|
||||
: "\\🔻";
|
||||
|
||||
var change200 = (stock.Change200d / 100).ToString("P1", Culture);
|
||||
|
||||
var price = stock.Price.ToString("C2", localCulture);
|
||||
|
||||
var eb = _eb.Create()
|
||||
.WithOkColor()
|
||||
.WithAuthor(stock.Ticker)
|
||||
.WithTitle(stock.Name)
|
||||
.AddField(GetText(strs.price), $"{sign} **{price}**", true)
|
||||
.AddField(GetText(strs.market_cap), stock.MarketCap.ToString("C0", localCulture), true)
|
||||
.AddField(GetText(strs.volume_24h), stock.DailyVolume.ToString("C0", localCulture), true)
|
||||
.AddField("Change", $"{change} ({changePercent})", true)
|
||||
.AddField("Change 50d", $"{sign50}{change50}", true)
|
||||
.AddField("Change 200d", $"{sign200}{change200}", true)
|
||||
.WithFooter(stock.Exchange);
|
||||
|
||||
await ctx.Channel.EmbedAsync(eb);
|
||||
}
|
||||
|
||||
|
||||
[Cmd]
|
||||
public async partial Task Crypto(string name)
|
||||
{
|
||||
|
@@ -0,0 +1,7 @@
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public interface IStockDataService
|
||||
{
|
||||
public Task<IReadOnlyCollection<StockData>> GetStockDataAsync(string query);
|
||||
Task<IReadOnlyCollection<SymbolData>> SearchSymbolAsync(string query);
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public class FinnHubSearchResponse
|
||||
{
|
||||
[JsonPropertyName("count")]
|
||||
public int Count { get; set; }
|
||||
|
||||
[JsonPropertyName("result")]
|
||||
public List<FinnHubSearchResult> Result { get; set; }
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
#nullable disable
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public class FinnHubSearchResult
|
||||
{
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; }
|
||||
|
||||
[JsonPropertyName("displaySymbol")]
|
||||
public string DisplaySymbol { get; set; }
|
||||
|
||||
[JsonPropertyName("symbol")]
|
||||
public string Symbol { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
using System.Net.Http.Json;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public sealed class PolygonApiClient : IDisposable
|
||||
{
|
||||
private const string BASE_URL = "https://api.polygon.io/v3";
|
||||
|
||||
private readonly HttpClient _httpClient;
|
||||
private readonly string _apiKey;
|
||||
|
||||
public PolygonApiClient(HttpClient httpClient, string apiKey)
|
||||
{
|
||||
_httpClient = httpClient;
|
||||
_apiKey = apiKey;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<PolygonTickerData>> TickersAsync(string? ticker = null, string? query = null)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
query = null;
|
||||
|
||||
if(query is not null)
|
||||
query = Uri.EscapeDataString(query);
|
||||
|
||||
var requestString = $"{BASE_URL}/reference/tickers"
|
||||
+ "?type=CS"
|
||||
+ "&active=true"
|
||||
+ "&order=asc"
|
||||
+ "&limit=1000"
|
||||
+ $"&apiKey={_apiKey}";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ticker))
|
||||
requestString += $"&ticker={ticker}";
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(query))
|
||||
requestString += $"&search={query}";
|
||||
|
||||
|
||||
var response = await _httpClient.GetFromJsonAsync<PolygonTickerResponse>(requestString);
|
||||
|
||||
if (response is null)
|
||||
return Array.Empty<PolygonTickerData>();
|
||||
|
||||
return response.Results;
|
||||
}
|
||||
|
||||
// public async Task<PolygonTickerDetailsV3> TickerDetailsV3Async(string ticker)
|
||||
// {
|
||||
// return new();
|
||||
// }
|
||||
|
||||
public void Dispose()
|
||||
=> _httpClient.Dispose();
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
#nullable disable
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public class PolygonTickerData
|
||||
{
|
||||
[JsonPropertyName("ticker")]
|
||||
public string Ticker { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("market")]
|
||||
public string Market { get; set; }
|
||||
|
||||
[JsonPropertyName("locale")]
|
||||
public string Locale { get; set; }
|
||||
|
||||
[JsonPropertyName("primary_exchange")]
|
||||
public string PrimaryExchange { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public string Type { get; set; }
|
||||
|
||||
[JsonPropertyName("active")]
|
||||
public bool Active { get; set; }
|
||||
|
||||
[JsonPropertyName("currency_name")]
|
||||
public string CurrencyName { get; set; }
|
||||
|
||||
[JsonPropertyName("cik")]
|
||||
public string Cik { get; set; }
|
||||
|
||||
[JsonPropertyName("composite_figi")]
|
||||
public string CompositeFigi { get; set; }
|
||||
|
||||
[JsonPropertyName("share_class_figi")]
|
||||
public string ShareClassFigi { get; set; }
|
||||
|
||||
[JsonPropertyName("last_updated_utc")]
|
||||
public DateTime LastUpdatedUtc { get; set; }
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
#nullable disable
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public class PolygonTickerResponse
|
||||
{
|
||||
[JsonPropertyName("status")]
|
||||
public string Status { get; set; }
|
||||
|
||||
[JsonPropertyName("results")]
|
||||
public List<PolygonTickerData> Results { get; set; }
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
// namespace NadekoBot.Modules.Searches;
|
||||
//
|
||||
// public sealed class PolygonStockDataService : IStockDataService
|
||||
// {
|
||||
// private readonly IHttpClientFactory _httpClientFactory;
|
||||
// private readonly IBotCredsProvider _credsProvider;
|
||||
//
|
||||
// public PolygonStockDataService(IHttpClientFactory httpClientFactory, IBotCredsProvider credsProvider)
|
||||
// {
|
||||
// _httpClientFactory = httpClientFactory;
|
||||
// _credsProvider = credsProvider;
|
||||
// }
|
||||
//
|
||||
// public async Task<IReadOnlyCollection<StockData>> GetStockDataAsync(string? query = null)
|
||||
// {
|
||||
// using var httpClient = _httpClientFactory.CreateClient();
|
||||
// using var client = new PolygonApiClient(httpClient, string.Empty);
|
||||
// var data = await client.TickersAsync(query: query);
|
||||
//
|
||||
// return data.Map(static x => new StockData()
|
||||
// {
|
||||
// Name = x.Name,
|
||||
// Ticker = x.Ticker,
|
||||
// });
|
||||
// }
|
||||
// }
|
15
src/NadekoBot/Modules/Searches/Crypto/_Common/StockData.cs
Normal file
15
src/NadekoBot/Modules/Searches/Crypto/_Common/StockData.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
#nullable disable
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public class StockData
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Ticker { get; set; }
|
||||
public double Price { get; set; }
|
||||
public long MarketCap { get; set; }
|
||||
public double Close { get; set; }
|
||||
public double Change50d { get; set; }
|
||||
public double Change200d { get; set; }
|
||||
public long DailyVolume { get; set; }
|
||||
public string Exchange { get; set; }
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
using System.Net.Http.Json;
|
||||
using YahooFinanceApi;
|
||||
|
||||
namespace NadekoBot.Modules.Searches;
|
||||
|
||||
public sealed class DefaultStockDataService : IStockDataService, INService
|
||||
{
|
||||
private readonly IHttpClientFactory _httpClientFactory;
|
||||
|
||||
public DefaultStockDataService(IHttpClientFactory httpClientFactory)
|
||||
{
|
||||
_httpClientFactory = httpClientFactory;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyCollection<StockData>> GetStockDataAsync(string query)
|
||||
{
|
||||
try
|
||||
{
|
||||
var symbols = await Yahoo.Symbols(query)
|
||||
.Fields(Field.LongName,
|
||||
Field.Symbol,
|
||||
Field.RegularMarketPrice,
|
||||
Field.RegularMarketPreviousClose,
|
||||
Field.MarketCap,
|
||||
Field.FiftyDayAverageChangePercent,
|
||||
Field.TwoHundredDayAverageChangePercent,
|
||||
Field.AverageDailyVolume10Day,
|
||||
Field.FullExchangeName)
|
||||
.QueryAsync();
|
||||
|
||||
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();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// what the hell is this api exception
|
||||
Log.Warning(ex, "Error getting stock data: {ErrorMessage}", ex.Message);
|
||||
return Array.Empty<StockData>();
|
||||
}
|
||||
}
|
||||
|
||||
public Task<IReadOnlyCollection<SymbolData>> SearchSymbolAsync(string query)
|
||||
{
|
||||
return Task.FromResult<IReadOnlyCollection<SymbolData>>(Array.Empty<SymbolData>());
|
||||
|
||||
// try
|
||||
// {
|
||||
// query = Uri.EscapeDataString(query);
|
||||
// using var http = _httpClientFactory.CreateClient();
|
||||
// var response = await http.GetFromJsonAsync<FinnHubSearchResponse>($"https://finnhub.io/api/v1/search"
|
||||
// + $"?q={query}"
|
||||
// + $"&token=");
|
||||
//
|
||||
// if (response is null)
|
||||
// return Array.Empty<SymbolData>();
|
||||
//
|
||||
// return response.Result
|
||||
// .Where(x => x.Type == "Common Stock")
|
||||
// .Select(static x => new SymbolData(x.Symbol, x.Description))
|
||||
// .ToList();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// Log.Warning(ex, "Error searching stock symbol: {ErrorMessage}", ex.Message);
|
||||
// return Array.Empty<SymbolData>();
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
public record SymbolData(string Symbol, string Description);
|
@@ -19,61 +19,64 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AngleSharp" Version="0.16.1"/>
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.7.10"/>
|
||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.2"/>
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0"/>
|
||||
<PackageReference Include="Discord.Net" Version="3.1.0"/>
|
||||
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92"/>
|
||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
|
||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.55.0.2449"/>
|
||||
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084"/>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.19.2"/>
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0"/>
|
||||
<PackageReference Include="AngleSharp" Version="0.16.1" />
|
||||
<PackageReference Include="AWSSDK.S3" Version="3.7.7.10" />
|
||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.2" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||
<PackageReference Include="Discord.Net" Version="3.1.0" />
|
||||
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92" />
|
||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138" />
|
||||
<PackageReference Include="Google.Apis.YouTube.v3" Version="1.55.0.2449" />
|
||||
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.19.2" />
|
||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.43.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Html2Markdown" Version="5.0.1.524"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1"/>
|
||||
<PackageReference Include="Html2Markdown" Version="5.0.1.524" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0"/>
|
||||
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0"/>
|
||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
||||
<PackageReference Include="Scrutor" Version="3.3.0"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1"/>
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0"/>
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4"/>
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010"/>
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.2.88"/>
|
||||
<PackageReference Include="YamlDotNet" Version="11.2.1"/>
|
||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="6.6.1"/>
|
||||
<PackageReference Include="Humanizer" Version="2.13.14"/>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="Scrutor" Version="3.3.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
|
||||
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="6.6.1" />
|
||||
<PackageReference Include="Humanizer" Version="2.13.14" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
|
||||
|
||||
<!-- Remove this when static abstract interface members support is released -->
|
||||
<PackageReference Include="System.Runtime.Experimental" Version="6.0.0" />
|
||||
|
||||
<!-- Used by .crypto command -->
|
||||
<PackageReference Include="YahooFinanceApi" Version="2.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj"/>
|
||||
<ProjectReference Include="..\NadekoBot.Generators\NadekoBot.Generators.csproj" OutputItemType="Analyzer"/>
|
||||
<ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj" />
|
||||
<ProjectReference Include="..\NadekoBot.Generators\NadekoBot.Generators.csproj" OutputItemType="Analyzer" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="data\strings\responses\responses.en-US.json"/>
|
||||
<AdditionalFiles Include="data\strings\responses\responses.en-US.json" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Protobuf Include="..\NadekoBot.Coordinator\Protos\coordinator.proto" GrpcServices="Client">
|
||||
@@ -102,13 +105,9 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<!-- TODO: Remove this when the conflict issue is fixed -->
|
||||
<Target Name="RemoveSystemRuntimeFromRefPack"
|
||||
BeforeTargets="_HandlePackageFileConflicts"
|
||||
Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
||||
<Target Name="RemoveSystemRuntimeFromRefPack" BeforeTargets="_HandlePackageFileConflicts" Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
||||
<ItemGroup>
|
||||
<Reference Remove="@(Reference)"
|
||||
Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
|
||||
'%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
||||
<Reference Remove="@(Reference)" Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
 '%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
||||
</ItemGroup>
|
||||
</Target>
|
||||
</Project>
|
||||
|
@@ -1,3 +1,5 @@
|
||||
using System.Buffers;
|
||||
|
||||
namespace NadekoBot.Extensions;
|
||||
|
||||
// made for expressions because they almost never get added
|
||||
@@ -29,4 +31,15 @@ public static class ArrayExtensions
|
||||
/// <returns>New array with updated elements</returns>
|
||||
public static TOut[] Map<TIn, TOut>(this TIn[] arr, Func<TIn, TOut> f)
|
||||
=> Array.ConvertAll(arr, x => f(x));
|
||||
|
||||
public static IReadOnlyCollection<TOut> Map<TIn, TOut>(this IReadOnlyCollection<TIn> col, Func<TIn, TOut> f)
|
||||
{
|
||||
var toReturn = new TOut[col.Count];
|
||||
|
||||
var i = 0;
|
||||
foreach (var item in col)
|
||||
toReturn[i++] = f(item);
|
||||
|
||||
return toReturn;
|
||||
}
|
||||
}
|
@@ -2033,6 +2033,11 @@ crypto:
|
||||
args:
|
||||
- "btc"
|
||||
- "bitcoin"
|
||||
stock:
|
||||
desc: "Shows basic information about a stock. Only symbols are supported. Full company names are not supported atm."
|
||||
args:
|
||||
- "tsla"
|
||||
- "amd"
|
||||
rolelevelreq:
|
||||
desc: "Set a level requirement on a self-assignable role."
|
||||
args:
|
||||
|
@@ -888,6 +888,7 @@
|
||||
"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}.",
|
||||
|
Reference in New Issue
Block a user