mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
dev: Added initial version of the grpc api. Added relevant dummy settings to creds (they have no effect rn)
dev: Yt searches now INTERNALLY return multiple results but there is no way right now to paginate plain text results dev: moved some stuff around
This commit is contained in:
@@ -789,7 +789,7 @@ public sealed class NadekoExpressionsService : IExecOnMessage, IReadyExecutor
|
||||
|
||||
if (newguildExpressions.TryGetValue(guildId, out var exprs))
|
||||
{
|
||||
return (exprs.Where(x => x.Trigger.Contains(query))
|
||||
return (exprs.Where(x => x.Trigger.Contains(query) || x.Response.Contains(query))
|
||||
.Skip(page * 9)
|
||||
.Take(9)
|
||||
.ToArray(), exprs.Length);
|
||||
|
@@ -227,7 +227,7 @@ public partial class Gambling
|
||||
if (page > 100)
|
||||
page = 100;
|
||||
|
||||
var waifus = _service.GetTopWaifusAtPage(page).ToList();
|
||||
var waifus = await _service.GetTopWaifusAtPage(page);
|
||||
|
||||
if (waifus.Count == 0)
|
||||
{
|
||||
|
@@ -300,10 +300,10 @@ public class WaifuService : INService, IReadyExecutor
|
||||
return (oldAff, success, remaining);
|
||||
}
|
||||
|
||||
public IEnumerable<WaifuLbResult> GetTopWaifusAtPage(int page, int perPage = 9)
|
||||
public async Task<IReadOnlyList<WaifuLbResult>> GetTopWaifusAtPage(int page, int perPage = 9)
|
||||
{
|
||||
using var uow = _db.GetDbContext();
|
||||
return uow.Set<WaifuInfo>().GetTop(perPage, page * perPage);
|
||||
await using var uow = _db.GetDbContext();
|
||||
return await uow.Set<WaifuInfo>().GetTop(perPage, page * perPage);
|
||||
}
|
||||
|
||||
public ulong GetWaifuUserId(ulong ownerId, string name)
|
||||
|
@@ -25,30 +25,30 @@ public static class WaifuExtensions
|
||||
return includes(waifus).AsQueryable().FirstOrDefault(wi => wi.Waifu.UserId == userId);
|
||||
}
|
||||
|
||||
public static IEnumerable<WaifuLbResult> GetTop(this DbSet<WaifuInfo> waifus, int count, int skip = 0)
|
||||
public static async Task<IReadOnlyList<WaifuLbResult>> GetTop(this DbSet<WaifuInfo> waifus, int count, int skip = 0)
|
||||
{
|
||||
ArgumentOutOfRangeException.ThrowIfNegative(count);
|
||||
|
||||
if (count == 0)
|
||||
return [];
|
||||
|
||||
return waifus.Include(wi => wi.Waifu)
|
||||
.Include(wi => wi.Affinity)
|
||||
.Include(wi => wi.Claimer)
|
||||
.OrderByDescending(wi => wi.Price)
|
||||
.Skip(skip)
|
||||
.Take(count)
|
||||
.Select(x => new WaifuLbResult
|
||||
{
|
||||
Affinity = x.Affinity == null ? null : x.Affinity.Username,
|
||||
AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator,
|
||||
Claimer = x.Claimer == null ? null : x.Claimer.Username,
|
||||
ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
|
||||
Username = x.Waifu.Username,
|
||||
Discrim = x.Waifu.Discriminator,
|
||||
Price = x.Price
|
||||
})
|
||||
.ToList();
|
||||
return await waifus.Include(wi => wi.Waifu)
|
||||
.Include(wi => wi.Affinity)
|
||||
.Include(wi => wi.Claimer)
|
||||
.OrderByDescending(wi => wi.Price)
|
||||
.Skip(skip)
|
||||
.Take(count)
|
||||
.Select(x => new WaifuLbResult
|
||||
{
|
||||
Affinity = x.Affinity == null ? null : x.Affinity.Username,
|
||||
AffinityDiscrim = x.Affinity == null ? null : x.Affinity.Discriminator,
|
||||
Claimer = x.Claimer == null ? null : x.Claimer.Username,
|
||||
ClaimerDiscrim = x.Claimer == null ? null : x.Claimer.Discriminator,
|
||||
Username = x.Waifu.Username,
|
||||
Discrim = x.Waifu.Discriminator,
|
||||
Price = x.Price
|
||||
})
|
||||
.ToListAsyncEF();
|
||||
}
|
||||
|
||||
public static decimal GetTotalValue(this DbSet<WaifuInfo> waifus)
|
||||
@@ -64,7 +64,7 @@ public static class WaifuExtensions
|
||||
public static async Task<WaifuInfoStats> GetWaifuInfoAsync(this DbContext ctx, ulong userId)
|
||||
{
|
||||
await ctx.EnsureUserCreatedAsync(userId);
|
||||
|
||||
|
||||
await ctx.Set<WaifuInfo>()
|
||||
.ToLinqToDBTable()
|
||||
.InsertOrUpdateAsync(() => new()
|
||||
|
@@ -43,8 +43,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
||||
+ "--no-check-certificate "
|
||||
+ "-i "
|
||||
+ "--yes-playlist "
|
||||
+ "-- \"{0}\"",
|
||||
scs.Data.YtProvider != YoutubeSearcher.Ytdl);
|
||||
+ "-- \"{0}\"");
|
||||
|
||||
_ytdlIdOperation = new("-4 "
|
||||
+ "--geo-bypass "
|
||||
@@ -56,8 +55,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
||||
+ "--get-thumbnail "
|
||||
+ "--get-duration "
|
||||
+ "--no-check-certificate "
|
||||
+ "-- \"{0}\"",
|
||||
scs.Data.YtProvider != YoutubeSearcher.Ytdl);
|
||||
+ "-- \"{0}\"");
|
||||
|
||||
_ytdlSearchOperation = new("-4 "
|
||||
+ "--geo-bypass "
|
||||
@@ -70,8 +68,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
||||
+ "--get-duration "
|
||||
+ "--no-check-certificate "
|
||||
+ "--default-search "
|
||||
+ "\"ytsearch:\" -- \"{0}\"",
|
||||
scs.Data.YtProvider != YoutubeSearcher.Ytdl);
|
||||
+ "\"ytsearch:\" -- \"{0}\"");
|
||||
}
|
||||
|
||||
private YtTrackData ResolveYtdlData(string ytdlOutputString)
|
||||
@@ -291,6 +288,7 @@ public sealed class YtdlYoutubeResolver : IYoutubeResolver
|
||||
|
||||
public Task<string?> GetStreamUrl(string videoId)
|
||||
=> CreateCacherFactory(videoId)();
|
||||
|
||||
private readonly struct YtTrackData
|
||||
{
|
||||
public readonly string Title;
|
||||
|
@@ -7,10 +7,9 @@ public sealed class DefaultSearchServiceFactory : ISearchServiceFactory, INServi
|
||||
{
|
||||
private readonly SearchesConfigService _scs;
|
||||
private readonly SearxSearchService _sss;
|
||||
private readonly YtDlpSearchService _ytdlp;
|
||||
private readonly GoogleSearchService _gss;
|
||||
|
||||
private readonly YtdlpYoutubeSearchService _ytdlp;
|
||||
private readonly YtdlYoutubeSearchService _ytdl;
|
||||
private readonly YoutubeDataApiSearchService _ytdata;
|
||||
private readonly InvidiousYtSearchService _iYtSs;
|
||||
private readonly GoogleScrapeService _gscs;
|
||||
@@ -20,19 +19,17 @@ public sealed class DefaultSearchServiceFactory : ISearchServiceFactory, INServi
|
||||
GoogleSearchService gss,
|
||||
GoogleScrapeService gscs,
|
||||
SearxSearchService sss,
|
||||
YtdlpYoutubeSearchService ytdlp,
|
||||
YtdlYoutubeSearchService ytdl,
|
||||
YtDlpSearchService ytdlp,
|
||||
YoutubeDataApiSearchService ytdata,
|
||||
InvidiousYtSearchService iYtSs)
|
||||
{
|
||||
_scs = scs;
|
||||
_sss = sss;
|
||||
_ytdlp = ytdlp;
|
||||
_gss = gss;
|
||||
_gscs = gscs;
|
||||
_iYtSs = iYtSs;
|
||||
|
||||
_ytdlp = ytdlp;
|
||||
_ytdl = ytdl;
|
||||
_ytdata = ytdata;
|
||||
}
|
||||
|
||||
@@ -57,9 +54,8 @@ public sealed class DefaultSearchServiceFactory : ISearchServiceFactory, INServi
|
||||
=> _scs.Data.YtProvider switch
|
||||
{
|
||||
YoutubeSearcher.YtDataApiv3 => _ytdata,
|
||||
YoutubeSearcher.Ytdlp => _ytdlp,
|
||||
YoutubeSearcher.Ytdl => _ytdl,
|
||||
YoutubeSearcher.Invidious => _iYtSs,
|
||||
_ => _ytdl
|
||||
YoutubeSearcher.Ytdlp => _ytdlp,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
@@ -93,16 +93,12 @@ public partial class Searches
|
||||
return;
|
||||
}
|
||||
|
||||
var embeds = new List<EmbedBuilder>(4);
|
||||
|
||||
|
||||
EmbedBuilder CreateEmbed(IImageSearchResultEntry entry)
|
||||
{
|
||||
return _sender.CreateEmbed()
|
||||
.WithOkColor()
|
||||
.WithAuthor(ctx.User)
|
||||
.WithTitle(query)
|
||||
.WithUrl("https://google.com")
|
||||
.WithImageUrl(entry.Link);
|
||||
}
|
||||
|
||||
@@ -120,55 +116,50 @@ public partial class Searches
|
||||
.WithDescription(GetText(strs.no_search_results));
|
||||
|
||||
var embed = CreateEmbed(item);
|
||||
embeds.Add(embed);
|
||||
|
||||
return embed;
|
||||
})
|
||||
.SendAsync();
|
||||
}
|
||||
|
||||
private TypedKey<string> GetYtCacheKey(string query)
|
||||
=> new($"search:youtube:{query}");
|
||||
private TypedKey<string[]> GetYtCacheKey(string query)
|
||||
=> new($"search:yt:{query}");
|
||||
|
||||
private async Task AddYoutubeUrlToCacheAsync(string query, string url)
|
||||
private async Task AddYoutubeUrlToCacheAsync(string query, string[] url)
|
||||
=> await _cache.AddAsync(GetYtCacheKey(query), url, expiry: 1.Hours());
|
||||
|
||||
private async Task<VideoInfo?> GetYoutubeUrlFromCacheAsync(string query)
|
||||
private async Task<VideoInfo[]?> GetYoutubeUrlFromCacheAsync(string query)
|
||||
{
|
||||
var result = await _cache.GetAsync(GetYtCacheKey(query));
|
||||
|
||||
if (!result.TryGetValue(out var url) || string.IsNullOrWhiteSpace(url))
|
||||
if (!result.TryGetValue(out var urls) || urls.Length == 0)
|
||||
return null;
|
||||
|
||||
return new VideoInfo()
|
||||
return urls.Map(url => new VideoInfo()
|
||||
{
|
||||
Url = url
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
[Cmd]
|
||||
public async Task Youtube([Leftover] string? query = null)
|
||||
public async Task Youtube([Leftover] string query)
|
||||
{
|
||||
query = query?.Trim();
|
||||
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
{
|
||||
await Response().Error(strs.specify_search_params).SendAsync();
|
||||
return;
|
||||
}
|
||||
query = query.Trim();
|
||||
|
||||
_ = ctx.Channel.TriggerTypingAsync();
|
||||
|
||||
var maybeResult = await GetYoutubeUrlFromCacheAsync(query)
|
||||
?? await _searchFactory.GetYoutubeSearchService().SearchAsync(query);
|
||||
if (maybeResult is not { } result || result is { Url: null })
|
||||
var maybeResults = await GetYoutubeUrlFromCacheAsync(query)
|
||||
?? await _searchFactory.GetYoutubeSearchService().SearchAsync(query);
|
||||
|
||||
if (maybeResults is not { } result || result.Length == 0)
|
||||
{
|
||||
await Response().Error(strs.no_results).SendAsync();
|
||||
return;
|
||||
}
|
||||
|
||||
await AddYoutubeUrlToCacheAsync(query, result.Url);
|
||||
await Response().Text(result.Url).SendAsync();
|
||||
await AddYoutubeUrlToCacheAsync(query, result.Map(x => x.Url));
|
||||
|
||||
await Response().Text(result[0].Url).SendAsync();
|
||||
}
|
||||
|
||||
// [Cmd]
|
||||
|
@@ -2,5 +2,5 @@
|
||||
|
||||
public interface IYoutubeSearchService
|
||||
{
|
||||
Task<VideoInfo?> SearchAsync(string query);
|
||||
Task<VideoInfo[]?> SearchAsync(string query);
|
||||
}
|
@@ -18,7 +18,7 @@ public sealed class InvidiousYtSearchService : IYoutubeSearchService, INService
|
||||
_rng = new();
|
||||
}
|
||||
|
||||
public async Task<VideoInfo?> SearchAsync(string query)
|
||||
public async Task<VideoInfo[]?> SearchAsync(string query)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
|
||||
@@ -35,6 +35,7 @@ public sealed class InvidiousYtSearchService : IYoutubeSearchService, INService
|
||||
var url = $"{instance}/api/v1/search"
|
||||
+ $"?q={query}"
|
||||
+ $"&type=video";
|
||||
|
||||
using var http = _http.CreateClient();
|
||||
var res = await http.GetFromJsonAsync<List<InvidiousSearchResponse>>(
|
||||
url);
|
||||
@@ -42,6 +43,6 @@ public sealed class InvidiousYtSearchService : IYoutubeSearchService, INService
|
||||
if (res is null or { Count: 0 })
|
||||
return null;
|
||||
|
||||
return new VideoInfo(res[0].VideoId);
|
||||
return res.Map(r => new VideoInfo(r.VideoId));
|
||||
}
|
||||
}
|
@@ -9,18 +9,15 @@ public sealed class YoutubeDataApiSearchService : IYoutubeSearchService, INServi
|
||||
_gapi = gapi;
|
||||
}
|
||||
|
||||
public async Task<VideoInfo?> SearchAsync(string query)
|
||||
public async Task<VideoInfo[]?> SearchAsync(string query)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(query);
|
||||
|
||||
var results = await _gapi.GetVideoLinksByKeywordAsync(query);
|
||||
var first = results.FirstOrDefault();
|
||||
if (first is null)
|
||||
|
||||
if(results.Count == 0)
|
||||
return null;
|
||||
|
||||
return new()
|
||||
{
|
||||
Url = first
|
||||
};
|
||||
|
||||
return results.Map(r => new VideoInfo(r));
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
namespace NadekoBot.Modules.Searches.Youtube;
|
||||
|
||||
public class YtDlpSearchService : IYoutubeSearchService, INService
|
||||
{
|
||||
private YtdlOperation CreateYtdlOp(int count)
|
||||
=> new YtdlOperation("-4 "
|
||||
+ "--ignore-errors --flat-playlist --skip-download --quiet "
|
||||
+ "--geo-bypass "
|
||||
+ "--encoding UTF8 "
|
||||
+ "--get-id "
|
||||
+ "--no-check-certificate "
|
||||
+ "--default-search "
|
||||
+ $"\"ytsearch{count}:\" -- \"{{0}}\"");
|
||||
|
||||
public async Task<VideoInfo[]?> SearchAsync(string query)
|
||||
{
|
||||
var op = CreateYtdlOp(5);
|
||||
var data = await op.GetDataAsync(query);
|
||||
var items = data?.Split('\n');
|
||||
if (items is null or { Length: 0 })
|
||||
return null;
|
||||
|
||||
return items
|
||||
.Map(x => new VideoInfo(x));
|
||||
}
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
namespace NadekoBot.Modules.Searches.Youtube;
|
||||
|
||||
public sealed class YtdlYoutubeSearchService : YoutubedlxServiceBase, INService
|
||||
{
|
||||
public override async Task<VideoInfo?> SearchAsync(string query)
|
||||
=> await InternalGetInfoAsync(query, false);
|
||||
}
|
@@ -1,7 +0,0 @@
|
||||
namespace NadekoBot.Modules.Searches.Youtube;
|
||||
|
||||
public sealed class YtdlpYoutubeSearchService : YoutubedlxServiceBase, INService
|
||||
{
|
||||
public override async Task<VideoInfo?> SearchAsync(string query)
|
||||
=> await InternalGetInfoAsync(query, true);
|
||||
}
|
@@ -1,34 +0,0 @@
|
||||
namespace NadekoBot.Modules.Searches.Youtube;
|
||||
|
||||
public abstract class YoutubedlxServiceBase : IYoutubeSearchService
|
||||
{
|
||||
private YtdlOperation CreateYtdlOp(bool isYtDlp)
|
||||
=> new YtdlOperation("-4 "
|
||||
+ "--geo-bypass "
|
||||
+ "--encoding UTF8 "
|
||||
+ "--get-id "
|
||||
+ "--no-check-certificate "
|
||||
+ "--default-search "
|
||||
+ "\"ytsearch:\" -- \"{0}\"",
|
||||
isYtDlp: isYtDlp);
|
||||
|
||||
protected async Task<VideoInfo?> InternalGetInfoAsync(string query, bool isYtDlp)
|
||||
{
|
||||
var op = CreateYtdlOp(isYtDlp);
|
||||
var data = await op.GetDataAsync(query);
|
||||
var items = data?.Split('\n');
|
||||
if (items is null or { Length: 0 })
|
||||
return null;
|
||||
|
||||
var id = items.FirstOrDefault(x => x.Length is > 5 and < 15);
|
||||
if (id is null)
|
||||
return null;
|
||||
|
||||
return new VideoInfo()
|
||||
{
|
||||
Url = $"https://youtube.com/watch?v={id}"
|
||||
};
|
||||
}
|
||||
|
||||
public abstract Task<VideoInfo?> SearchAsync(string query);
|
||||
}
|
@@ -77,9 +77,9 @@ public sealed class FollowedStreamConfig
|
||||
|
||||
public enum YoutubeSearcher
|
||||
{
|
||||
YtDataApiv3,
|
||||
Ytdl,
|
||||
Ytdlp,
|
||||
Invid,
|
||||
YtDataApiv3 = 0,
|
||||
Ytdl = 1,
|
||||
Ytdlp = 1,
|
||||
Invid = 3,
|
||||
Invidious = 3
|
||||
}
|
Reference in New Issue
Block a user