TheRightGuy

change more

Feb 28th, 2022
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 7.00 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.  
  84.         } catch (IOException e) {
  85.             event.getHook().setEphemeral(true).editOriginal(RENDERING_ERROR).queue();
  86.             logger.warn(
  87.                     "Unable to render latex, could not convert the image into an attachable form. Formula was {}",
  88.                     latex, e);
  89.         }
  90.     }
  91.  
  92.     @Nullable
  93.     private ByteArrayOutputStream getRenderedTextImageStream(@NotNull SlashCommandEvent event,
  94.             String latex, Image image) {
  95.         if (image == null) {
  96.             return null;
  97.         }
  98.         BufferedImage renderedTextImage = new BufferedImage(image.getWidth(null),
  99.                 image.getHeight(null), BufferedImage.TYPE_4BYTE_ABGR);
  100.         renderedTextImage.getGraphics().drawImage(image, 0, 0, null);
  101.         ByteArrayOutputStream renderedTextImageStream = new ByteArrayOutputStream();
  102.  
  103.         ImageIO.write(renderedTextImage, "png", renderedTextImageStream);
  104.  
  105.         return renderedTextImageStream;
  106.     }
  107.  
  108.     @Nullable
  109.     private Image renderImage(@NotNull SlashCommandEvent event, String latex, TeXFormula formula) {
  110.         Image image = formula.createBufferedImage(TeXConstants.STYLE_DISPLAY, DEFAULT_IMAGE_SIZE,
  111.                 FOREGROUND_COLOR, BACKGROUND_COLOR);
  112.         if (image.getWidth(null) == -1 || image.getHeight(null) == -1) {
  113.             event.getHook().setEphemeral(true).editOriginal(RENDERING_ERROR).queue();
  114.             logger.warn(
  115.                     "Unable to render latex, image does not have an accessible width or height. Formula was {}",
  116.                     latex);
  117.             return null;
  118.         }
  119.         return image;
  120.     }
  121.  
  122.     private void sendImage(@NotNull SlashCommandEvent event, String userID, String latex,
  123.             Image image) {
  124.  
  125.         ByteArrayOutputStream renderedTextImageStream =
  126.                 getRenderedTextImageStream(event, latex, image);
  127.         if (renderedTextImageStream == null) {
  128.             return;
  129.         }
  130.         event.getHook()
  131.             .editOriginal(renderedTextImageStream.toByteArray(), "tex.png")
  132.             .setActionRow(Button.of(ButtonStyle.DANGER, generateComponentId(userID), "Delete"))
  133.             .queue();
  134.     }
  135.  
  136.     /**
  137.      *
  138.      * Converts inline latex like: {@code hello $\frac{x}{2}$ world} to full latex
  139.      * {@code \text{hello}\frac{x}{2}\text{ world}}.
  140.      *
  141.      */
  142.     @NotNull
  143.     private String convertInlineLatexToFull(@NotNull String latex) {
  144.         if (isInvalidInlineFormat(latex)) {
  145.             throw new ParseException(
  146.                     "The amount of $-symbols must be divisible by two. Did you forget to close an expression? ");
  147.         }
  148.         Matcher matcher = INLINE_LATEX_REPLACEMENT.matcher(latex);
  149.         StringBuilder sb = new StringBuilder();
  150.         while (matcher.find()) {
  151.             boolean isInsideMathRegion = matcher.group(1) != null;
  152.             if (isInsideMathRegion) {
  153.                 sb.append(matcher.group(1).replace("$", ""));
  154.             } else {
  155.                 sb.append("\\text{").append(matcher.group(2)).append("}");
  156.             }
  157.         }
  158.  
  159.         return sb.toString();
  160.     }
  161.  
  162.     private boolean isInvalidInlineFormat(String latex) {
  163.         return latex.chars().filter(ch -> ch == '$').count() % 2 == 1;
  164.     }
  165.  
  166.     @Override
  167.     public void onButtonClick(@NotNull final ButtonClickEvent event,
  168.             @NotNull final List<String> args) {
  169.         if (!args.get(0).equals(Objects.requireNonNull(event.getMember()).getId())) {
  170.             event.reply("You are not the person who executed the command, you cannot do that")
  171.                 .setEphemeral(true)
  172.                 .queue();
  173.             return;
  174.         }
  175.         event.getMessage().delete().queue();
  176.     }
  177. }
  178.  
Add Comment
Please, Sign In to add comment