Skip to content

Commit c57939d

Browse files
authored
Merge pull request #29 from pk9r/feat/tcg-card
Feat/tcg card
2 parents bc278b3 + e78ee9f commit c57939d

18 files changed

Lines changed: 453 additions & 75 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using GitcgNetCord.MainApp.Infrastructure.HoyolabServices;
2+
using NetCord;
3+
using NetCord.Rest;
4+
using NetCord.Services.ApplicationCommands;
5+
6+
namespace GitcgNetCord.MainApp.Commands.Autocompletes;
7+
8+
public class CardAutocompleteProvider(
9+
HoyolabCardActionService cardActionService
10+
) : IAutocompleteProvider<AutocompleteInteractionContext>
11+
{
12+
public async ValueTask<
13+
IEnumerable<ApplicationCommandOptionChoiceProperties>?
14+
> GetChoicesAsync(
15+
ApplicationCommandInteractionDataOption option,
16+
AutocompleteInteractionContext context
17+
)
18+
{
19+
var keyword = option.Value!;
20+
21+
List<HoyolabHttpClient.Responses.CardActions.Data> cardActions = [
22+
await cardActionService.GetCardActionsAsync(),
23+
await cardActionService.GetCardActionsAsync("vi-vn")
24+
];
25+
26+
var suggestions = cardActions
27+
.SelectMany(x => x.Actions)
28+
.DistinctBy(x => x.Basic.Name)
29+
.Where(x => StartsWith(x.Basic.Name, keyword) || Contains(x.Basic.Name, keyword))
30+
.OrderBy(x => StartsWith(x.Basic.Name, keyword) ? 0 : 1)
31+
.Select(x => new ApplicationCommandOptionChoiceProperties(x.Basic.Name, x.Basic.ItemId));
32+
33+
return suggestions.Take(25);
34+
35+
bool StartsWith(string x, string k)
36+
{
37+
return x.StartsWith(
38+
value: k,
39+
comparisonType: StringComparison.OrdinalIgnoreCase
40+
);
41+
}
42+
43+
bool Contains(string x, string k)
44+
{
45+
return x.Contains(
46+
value: k,
47+
comparisonType: StringComparison.OrdinalIgnoreCase
48+
);
49+
}
50+
}
51+
}

src/GitcgNetCord.MainApp/Commands/Slash/DeckSlashCommand.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1-
using System.Collections.Immutable;
2-
using GitcgNetCord.MainApp.Commands.Autocompletes;
1+
using GitcgNetCord.MainApp.Commands.Autocompletes;
32
using GitcgNetCord.MainApp.Commands.Interactions;
43
using GitcgNetCord.MainApp.Entities.Repositories;
54
using GitcgNetCord.MainApp.Enums;
65
using GitcgNetCord.MainApp.Extensions;
76
using GitcgNetCord.MainApp.Infrastructure.HoyolabServices;
87
using GitcgNetCord.MainApp.Models;
9-
using GitcgPainter.ImageCreators.Deck.Abstractions;
108
using HoyolabHttpClient;
119
using NetCord;
1210
using NetCord.Rest;
1311
using NetCord.Services.ApplicationCommands;
12+
using System.Collections.Immutable;
1413
using Color = System.Drawing.Color;
1514
using IDeckImageCreationService = GitcgSharp.Shared.ImageCreators.Deck.Abstractions.IDeckImageCreationService;
1615

@@ -63,7 +62,7 @@ await HoyolabAccountsSlashCommand
6362
}
6463

6564
await context.Interaction.SendResponseAsync(
66-
callback: InteractionCallback.DeferredMessage(MessageFlags.IsComponentsV2)
65+
callback: InteractionCallback.DeferredMessage()
6766
);
6867

