Premier commit - Inclusion dans le système git

This commit is contained in:
Mysaa 2021-05-24 15:34:36 +02:00
commit 1ab160b5f6
5 changed files with 617 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.settings
.gradle
.project
.classpath
bin/

View File

@ -0,0 +1,345 @@
package com.bernard.julatex;
import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;
import java.util.HashMap;
import java.util.Map;
import java.util.PrimitiveIterator.OfInt;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import com.bernard.juliabot.api.Command;
import com.bernard.juliabot.api.JuLIAddon;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.MessageBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageChannel;
import net.dv8tion.jda.api.entities.User;
@JuLIAddon(name = "juliatex", devs="Mysaa", version = "20w33a")
public class Juliatex {
//TODO globally add a check-null parsing to every method call
public static Map<Long,LogMode> userLogModes = new HashMap<>();
static {
UserConfigManager.retieveUserLogModes();
}
public static final String imageStorageRoot = "/var/julia/juliatex/images/";
public static final String pngLatexPath = "/opt/pnglatex";
public static final String wipText = "Je suis super motivé mais les développeurs ont préféré perdre leur temps à écrire un texte disant qu'il ont pas fait leur taff plutot que de réellement faire le contenu ... Ah ben Bravo !";
public static final LogMode DEFAULT_LOGMODE = LogMode.RISKY;
@Command(name = "£atex", admin = false, description = "Affiche une image a partir du latex")
public void latex(Message message, User user, MessageChannel channel) {
try {
if(!userLogModes.containsKey(user.getIdLong()))
userLogModes.put(user.getIdLong(), DEFAULT_LOGMODE);
System.out.println("Ca fonctionne ? : "+user);
String formula = message.getContentRaw().substring(7);
JuliatexConfig config = UserConfigManager.getUserSelectedConfig(user);
File f = new File(imageStorageRoot, Long.toString(hash(formula, config)) + ".png");
String error = latexToPng(formula,f.getAbsolutePath(),config);
if(!error.isEmpty()) {
MessageBuilder errMsg = new MessageBuilder();
errMsg.append("Bon, le latex était pas super content, voila ce qu'il m'a renvoyé");
errMsg.appendCodeBlock(error, "");
Message msg = errMsg.build();
Long mid = msg.getIdLong();
System.out.println("L'ID : "+mid);
channel.sendMessage(msg).complete().delete().completeAfter(20, TimeUnit.SECONDS);
//channel.deleteMessageById(mid).completeAfter(20, TimeUnit.SECONDS);
}
if(!f.exists()) {
channel.sendMessage("Le moteur latex m'a renvoye une image neant, du coup, je vous l'affiche dans ce message. Bon visionnage (J'imagine) !");
return;
}
channel.sendFile(f).complete();
} catch (IOException e) {
System.err.println("AQueFichierProbleme");
} catch (InterruptedException e) {
System.err.println("AQueThreadProbleme");
} catch (StopEverythingException e) {
notifyUser("Un message d'arret géneral a été lancé (surement du a une erreur) ... donc ... j'écoute. (Du moins je vous le fait croire)", user, channel);
System.err.println("Erreur générale lancée : user="+user+" ; message="+message+" ; channel="+channel);
}
}
@Command(name = "£atech", admin = true, description = "Règle les paramètres utilisateurs de JuL'IAtex")
public void latech(Message message, User user, MessageChannel channel) {
String[] args = parseArguments(message.getContentRaw());
if(args.length < 2) {
notifyUser("La commende £atech prends au moins un argument (l'action <20> <20>ffectuer)", user, channel);
return;
}
switch (args[1]) {
case "add":// "configName" = "dumpedText" |||||| "configName" ["parentName"]
case "new":
case "ajouter":
case "nouveau":
if(args.length < 3) {
notifyUser("La commande '£atech "+args[1]+"' prends au moins un argument (le nom de la nouvelle config)", user, channel);
return;
}
JuliatexConfig model = UserConfigManager.getDefaultConfig();
switch (args.length) {
case 4:
model = UserConfigManager.getUserConfig(user, args[3]);
if(model == null){
notifyUser("Je ne connais pas la configuration '"+args[3]+"'", user, channel);
return;
}
case 3:
if(!Regexer.configName.matcher(args[2]).matches()){
notifyUser("'"+args[2]+"' n'est pas un nom de configuration valide (la regex c'est '"+Regexer.configName.pattern()+"' pour ceux que ca intéresse)", user, channel);
return;
}
UserConfigManager.addConfig(user, args[2],model);
break;
case 5:
if(!args[3].equals("=")){
notifyUser("La commende '£atech "+args[1]+"' ne s'utilise pas comme ca ! (voir '£atex help')", user, channel);
return;
}
notifyUser("Je gere pas encore les dumps ... déso pas déso", user, channel);
break;
default:
notifyUser("La commende '£atech "+args[0]+"' ne s'utilise pas comme ca ! (voir '£atex help')", user, channel);
return;
}
break;
case "rm":// "configName
case "remove":
case "delete":
case "suppr":// "configName
case "supprimer":
case "enlever":
if(args.length != 3) {
notifyUser("La commande '£atech "+args[1]+"' prends un argument (le nom de la config à supprimer)", user, channel);
return;
}
JuliatexConfig target = UserConfigManager.getUserConfig(user, args[2]);
if(target == null){
notifyUser("Je ne connais pas la configuration '"+args[2]+"'", user, channel);
return;
}
UserConfigManager.delConfig(user, args[2]);
break;
case "use":
case "take": // "configName"
case "utiliser":
case "prendre":
if(args.length != 3) {
notifyUser("La commande '£atech "+args[1]+"' prends un argument (le nom de la config à utiliser)", user, channel);
return;
}
JuliatexConfig cible = UserConfigManager.getUserConfig(user, args[2]);
if(cible == null){
notifyUser("Je ne connais pas la configuration '"+args[2]+"'", user, channel);
return;
}
UserConfigManager.useConfig(user, cible);
break;
case "change":
case "modify": //"configName" "param"="value" "param"="value" "param"="value"
case "alter":
case "changer":
case "modifier":
case "alterer":
notifyUser(wipText, user, channel);
if(args.length < 3) {
notifyUser("La commande '£atech "+args[1]+"' prends au moins un argument (le nom de la config à changer)", user, channel);
return;
}
JuliatexConfig config = UserConfigManager.getUserConfig(user, args[2]);
for(int paramIndex = 3;args.length > paramIndex;paramIndex++) {
OfInt arg = args[paramIndex].chars().iterator();
String field = "";
String value = "";
char c;
while((c = (char)(int)arg.next()) != '=')
field += c;
while(arg.hasNext())
value += (char)(int)arg.next();
try {
System.out.println("Set config file "+args[2]+" from \""+field+"\" with \""+ value +"\"");
config.setField(field,value);
}catch(IllegalArgumentException e) {
notifyUser(e.getMessage(), user, channel);
}
}
try {
UserConfigManager.saveConfig(user,args[2],config,false);
} catch (IOException e) {
notifyUser("A que je n'arive pas a modifier la config, les changements ne seront valable que si JuL'IA ne redémarre pas ... contactez un développeur (même si ils doivent déjà être prévenus)", user, channel);
}
notifyUser("C'est fini !!!", user, channel);
break;
case "dump": // configName
case "cracher":
notifyUser(wipText, user, channel);
if(args.length < 3) {
notifyUser("La commande '£atech "+args[1]+"' prends au moins un argument (le nom de la config à changer)", user, channel);
return;
}
JuliatexConfig configToDump = UserConfigManager.getUserConfig(user, args[2]);
notifyUser("Et JuL'IA cracha '"+args[2]+"' : ```"+configToDump.toString()+"```", user, channel);
break;
case "lacommandedinitialisationdelaquelleilestdurdetrouverlenomauhasard":
notifyUser("Ceci est une commande de debug ...", user, channel);
userLogModes = new HashMap<>();
UserConfigManager.selecteds = new HashMap<>();
//XXX UserConfigManager.saveSelectedConfigs();
//XXX UserConfigManager.saveUserLogModes();
break;
case "help":
case "aide":
case "ausecours":
case "?":
case "42":
case "quoi?":
EmbedBuilder builder = new EmbedBuilder();
builder.setTitle("Voila les commandes de £atech disponible");
builder.addField("add|new|ajouter|nouveau","Crée une nouvelle config\n"
+"\t\t\"configName\" = \"dumpedText\"\n"
+"\t\t\"configName\" [\"parentName\"]",false);
builder.addField("rm|remove|suppr|delete|supprimer|enlever","Supprime une config\n"
+"\t\t\"configName\"",false);
builder.addField("use|take|utiliser|prendre","Sélectionne une config\n"
+"\t\t\"configName\"",false);
builder.addField("change|modify|alter|changer|modifier|alterer","Modifie une config\n"
+"\t\t\"configName\" \"param\"=\"value\" \"param\"=\"value\" \"param\"=\"value\" etc... ",false);
builder.addField("dump|cracher","Renvoie un texte décrivant la config (pour le partage)\n"
+"\t\t\"configName\"",false);
builder.addField("help|aide|ausecours|?|42|quoi?","Je suis sur que vous savez ce que ca veut dire !\n"
+"\t\t Vous voulez mettre quoi comme paramètres à un help ?!",false);
builder.setColor(Color.getHSBColor((float)Math.random(), 1.0f, 1.0f));
channel.sendMessage(builder.build()).complete();
break;
default:
notifyUser("Je ne connais pas cette action : '"+args[0]+"' ... Je crois que tu t'es trompé", user, channel);
return;
}
}
public static final void notifyUser(String problem,User user,MessageChannel channel) {
MessageBuilder builder = new MessageBuilder();
builder.append(user);
builder.append(" : ");
builder.append(problem);
if(channel == null)
channel = user.openPrivateChannel().complete();
channel.sendMessage(builder.build()).complete();
}
public static final String[] parseArguments(String args) {
return args.split(" ");//TODO space escape
}
public static final String latexToPng(String formula, String outputFile, JuliatexConfig config)
throws IOException, InterruptedException {
ProcessBuilder pb = new ProcessBuilder(
new String[] { "/opt/pnglatex", "-b", config.getBgColor(), "-B", config.getBorderColor(), "-F",
config.getFgColor(), "-f", formula, "-d", Integer.toString(config.getDpi(), 10), "-o",
outputFile, "-P", Integer.toString(config.getPadding(), 10), "-m",
Integer.toString(config.getMargin(), 10), config.getEnvironnement().isEmpty()?"":"-e", config.getEnvironnement(), config.getHeader().isEmpty()?"":"-H",
config.getHeader(), "-O", config.optimize() ? "1" : "0", config.getPackages().isEmpty()?"":"-p "+ config.getPackages() });
System.out.println("Launching command : "+String.join(" ", pb.command()));
File tempErrFile = File.createTempFile(outputFile, "err");
tempErrFile.createNewFile();
pb.redirectError(tempErrFile);
pb.redirectOutput(Redirect.INHERIT);
Process p = pb.start();
p.waitFor();
FileInputStream fis = new FileInputStream(tempErrFile);
byte[] data = new byte[(int) tempErrFile.length()];
fis.read(data);
fis.close();
tempErrFile.delete();
return new String(data, "UTF-8");
}
public static boolean log(User user,String message) {
LogMode logMode = userLogModes.get(user.getIdLong());
if(logMode == LogMode.NORMAL) {
notifyUser(message, user, null);//notify the user itself
}else if(logMode == LogMode.SAFELY) {
throw new StopEverythingException();
}
return logMode == LogMode.RISKY;
}
//////// FNV-1 HASH FUNCTION (très rapide,non cryprographique) ////////////
public static final long FNV_1_START = 0xCBF29CE484222325L;
public static final long FNV_1_XOR = 0x100000001B3L;
public static long hash(String formula, JuliatexConfig config) {
long hash = FNV_1_START;
for (byte b : formula.getBytes()) {
hash *= FNV_1_XOR;
hash ^= b & 0xFF;
}
for (byte b : config.toString().getBytes()) {
hash *= FNV_1_XOR;
hash ^= b & 0xFF;
}
return hash;
}
public static enum LogMode{
RISKY,NORMAL,SAFELY;
}
public static class Regexer {
public static final Pattern configName = Pattern.compile("[-a-zA-Z0-9_]+");
}
}

