Skip to content

Commit

Permalink
[AB-xxx] adding amongusText image generation
Browse files Browse the repository at this point in the history
  • Loading branch information
Sheldan committed Jan 24, 2024
1 parent 6030904 commit 71c18f8
Show file tree
Hide file tree
Showing 97 changed files with 337 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package dev.sheldan.abstracto.imagegeneration.command;

import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.model.AttachedFile;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationFeatureDefinition;
import dev.sheldan.abstracto.imagegeneration.config.ImageGenerationSlashCommandNames;
import dev.sheldan.abstracto.imagegeneration.service.ImageGenerationService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;

@Component
public class AmongusText extends AbstractConditionableCommand {

public static final String TEXT_PARAMETER_KEY = "text";

@Autowired
private ImageGenerationService imageGenerationService;

@Autowired
private TemplateService templateService;

@Autowired
private ChannelService channelService;

@Autowired
private FileService fileService;

@Autowired
private InteractionService interactionService;

@Autowired
private SlashCommandParameterService slashCommandParameterService;

private static final String AMONGUS_TEXT_EMBED_TEMPLATE_KEY = "amongusText_response";

@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
String text = (String) commandContext.getParameters().getParameters().get(0);
File amongusTextImage = imageGenerationService.getAmongusTextImage(text);
MessageToSend messageToSend = templateService.renderEmbedTemplate(AMONGUS_TEXT_EMBED_TEMPLATE_KEY, new Object());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
.file(amongusTextImage)
.fileName("amongus.png")
.build();
messageToSend.getAttachedFiles().add(file);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
.thenApply(unused -> CommandResult.fromIgnored());
}

@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
event.deferReply().queue();
String text = slashCommandParameterService.getCommandOption(TEXT_PARAMETER_KEY, event, String.class);
File amongusTextImage = imageGenerationService.getAmongusTextImage(text);
MessageToSend messageToSend = templateService.renderEmbedTemplate(AMONGUS_TEXT_EMBED_TEMPLATE_KEY, new Object());
// template support does not support binary files
AttachedFile file = AttachedFile
.builder()
.file(amongusTextImage)
.fileName("amongus.png")
.build();
messageToSend.getAttachedFiles().add(file);
return FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(messageToSend, event.getHook()))
.thenAccept(unused -> fileService.safeDeleteIgnoreException(messageToSend.getAttachedFiles().get(0).getFile()))
.thenApply(unused -> CommandResult.fromIgnored());
}

@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
Parameter memberParameter = Parameter
.builder()
.name(TEXT_PARAMETER_KEY)
.type(String.class)
.remainder(true)
.templated(true)
.build();
parameters.add(memberParameter);

HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();

SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(ImageGenerationSlashCommandNames.IMAGE_GENERATION)
.groupName("memes")
.commandName("amongustext")
.build();

return CommandConfiguration.builder()
.name("amongusText")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.parameters(parameters)
.help(helpInfo)
.build();
}

