Skip to content

Commit 7079e13

Browse files
Refactor GptImage and TransformImage command handlers
1 parent 6de7a50 commit 7079e13

File tree

9 files changed

+360
-517
lines changed

9 files changed

+360
-517
lines changed

src/Kattbot/CommandHandlers/Images/GptImage.cs

Lines changed: 171 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,37 @@ public GptImageCommand(CommandContext ctx, string prompt)
2828
public string Prompt { get; }
2929
}
3030

31-
public class GptImageHandler : IRequestHandler<GptImageCommand>
31+
public class GptImageAvatarCommand : CommandRequest
32+
{
33+
public GptImageAvatarCommand(CommandContext ctx, DiscordUser user, string prompt)
34+
: base(ctx)
35+
{
36+
Prompt = prompt;
37+
User = user;
38+
}
39+
40+
public string Prompt { get; }
41+
42+
public DiscordUser User { get; }
43+
}
44+
45+
public class GptImageEmoteCommand : CommandRequest
46+
{
47+
public GptImageEmoteCommand(CommandContext ctx, DiscordEmoji emoji, string prompt)
48+
: base(ctx)
49+
{
50+
Prompt = prompt;
51+
Emoji = emoji;
52+
}
53+
54+
public string Prompt { get; }
55+
56+
public DiscordEmoji Emoji { get; }
57+
}
58+
59+
public class GptImageHandler : IRequestHandler<GptImageCommand>,
60+
IRequestHandler<GptImageAvatarCommand>,
61+
IRequestHandler<GptImageEmoteCommand>
3262
{
3363
private const string GptImageModel = "gpt-image-1";
3464
private const string Moderation = "low";
@@ -38,17 +68,20 @@ public class GptImageHandler : IRequestHandler<GptImageCommand>
3868

3969
private readonly GptImagesHttpClient _gptImagesHttpClient;
4070
private readonly ImageService _imageService;
71+
private readonly DiscordResolver _discordResolver;
4172
private readonly ILogger<GptImageHandler> _logger;
4273

4374
private readonly string[] _supportedImageFormats = ["png", "jpg", "webp"];
4475

4576
public GptImageHandler(
4677
GptImagesHttpClient gptImagesHttpClient,
4778
ImageService imageService,
79+
DiscordResolver discordResolver,
4880
ILogger<GptImageHandler> logger)
4981
{
5082
_gptImagesHttpClient = gptImagesHttpClient;
5183
_imageService = imageService;
84+
_discordResolver = discordResolver;
5285
_logger = logger;
5386
}
5487

@@ -66,6 +99,7 @@ public async Task Handle(GptImageCommand request, CancellationToken cancellation
6699
string? imageUrl = await discordMessage.GetImageUrlFromMessage(_logger);
67100

68101
CreateImageResponse response;
102+
var userId = request.Ctx.User.Id.ToString();
69103

70104
if (imageUrl == null)
71105
{
@@ -75,40 +109,68 @@ public async Task Handle(GptImageCommand request, CancellationToken cancellation
75109
Model = GptImageModel,
76110
Quality = Quality,
77111
Moderation = Moderation,
78-
User = request.Ctx.User.Id.ToString(),
112+
User = userId,
79113
};
80114

81115
response = await _gptImagesHttpClient.CreateImage(imageRequest, cancellationToken);
82116
}
83117
else
84118
{
85-
Image editImage = await _imageService.DownloadImage(imageUrl);
119+
response = await CreateImageEdit(imageUrl, prompt, userId, cancellationToken);
120+
}
86121

87-
Image imageInSupportedFormat =
88-
await ImageService.EnsureSupportedImageFormatOrPng(editImage, _supportedImageFormats);
122+
if (response.Data == null || !response.Data.Any()) throw new Exception("Empty result");
89123

90-
Image resizedImage = await ImageService.EnsureMaxImageFileSize(
91-
imageInSupportedFormat,
92-
MaxImageSizeInMb);
124+
ImageResponseData imageData = response.Data.First();
93125

94-
var tempFileName = $"{Guid.NewGuid()}.png";
126+
Image image = ImageService.ConvertBase64ToImage(imageData.B64Json);
95127

96-
ImageStreamResult inputImageStream = await ImageService.GetImageStream(resizedImage);
128+
ImageStreamResult imageStream = await ImageService.GetImageStream(image);
97129

98-
var editImageRequest = new CreateImageEditRequest
99-
{
100-
Prompt = prompt,
101-
Image = inputImageStream.MemoryStream.ToArray(),
102-
Model = GptImageModel,
103-
Quality = Quality,
104-
User = request.Ctx.User.Id.ToString(),
105-
};
130+
string fileName = prompt.ToSafeFilename(imageStream.FileExtension);
106131

107-
response = await _gptImagesHttpClient.CreateImageEdit(
108-
editImageRequest,
109-
tempFileName,
110-
cancellationToken);
111-
}
132+
string truncatedPrompt = prompt.Length > DiscordConstants.MaxEmbedTitleLength
133+
? $"{prompt[..(DiscordConstants.MaxEmbedTitleLength - 3)]}..."
134+
: prompt;
135+
136+
DiscordEmbedBuilder eb = new DiscordEmbedBuilder()
137+
.WithTitle(truncatedPrompt)
138+
.WithImageUrl($"attachment://{fileName}");
139+
140+
DiscordMessageBuilder mb = new DiscordMessageBuilder()
141+
.AddFile(fileName, imageStream.MemoryStream)
142+
.AddEmbed(eb)
143+
.WithContent($"There you go {request.Ctx.Member?.Mention ?? "Unknown user"}");
144+
145+
await request.Ctx.RespondAsync(mb);
146+
}
147+
finally
148+
{
149+
await ackMessage.DeleteAsync();
150+
}
151+
}
152+
153+
public async Task Handle(GptImageAvatarCommand request, CancellationToken cancellationToken)
154+
{
155+
CommandContext ctx = request.Ctx;
156+
DiscordMessage discordMessage = ctx.Message;
157+
DiscordUser user = request.User;
158+
DiscordGuild guild = ctx.Guild;
159+
160+
DiscordMessage ackMessage = await request.Ctx.RespondAsync("Working on it");
161+
162+
try
163+
{
164+
string prompt = discordMessage.SubstituteMentions(request.Prompt);
165+
166+
DiscordMember userAsMember = await _discordResolver.ResolveGuildMember(guild, user.Id) ??
167+
throw new Exception("Invalid user");
168+
169+
string imageUrl = userAsMember.GuildAvatarUrl ?? userAsMember.AvatarUrl;
170+
171+
var userId = request.Ctx.User.Id.ToString();
172+
173+
CreateImageResponse response = await CreateImageEdit(imageUrl, prompt, userId, cancellationToken);
112174

113175
if (response.Data == null || !response.Data.Any()) throw new Exception("Empty result");
114176

@@ -140,4 +202,90 @@ public async Task Handle(GptImageCommand request, CancellationToken cancellation
140202
await ackMessage.DeleteAsync();
141203
}
142204
}
205+
206+
public async Task Handle(GptImageEmoteCommand request, CancellationToken cancellationToken)
207+
{
208+
CommandContext ctx = request.Ctx;
209+
DiscordMessage discordMessage = ctx.Message;
210+
211+
DiscordEmoji emoji = request.Emoji;
212+
213+
DiscordMessage ackMessage = await request.Ctx.RespondAsync("Working on it");
214+
215+
try
216+
{
217+
string prompt = discordMessage.SubstituteMentions(request.Prompt);
218+
219+
string imageUrl = emoji.GetEmojiImageUrl();
220+
221+
var userId = request.Ctx.User.Id.ToString();
222+
223+
CreateImageResponse response = await CreateImageEdit(imageUrl, prompt, userId, cancellationToken);
224+
225+
if (response.Data == null || !response.Data.Any()) throw new Exception("Empty result");
226+
227+
ImageResponseData imageData = response.Data.First();
228+
229+
Image image = ImageService.ConvertBase64ToImage(imageData.B64Json);
230+
231+
ImageStreamResult imageStream = await ImageService.GetImageStream(image);
232+
233+
string fileName = prompt.ToSafeFilename(imageStream.FileExtension);
234+
235+
string truncatedPrompt = prompt.Length > DiscordConstants.MaxEmbedTitleLength
236+
? $"{prompt[..(DiscordConstants.MaxEmbedTitleLength - 3)]}..."
237+
: prompt;
238+
239+
DiscordEmbedBuilder eb = new DiscordEmbedBuilder()
240+
.WithTitle(truncatedPrompt)
241+
.WithImageUrl($"attachment://{fileName}");
242+
243+
DiscordMessageBuilder mb = new DiscordMessageBuilder()
244+
.AddFile(fileName, imageStream.MemoryStream)
245+
.AddEmbed(eb)
246+
.WithContent($"There you go {request.Ctx.Member?.Mention ?? "Unknown user"}");
247+
248+
await request.Ctx.RespondAsync(mb);
249+
}
250+
finally
251+
{
252+
await ackMessage.DeleteAsync();
253+
}
254+
}
255+
256+
private async Task<CreateImageResponse> CreateImageEdit(
257+
string imageUrl,
258+
string prompt,
259+
string userId,
260+
CancellationToken cancellationToken)
261+
{
262+
Image editImage = await _imageService.DownloadImage(imageUrl);
263+
264+
Image imageInSupportedFormat =
265+
await ImageService.EnsureSupportedImageFormatOrPng(editImage, _supportedImageFormats);
266+
267+
Image resizedImage = await ImageService.EnsureMaxImageFileSize(
268+
imageInSupportedFormat,
269+
MaxImageSizeInMb);
270+
271+
var tempFileName = $"{Guid.NewGuid()}.png";
272+
273+
ImageStreamResult inputImageStream = await ImageService.GetImageStream(resizedImage);
274+
275+
var editImageRequest = new CreateImageEditRequest
276+
{
277+
Prompt = prompt,
278+
Image = inputImageStream.MemoryStream.ToArray(),
279+
Model = GptImageModel,
280+
Quality = Quality,
281+
User = userId,
282+
};
283+
284+
CreateImageResponse response = await _gptImagesHttpClient.CreateImageEdit(
285+
editImageRequest,
286+
tempFileName,
287+
cancellationToken);
288+
289+
return response;
290+
}
143291
}

src/Kattbot/CommandHandlers/Images/GptImageAvatar.cs

Lines changed: 0 additions & 129 deletions
This file was deleted.

0 commit comments

Comments
 (0)