6968
var decodeResult = await decoder.DecodeAsync(
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
using GitcgNetCord.MainApp.Commands.Autocompletes;
2+
using GitcgNetCord.MainApp.Extensions;
3+
using GitcgNetCord.MainApp.Infrastructure.HoyolabServices;
4+
using HoyolabHttpClient;
5+
using NetCord;
6+
using NetCord.Rest;
7+
using NetCord.Services.ApplicationCommands;
8+
using System.Collections.Immutable;
9+
using System.Text;
10+
using System.Text.RegularExpressions;
11+
using Color = System.Drawing.Color;
12+
13+
namespace GitcgNetCord.MainApp.Commands.Slash;
14+
15+
public partial class TcgCardSlashCommand
16+
{
17+
public static async Task ExecuteAsync(
18+
HoyolabHttpClientService hoyolab,
19+
IServiceProvider serviceProvider,
20+
ApplicationCommandContext context,
21+
[
22+
SlashCommandParameter(
23+
Description = "Card ID.",
24+
AutocompleteProviderType = typeof(CardAutocompleteProvider)
25+
)
26+
]
27+
int cardId,
28+
[
29+
SlashCommandParameter(
30+
Description = "Language. Default: `en-us`.",
31+
AutocompleteProviderType = typeof(LanguageAutocompleteProvider)
32+
)
33+
]
34+
string lang = "en-us"
35+
)
36+
{
37+
var cardActionService = serviceProvider
38+
.GetRequiredService<HoyolabCardActionService>();
39+
40+
await context.Interaction.SendResponseAsync(
41+
callback: InteractionCallback.DeferredMessage()
42+
);
43+
44+
HoyolabHttpClient.Responses.ActionSkill.Data data;
45+
46+
try
47+
{
48+
data = await hoyolab.GetActionSkillAsync(cardId, lang);
49+
}
50+
catch (Exception e)
51+
{
52+
await context.Interaction.ModifyResponseAsync(message =>
53+
{
54+
//message.WithFlags(MessageFlags.IsComponentsV2);
55+
message.AddEmbeds(new EmbedProperties()
56+
.WithTitle("Error")
57+
.WithDescription(e.Message)
58+
.WithColor(Color.Red.ToNetCordColor())
59+
);
60+
});
61+
return;
62+
}
63+
64+
var appEmojis = await context.Client.Rest
65+
.GetApplicationEmojisAsync(context.Client.Id);
66+
var emojis = appEmojis.ToImmutableDictionary(x => x.Name);
67+
68+
var cardActions = await cardActionService
69+
.GetCardActionsAsync(lang);
70+
var action = cardActions.Actions
71+
.First(x => x.Basic.ItemId == cardId);
72+
73+
await context.Interaction.ModifyResponseAsync(message =>
74+
{
75+
message.WithFlags(MessageFlags.IsComponentsV2);
76+
message.AddComponents([new ComponentContainerProperties()
77+
.WithAccentColor(Color.Purple.ToNetCordColor())
78+
.AddComponents(
79+
new TextDisplayProperties(
80+
$"""
81+
## {GetName(cardId)}
82+
"""
83+
),
84+
new MediaGalleryProperties().AddItems(
85+
new MediaGalleryItemProperties(
86+
new ComponentMediaProperties(action.Basic.Icon))
87+
),
88+
new TextDisplayProperties(
89+
$"{GetDesc(data.Desc)}"
90+
)
91+
)
92+
]);
93+
});
94+
95+
return;
96+
97+
string GetName(int cardId)
98+
{
99+
var builder = new StringBuilder();
100+
if (data.Cost2Raw > 0)
101+
{
102+
builder.Append($"<img src='{data.Cost2TypeIcon}' /> - ");
103+
}
104+
builder.Append($"{data.Cost1Raw} ");
105+
builder.Append($"<img src='{data.Cost1TypeIcon}' /> ");
106+
builder.Append($"- {action.Basic.Name} ");
107+
108+
return GetDesc(builder.ToString());
109+
}
110+
111+
string GetDesc(string desc)
112+
{
113+
var result = desc;
114+
115+
result = result.Replace("\\n", "\n");
116+
117+
result = ImgTagRegex().Replace(result, match =>
118+
{
119+
var url = match.Groups["url"].Value;
120+
121+
ApplicationEmoji? emoji = null;
122+
123+
var availableEmojis = HoyolabSharedUtils.UrlToEmojis
124+
.TryGetValue(url, out var emojiKey)
125+
&& emojis.TryGetValue(emojiKey, out emoji);
126+
127+
if (availableEmojis) return emoji!.ToString();
128+
129+
return match.Value; // leave unchanged if not found
130+
});
131+
132+
while (ColorTagRegex().IsMatch(result))
133+
{
134+
result = ColorTagRegex().Replace(result, "**$1**");
135+
}
136+
137+
return result;
138+
}
139+
}
140+
141+
[
142+
GeneratedRegex(
143+
pattern: @"<color\b[^>]*>(.*?)</color>",
144+
options: RegexOptions.IgnoreCase | RegexOptions.Singleline
145+
)
146+
]
147+
private static partial Regex ColorTagRegex();
148+
149+
[
150+
GeneratedRegex(
151+
pattern: @"<img\s+src=['""](?<url>[^'""]+)['""][^>]*\/?>",
152+
options: RegexOptions.IgnoreCase, cultureName: "en-US"
153+
)
154+
]
155+
private static partial Regex ImgTagRegex();
156+
}

src/GitcgNetCord.MainApp/Commands/Slash/UpdateRoleEmojisSlashCommand.cs renamed to src/GitcgNetCord.MainApp/Commands/Slash/UpdateEmojisSlashCommand.cs

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
1-
using System.Collections.Concurrent;
2-
using System.Collections.Immutable;
31
using GitcgNetCord.MainApp.Commands.Preconditions;
4-
using GitcgPainter;
5-
using GitcgPainter.Extensions;
2+
using GitcgSkia;
3+
using GitcgSkia.Extensions;
64
using HoyolabHttpClient;
75
using NetCord;
86
using NetCord.Rest;
97
using NetCord.Services.ApplicationCommands;
108
using SixLabors.ImageSharp;
9+
using SkiaSharp;
10+
using System.Collections.Concurrent;
11+
using System.Collections.Immutable;
1112

1213
namespace GitcgNetCord.MainApp.Commands.Slash;
1314

14-
public static class UpdateRoleEmojisSlashCommand
15+
public static class UpdateEmojisSlashCommand
1516
{
1617
private static ImmutableDictionary<string, ApplicationEmoji> _emojis = null!;
1718

@@ -36,9 +37,14 @@ await context.Interaction.SendResponseAsync(
3637

3738
ConcurrentBag<ApplicationEmoji> newEmotes = [];
3839

39-
await Task.WhenAll(
40-
response.Roles.Select(CreateApplicationEmoteAsync)
41-
);
40+
await Task.WhenAll([
41+
..response.Roles.Select(CreateApplicationEmoteByRoleAsync),
42+
..HoyolabSharedUtils.UrlToEmojis.Select(
43+
x => CreateApplicationEmoteByUrlAsync(
44+
emojiName: x.Value, url: x.Key
45+
)
46+
)
47+
]);
4248

4349
var newEmotesString = string.Join(" ", newEmotes);
4450

@@ -53,27 +59,49 @@ Emojis updated!
5359

5460
return;
5561

56-
async Task CreateApplicationEmoteAsync(
62+
async Task CreateApplicationEmoteByRoleAsync(
5763
HoyolabHttpClient.Models.Role role
5864
)
5965
{
6066
var emojiName = role.Basic.ItemId.ToString();
67+
var url = role.Basic.IconSmall;
68+
69+
await CreateApplicationEmoteByUrlAsync(emojiName, url);
70+
}
6171

72+
async Task CreateApplicationEmoteByUrlAsync(
73+
string emojiName, string url
74+
)
75+
{
6276
// Skip if the emote already exists
6377
if (_emojis.ContainsKey(emojiName))
6478
return;
6579

66-
using var iconSmall = await imageCacheService
67-
.LoadIconSmallAsync(role: role);
68-
using var memoryStream = new MemoryStream();
69-
await iconSmall.SaveAsPngAsync(memoryStream);
80+
if (url == "https://act-webstatic.hoyoverse.com/hk4e/e20200928calculate/")
81+
{
82+
url += "item_icon/67c7f719/8b295c3fed21771dcced6055cdf2f2ce.png";
83+
}
84+
85+
using var icon = await imageCacheService.LoadHttpImageAsync(url);
86+
87+
await EncodeThenUploadAsync(emojiName, icon);
88+
}
89+
90+
async Task EncodeThenUploadAsync(
91+
string emojiName, SKBitmap icon
92+
)
93+
{
94+
using var encoded = icon.Encode(
95+
format: SKEncodedImageFormat.Webp,
96+
quality: 100
97+
);
7098

7199
var emoji = await context.Client.Rest.CreateApplicationEmojiAsync(
72100
applicationId: applicationId,
73101
new ApplicationEmojiProperties(
74102
name: emojiName,
75103
image: new ImageProperties()
76-
.WithData(memoryStream.GetBuffer())
104+
.WithData(encoded.ToArray())
77105
)
78106
);
79107

src/GitcgNetCord.MainApp/Extensions/Extensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ this IHost host
2929
host.AddHoyolabGcgModule();
3030

3131
// Utility modules
32-
host.AddRoleEmojisModule();
32+
host.AddUpdateEmojisModule();
3333
}
3434

3535
public static void AddNetCordServices(
@@ -64,6 +64,7 @@ this IServiceCollection services
6464
{
6565
services.AddHoyolabHttpClient();
6666
services.AddSingleton<HoyolabCardRoleService>();
67+
services.AddSingleton<HoyolabCardActionService>();
6768
services.AddSingleton<HoyolabDecoder>();
6869
services.AddSingleton<HoyolabDeckAccountService>();
6970
services.AddSingleton<HoyolabGcgBasicInfoService>();
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using HoyolabHttpClient;
2+
using HoyolabHttpClient.Responses.CardActions;
3+
using Microsoft.Extensions.Caching.Hybrid;
4+
5+
namespace GitcgNetCord.MainApp.Infrastructure.HoyolabServices;
6+
7+
public class HoyolabCardActionService(
8+
HoyolabCardRoleService cardRoleService,
9+
HoyolabHttpClientService hoyolab,
10+
HybridCache cache
11+
)
12+
{
13+
public async Task<Data>
14+
GetCardActionsAsync(string lang = "en-us")
15+
{
16+
var cacheKey = $"card-action:{lang}";
17+
var cached = await cache
18+
.GetOrCreateAsync(
19+
key: cacheKey,
20+
factory: FetchAsync
21+
);
22+
23+
return cached;
24+
25+
async ValueTask<Data> FetchAsync(
26+
CancellationToken cancellationToken = default
27+
)
28+
{
29+
var roleData = await cardRoleService.GetCardRolesAsync(lang);
30+
var roleIds = roleData.Roles
31+
.Select(x => x.Basic.ItemId);
32+
33+
var cardActions = await hoyolab
34+
.GetCardActionsAsync(roleIds, lang);
35+
return cardActions;
36+
}
37+
}
38+
}

src/GitcgNetCord.MainApp/Infrastructure/HoyolabServices/HoyolabDecoder.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using GitcgNetCord.MainApp.Enums;
22
using GitcgNetCord.MainApp.Models;
33
using HoyolabHttpClient;
4-
using HoyolabHttpClient.Models.Interfaces;
54
using Microsoft.Extensions.Options;
65
using SharedUtils;
76

0 commit comments

Comments
 (0)