TheRightGuy

rewrite take 3

Feb 28th, 2022
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.04 KB | None | 0 0
  1. package org.togetherjava.tjbot.commands.mathcommands;
  2.  
  3. import net.dv8tion.jda.api.events.interaction.ButtonClickEvent;
  4. import net.dv8tion.jda.api.events.interaction.SlashCommandEvent;
  5. import net.dv8tion.jda.api.interactions.commands.OptionType;
  6. import net.dv8tion.jda.api.interactions.components.Button;
  7. import net.dv8tion.jda.api.interactions.components.ButtonStyle;
  8. import org.jetbrains.annotations.NotNull;
  9. import org.jetbrains.annotations.Nullable;
  10. import org.scilab.forge.jlatexmath.ParseException;
  11. import org.scilab.forge.jlatexmath.TeXConstants;
  12. import org.scilab.forge.jlatexmath.TeXFormula;
  13. import org.slf4j.Logger;
  14. import org.slf4j.LoggerFactory;
  15. import org.togetherjava.tjbot.commands.SlashCommandAdapter;
  16. import org.togetherjava.tjbot.commands.SlashCommandVisibility;
  17.  
  18. import javax.imageio.ImageIO;
  19. import java.awt.*;
  20. import java.awt.image.BufferedImage;
  21. import java.io.ByteArrayOutputStream;
  22. import java.io.IOException;
  23. import java.util.List;
  24. import java.util.Objects;
  25. import java.util.regex.Matcher;
  26. import java.util.regex.Pattern;
  27.  
  28. /**
  29.  * Implementation of a tex command which takes a string and renders an image corresponding to the
  30.  * mathematical expression in that string.
  31.  * <p>
  32.  * The implemented command is {@code /tex}. This has a single option called {@code latex} which is a
  33.  * string. If it is invalid latex or there is an error in rendering the image, it displays an error
  34.  * message.
  35.  */
  36.  
  37. public class TeXCommand extends SlashCommandAdapter {
  38.  
  39.     private static final String LATEX_OPTION = "latex";
  40.     // Matches regions between two dollars, like '$foo$'.
  41.     private static final String MATH_REGION = "(\\$[^$]+\\$)";
  42.     private static final String TEXT_REGION = "([^$]+)";
  43.     private static final Pattern INLINE_LATEX_REPLACEMENT =
  44.             Pattern.compile(MATH_REGION + "|" + TEXT_REGION);
  45.     private static final String RENDERING_ERROR = "There was an error generating the image";
  46.     private static final float DEFAULT_IMAGE_SIZE = 40F;
  47.     private static final Color BACKGROUND_COLOR = Color.decode("#36393F");
  48.     private static final Color FOREGROUND_COLOR = Color.decode("#FFFFFF");
  49.     private static final Logger logger = LoggerFactory.getLogger(TeXCommand.class);
  50.  
  51.     /**
  52.      * Creates a new Instance.
  53.      */
  54.     public TeXCommand() {
  55.         super("tex", "Renders LaTeX, also supports inline $-regions like 'see this $frac[x}{2}$'.",
  56.                 SlashCommandVisibility.GUILD);
  57.         getData().addOption(OptionType.STRING, LATEX_OPTION,
  58.                 "The latex which is rendered as an image", true);
  59.     }
  60.  
  61.     @Override
  62.     public void onSlashCommand(@NotNull final SlashCommandEvent event) {
  63.         String latex = Objects.requireNonNull(event.getOption(LATEX_OPTION)).getAsString();
  64.         String userID = (Objects.requireNonNull(event.getMember()).getId());
  65.         TeXFormula formula;
  66.  
  67.         try {
  68.             event.deferReply().queue();
  69.  
  70.             if (latex.contains("$")) {
  71.                 latex = convertInlineLatexToFull(latex);
  72.             }
  73.  
  74.             formula = new TeXFormula(latex);
  75.  
  76.             Image image = renderImage(event, latex, formula);
  77.  
  78.             sendImage(event, userID, latex, image);
  79.  
  80.         } catch (ParseException e) {
  81.             event.reply("That is an invalid latex: " + e.getMessage()).setEphemeral(true).queue();
  82.  
  83.         } catch (IOException e) {
  84.             event.getHook().setEphemeral(true).editOriginal(RENDERING_ERROR).queue();
  85.             logger.warn(
  86.                     "Unable to render latex, could not convert the image into an attachable form. Formula was {}",
  87.                     latex, e);
  88.         } catch (IllegalStateException e){
  89.             event.getHook().setEphemeral(true).editOriginal(RENDERING_ERROR).queue();
  90.  
  91.             logger.warn("Unable to render latex, image does not have an accessible width or height. Formula was {}", latex,e);
  92.         }
  93.     }
  94.  
  95.  
  96.     private @NotNull Image renderImage(@NotNull TeXFormula formula) {
  97.         Image image = formula.createBufferedImage(TeXConstants.STYLE_DISPLAY, DEFAULT_IMAGE_SIZE,
  98.                 FOREGROUND_COLOR, BACKGROUND_COLOR);
  99.         if (image.getWidth(null) == -1 || image.getHeight(null) == -1) {
  100.             throw new IllegalStateException("Image has no height or width");
  101.         }
  102.  
  103.         return image;
  104.     }
  105.  
  106.     private void sendImage(@NotNull SlashCommandEvent event, @NotNull String userID, @NotNull String latex,
  107.                            @NotNull Image image) throws IOException{
  108.  
  109.         ByteArrayOutputStream renderedTextImageStream =
  110.                 getRenderedTextImageStream(event, latex, image);
  111.         if (renderedTextImageStream == null) {
  112.             return;
  113.         }
  114.         event.getHook()
  115.             .editOriginal(renderedTextImageStream.toByteArray(), "tex.png")
  116.             .setActionRow(Button.of(ButtonStyle.DANGER, generateComponentId(userID), "Delete"))
  117.             .queue();
  118.     }
  119.  
  120.     @Nullable
  121.     private ByteArrayOutputStream getRenderedTextImageStream(@NotNull Image image) throws IOException {
  122.         if (image == null) {
  123.             return null;
  124.         }
  125.         BufferedImage renderedTextImage = new BufferedImage(image.getWidth(null),
  126.                 image.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR);
  127.         renderedTextImage.getGraphics().drawImage(image, 0, 0, null);
  128.         ByteArrayOutputStream renderedTextImageStream = new ByteArrayOutputStream();
  129.  
  130.         ImageIO.write(renderedTextImage, "png", renderedTextImageStream);
  131.  
  132.         return renderedTextImageStream;
  133.     }
  134.  
  135.     /**
  136.      *
  137.      * Converts inline latex like: {@code hello $\frac{x}{2}$ world} to full latex
  138.      * {@code \text{hello}\frac{x}{2}\text{ world}}.
  139.      *
  140.      */
  141.     @NotNull
  142.     private String convertInlineLatexToFull(@NotNull String latex) {
  143.         if (isInvalidInlineFormat(latex)) {
  144.             throw new ParseException(
  145.                     "The amount of $-symbols must be divisible by two. Did you forget to close an expression? ");
  146.         }
  147.         Matcher matcher = INLINE_LATEX_REPLACEMENT.matcher(latex);
  148.         StringBuilder sb = new StringBuilder();
  149.         while (matcher.find()) {
  150.             boolean isInsideMathRegion = matcher.group(1) != null;
  151.             if (isInsideMathRegion) {
  152.                 sb.append(matcher.group(1).replace("$", ""));
  153.             } else {
  154.                 sb.append("\\text{").append(matcher.group(2)).append("}");
  155.             }
  156.         }
  157.  
  158.         return sb.toString();
  159.     }
  160.  
  161.     private boolean isInvalidInlineFormat(String latex) {
  162.         return latex.chars().filter(ch -> ch == '$').count() % 2 == 1;
  163.     }
  164.  
  165.     @Override
  166.     public void onButtonClick(@NotNull final ButtonClickEvent event,
  167.             @NotNull final List<String> args) {
  168.         if (!args.get(0).equals(Objects.requireNonNull(event.getMember()).getId())) {
  169.             event.reply("You are not the person who executed the command, you cannot do that")
  170.                 .setEphemeral(true)
  171.                 .queue();
  172.             return;
  173.         }
  174.         event.getMessage().delete().queue();
  175.     }
  176. }
  177.  
Add Comment
Please, Sign In to add comment