mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28: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_indexers = true:suggestion
|
||||||
csharp_style_expression_bodied_lambdas = true:suggestion
|
csharp_style_expression_bodied_lambdas = true:suggestion
|
||||||
csharp_style_expression_bodied_local_functions = true:suggestion
|
csharp_style_expression_bodied_local_functions = true:suggestion
|
||||||
csharp_style_expression_bodied_methods = when_on_single_line:warning
|
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
|
||||||
csharp_style_expression_bodied_operators = when_on_single_line:warning
|
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
|
||||||
csharp_style_expression_bodied_properties = true:suggestion
|
csharp_style_expression_bodied_properties = true:suggestion
|
||||||
|
|
||||||
# Pattern matching preferences
|
# 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.
|
[Comment(@"https://pro.coinmarketcap.com/account/ api key. There is a free plan for personal use.
|
||||||
Used for cryptocurrency related commands.")]
|
Used for cryptocurrency related commands.")]
|
||||||
public string CoinmarketcapApiKey { get; set; }
|
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")]
|
[Comment(@"Api key used for Osu related commands. Obtain this key at https://osu.ppy.sh/p/api")]
|
||||||
public string OsuApiKey { get; set; }
|
public string OsuApiKey { get; set; }
|
||||||
|
@@ -8,6 +8,110 @@ public partial class Searches
|
|||||||
{
|
{
|
||||||
public partial class CryptoCommands : NadekoSubmodule<CryptoService>
|
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]
|
[Cmd]
|
||||||
public async partial Task Crypto(string name)
|
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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="AngleSharp" Version="0.16.1"/>
|
<PackageReference Include="AngleSharp" Version="0.16.1" />
|
||||||
<PackageReference Include="AWSSDK.S3" Version="3.7.7.10"/>
|
<PackageReference Include="AWSSDK.S3" Version="3.7.7.10" />
|
||||||
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.2"/>
|
<PackageReference Include="CodeHollow.FeedReader" Version="1.2.2" />
|
||||||
<PackageReference Include="CommandLineParser" Version="2.8.0"/>
|
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||||
<PackageReference Include="Discord.Net" Version="3.1.0"/>
|
<PackageReference Include="Discord.Net" Version="3.1.0" />
|
||||||
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92"/>
|
<PackageReference Include="CoreCLR-NCalc" Version="2.2.92" />
|
||||||
<PackageReference Include="Google.Apis.Urlshortener.v1" Version="1.41.1.138"/>
|
<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.YouTube.v3" Version="1.55.0.2449" />
|
||||||
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084"/>
|
<PackageReference Include="Google.Apis.Customsearch.v1" Version="1.49.0.2084" />
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.19.2"/>
|
<PackageReference Include="Google.Protobuf" Version="3.19.2" />
|
||||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0"/>
|
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.41.0" />
|
||||||
<PackageReference Include="Grpc.Tools" Version="2.43.0">
|
<PackageReference Include="Grpc.Tools" Version="2.43.0">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Html2Markdown" Version="5.0.1.524"/>
|
<PackageReference Include="Html2Markdown" Version="5.0.1.524" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.1" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.1">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1"/>
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" 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="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
|
||||||
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0"/>
|
<PackageReference Include="NetEscapades.Configuration.Yaml" Version="2.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0"/>
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" 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.Http" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Console" 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.Extensions.Logging.Debug" Version="6.0.0" />
|
||||||
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2"/>
|
<PackageReference Include="Microsoft.SyndicationFeed.ReaderWriter" Version="1.0.2" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="Scrutor" Version="3.3.0"/>
|
<PackageReference Include="Scrutor" Version="3.3.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1"/>
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0"/>
|
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.0" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4"/>
|
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010"/>
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta0010" />
|
||||||
<PackageReference Include="StackExchange.Redis" Version="2.2.88"/>
|
<PackageReference Include="StackExchange.Redis" Version="2.2.88" />
|
||||||
<PackageReference Include="YamlDotNet" Version="11.2.1"/>
|
<PackageReference Include="YamlDotNet" Version="11.2.1" />
|
||||||
<PackageReference Include="linq2db.EntityFrameworkCore" Version="6.6.1"/>
|
<PackageReference Include="linq2db.EntityFrameworkCore" Version="6.6.1" />
|
||||||
<PackageReference Include="Humanizer" Version="2.13.14"/>
|
<PackageReference Include="Humanizer" Version="2.13.14" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0"/>
|
<PackageReference Include="JetBrains.Annotations" Version="2021.3.0" />
|
||||||
|
|
||||||
<!-- Remove this when static abstract interface members support is released -->
|
<!-- Remove this when static abstract interface members support is released -->
|
||||||
<PackageReference Include="System.Runtime.Experimental" Version="6.0.0" />
|
<PackageReference Include="System.Runtime.Experimental" Version="6.0.0" />
|
||||||
|
|
||||||
|
<!-- Used by .crypto command -->
|
||||||
|
<PackageReference Include="YahooFinanceApi" Version="2.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj"/>
|
<ProjectReference Include="..\ayu\Ayu.Discord.Voice\Ayu.Discord.Voice.csproj" />
|
||||||
<ProjectReference Include="..\NadekoBot.Generators\NadekoBot.Generators.csproj" OutputItemType="Analyzer"/>
|
<ProjectReference Include="..\NadekoBot.Generators\NadekoBot.Generators.csproj" OutputItemType="Analyzer" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="data\strings\responses\responses.en-US.json"/>
|
<AdditionalFiles Include="data\strings\responses\responses.en-US.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Protobuf Include="..\NadekoBot.Coordinator\Protos\coordinator.proto" GrpcServices="Client">
|
<Protobuf Include="..\NadekoBot.Coordinator\Protos\coordinator.proto" GrpcServices="Client">
|
||||||
@@ -102,13 +105,9 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<!-- TODO: Remove this when the conflict issue is fixed -->
|
<!-- TODO: Remove this when the conflict issue is fixed -->
|
||||||
<Target Name="RemoveSystemRuntimeFromRefPack"
|
<Target Name="RemoveSystemRuntimeFromRefPack" BeforeTargets="_HandlePackageFileConflicts" Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
||||||
BeforeTargets="_HandlePackageFileConflicts"
|
|
||||||
Condition="'@(Reference -> WithMetadataValue('NugetPackageId', 'System.Runtime.Experimental'))' != ''">
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Remove="@(Reference)"
|
<Reference Remove="@(Reference)" Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
 '%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
||||||
Condition="$([System.String]::Copy(%(Reference.Identity)).Contains('System.Runtime.dll')) and
|
|
||||||
'%(Reference.NuGetPackageId)' == 'Microsoft.NETCore.App.Ref'" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Target>
|
</Target>
|
||||||
</Project>
|
</Project>
|
||||||
|
@@ -1,3 +1,5 @@
|
|||||||
|
using System.Buffers;
|
||||||
|
|
||||||
namespace NadekoBot.Extensions;
|
namespace NadekoBot.Extensions;
|
||||||
|
|
||||||
// made for expressions because they almost never get added
|
// made for expressions because they almost never get added
|
||||||
@@ -29,4 +31,15 @@ public static class ArrayExtensions
|
|||||||
/// <returns>New array with updated elements</returns>
|
/// <returns>New array with updated elements</returns>
|
||||||
public static TOut[] Map<TIn, TOut>(this TIn[] arr, Func<TIn, TOut> f)
|
public static TOut[] Map<TIn, TOut>(this TIn[] arr, Func<TIn, TOut> f)
|
||||||
=> Array.ConvertAll(arr, x => f(x));
|
=> 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:
|
args:
|
||||||
- "btc"
|
- "btc"
|
||||||
- "bitcoin"
|
- "bitcoin"
|
||||||
|
stock:
|
||||||
|
desc: "Shows basic information about a stock. Only symbols are supported. Full company names are not supported atm."
|
||||||
|
args:
|
||||||
|
- "tsla"
|
||||||
|
- "amd"
|
||||||
rolelevelreq:
|
rolelevelreq:
|
||||||
desc: "Set a level requirement on a self-assignable role."
|
desc: "Set a level requirement on a self-assignable role."
|
||||||
args:
|
args:
|
||||||
|
@@ -888,6 +888,7 @@
|
|||||||
"volume_24h": "Volume (24h)",
|
"volume_24h": "Volume (24h)",
|
||||||
"change_7d_24h": "Change (7d / 24h)",
|
"change_7d_24h": "Change (7d / 24h)",
|
||||||
"crypto_not_found": "CryptoCurrency with that name was not found.",
|
"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}?",
|
"did_you_mean": "Did you mean {0}?",
|
||||||
"self_assign_level_req": "Self assignable role {0} now requires at least server level {1}.",
|
"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}.",
|
"self_assign_not_level": "That self-assignable role requires at least server level {0}.",
|
||||||
|
Reference in New Issue
Block a user