@Override
public FeatureDefinition getFeature() {
return ImageGenerationFeatureDefinition.IMAGE_GENERATION;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package dev.sheldan.abstracto.imagegeneration.service;

import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.RequestException;
import dev.sheldan.abstracto.core.service.HttpService;
import dev.sheldan.abstracto.imagegeneration.exception.AmongusTextRequestException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
Expand All @@ -22,6 +24,9 @@ public class ImageGenerationServiceBean implements ImageGenerationService {
@Value("${abstracto.feature.imagegeneration.bonk.url}")
private String bonkUrl;

@Value("${abstracto.feature.imagegeneration.amongusText.url}")
private String amongusTextUrl;

@Autowired
private HttpService httpService;

Expand Down Expand Up @@ -52,4 +57,15 @@ public File getBonkGif(String imageUrl) {
}
}

@Override
public File getAmongusTextImage(String text) {
try {
return httpService.downloadFileToTempFile(amongusTextUrl.replace("{1}", text));
} catch (IOException e) {
throw new AbstractoRunTimeException(String.format("Failed to download amongus text with error %s", text, e.getMessage()));
} catch (RequestException exception) {
throw new AmongusTextRequestException(text, exception.getErrorMessage());
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,6 @@ abstracto.feature.imagegeneration.pat.url=http://${PRIVATE_REST_API_HOST}:${PRIV
abstracto.feature.imagegeneration.pat.imagesize=128

abstracto.feature.imagegeneration.bonk.url=http://${PRIVATE_REST_API_HOST}:${PRIVATE_REST_API_PORT}/memes/bonk/file.gif?url={1}
abstracto.feature.imagegeneration.bonk.imagesize=128
abstracto.feature.imagegeneration.bonk.imagesize=128

abstracto.feature.imagegeneration.amongusText.url=http://${PRIVATE_REST_API_HOST}:${PRIVATE_REST_API_PORT}/memes/amogus/text?text={1}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="imageGenerationFeature" value="(SELECT id FROM feature WHERE key = 'imageGeneration')"/>

<changeSet author="Sheldan" id="amongusText-commands">
<insert tableName="command">
<column name="name" value="amongusText"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${imageGenerationFeature}"/>
</insert>
</changeSet>

</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.5.15/collection.xml" relativeToChangelogFile="true"/>
<include file="1.5.19/collection.xml" relativeToChangelogFile="true"/>
<include file="1.5.22/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package dev.sheldan.abstracto.imagegeneration.exception;

import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
import dev.sheldan.abstracto.imagegeneration.model.exception.AmongusTextRequestExceptionModel;

public class AmongusTextRequestException extends AbstractoTemplatableException {
private final AmongusTextRequestExceptionModel model;

public AmongusTextRequestException(String inputText, String errorMessage) {
this.model = AmongusTextRequestExceptionModel
.builder()
.inputText(inputText)
.errorMessage(errorMessage)
.build();
}

@Override
public String getTemplateName() {
return "amongusText_exception";
}

@Override
public Object getTemplateModel() {
return model;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.imagegeneration.model.exception;

import lombok.Builder;
import lombok.Getter;

import java.io.Serializable;

@Getter
@Builder
public class AmongusTextRequestExceptionModel implements Serializable {
private String inputText;
private String errorMessage;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public interface ImageGenerationService {
File getTriggeredGif(String imageUrl);
File getPatGif(String imageUrl);
File getBonkGif(String imageUrl);
File getAmongusTextImage(String text);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dev.sheldan.abstracto.core.service;


import dev.sheldan.abstracto.core.exception.RequestException;
import dev.sheldan.abstracto.core.utils.FileService;
import okhttp3.OkHttpClient;
import okhttp3.Request;
Expand All @@ -25,6 +26,13 @@ public File downloadFileToTempFile(String url) throws IOException {
Request request = new Request.Builder().url(url).get().build();
File tempFile = fileService.createTempFile(Math.random() + "");
Response execute = client.newCall(request).execute();
if(!execute.isSuccessful()) {
throw RequestException
.builder()
.errorMessage(execute.body().string())
.httpCode(execute.code())
.build();
}
fileService.writeBytesToFile(tempFile, execute.body().bytes());
return tempFile;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package dev.sheldan.abstracto.core.exception;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class RequestException extends RuntimeException {
private String errorMessage; // we just assume the body will be a plain text instead
private Integer httpCode;
}
85 changes: 85 additions & 0 deletions python/components/image-gen/python/endpoints/amogus.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from __main__ import app

from PIL import Image, ImageOps
from flask import request
import logging
import math
import re
import random

from utils import flask_utils

vertical_padding = 5
horizontal_padding = 5

max_chars = 2000
chars_per_row = 50


space_width = 80

character_height = 120


@app.route('/memes/amogus/text')
def generate_amogus_text():
text = request.args.get('text', type=str)
if text is None:
return 'no text', 400
if len(text) > max_chars:
return f'too long text, max {max_chars}', 400
text = text.lower()
text = re.sub(r'[^a-z!\\? ]', "", text)
if len(text) == 0:
return 'No valid characters found.', 400
logging.info(f'Rendering amogus text.')
image_cache = {}
images_to_use = []

try:
for character in text:
if character == ' ':
images_to_use.append(None)
continue
# manual correction for filenames
if character == '?':
character = 'zq'
if character == '!':
character = 'zx'
chosen_sub_image = random.randint(1, 3)
cache_key = f'{character}{chosen_sub_image}'
if cache_key not in image_cache:
character_image = Image.open(f'resources/img/amogus/crewmate-{character}{chosen_sub_image}.png').__enter__()
old_character_width, old_character_height = character_image.size
character_ratio = old_character_height / character_height
desired_width = int(old_character_width * character_ratio)
resized_character_image = ImageOps.contain(character_image, (desired_width, character_height))
image_cache[cache_key] = resized_character_image
image_to_use = resized_character_image
else:
image_to_use = image_cache[cache_key]
images_to_use.append(image_to_use)
# the line length is defined by the amount of characters, not once a certain width is reached
lines = [images_to_use[i:i + chars_per_row] for i in range(0, len(images_to_use), chars_per_row)]
# calculate the length of each line, and then take the widest one
line_widths = [sum(image_to_use.size[0] + horizontal_padding if image_to_use is not None else space_width for image_to_use in character_line) for character_line in lines]
canvas_width = max(line_widths)
row_count = math.ceil(len(text) / chars_per_row)
canvas_height = row_count * (character_height + vertical_padding)
drawing_board = Image.new('RGBA', (canvas_width, canvas_height), (0, 0, 0, 0))
start_x = 0
start_y = 0
for index, image_to_use in enumerate(images_to_use):
if index > 0 and (index % chars_per_row) == 0:
start_y += character_height + vertical_padding
start_x = 0
if image_to_use is not None:
drawing_board.paste(image_to_use, (start_x, start_y), image_to_use)
start_x += image_to_use.size[0] + horizontal_padding
else:
start_x += space_width
finally:
for cached_image in image_cache.values():
cached_image.__exit__(None, None, None)
return flask_utils.serve_pil_image(drawing_board)

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 71c18f8

Please sign in to comment.