View File

@ -0,0 +1,123 @@
package com.bernard.julatex;
import java.io.Serializable;
public class JuliatexConfig implements Serializable{
private static final long serialVersionUID = 4384323400837152293L;
int padding;
int margin;
String packages;
String bgColor,fgColor,borderColor;
int dpi;
String environnement;
String header;
boolean optimize;
public JuliatexConfig(int padding, int margin, String packages, String bgColor, String fgColor, String borderColor,
int dpi, String environnement, String header, boolean optimize) {
super();
this.padding = padding;
this.margin = margin;
this.packages = packages;
this.bgColor = bgColor;
this.fgColor = fgColor;
this.borderColor = borderColor;
this.dpi = dpi;
this.environnement = environnement;
this.header = header;
this.optimize = optimize;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
public int getPadding() {
return padding;
}
public int getMargin() {
return margin;
}
public String getPackages() {
return packages;
}
public String getBgColor() {
return bgColor;
}
public String getFgColor() {
return fgColor;
}
public String getBorderColor() {
return borderColor;
}
public int getDpi() {
return dpi;
}
public String getEnvironnement() {
return environnement;
}
public String getHeader() {
return header;
}
public boolean optimize() {
return optimize;
}
public void setField(String field, String value) {
switch(field) {//TDOD traduce in every language + special names
case "padding":
padding = Integer.parseUnsignedInt(value, 10);
break;
case "margin":
margin = Integer.parseUnsignedInt(value, 10);
break;
case "packages":
packages = value;
break;
case "bgColor":
bgColor = value;//TODO super color parsing (pour les trois)
break;
case "fgColor":
fgColor = value;
break;
case "borderColor":
borderColor = value;
break;
case "dpi":
dpi = Integer.parseUnsignedInt(value, 10);
break;
case "environnement":
environnement = value;
break;
case "header":
header = value;
break;
case "optimize":
if(value == "1" || value == "true" || value == "oui")
optimize = true;
else if(value == "1" || value == "true" || value == "oui")
optimize = false;
else
throw new IllegalArgumentException("La valeur de "+field+" doit etre comprehensible comme un boolean !");
break;
default:
throw new IllegalArgumentException("Malgrès tous mes efforts, je n'arrive pas a comprendre ce qui doit etre modifié ... essayez autre chose que "+field);
}
}
}

View File

@ -0,0 +1,10 @@
package com.bernard.julatex;
public class StopEverythingException extends RuntimeException {
/**
*
*/
private static final long serialVersionUID = 2080456590173832156L;
}

View File

@ -0,0 +1,134 @@
package com.bernard.julatex;
import static com.bernard.julatex.Juliatex.log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import net.dv8tion.jda.api.entities.User;
public class UserConfigManager {
public static Map<Long,JuliatexConfig> selecteds = new HashMap<>();
public static final String varFolder = "/var/julia/juliatex/";
public static final String juliatexConfigExtension = "juliatexconfig";
public static final String defaultConfigName = "default";
public static final JuliatexConfig addConfig(User user,String configName,JuliatexConfig model) {
try {
saveConfig(user, configName, model, true);
} catch (IOException e) {
e.printStackTrace();
if(log(user,"Une erreur interne I/O est survenue, contactez les développeurs. Immediatement... C'EST UN ORDRE !!!"));
return getDefaultConfig();
}
return getUserConfig(user, configName);
}
public static final JuliatexConfig useConfig(User user,JuliatexConfig cible) {
selecteds.remove(user.getIdLong());//TODO voir si'il y a rien de plus propre genre un selected.replace();
selecteds.put(user.getIdLong(),cible);
return cible;
}
public static final void delConfig(User user,String targetName) {
File file = getUserConfigFilePath(user, targetName).toFile();
if(file.exists())
file.delete();
else
log(user,"La config à supprimer n'existe pas .... C'est problêmatique !");
}
public static final JuliatexConfig getUserSelectedConfig(User user){
long id = user.getIdLong();
if(selecteds.containsKey(id))
return selecteds.get(id);
else if(log(user,"Vous n'avez pas de config sélectionnée .... faut me dire comment vous avez fait !"))
return getDefaultConfig();
else
return null;
}
public static final JuliatexConfig getUserConfig(User user,String configName){
File configFile = getUserConfigFilePath(user, configName).toFile();
if(!configFile.exists())
configFile = getDefaultConfigFilePath(configName).toFile();
if(!configFile.exists())
if(log(user,"La configuration demandée n'existe pas.... Je suis une IA qui est censé deviné votre configuration à partir du nom mais ..... la flemme !"))
return getDefaultConfig();
else
return null;
FileInputStream fis = null;
try {
fis = new FileInputStream(configFile);
JuliatexConfig config = parseConfig(fis);
return config;
} catch (FileNotFoundException e) {
// TODO Auto-generated truc
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(fis != null)
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public static final JuliatexConfig getDefaultConfig() {
return new JuliatexConfig(3, 0, "", "Transparent", "White", "", 3000, "", "", false);
}
public static final JuliatexConfig parseConfig(InputStream is) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(is);
return (JuliatexConfig) ois.readObject();
}
public static final void dumpConfig(OutputStream os,JuliatexConfig config) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(config);
}
private static Path getUserConfigFilePath(User user,String configName) {
return Paths.get(varFolder, "users",Long.toString(user.getIdLong(), 10),configName+"."+juliatexConfigExtension);
}
private static Path getDefaultConfigFilePath(String configName) {
return Paths.get(varFolder, "templates",configName+"."+juliatexConfigExtension);
}
public static void saveConfig(User user, String configName, JuliatexConfig config,boolean createIfDontExists) throws IOException {
File saveFile = getUserConfigFilePath(user, configName).toFile();
if(!(createIfDontExists || saveFile.exists()))
throw new IllegalArgumentException("Le fichier "+saveFile.getAbsolutePath()+" n'existe pas et ne doit pas être créé");
saveFile.createNewFile();
FileOutputStream fos = new FileOutputStream(saveFile);
dumpConfig(fos, config);
fos.close();
}
public static void retieveUserLogModes() {
// TODO Auto-generated method stub
}
}