Compare commits
No commits in common. "v5" and "5.3.8" have entirely different histories.
14 changed files with 94 additions and 231 deletions
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -2,25 +2,6 @@
|
||||||
|
|
||||||
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
|
Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except date format. a-c-f-r-o
|
||||||
|
|
||||||
## [5.3.9] - 31.01.2025
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
- Added `.todo archive done <name>`
|
|
||||||
- Creates an archive of only currently completed todos
|
|
||||||
- An alternative to ".todo archive add <name>" which moves all todos to an archive
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
- Increased todo and archive limits slightly
|
|
||||||
- Global ellie captcha patron ad will show 12.5% of the time now, down from 20%, and be smaller
|
|
||||||
- `.remind` now has a 1 year max timeout, up from 2 months
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
- Captcha is now slightly bigger, with larger margin, to mitigate phone edge issues
|
|
||||||
- Fixed `.stock` command, unless there is some ip blocking going on
|
|
||||||
|
|
||||||
## [5.3.8] - 29.01.2025
|
## [5.3.8] - 29.01.2025
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
@ -214,7 +195,7 @@ Mostly based on [keepachangelog](https://keepachangelog.com/en/1.1.0/) except da
|
||||||
|
|
||||||
- Self Assigned Roles reworked! Use `.h .sar` for the list of commands
|
- Self Assigned Roles reworked! Use `.h .sar` for the list of commands
|
||||||
- `.sar autodel`
|
- `.sar autodel`
|
||||||
- Toggles the automatic deletion of the user's message and Ellie's confirmations for .iam and .iamn commands.
|
- Toggles the automatic deletion of the user's message and Nadeko's confirmations for .iam and .iamn commands.
|
||||||
- `.sar ad`
|
- `.sar ad`
|
||||||
- Adds a role to the list of self-assignable roles. You can also specify a group.
|
- Adds a role to the list of self-assignable roles. You can also specify a group.
|
||||||
- If 'Exclusive self-assignable roles' feature is enabled (.sar exclusive), users will be able to pick one role
|
- If 'Exclusive self-assignable roles' feature is enabled (.sar exclusive), users will be able to pick one role
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>true</ImplicitUsings>
|
<ImplicitUsings>true</ImplicitUsings>
|
||||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||||
<Version>5.3.9</Version>
|
<Version>5.3.8</Version>
|
||||||
|
|
||||||
<!-- Output/build -->
|
<!-- Output/build -->
|
||||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||||
|
|
|
@ -162,15 +162,15 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||||
|
|
||||||
if (password is not null)
|
if (password is not null)
|
||||||
{
|
{
|
||||||
var img = _captchaService.GetPasswordImage(password);
|
var img = GetPasswordImage(password);
|
||||||
await using var stream = await img.ToStreamAsync();
|
await using var stream = await img.ToStreamAsync();
|
||||||
var toSend = Response()
|
var toSend = Response()
|
||||||
.File(stream, "timely.png");
|
.File(stream, "timely.png");
|
||||||
|
|
||||||
#if GLOBAL_ELLIE
|
#if GLOBAL_ELLIE
|
||||||
if (_rng.Next(0, 8) == 0)
|
if (_rng.Next(0, 5) == 0)
|
||||||
toSend = toSend
|
toSend = toSend
|
||||||
.Text("*[Sub on Patreon](https://patreon.com/elliebot) to remove captcha.*");
|
.Confirm("[Sub on Patreon](https://patreon.com/elliebot) to remove captcha.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var captchaMessage = await toSend.SendAsync();
|
var captchaMessage = await toSend.SendAsync();
|
||||||
|
@ -194,6 +194,39 @@ public partial class Gambling : GamblingModule<GamblingService>
|
||||||
await ClaimTimely();
|
await ClaimTimely();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Image<Rgba32> GetPasswordImage(string password)
|
||||||
|
{
|
||||||
|
var img = new Image<Rgba32>(50, 24);
|
||||||
|
|
||||||
|
var font = _fonts.NotoSans.CreateFont(22);
|
||||||
|
var outlinePen = new SolidPen(Color.Black, 0.5f);
|
||||||
|
var strikeoutRun = new RichTextRun
|
||||||
|
{
|
||||||
|
Start = 0,
|
||||||
|
End = password.GetGraphemeCount(),
|
||||||
|
Font = font,
|
||||||
|
StrikeoutPen = new SolidPen(Color.White, 4),
|
||||||
|
TextDecorations = TextDecorations.Strikeout
|
||||||
|
};
|
||||||
|
// draw password on the image
|
||||||
|
img.Mutate(x =>
|
||||||
|
{
|
||||||
|
x.DrawText(new RichTextOptions(font)
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
|
FallbackFontFamilies = _fonts.FallBackFonts,
|
||||||
|
Origin = new(25, 12),
|
||||||
|
TextRuns = [strikeoutRun]
|
||||||
|
},
|
||||||
|
password,
|
||||||
|
Brushes.Solid(Color.White),
|
||||||
|
outlinePen);
|
||||||
|
});
|
||||||
|
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task ClaimTimely()
|
private async Task ClaimTimely()
|
||||||
{
|
{
|
||||||
var period = Config.Timely.Cooldown;
|
var period = Config.Timely.Cooldown;
|
||||||
|
|
|
@ -16,7 +16,7 @@ public sealed class CaptchaService(FontProvider fonts, IBotCache cache, IPatrona
|
||||||
|
|
||||||
public Image<Rgba32> GetPasswordImage(string password)
|
public Image<Rgba32> GetPasswordImage(string password)
|
||||||
{
|
{
|
||||||
var img = new Image<Rgba32>(60, 34);
|
var img = new Image<Rgba32>(50, 24);
|
||||||
|
|
||||||
var font = fonts.NotoSans.CreateFont(22);
|
var font = fonts.NotoSans.CreateFont(22);
|
||||||
var outlinePen = new SolidPen(Color.Black, 0.5f);
|
var outlinePen = new SolidPen(Color.Black, 0.5f);
|
||||||
|
@ -38,7 +38,7 @@ public sealed class CaptchaService(FontProvider fonts, IBotCache cache, IPatrona
|
||||||
HorizontalAlignment = HorizontalAlignment.Center,
|
HorizontalAlignment = HorizontalAlignment.Center,
|
||||||
VerticalAlignment = VerticalAlignment.Center,
|
VerticalAlignment = VerticalAlignment.Center,
|
||||||
FallbackFontFamilies = fonts.FallBackFonts,
|
FallbackFontFamilies = fonts.FallBackFonts,
|
||||||
Origin = new(30, 15),
|
Origin = new(25, 12),
|
||||||
TextRuns = [strikeoutRun]
|
TextRuns = [strikeoutRun]
|
||||||
},
|
},
|
||||||
password,
|
password,
|
||||||
|
|
|
@ -33,9 +33,9 @@ public partial class Games
|
||||||
.File(stream, "timely.png");
|
.File(stream, "timely.png");
|
||||||
|
|
||||||
#if GLOBAL_ELLIE
|
#if GLOBAL_ELLIE
|
||||||
if (_rng.Next(0, 8) == 0)
|
if (_rng.Next(0, 5) == 0)
|
||||||
toSend = toSend
|
toSend = toSend
|
||||||
.Text("*[Sub on Patreon](https://patreon.com/elliebot) to remove captcha.*");
|
.Confirm("[Sub on Patreon](https://patreon.com/elliebot) to remove captcha.");
|
||||||
#endif
|
#endif
|
||||||
var captcha = await toSend.SendAsync();
|
var captcha = await toSend.SendAsync();
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,6 @@
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
using CsvHelper.Configuration;
|
using CsvHelper.Configuration;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Net;
|
|
||||||
using System.Net.Http.Json;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace EllieBot.Modules.Searches;
|
namespace EllieBot.Modules.Searches;
|
||||||
|
@ -11,57 +9,54 @@ namespace EllieBot.Modules.Searches;
|
||||||
public sealed class DefaultStockDataService : IStockDataService, IEService
|
public sealed class DefaultStockDataService : IStockDataService, IEService
|
||||||
{
|
{
|
||||||
private readonly IHttpClientFactory _httpClientFactory;
|
private readonly IHttpClientFactory _httpClientFactory;
|
||||||
private readonly IBotCache _cache;
|
|
||||||
|
|
||||||
public DefaultStockDataService(IHttpClientFactory httpClientFactory, IBotCache cache)
|
public DefaultStockDataService(IHttpClientFactory httpClientFactory)
|
||||||
=> (_httpClientFactory, _cache) = (httpClientFactory, cache);
|
=> _httpClientFactory = httpClientFactory;
|
||||||
|
|
||||||
private static TypedKey<StockData> GetStockDataKey(string query)
|
|
||||||
=> new($"stockdata:{query}");
|
|
||||||
|
|
||||||
public async Task<StockData?> GetStockDataAsync(string query)
|
public async Task<StockData?> GetStockDataAsync(string query)
|
||||||
{
|
|
||||||
ArgumentException.ThrowIfNullOrWhiteSpace(query);
|
|
||||||
|
|
||||||
return await _cache.GetOrAddAsync(GetStockDataKey(query.Trim().ToLowerInvariant()),
|
|
||||||
() => GetStockDataInternalAsync(query),
|
|
||||||
expiry: TimeSpan.FromHours(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<StockData?> GetStockDataInternalAsync(string query)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!query.IsAlphaNumeric())
|
if (!query.IsAlphaNumeric())
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var info = await GetNasdaqDataResponse<NasdaqSummaryResponse>(
|
using var http = _httpClientFactory.CreateClient();
|
||||||
$"https://api.nasdaq.com/api/quote/{query}/summary?assetclass=stocks");
|
|
||||||
|
|
||||||
if (info?.Data is not { } d || d.SummaryData is not { } sd)
|
var quoteHtmlPage = $"https://finance.yahoo.com/quote/{query.ToUpperInvariant()}";
|
||||||
|
|
||||||
|
var config = Configuration.Default.WithDefaultLoader();
|
||||||
|
using var document = await BrowsingContext.New(config).OpenAsync(quoteHtmlPage);
|
||||||
|
|
||||||
|
var tickerName = document.QuerySelector("div.top > .left > .container > h1")
|
||||||
|
?.TextContent;
|
||||||
|
|
||||||
|
if (tickerName is null)
|
||||||
return default;
|
return default;
|
||||||
|
|
||||||
var closePrice = double.Parse(sd.PreviousClose.Value?.Substring(1) ?? "0",
|
var marketcap = document
|
||||||
NumberStyles.Any,
|
.QuerySelector("li > span > fin-streamer[data-field='marketCap']")
|
||||||
CultureInfo.InvariantCulture);
|
?.TextContent;
|
||||||
|
|
||||||
var price = d.BidAsk.Bid.Value.IndexOf('*') is var idx and > 0
|
|
||||||
&& double.TryParse(d.BidAsk.Bid.Value.Substring(1, idx - 1),
|
var volume = document.QuerySelector("li > span > fin-streamer[data-field='regularMarketVolume']")
|
||||||
NumberStyles.Any,
|
?.TextContent;
|
||||||
CultureInfo.InvariantCulture,
|
|
||||||
out var bid)
|
var close = document.QuerySelector("li > span > fin-streamer[data-field='regularMarketPreviousClose']")
|
||||||
? bid
|
?.TextContent
|
||||||
: double.NaN;
|
?? "0";
|
||||||
|
|
||||||
|
var price = document.QuerySelector("fin-streamer.livePrice > span")
|
||||||
|
?.TextContent
|
||||||
|
?? "0";
|
||||||
|
|
||||||
return new()
|
return new()
|
||||||
{
|
{
|
||||||
Name = query,
|
Name = tickerName,
|
||||||
Symbol = info.Data.Symbol,
|
Symbol = query,
|
||||||
Price = price,
|
Price = double.Parse(price, NumberStyles.Any, CultureInfo.InvariantCulture),
|
||||||
Close = closePrice,
|
Close = double.Parse(close, NumberStyles.Any, CultureInfo.InvariantCulture),
|
||||||
MarketCap = sd.MarketCap.Value,
|
MarketCap = marketcap,
|
||||||
DailyVolume =
|
DailyVolume = (long)double.Parse(volume ?? "0", NumberStyles.Any, CultureInfo.InvariantCulture),
|
||||||
(long)double.Parse(sd.AverageVolume.Value ?? "0", NumberStyles.Any, CultureInfo.InvariantCulture),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -71,36 +66,6 @@ public sealed class DefaultStockDataService : IStockDataService, IEService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<NasdaqDataResponse<T>?> GetNasdaqDataResponse<T>(string url)
|
|
||||||
{
|
|
||||||
using var httpClient = _httpClientFactory.CreateClient("google:search");
|
|
||||||
|
|
||||||
var req = new HttpRequestMessage(HttpMethod.Get,
|
|
||||||
url)
|
|
||||||
{
|
|
||||||
Headers =
|
|
||||||
{
|
|
||||||
{ "Host", "api.nasdaq.com" },
|
|
||||||
{ "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:132.0) Gecko/20100101 Firefox/132.0" },
|
|
||||||
{ "Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" },
|
|
||||||
{ "Accept-Language", "en-US,en;q=0.5" },
|
|
||||||
{ "Accept-Encoding", "gzip, deflate, br, zstd" },
|
|
||||||
{ "Connection", "keep-alive" },
|
|
||||||
{ "Upgrade-Insecure-Requests", "1" },
|
|
||||||
{ "Sec-Fetch-Dest", "document" },
|
|
||||||
{ "Sec-Fetch-Mode", "navigate" },
|
|
||||||
{ "Sec-Fetch-Site", "none" },
|
|
||||||
{ "Sec-Fetch-User", "?1" },
|
|
||||||
{ "Priority", "u=0, i" },
|
|
||||||
{ "TE", "trailers" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var res = await httpClient.SendAsync(req);
|
|
||||||
|
|
||||||
var info = await res.Content.ReadFromJsonAsync<NasdaqDataResponse<T>>();
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<SymbolData>> SearchSymbolAsync(string query)
|
public async Task<IReadOnlyCollection<SymbolData>> SearchSymbolAsync(string query)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(query))
|
if (string.IsNullOrWhiteSpace(query))
|
||||||
|
@ -126,37 +91,22 @@ public sealed class DefaultStockDataService : IStockDataService, IEService
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TypedKey<IReadOnlyCollection<CandleData>> GetCandleDataKey(string query)
|
private static CsvConfiguration _csvConfig = new(CultureInfo.InvariantCulture);
|
||||||
=> new($"candledata:{query}");
|
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<CandleData>> GetCandleDataAsync(string query)
|
public async Task<IReadOnlyCollection<CandleData>> GetCandleDataAsync(string query)
|
||||||
=> await _cache.GetOrAddAsync(GetCandleDataKey(query),
|
|
||||||
async () => await GetCandleDataInternalAsync(query),
|
|
||||||
expiry: TimeSpan.FromHours(4))
|
|
||||||
?? [];
|
|
||||||
|
|
||||||
public async Task<IReadOnlyCollection<CandleData>> GetCandleDataInternalAsync(string query)
|
|
||||||
{
|
{
|
||||||
using var http = _httpClientFactory.CreateClient();
|
using var http = _httpClientFactory.CreateClient();
|
||||||
|
await using var resStream = await http.GetStreamAsync(
|
||||||
|
$"https://query1.finance.yahoo.com/v7/finance/download/{query}"
|
||||||
|
+ $"?period1={DateTime.UtcNow.Subtract(30.Days()).ToTimestamp()}"
|
||||||
|
+ $"&period2={DateTime.UtcNow.ToTimestamp()}"
|
||||||
|
+ "&interval=1d");
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
using var textReader = new StreamReader(resStream);
|
||||||
var fromdate = now.Subtract(30.Days()).ToString("yyyy-MM-dd");
|
using var csv = new CsvReader(textReader, _csvConfig);
|
||||||
var todate = now.ToString("yyyy-MM-dd");
|
var records = csv.GetRecords<YahooFinanceCandleData>().ToArray();
|
||||||
|
|
||||||
var res = await GetNasdaqDataResponse<NasdaqChartResponse>(
|
return records
|
||||||
$"https://api.nasdaq.com/api/quote/{query}/chart?assetclass=stocks"
|
.Map(static x => new CandleData(x.Open, x.Close, x.High, x.Low, x.Volume));
|
||||||
+ $"&fromdate={fromdate}"
|
|
||||||
+ $"&todate={todate}");
|
|
||||||
|
|
||||||
if (res?.Data?.Chart is not { } chart)
|
|
||||||
return Array.Empty<CandleData>();
|
|
||||||
|
|
||||||
|
|
||||||
return chart.Select(d => new CandleData(d.Z.Open,
|
|
||||||
d.Z.Close,
|
|
||||||
d.Z.High,
|
|
||||||
d.Z.Low,
|
|
||||||
(long)double.Parse(d.Z.Volume, NumberStyles.Any, CultureInfo.InvariantCulture)))
|
|
||||||
.ToList();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,20 +0,0 @@
|
||||||
namespace EllieBot.Modules.Searches;
|
|
||||||
|
|
||||||
public sealed class NasdaqChartResponse
|
|
||||||
{
|
|
||||||
public required NasdaqChartResponseData[] Chart { get; init; }
|
|
||||||
|
|
||||||
public sealed class NasdaqChartResponseData
|
|
||||||
{
|
|
||||||
public required CandleData Z { get; init; }
|
|
||||||
|
|
||||||
public sealed class CandleData
|
|
||||||
{
|
|
||||||
public required decimal High { get; init; }
|
|
||||||
public required decimal Low { get; init; }
|
|
||||||
public required decimal Open { get; init; }
|
|
||||||
public required decimal Close { get; init; }
|
|
||||||
public required string Volume { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace EllieBot.Modules.Searches;
|
|
||||||
|
|
||||||
public sealed class NasdaqDataResponse<T>
|
|
||||||
{
|
|
||||||
public required T? Data { get; init; }
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace EllieBot.Modules.Searches;
|
|
||||||
|
|
||||||
public sealed class NasdaqSummaryResponse
|
|
||||||
{
|
|
||||||
public required string Symbol { get; init; }
|
|
||||||
|
|
||||||
public required NasdaqSummaryResponseData SummaryData { get; init; }
|
|
||||||
public required NasdaqSummaryBidAsk BidAsk { get; init; }
|
|
||||||
|
|
||||||
public sealed class NasdaqSummaryBidAsk
|
|
||||||
{
|
|
||||||
[JsonPropertyName("Bid * Size")]
|
|
||||||
public required NasdaqBid Bid { get; init; }
|
|
||||||
|
|
||||||
public sealed class NasdaqBid
|
|
||||||
{
|
|
||||||
public required string Value { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class NasdaqSummaryResponseData
|
|
||||||
{
|
|
||||||
public required PreviousCloseData PreviousClose { get; init; }
|
|
||||||
public required MarketCapData MarketCap { get; init; }
|
|
||||||
public required AverageVolumeData AverageVolume { get; init; }
|
|
||||||
|
|
||||||
public sealed class PreviousCloseData
|
|
||||||
{
|
|
||||||
public required string Value { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class MarketCapData
|
|
||||||
{
|
|
||||||
public required string Value { get; init; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class AverageVolumeData
|
|
||||||
{
|
|
||||||
public required string Value { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -183,7 +183,7 @@ public partial class Utility
|
||||||
{
|
{
|
||||||
var time = DateTime.UtcNow + ts;
|
var time = DateTime.UtcNow + ts;
|
||||||
|
|
||||||
if (ts > TimeSpan.FromDays(366))
|
if (ts > TimeSpan.FromDays(60))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (ctx.Guild is not null)
|
if (ctx.Guild is not null)
|
||||||
|
|
|
@ -150,26 +150,7 @@ public partial class Utility
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task TodoArchiveAdd([Leftover] string name)
|
public async Task TodoArchiveAdd([Leftover] string name)
|
||||||
{
|
{
|
||||||
var result = await _service.ArchiveTodosAsync(ctx.User.Id, name, false);
|
var result = await _service.ArchiveTodosAsync(ctx.User.Id, name);
|
||||||
if (result == ArchiveTodoResult.NoTodos)
|
|
||||||
{
|
|
||||||
await Response().Error(strs.todo_no_todos).SendAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result == ArchiveTodoResult.MaxLimitReached)
|
|
||||||
{
|
|
||||||
await Response().Error(strs.todo_archive_max_limit).SendAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ctx.OkAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
[Cmd]
|
|
||||||
public async Task TodoArchiveDone([Leftover] string name)
|
|
||||||
{
|
|
||||||
var result = await _service.ArchiveTodosAsync(ctx.User.Id, name, true);
|
|
||||||
if (result == ArchiveTodoResult.NoTodos)
|
if (result == ArchiveTodoResult.NoTodos)
|
||||||
{
|
{
|
||||||
await Response().Error(strs.todo_no_todos).SendAsync();
|
await Response().Error(strs.todo_no_todos).SendAsync();
|
||||||
|
@ -212,7 +193,7 @@ public partial class Utility
|
||||||
|
|
||||||
foreach (var archivedList in items)
|
foreach (var archivedList in items)
|
||||||
{
|
{
|
||||||
eb.AddField($"id: {new kwum(archivedList.Id)}", archivedList.Name, true);
|
eb.AddField($"id: {archivedList.Id.ToString()}", archivedList.Name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return eb;
|
return eb;
|
||||||
|
@ -221,7 +202,7 @@ public partial class Utility
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task TodoArchiveShow(kwum id)
|
public async Task TodoArchiveShow(int id)
|
||||||
{
|
{
|
||||||
var list = await _service.GetArchivedTodoListAsync(ctx.User.Id, id);
|
var list = await _service.GetArchivedTodoListAsync(ctx.User.Id, id);
|
||||||
if (list == null || list.Items.Count == 0)
|
if (list == null || list.Items.Count == 0)
|
||||||
|
@ -253,7 +234,7 @@ public partial class Utility
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cmd]
|
[Cmd]
|
||||||
public async Task TodoArchiveDelete(kwum id)
|
public async Task TodoArchiveDelete(int id)
|
||||||
{
|
{
|
||||||
if (!await _service.ArchiveDeleteAsync(ctx.User.Id, id))
|
if (!await _service.ArchiveDeleteAsync(ctx.User.Id, id))
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,8 +6,8 @@ namespace EllieBot.Modules.Utility;
|
||||||
|
|
||||||
public sealed class TodoService : IEService
|
public sealed class TodoService : IEService
|
||||||
{
|
{
|
||||||
private const int ARCHIVE_MAX_COUNT = 18;
|
private const int ARCHIVE_MAX_COUNT = 9;
|
||||||
private const int TODO_MAX_COUNT = 36;
|
private const int TODO_MAX_COUNT = 27;
|
||||||
|
|
||||||
private readonly DbService _db;
|
private readonly DbService _db;
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ public sealed class TodoService : IEService
|
||||||
.DeleteAsync();
|
.DeleteAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<ArchiveTodoResult> ArchiveTodosAsync(ulong userId, string name, bool onlyDone)
|
public async Task<ArchiveTodoResult> ArchiveTodosAsync(ulong userId, string name)
|
||||||
{
|
{
|
||||||
// create a new archive
|
// create a new archive
|
||||||
|
|
||||||
|
@ -140,7 +140,7 @@ public sealed class TodoService : IEService
|
||||||
|
|
||||||
var updated = await ctx
|
var updated = await ctx
|
||||||
.GetTable<TodoModel>()
|
.GetTable<TodoModel>()
|
||||||
.Where(x => x.UserId == userId && (!onlyDone || x.IsDone) && x.ArchiveId == null)
|
.Where(x => x.UserId == userId && x.ArchiveId == null)
|
||||||
.Set(x => x.ArchiveId, inserted.Id)
|
.Set(x => x.ArchiveId, inserted.Id)
|
||||||
.UpdateAsync();
|
.UpdateAsync();
|
||||||
|
|
||||||
|
|
|
@ -1441,11 +1441,6 @@ todoarchivedelete:
|
||||||
- del
|
- del
|
||||||
- remove
|
- remove
|
||||||
- rm
|
- rm
|
||||||
todoarchivedone:
|
|
||||||
- done
|
|
||||||
- compelete
|
|
||||||
- finish
|
|
||||||
- completed
|
|
||||||
todoedit:
|
todoedit:
|
||||||
- edit
|
- edit
|
||||||
- change
|
- change
|
||||||
|
|
|
@ -4524,13 +4524,6 @@ todoarchiveadd:
|
||||||
params:
|
params:
|
||||||
- name:
|
- name:
|
||||||
desc: "The name of the archive to be created."
|
desc: "The name of the archive to be created."
|
||||||
todoarchivedone:
|
|
||||||
desc: Creates a new archive with the specified name using only completed current todos.
|
|
||||||
ex:
|
|
||||||
- Success!
|
|
||||||
params:
|
|
||||||
- name:
|
|
||||||
desc: "The name of the archive to be created."
|
|
||||||
todoarchivelist:
|
todoarchivelist:
|
||||||
desc: Lists all archived todo lists.
|
desc: Lists all archived todo lists.
|
||||||
ex:
|
ex:
|
||||||
|
|
Loading…
Add table
Reference in a new issue