TheRightGuy

paste 2

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