[Fix #11] 修复在图片缓存失效的情况下, 'getImageToBotCode'依然会尝试从缓存获取图片File对象导致NPE;

[Change] BotCommandProcess 调整字符串拼接形式, 统一错误提示语的格式;
This commit is contained in:
LamGC 2020-06-04 09:29:53 +08:00
parent eb2de09859
commit 69da2b02ac
4 changed files with 125 additions and 57 deletions

View File

@ -110,34 +110,27 @@ public class BotCommandProcess {
@Command(defaultCommand = true) @Command(defaultCommand = true)
public static String help() { public static String help() {
StringBuilder helpStrBuilder = new StringBuilder(); return "CGJ Bot使用指南" + "\n" +
helpStrBuilder.append("CGJ Bot使用指南").append("\n"); "使用方法:.cgj <命令> [参数...]" + "\n" +
helpStrBuilder.append("使用方法:.cgj <命令> [参数...]").append("\n"); "例如查询作品信息功能:" + "\n" +
helpStrBuilder.append("例如查询作品信息功能:").append("\n"); ".cgj info -id 80846159" + "\n" +
helpStrBuilder.append(".cgj info -id 80846159").append("\n"); "目前可用的命令:" + "\n" +
helpStrBuilder.append("目前可用的命令:").append("\n"); "\t" + "ranking - 获取今天或指定日期排行榜的前10名作品" + "\n" +
helpStrBuilder.append("\t").append("ranking - 获取今天或指定日期排行榜的前10名作品").append("\n"); "\t\t" + "-date - 指定查询日期(年-月-日)" + "\n" +
helpStrBuilder.append("\t\t").append("-date - 指定查询日期(年-月-日)").append("\n"); "\t\t" + "-type - 排行榜类型(illust/插画, ugoira/动图, manga/漫画)" + "\n" +
helpStrBuilder.append("\t\t").append("-type - 排行榜类型(illust/插画, ugoira/动图, manga/漫画)").append("\n"); "\t" + "search - 搜索指定关键词并显示前10个作品" + "\n" +
"\t\t" + "-content - 搜索内容" + "\n" +
helpStrBuilder.append("\t").append("search - 搜索指定关键词并显示前10个作品").append("\n"); "\t" + "link - 获取作品的Pixiv页面" + "\n" +
helpStrBuilder.append("\t\t").append("-content - 搜索内容").append("\n"); "\t\t" + "-id - 作品id" + "\n" +
"\t" + "info - 获取Pixiv作品信息" + "\n" +
helpStrBuilder.append("\t").append("link - 获取作品的Pixiv页面").append("\n"); "\t\t" + "-id - 作品id" + "\n" +
helpStrBuilder.append("\t\t").append("-id - 作品id").append("\n"); "\t" + "image - 获取指定作品的图片" + "\n" +
"\t\t" + "-id - 作品id" + "\n" +
helpStrBuilder.append("\t").append("info - 获取Pixiv作品信息").append("\n"); "\t\t" + "-quality - 图片质量(original/原图 regular/预览图)" + "\n" +
helpStrBuilder.append("\t\t").append("-id - 作品id").append("\n"); "\t\t" + "-page - 页数" + "\n" +
"\t" + "report - 报告不当作品" + "\n" +
helpStrBuilder.append("\t").append("image - 获取指定作品的图片").append("\n"); "\t\t" + "-id - 作品Id" + "\n" +
helpStrBuilder.append("\t\t").append("-id - 作品id").append("\n"); "\t\t" + "-msg - 报告原因" + "\n";
helpStrBuilder.append("\t\t").append("-quality - 图片质量(original/原图 regular/预览图)").append("\n");
helpStrBuilder.append("\t\t").append("-page - 页数").append("\n");
helpStrBuilder.append("\t").append("report - 报告不当作品").append("\n");
helpStrBuilder.append("\t\t").append("-id - 作品Id").append("\n");
helpStrBuilder.append("\t\t").append("-msg - 报告原因").append("\n");
return helpStrBuilder.toString();
} }
/** /**
@ -158,25 +151,24 @@ public class BotCommandProcess {
} }
JsonObject illustPreLoadData = getIllustPreLoadData(illustId, false); JsonObject illustPreLoadData = getIllustPreLoadData(illustId, false);
StringBuilder builder = new StringBuilder("色图姬帮你了解了这个作品的信息!\n"); // Java 6 开始, 编译器会将用'+'进行的字符串拼接将自动转换成StringBuilder拼接
builder.append("---------------- 作品信息 ----------------"); return "色图姬帮你了解了这个作品的信息!\n" + "---------------- 作品信息 ----------------" +
builder.append("\n作品Id: ").append(illustId); "\n作品Id: " + illustId +
builder.append("\n作品标题").append(illustPreLoadData.get("illustTitle").getAsString()); "\n作品标题" + illustPreLoadData.get("illustTitle").getAsString() +
builder.append("\n作者(作者Id)").append(illustPreLoadData.get("userName").getAsString()) "\n作者(作者Id)" + illustPreLoadData.get("userName").getAsString() +
.append("(").append(illustPreLoadData.get("userId").getAsInt()).append(")"); "(" + illustPreLoadData.get("userId").getAsInt() + ")" +
builder.append("\n点赞数").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.LIKE.attrName).getAsInt()); "\n点赞数" + illustPreLoadData.get(PreLoadDataComparator.Attribute.LIKE.attrName).getAsInt() +
builder.append("\n收藏数").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.BOOKMARK.attrName).getAsInt()); "\n收藏数" + illustPreLoadData.get(PreLoadDataComparator.Attribute.BOOKMARK.attrName).getAsInt() +
builder.append("\n围观数").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.VIEW.attrName).getAsInt()); "\n围观数" + illustPreLoadData.get(PreLoadDataComparator.Attribute.VIEW.attrName).getAsInt() +
builder.append("\n评论数").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.COMMENT.attrName).getAsInt()); "\n评论数" + illustPreLoadData.get(PreLoadDataComparator.Attribute.COMMENT.attrName).getAsInt() +
builder.append("\n页数").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.PAGE.attrName).getAsInt()).append(""); "\n页数" + illustPreLoadData.get(PreLoadDataComparator.Attribute.PAGE.attrName).getAsInt() + "" +
builder.append("\n作品链接").append(artworksLink(fromGroup, illustId)).append("\n"); "\n作品链接" + artworksLink(fromGroup, illustId) + "\n" +
builder.append("---------------- 作品图片 ----------------\n"); "---------------- 作品图片 ----------------\n" +
builder.append(getImageById(fromGroup, illustId, PixivDownload.PageQuality.REGULAR, 1)).append("\n"); getImageById(fromGroup, illustId, PageQuality.REGULAR, 1) + "\n" +
builder.append("使用 \".cgj image -id ") "使用 \".cgj image -id " +
.append(illustId) illustId +
.append("\" 获取原图。\n如有不当作品可使用\".cgj report -id ") "\" 获取原图。\n如有不当作品可使用\".cgj report -id " +
.append(illustId).append("\"向色图姬反馈。"); illustId + "\"向色图姬反馈。";
return builder.toString();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -583,7 +575,7 @@ public class BotCommandProcess {
} }
} catch (IOException e) { } catch (IOException e) {
log.warn("作品信息无法获取!", e); log.warn("作品信息无法获取!", e);
return "发生网络异常,无法获取图片!"; return "发生网络异常,无法获取图片!";
} }
List<String> pagesList; List<String> pagesList;
@ -631,10 +623,16 @@ public class BotCommandProcess {
} }
try { try {
ImageCacheStore.executeCacheRequest(new ImageCacheObject(imageCache, illustId, downloadLink, imageFile)); Throwable throwable = ImageCacheStore.executeCacheRequest(new ImageCacheObject(imageCache, illustId, downloadLink, imageFile));
if(throwable != null) {
throw throwable;
}
} catch (InterruptedException e) { } catch (InterruptedException e) {
log.warn("图片缓存被中断", e); log.warn("图片缓存被中断", e);
return "(错误:图片获取超时)"; return "(错误:图片获取超时)";
} catch (Throwable e) {
log.error("图片 {} 获取失败:\n{}", illustId + "p" + pageIndex, Throwables.getStackTraceAsString(e));
return "(错误: 图片获取出错)";
} }
} else { } else {
log.debug("图片 {} 缓存命中.", fileName); log.debug("图片 {} 缓存命中.", fileName);
@ -650,7 +648,7 @@ public class BotCommandProcess {
* @return 返回设定好参数的BotCode * @return 返回设定好参数的BotCode
*/ */
private static BotCode getImageToBotCode(File targetFile, boolean updateCache) { private static BotCode getImageToBotCode(File targetFile, boolean updateCache) {
String fileName = targetFile.getName(); String fileName = Objects.requireNonNull(targetFile, "targetFile is null").getName();
BotCode code = BotCode.parse(CQCode.image(getImageStoreDir().getName() + "/" + fileName)); BotCode code = BotCode.parse(CQCode.image(getImageStoreDir().getName() + "/" + fileName));
code.addParameter("absolutePath", targetFile.getAbsolutePath()); code.addParameter("absolutePath", targetFile.getAbsolutePath());
code.addParameter("imageName", fileName.substring(0, fileName.lastIndexOf("."))); code.addParameter("imageName", fileName.substring(0, fileName.lastIndexOf(".")));

View File

@ -1,6 +1,7 @@
package net.lamgc.cgj.bot.cache; package net.lamgc.cgj.bot.cache;
import net.lamgc.cgj.Main; import net.lamgc.cgj.Main;
import net.lamgc.cgj.bot.cache.exception.HttpRequestException;
import net.lamgc.cgj.pixiv.PixivURL; import net.lamgc.cgj.pixiv.PixivURL;
import net.lamgc.cgj.util.URLs; import net.lamgc.cgj.util.URLs;
import net.lamgc.utils.event.EventHandler; import net.lamgc.utils.event.EventHandler;
@ -60,8 +61,9 @@ public class ImageCacheHandler implements EventHandler {
throw e; throw e;
} }
if(response.getStatusLine().getStatusCode() != 200) { if(response.getStatusLine().getStatusCode() != 200) {
log.warn("Http请求异常{}", response.getStatusLine()); HttpRequestException requestException = new HttpRequestException(response);
throw new IOException("Http Response Error: " + response.getStatusLine()); log.warn("Http请求异常{}", requestException.getStatusLine());
throw requestException;
} }
log.debug("正在下载...(Content-Length: {}KB)", response.getEntity().getContentLength() / 1024); log.debug("正在下载...(Content-Length: {}KB)", response.getEntity().getContentLength() / 1024);

View File

@ -1,9 +1,14 @@
package net.lamgc.cgj.bot.cache; package net.lamgc.cgj.bot.cache;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import net.lamgc.cgj.bot.cache.exception.HttpRequestException;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.Map;
import java.util.concurrent.*; import java.util.concurrent.*;
@ -34,29 +39,30 @@ public final class ImageCacheStore {
* 传递图片缓存任务, 并等待缓存完成. * 传递图片缓存任务, 并等待缓存完成.
* @param cacheObject 缓存任务组 * @param cacheObject 缓存任务组
*/ */
public static void executeCacheRequest(ImageCacheObject cacheObject) throws InterruptedException { public static Throwable executeCacheRequest(ImageCacheObject cacheObject) throws InterruptedException {
Task task = getTaskState(cacheObject); Task task = getTaskState(cacheObject);
if(task.taskState.get() == TaskState.COMPLETE) { if(task.taskState.get() == TaskState.COMPLETE) {
return; return null;
} }
boolean locked = false; boolean locked = false;
try { try {
if(task.taskState.get() == TaskState.COMPLETE) { if(task.taskState.get() == TaskState.COMPLETE) {
return; return null;
} }
task.lock.lock(); task.lock.lock();
locked = true; locked = true;
// 双重检查 // 双重检查
if(task.taskState.get() == TaskState.COMPLETE) { if(task.taskState.get() == TaskState.COMPLETE) {
return; return null;
} }
// 置任务状态 // 置任务状态
task.taskState.set(TaskState.RUNNING); task.taskState.set(TaskState.RUNNING);
Throwable throwable = null;
try { try {
Throwable throwable = imageCacheExecutor.submit(() -> { throwable = imageCacheExecutor.submit(() -> {
try { try {
handler.getImageToCache(cacheObject); handler.getImageToCache(cacheObject);
} catch (Throwable e) { } catch (Throwable e) {
@ -73,6 +79,7 @@ public final class ImageCacheStore {
} catch (ExecutionException e) { } catch (ExecutionException e) {
log.error("执行图片缓存任务时发生异常", e); log.error("执行图片缓存任务时发生异常", e);
} }
return throwable;
} finally { } finally {
if(locked) { if(locked) {
task.lock.unlock(); task.lock.unlock();
@ -88,6 +95,23 @@ public final class ImageCacheStore {
return cacheMap.get(cacheObject); return cacheMap.get(cacheObject);
} }
/**
* 获取错误信息
*/
public static String getErrorMessageFromThrowable(Throwable throwable, boolean onlyRequestException) {
if(throwable == null) {
return "";
} else if(!(throwable instanceof HttpRequestException)) {
if(onlyRequestException) {
return "";
}
return throwable.getMessage();
}
JsonObject result = new Gson()
.fromJson(((HttpRequestException) throwable).getContent(), JsonObject.class);
return result.get("msg").getAsString();
}
/** /**
* 任务状态 * 任务状态
*/ */

View File

@ -0,0 +1,44 @@
package net.lamgc.cgj.bot.cache.exception;
import java.io.IOException;
import java.util.Objects;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.util.EntityUtils;
public class HttpRequestException extends IOException {
private static final long serialVersionUID = -2229221075943552798L;
private final StatusLine statusLine;
private final String content;
public HttpRequestException(HttpResponse response) throws IOException {
this(response.getStatusLine(), EntityUtils.toString(response.getEntity()));
}
public HttpRequestException(StatusLine statusLine, String content) {
super("Http Response Error: " + Objects.requireNonNull(statusLine, "statusLine is null") +
", Response Content: " + (content == null ? "null" : '\'' + content + '\''));
this.statusLine = statusLine;
this.content = content;
}
/**
* 获取Http状态行
*/
public StatusLine getStatusLine() {
return statusLine;
}
/**
* 获取Response内容
* @return 如果没有返回, 则返回null
*/
public String getContent() {
return content;
}
}