Skip to content

Commit

Permalink
fix(Plugin): fix #115 auto update path bug
Browse files Browse the repository at this point in the history
  • Loading branch information
Dituon committed Aug 24, 2024
1 parent 4913f96 commit 2e73558
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 152 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ dependencies {
compileOnly 'org.projectlombok:lombok:1.18.34'
annotationProcessor 'org.projectlombok:lombok:1.18.34'

implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.3'
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1'
implementation "com.madgag:animated-gif-lib:1.4"
implementation "net.coobird:thumbnailator:0.4.19"
implementation 'com.jhlabs:filters:2.0.235-1'
Expand Down
219 changes: 91 additions & 128 deletions src/main/java/moe/dituon/petpet/plugin/DataUpdater.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,156 +2,119 @@

import moe.dituon.petpet.share.BasePetService;

import java.io.*;
import java.net.HttpURLConnection;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.MalformedInputException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.stream.Collectors;
import java.util.HashMap;
import java.util.Map;

public class DataUpdater {
public static final String DEFAULT_REPO_URL = "https://raw.githubusercontent.com/Dituon/petpet/main";
public static final String DEFAULT_REPO_DATA_PATH = "/data/xmmt.dituon.petpet/";
public static final String DEFAULT_REPO_URL =
"https://github.com/Dituon/petpet/raw/main/";
public static final String DEFAULT_REPO_DATA_PATH = "./data/xmmt.dituon.petpet/";
public static final String INDEX_FILE = "index.json";
public static final String TEMPLATE_FILE = "data.json";
private final String[] repositoryUrls;
private final File targetDir;
private final PluginPetService service;

protected final PluginPetService service;
protected final Path targetPath;
protected final Map<String, Integer> templateLengthMap = new HashMap<>(256);

public DataUpdater(PluginPetService service, File targetDir) {
this.repositoryUrls = service.repositoryUrls;
this.targetDir = targetDir;
this.service = service;
this.targetPath = targetDir.toPath();
}

public boolean autoUpdate() {
List<UpdateIndex> indices = new ArrayList<>(repositoryUrls.length);
for (String repositoryUrl : repositoryUrls) {
String url = joinPath(repositoryUrl, INDEX_FILE);
try {
UpdateIndex index = UpdateIndex.parse(getUrlText(url));
index.setUrl(repositoryUrl);
indices.add(index);
} catch (IOException ex) {
PluginPetService.LOGGER.warning("无法连接到远程仓库: " + repositoryUrl, ex);
} catch (Exception ex) {
PluginPetService.LOGGER.warning("无法解析索引文件: " + url, ex);
}
}

if (!checkUpdate(indices)) {
PluginPetService.LOGGER.info("开始更新PetData");
updateData(indices);
return true;
protected byte[] getUrlBytes(URL url) throws IOException {
try (InputStream in = url.openStream()) {
return in.readAllBytes();
}
return false;
}

public void updateData(Iterable<UpdateIndex> indices) {
Map<String, String> templateMap = new HashMap<>(256);
Map<String, String> fontMap = new HashMap<>(8);
for (UpdateIndex index : indices) {
String baseUrl = index.getUrl() + '/' + index.getDataPath() + '/';

index.getDataList().forEach(key -> templateMap.put(key, baseUrl + key));
index.getFontList().forEach(font -> fontMap.put(font, baseUrl + BasePetService.FONTS_FOLDER));
}

for (var entry : templateMap.entrySet()) {
String key = entry.getKey();
if (isExcludedKey(key)) {
continue;
}

String url = entry.getValue();
if (!saveAs(url, TEMPLATE_FILE)) {
PluginPetService.LOGGER.warning("无法从远程仓库下载 PetTemplate: " + url);
continue;
}
short i = 0;
while (saveAs(url, i + ".png")) i++;
PluginPetService.LOGGER.info("PetTemplate/" + key + "下载成功 (length:" + i + ')');
}

File localFontsDir = new File(DEFAULT_REPO_DATA_PATH + BasePetService.FONTS_FOLDER);
Set<String> localFonts = localFontsDir.exists() && localFontsDir.isDirectory() && localFontsDir.canRead() ?
Arrays.stream(Objects.requireNonNull(localFontsDir.listFiles())).map(File::getName).collect(Collectors.toSet()) :
Collections.emptySet();

for (var entry : fontMap.entrySet()) {
String font = entry.getKey(), url = entry.getValue();
if (localFonts.contains(font)) continue;

if (!saveAs(url, font)) {
PluginPetService.LOGGER.warning("无法从远程仓库下载PetFont: " + url);
return;
}

PluginPetService.LOGGER.info("PetFont/" + font + "下载成功");
}


for (String font : fontMap.keySet()) {
if (localFonts.contains(font)) continue;
if (!saveAs(BasePetService.FONTS_FOLDER, font)) {
PluginPetService.LOGGER.warning("无法从远程仓库下载PetFont: " + joinPath(BasePetService.FONTS_FOLDER, font));
return;
}
PluginPetService.LOGGER.info("PetFont/" + font + "下载成功");
}
protected String getUrlText(URL url) throws IOException {
return new String(getUrlBytes(url), StandardCharsets.UTF_8);
}

public boolean checkUpdate(Iterable<UpdateIndex> indices) {
for (UpdateIndex index : indices) {
if (BasePetService.VERSION != index.getVersion()) {
PluginPetService.LOGGER.info(
"PetpetPlugin 可更新到最新版本: " + index.getVersion() +
" (当前版本 " + BasePetService.VERSION + ") 养成更新的好习惯哦 (*/ω\*)"
);
}

for (String key : index.getDataList()) {
if (isExcludedKey(key)) continue;
PluginPetService.LOGGER.info("发现新增 Petpet 模板");
return false;
}
protected void saveAs(URL url, Path path) throws IOException {
try (InputStream in = url.openStream()){
Files.createDirectories(path.getParent());
Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
}
return true;
}

protected String getUrlText(String url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8));
StringBuilder str = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) str.append(line);
reader.close();
connection.disconnect();
return str.toString();
}

protected boolean saveAs(String baseUrl, String filePath) {
String url = joinPath(baseUrl, filePath);
Path target = Paths.get(targetDir.getAbsolutePath(), filePath);
try (InputStream ins = new URL(url).openStream()) {
Files.createDirectories(target.getParent());
Files.copy(ins, target, StandardCopyOption.REPLACE_EXISTING);
} catch (IOException ignore) {
return false;
public boolean autoUpdate() {
var baseFontPath = targetPath.resolve(BasePetService.FONTS_FOLDER);
for (String baseRepoUrl : service.repositoryUrls) {
try {
var url = new URI(baseRepoUrl + '/').normalize();
var indexUrl = url.resolve(INDEX_FILE).toURL();
var index = UpdateIndex.fromString(getUrlText(indexUrl));
var baseDataUrl = url.resolve(index.getDataPath() + '/');

// check plugin
if (BasePetService.VERSION != index.getVersion()) {
PluginPetService.LOGGER.info(
"PetpetPlugin 可更新到最新版本: " + index.getVersion() +
" (当前版本 " + BasePetService.VERSION + ") 养成更新的好习惯哦 (*/ω\*)"
);
}

// update templates
for (String templateKey : index.getDataList()) {
if (service.getDataMap().containsKey(templateKey)
|| service.disabledKey.contains(templateKey)
|| this.templateLengthMap.containsKey(templateKey)) {
continue;
}
var templateKeyPath = templateKey + '/';

// save data.json
saveAs(
baseDataUrl.resolve(templateKeyPath).resolve(TEMPLATE_FILE).toURL(),
targetPath.resolve(templateKeyPath).resolve(TEMPLATE_FILE)
);
int count = 0;
while (true) {
// save backgrounds
var imageName = count + ".png";
var imageUri = baseDataUrl.resolve(templateKeyPath).resolve(imageName);
var imagePath = targetPath.resolve(templateKeyPath).resolve(imageName);
try {
saveAs(imageUri.toURL(), imagePath);
count++;
} catch (MalformedInputException | FileNotFoundException ex) {
break;
}
}
BasePetService.LOGGER.info("Petpet 模板 " + templateKey + " 下载成功 (length:" + count + ')');
this.templateLengthMap.put(templateKey, count);
}

// update fonts
var baseFontUri = baseDataUrl.resolve(BasePetService.FONTS_FOLDER + '/');
for (String fontName : index.getFontList()) {
var fontPath = baseFontPath.resolve(fontName);
if (Files.exists(fontPath)) {
continue;
}
saveAs(baseFontUri.resolve(fontName).toURL(), fontPath);
BasePetService.LOGGER.info("Petpet 字体 " + fontName + " 下载成功");
}
} catch (URISyntaxException | MalformedURLException ex) {
BasePetService.LOGGER.warning("仓库地址解析失败: " + baseRepoUrl, ex);
} catch (IOException ex) {
BasePetService.LOGGER.warning("无法连接到远程仓库: " + baseRepoUrl, ex);
}
}
return true;
}

protected boolean isExcludedKey(String key) {
return service.getDataMap().containsKey(key) || service.disabledKey.contains(key);
}

protected String joinPath(String basePath, String fileName) {
return basePath + '/' + fileName;
return !this.templateLengthMap.isEmpty();
}
}
16 changes: 0 additions & 16 deletions src/main/java/moe/dituon/petpet/plugin/PluginServiceConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,4 @@ enum class ReplyFormat {

enum class DisablePolicy {
NONE, NUDGE, MESSAGE, FULL
}

@Serializable
data class UpdateIndex(
val version: Float = 0f,
val dataPath: String = DataUpdater.DEFAULT_REPO_DATA_PATH,
val dataList: List<String> = emptyList(),
val fontList: List<String> = emptyList(),
var url: String = ""
) {
companion object {
@JvmStatic
fun parse(str: String): UpdateIndex {
return encodeDefaultsIgnoreUnknownKeysJson.decodeFromString(str)
}
}
}
33 changes: 33 additions & 0 deletions src/main/java/moe/dituon/petpet/plugin/UpdateIndex.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package moe.dituon.petpet.plugin

import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import moe.dituon.petpet.share.encodeDefaultsIgnoreUnknownKeysJson

@Serializable
data class UpdateIndex(
val version: Float = 0f,
val dataList: List<String> = emptyList(),
val fontList: List<String> = emptyList(),
val dataPath: String = DataUpdater.DEFAULT_REPO_DATA_PATH,
) {
companion object {
@JvmStatic
fun fromString(str: String): UpdateIndex {
return encodeDefaultsIgnoreUnknownKeysJson.decodeFromString(str)
}
}
}

@Serializable
data class UpdateIndexMap(
val length: Map<String, Int> = emptyMap()
) {
companion object {
@JvmStatic
fun fromString(str: String): UpdateIndexMap {
return encodeDefaultsIgnoreUnknownKeysJson.decodeFromString(str)
}
}
}

4 changes: 2 additions & 2 deletions src/main/java/moe/dituon/petpet/share/BaseLogger.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static BaseLogger getInstance() {
return BaseLoggerInner.INSTANCE;
}

private LoggerInterface logger = new JavaUtilLogger("Petpet");
protected LoggerInterface logger = new JavaUtilLogger("Petpet");
public static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

public void setLogger(LoggerInterface loggerInterface) {
Expand All @@ -43,7 +43,7 @@ public void error(String message, Throwable throwable) {
logger.error(message, throwable);
}

private interface LoggerInterface {
public interface LoggerInterface {
void info(String message);

void warning(String message);
Expand Down
16 changes: 11 additions & 5 deletions src/main/java/moe/dituon/petpet/websocket/gocq/GoCQPetpet.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,18 @@ public boolean removeEldestEntry(Map.Entry eldest) {

if (service.autoUpdate){
new Thread(() -> {
var uploader = new DataUpdater(service, service.dataRoot);
if (uploader.autoUpdate()){
LOGGER.info("Petpet 模板更新完毕, 正在重载");
service.readData();
try {
System.out.println(123);
var uploader = new DataUpdater(service, service.dataRoot);
if (uploader.autoUpdate()){
LOGGER.info("Petpet 模板更新完毕, 正在重载");
service.readData();
}
}catch (Exception e) {
LOGGER.warning(e.toString(), e);
}
});

}).start();
}

try{
Expand Down

0 comments on commit 2e73558

Please sign in to comment.