1
0
mirror of https://github.com/demodude4u/Factorio-FBSR.git synced 2025-01-11 18:24:27 +02:00

Added upload database to remember URLs and recall

This commit is contained in:
Weston Rye 2022-04-08 04:27:54 -04:00
parent 5845a390e8
commit d8c27dd9f0
4 changed files with 122 additions and 147 deletions

View File

@ -81,11 +81,6 @@
<artifactId>okhttp</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.pircbotx</groupId>
<artifactId>pircbotx</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
@ -96,5 +91,10 @@
<artifactId>jaxb-api</artifactId>
<version>2.2.11</version>
</dependency>
<dependency>
<groupId>org.dizitart</groupId>
<artifactId>nitrite</artifactId>
<version>3.4.4</version>
</dependency>
</dependencies>
</project>

View File

@ -59,7 +59,10 @@ public class BlueprintStringData {
private final Optional<String> label;
private final MapVersion version;
private final String blueprintStringRaw;
public BlueprintStringData(String blueprintString) throws IllegalArgumentException, IOException {
this.blueprintStringRaw = blueprintString;
String versionChar = blueprintString.substring(0, 1);
try {
if (Integer.parseInt(versionChar) != 0) {
@ -138,4 +141,9 @@ public class BlueprintStringData {
public JSONObject json() {
return json;
}
@Override
public String toString() {
return blueprintStringRaw;
}
}

View File

@ -1,37 +1,40 @@
package com.demod.fbsr;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map.Entry;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import org.json.JSONArray;
import org.apache.commons.codec.binary.Hex;
import org.dizitart.no2.Document;
import org.dizitart.no2.IndexOptions;
import org.dizitart.no2.IndexType;
import org.dizitart.no2.Nitrite;
import org.dizitart.no2.NitriteCollection;
import org.dizitart.no2.filters.Filters;
import org.json.JSONException;
import org.json.JSONObject;
import com.demod.factorio.Utils;
import com.demod.fbsr.app.BlueprintBotDiscordService;
import com.demod.fbsr.app.ServiceFinder;
import com.google.common.util.concurrent.Uninterruptibles;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
public final class WebUtils {
private static Nitrite db = initializeDatabase();
private static NitriteCollection dbUploads = db.getCollection("uploads");
public static void addPossiblyLargeEmbedField(EmbedBuilder builder, String name, String value, boolean inline)
throws IOException {
if (value.length() <= MessageEmbed.VALUE_MAX_LENGTH) {
@ -42,6 +45,35 @@ public final class WebUtils {
}
}
private static void addToUploadedDatabase(String fileHash, String url) {
dbUploads.insert(Document.createDocument("hash", fileHash).put("url", url));
}
private static Optional<String> checkIfUploadedAlready(String fileHash) {
return Optional.ofNullable(dbUploads.find(Filters.eq("hash", fileHash)).firstOrDefault())
.flatMap(d -> Optional.ofNullable(d.get("url").toString()));
}
private static byte[] generateDiscordFriendlyPNGImage(BufferedImage image) {
byte[] imageData = WebUtils.getImageData(image);
while (imageData.length > Message.MAX_FILE_SIZE) {
image = RenderUtils.scaleImage(image, image.getWidth() / 2, image.getHeight() / 2);
imageData = WebUtils.getImageData(image);
}
return imageData;
}
private static String generateFileHash(byte[] fileData) {
try {
return Hex.encodeHexString(MessageDigest.getInstance("MD5").digest(fileData));
} catch (NoSuchAlgorithmException e) {
throw new InternalError(e);
}
}
public static byte[] getImageData(BufferedImage image) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ImageIO.write(image, "PNG", baos);
@ -52,6 +84,22 @@ public final class WebUtils {
}
}
private static Nitrite initializeDatabase() {
try {
Nitrite db = Nitrite.builder().compressed().filePath("database.db").openOrCreate();
NitriteCollection dbUploads = db.getCollection("uploads");
if (!dbUploads.hasIndex("hash")) {
dbUploads.createIndex("hash", IndexOptions.indexOptions(IndexType.Unique));
}
return db;
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
public static InputStream limitMaxBytes(InputStream delegate, int maxBytes) {
return new InputStream() {
int byteCount = 0;
@ -90,110 +138,31 @@ public final class WebUtils {
return Utils.readJsonFromStream(new URL(url).openStream());
}
public static URL uploadToBundly_BROKEN(String title, String description, List<Entry<URL, String>> links)
throws IOException {
JSONObject request = new JSONObject();
request.put("title", title);
request.put("desc", description);
JSONArray items = new JSONArray();
for (Entry<URL, String> pair : links) {
JSONObject item = new JSONObject();
item.put("url", pair.getKey().toString());
item.put("id", Long.toString(System.currentTimeMillis()));
item.put("caption", pair.getValue());
items.put(item);
// XXX Lazy approach to make sure id is unique...
Uninterruptibles.sleepUninterruptibly(1, TimeUnit.MILLISECONDS);
}
request.put("items", items);
URL url = new URL("http://bundly.io/createBundle");
URLConnection connection = url.openConnection();
connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
connection.setDoOutput(true);
connection.getOutputStream().write(request.toString().getBytes(StandardCharsets.UTF_8));
connection.getOutputStream().flush();
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line = br.readLine();
if (line == null || line.length() > 8) {
throw new IOException("Bundly.io returned an invalid response: " + line);
}
URL resultUrl = new URL("http://bundly.io/" + line);
HttpURLConnection resultTestConnection = (HttpURLConnection) resultUrl.openConnection();
resultTestConnection.setRequestMethod("GET");
resultTestConnection.connect();
if (resultTestConnection.getResponseCode() == 500) {
throw new IOException("Bundly.io is still broken. :(");
}
return resultUrl;
}
public static String uploadToHostingService(String fileName, BufferedImage image) throws IOException {
return uploadToHostingService(fileName, generateDiscordFriendlyPNGImage(image));
}
public static URL uploadToHostingService(String fileName, BufferedImage image) throws IOException {
public static String uploadToHostingService(String fileName, byte[] fileData) throws IOException {
String fileHash = generateFileHash(fileData);
Optional<String> alreadyUploaded = checkIfUploadedAlready(fileHash);
if (alreadyUploaded.isPresent()) {
return alreadyUploaded.get();
}
Optional<BlueprintBotDiscordService> discordService = ServiceFinder
.findService(BlueprintBotDiscordService.class);
if (discordService.isPresent()) {
// Discord original -> image hosting -> Discord scaled
try {
return discordService.get().useDiscordForImageHosting(fileName, image, false);
} catch (Exception e) {
try {
return uploadToRemoteHosting(fileName, getImageData(image));
} catch (Exception e1) {
return discordService.get().useDiscordForImageHosting(fileName, image, true);
}
String url = discordService.get().useDiscordForFileHosting(fileName, fileData).toString();
addToUploadedDatabase(fileHash, url);
return url;
} catch (Exception e2) {
throw new IOException("File hosting failed!", e2);
}
} else {
return uploadToRemoteHosting(fileName, getImageData(image));
}
}
public static URL uploadToHostingService(String fileName, byte[] fileData) throws IOException {
try {
return uploadToRemoteHosting(fileName, fileData);
} catch (Exception e) {
Optional<BlueprintBotDiscordService> discordService = ServiceFinder
.findService(BlueprintBotDiscordService.class);
if (discordService.isPresent()) {
try {
return discordService.get().useDiscordForFileHosting(fileName, fileData);
} catch (Exception e2) {
throw new IOException("File hosting failed!", e2);
}
}
throw new IOException("File hosting failed!");
}
}
@SuppressWarnings("unused")
private static URL uploadToMixtapeMoe_BROKEN(String fileName, byte[] fileData) throws IOException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(fileData)) {
MultipartUtility utility = new MultipartUtility("https://mixtape.moe/upload.php", "UTF-8");
utility.addFormField("name", fileName);
utility.addFilePart("files[]", fileName, bais);
return new URL(
new JSONObject(utility.finish().get(0)).getJSONArray("files").getJSONObject(0).getString("url"));
}
}
@SuppressWarnings("unused")
private static URL uploadToNyaIs_BROKEN(String fileName, byte[] fileData) throws IOException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(fileData)) {
MultipartUtility utility = new MultipartUtility("https://nya.is/upload", "UTF-8");
utility.addFormField("name", fileName);
utility.addFilePart("files[]", fileName, bais);
return new URL(
new JSONObject(utility.finish().get(0)).getJSONArray("files").getJSONObject(0).getString("url"));
}
}
private static URL uploadToRemoteHosting(String fileName, byte[] fileData) throws IOException {
throw new IOException("No Image Host available!");// FIXME
throw new IOException("File hosting failed! (Discord not available)");
}
private WebUtils() {

View File

@ -152,15 +152,6 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
}
}
private byte[] generateDiscordFriendlyPNGImage(BufferedImage image) {
byte[] imageData = WebUtils.getImageData(image);
if (imageData.length > 8000000) {
return generateDiscordFriendlyPNGImage(
RenderUtils.scaleImage(image, image.getWidth() / 2, image.getHeight() / 2));
}
return imageData;
}
public DiscordBot getBot() {
return bot;
}
@ -235,12 +226,12 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
List<Blueprint> blueprints = blueprintStringDatas.stream().flatMap(bs -> bs.getBlueprints().stream())
.collect(Collectors.toList());
List<Entry<URL, String>> links = new ArrayList<>();
List<Entry<String, String>> links = new ArrayList<>();
for (Blueprint blueprint : blueprints) {
try {
blueprint.json().remove("index");
URL url = WebUtils.uploadToHostingService("blueprint.txt",
String url = WebUtils.uploadToHostingService("blueprint.txt",
BlueprintStringData.encode(blueprint.json()).getBytes());
links.add(new SimpleEntry<>(url, blueprint.getLabel().orElse(null)));
} catch (Exception e) {
@ -254,7 +245,7 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
ArrayDeque<String> lines = links.stream()
.map(p -> (p.getValue() != null && !p.getValue().isEmpty())
? ("[" + p.getValue() + "](" + p.getKey() + ")")
: p.getKey().toString())
: p.getKey())
.collect(Collectors.toCollection(ArrayDeque::new));
while (!lines.isEmpty()) {
EmbedBuilder builder = new EmbedBuilder();
@ -584,13 +575,13 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
} else if (images.size() == 1) {
Entry<Optional<String>, BufferedImage> entry = images.get(0);
BufferedImage image = entry.getValue();
URL url = WebUtils.uploadToHostingService("blueprint.png", image);
String url = WebUtils.uploadToHostingService("blueprint.png", image);
EmbedBuilder builder = new EmbedBuilder();
builder.setImage(url.toString());
builder.setImage(url);
embedBuilders.add(builder);
} else {
List<Entry<URL, String>> links = new ArrayList<>();
List<Entry<String, String>> links = new ArrayList<>();
for (Entry<Optional<String>, BufferedImage> entry : images) {
BufferedImage image = entry.getValue();
links.add(new SimpleEntry<>(WebUtils.uploadToHostingService("blueprint.png", image),
@ -599,29 +590,46 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
ArrayDeque<String> lines = new ArrayDeque<>();
for (int i = 0; i < links.size(); i++) {
Entry<URL, String> entry = links.get(i);
Entry<String, String> entry = links.get(i);
if (entry.getValue() != null && !entry.getValue().trim().isEmpty()) {
lines.add("[" + entry.getValue().trim() + "](" + entry.getKey() + ")");
} else {
lines.add("[Blueprint " + (i + 1) + "](" + entry.getKey() + ")");
lines.add("[Blueprint Image " + (i + 1) + "](" + entry.getKey() + ")");
}
}
while (!lines.isEmpty()) {
EmbedBuilder builder = new EmbedBuilder();
StringBuilder description = new StringBuilder();
while (!lines.isEmpty()) {
if (description.length() + lines.peek().length() + 1 < MessageEmbed.DESCRIPTION_MAX_LENGTH) {
description.append(lines.pop()).append('\n');
} else {
break;
for (int i = 0; i < 3 && !lines.isEmpty(); i++) {
StringBuilder description = new StringBuilder();
while (!lines.isEmpty()) {
if (description.length() + lines.peek().length() + 1 < MessageEmbed.VALUE_MAX_LENGTH) {
description.append(lines.pop()).append('\n');
} else {
break;
}
}
builder.addField("", description.toString(), true);
}
builder.setDescription(description);
embedBuilders.add(builder);
}
}
if (!blueprintStrings.isEmpty() && !embedBuilders.isEmpty()) {
List<String> links = new ArrayList<>();
for (int i = 0; i < blueprintStrings.size(); i++) {
BlueprintStringData blueprintString = blueprintStrings.get(i);
String label = blueprintString.getLabel().orElse(
(blueprintString.getBlueprints().size() > 1 ? "Blueprint Book " : "Blueprint ") + (i + 1));
String link = WebUtils.uploadToHostingService("blueprint.txt", blueprintString.toString().getBytes());
links.add("[" + label + "](" + link + ")");
}
embedBuilders.get(0).getFields().add(0,
new Field("Blueprint String" + (blueprintStrings.size() > 1 ? "s" : ""),
links.stream().collect(Collectors.joining("\n")), false));
}
return embedBuilders;
}
@ -633,8 +641,8 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
json.put("version", FBSR.getVersion());
json.put("data", Utils.<JSONObject>convertLuaToJson(lua));
String fileName = category + "_" + name + "_dump_" + FBSR.getVersion() + ".json";
URL url = WebUtils.uploadToHostingService(fileName, json.toString(2).getBytes());
event.reply(category + " " + name + " lua dump: [" + fileName + "](" + url.toString() + ")");
String url = WebUtils.uploadToHostingService(fileName, json.toString(2).getBytes());
event.reply(category + " " + name + " lua dump: [" + fileName + "](" + url + ")");
}
@Override
@ -848,14 +856,4 @@ public class BlueprintBotDiscordService extends AbstractIdleService {
Message message = channel.sendFile(fileData, fileName).complete();
return new URL(message.getAttachments().get(0).getUrl());
}
public URL useDiscordForImageHosting(String fileName, BufferedImage image, boolean downscaleIfNeeded)
throws IOException {
TextChannel channel = bot.getJDA().getTextChannelById(hostingChannelID);
Message message = channel
.sendFile(downscaleIfNeeded ? generateDiscordFriendlyPNGImage(image) : WebUtils.getImageData(image),
fileName)
.complete();
return new URL(message.getAttachments().get(0).getUrl());
}
}