mirror of
https://github.com/LamGC/ContentGrabbingJi.git
synced 2025-07-01 21:07:25 +00:00
Compare commits
49 Commits
Author | SHA1 | Date | |
---|---|---|---|
10cad32efa | |||
22a113ef38 | |||
6824b12e8f | |||
1c556c5b94 | |||
dbc9f4c90b | |||
d3c1975722 | |||
c09b750fe6 | |||
ca479ef1af | |||
c2c49d2355 | |||
22e74e8cd5 | |||
d549c5674d | |||
c6952de84c | |||
410d6c0828 | |||
aaa1bc932b | |||
a1e54e70d9 | |||
16522155e1 | |||
5f796f7da0 | |||
5a52dd9208 | |||
b53aafa81b | |||
28aa086f15 | |||
0eadefa74f | |||
4afa414725 | |||
0f202cb076 | |||
1f3d99ac10 | |||
d33f4028d1 | |||
4020bbfea8 | |||
19605a9401 | |||
21466a49f9 | |||
73ae9a268b | |||
620c3785ad | |||
2df5513727 | |||
8798633c2c | |||
9dfc20a525 | |||
dd88f2acab | |||
36460e4c34 | |||
084be3970a | |||
11005b0f6c | |||
970be847a0 | |||
09fa1bd2e8 | |||
ae27141fea | |||
b328def8f9 | |||
dbfed874c0 | |||
f02b0e9e98 | |||
260cfe3dd0 | |||
c1427379c6 | |||
f844d150e8 | |||
2ec696b3cd | |||
c8c6dbe4fd | |||
4dff477ded |
38
pom.xml
38
pom.xml
@ -4,9 +4,9 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>CGJ_2</artifactId>
|
||||
<version>2.1.0</version>
|
||||
<groupId>net.lamgc</groupId>
|
||||
<artifactId>ContentGrabbingJi</artifactId>
|
||||
<version>2.3.0</version>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
@ -19,7 +19,7 @@
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
|
||||
<mirai.CoreVersion>0.32.0</mirai.CoreVersion>
|
||||
<mirai.CoreVersion>0.39.4</mirai.CoreVersion>
|
||||
<mirai.JaptVersion>1.1.1</mirai.JaptVersion>
|
||||
<kotlin.version>1.3.71</kotlin.version>
|
||||
<ktor.version>1.3.2</ktor.version>
|
||||
@ -30,14 +30,26 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<configuration>
|
||||
<source>8</source>
|
||||
<target>8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>3.0.2</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>log4j2-test.xml</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>2.2.6.RELEASE</version>
|
||||
<configuration>
|
||||
<mainClass>net.lamgc.cgj.Main</mainClass>
|
||||
<classifier>exec</classifier>
|
||||
@ -50,20 +62,6 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>process-sources</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@ -153,12 +151,12 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-jvm</artifactId>
|
||||
<artifactId>mirai-core</artifactId>
|
||||
<version>${mirai.CoreVersion}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-qqandroid-jvm</artifactId>
|
||||
<artifactId>mirai-core-qqandroid</artifactId>
|
||||
<version>${mirai.CoreVersion}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -7,8 +7,8 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.lamgc.cgj.bot.CQConfig;
|
||||
import net.lamgc.cgj.bot.MiraiMain;
|
||||
import net.lamgc.cgj.bot.framework.coolq.CQConfig;
|
||||
import net.lamgc.cgj.bot.framework.mirai.MiraiMain;
|
||||
import net.lamgc.cgj.pixiv.*;
|
||||
import net.lamgc.cgj.proxy.PixivAccessProxyServer;
|
||||
import net.lamgc.plps.PixivLoginProxyServer;
|
||||
|
32
src/main/java/net/lamgc/cgj/bot/AutoSender.java
Normal file
32
src/main/java/net/lamgc/cgj/bot/AutoSender.java
Normal file
@ -0,0 +1,32 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 自动发送器
|
||||
*/
|
||||
public abstract class AutoSender {
|
||||
|
||||
private MessageSender messageSender;
|
||||
|
||||
/**
|
||||
* 构造一个自动发送器
|
||||
* @param messageSender 自动发送器所使用的消息发送器
|
||||
*/
|
||||
public AutoSender(MessageSender messageSender) {
|
||||
this.messageSender = Objects.requireNonNull(messageSender);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设置等等消息发送器
|
||||
* @return 消息发送器
|
||||
*/
|
||||
MessageSender getMessageSender() {
|
||||
return this.messageSender;
|
||||
}
|
||||
|
||||
public abstract void send();
|
||||
|
||||
}
|
@ -1,29 +1,45 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import net.lamgc.cgj.bot.message.MessageSenderBuilder;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.lamgc.cgj.pixiv.PixivDownload;
|
||||
import net.lamgc.cgj.pixiv.PixivURL;
|
||||
import net.lamgc.utils.base.runner.Argument;
|
||||
import net.lamgc.utils.base.runner.Command;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class BotAdminCommandProcess {
|
||||
|
||||
private final static Logger log = LoggerFactory.getLogger(BotAdminCommandProcess.class.getSimpleName());
|
||||
|
||||
private final static File globalPropFile = new File("./global.properties");
|
||||
private final static File globalPropFile = new File("global.properties");
|
||||
|
||||
private final static File pushListFile = new File("pushList.json");
|
||||
|
||||
private final static Hashtable<Long, JsonObject> pushInfoMap = new Hashtable<>();
|
||||
|
||||
private final static Gson gson = new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create();
|
||||
|
||||
@Command
|
||||
public String clearCache() {
|
||||
public static String cleanCache() {
|
||||
BotCommandProcess.clearCache();
|
||||
return "操作已完成.";
|
||||
}
|
||||
|
||||
@Command
|
||||
public String setGlobalProperty(@Argument(name = "key") String key, @Argument(name = "value") String value, @Argument(name = "save", force = false) boolean saveNow) {
|
||||
public static String setGlobalProperty(@Argument(name = "key") String key, @Argument(name = "value") String value, @Argument(name = "save", force = false) boolean saveNow) {
|
||||
String lastValue = BotCommandProcess.globalProp.getProperty(key);
|
||||
BotCommandProcess.globalProp.setProperty(key, Strings.nullToEmpty(value));
|
||||
if(saveNow) {
|
||||
@ -33,12 +49,12 @@ public class BotAdminCommandProcess {
|
||||
}
|
||||
|
||||
@Command
|
||||
public String getGlobalProperty(@Argument(name = "key") String key) {
|
||||
public static String getGlobalProperty(@Argument(name = "key") String key) {
|
||||
return "全局配置项 " + key + " 当前值: " + BotCommandProcess.globalProp.getProperty(key, "(Empty)");
|
||||
}
|
||||
|
||||
@Command
|
||||
public String saveGlobalProperties() {
|
||||
public static String saveGlobalProperties() {
|
||||
log.info("正在保存全局配置文件...");
|
||||
|
||||
try {
|
||||
@ -58,7 +74,7 @@ public class BotAdminCommandProcess {
|
||||
}
|
||||
|
||||
@Command
|
||||
public String loadGlobalProperties(@Argument(name = "reload", force = false) boolean reload) {
|
||||
public static String loadGlobalProperties(@Argument(name = "reload", force = false) boolean reload) {
|
||||
Properties cache = new Properties();
|
||||
if(!globalPropFile.exists()) {
|
||||
return "未找到全局配置文件, 无法重载";
|
||||
@ -79,7 +95,7 @@ public class BotAdminCommandProcess {
|
||||
}
|
||||
|
||||
@Command
|
||||
public String runUpdateTask(@Argument(force = false, name = "date") Date queryTime) {
|
||||
public static String runUpdateTask(@Argument(force = false, name = "date") Date queryTime) {
|
||||
try {
|
||||
BotCommandProcess.runUpdateTimer(queryTime);
|
||||
} catch (Exception e) {
|
||||
@ -89,4 +105,227 @@ public class BotAdminCommandProcess {
|
||||
return "操作已完成.";
|
||||
}
|
||||
|
||||
private final static String RANKING_SETTING_TIME_MIN = "time.min";
|
||||
private final static String RANKING_SETTING_TIME_FLOAT = "time.float";
|
||||
private final static String RANKING_SETTING_RANKING_START = "ranking.start";
|
||||
private final static String RANKING_SETTING_RANKING_END = "ranking.end";
|
||||
private final static String RANKING_SETTING_RANKING_MODE = "ranking.mode";
|
||||
private final static String RANKING_SETTING_RANKING_CONTENT_TYPE = "ranking.contentType";
|
||||
private final static String RANKING_SETTING_PAGE_QUALITY = "page.quality";
|
||||
|
||||
@Command
|
||||
public static String addPushGroup(
|
||||
@Argument(name = "$fromGroup") long fromGroup,
|
||||
@Argument(name = "group", force = false, defaultValue = "0") long groupId,
|
||||
@Argument(name = "minTime", force = false, defaultValue = "21600000") long minTime,
|
||||
@Argument(name = "floatTime", force = false, defaultValue = "10800000") int floatTime,
|
||||
@Argument(name = "rankingStart", force = false, defaultValue = "1") int rankingStart,
|
||||
@Argument(name = "rankingStop", force = false, defaultValue = "150") int rankingStop,
|
||||
@Argument(name = "mode", force = false, defaultValue = "DAILY") String rankingMode,
|
||||
@Argument(name = "type", force = false, defaultValue = "ILLUST") String rankingContentType,
|
||||
@Argument(name = "original", force = false, defaultValue = "false") boolean original
|
||||
) {
|
||||
long group = groupId <= 0 ? fromGroup : groupId;
|
||||
JsonObject setting = new JsonObject();
|
||||
setting.addProperty(RANKING_SETTING_TIME_MIN, minTime);
|
||||
setting.addProperty(RANKING_SETTING_TIME_FLOAT, floatTime);
|
||||
setting.addProperty(RANKING_SETTING_RANKING_START, rankingStart);
|
||||
setting.addProperty(RANKING_SETTING_RANKING_END, rankingStop);
|
||||
setting.addProperty(RANKING_SETTING_RANKING_MODE, rankingMode);
|
||||
setting.addProperty(RANKING_SETTING_RANKING_CONTENT_TYPE, rankingContentType);
|
||||
setting.addProperty(RANKING_SETTING_PAGE_QUALITY, original ?
|
||||
PixivDownload.PageQuality.ORIGINAL.name() :
|
||||
PixivDownload.PageQuality.REGULAR.name());
|
||||
if(pushInfoMap.containsKey(group)) {
|
||||
log.info("群 {} 已存在Timer, 删除Timer...", group);
|
||||
removePushGroup(fromGroup, groupId);
|
||||
}
|
||||
|
||||
log.info("正在增加Timer...(Setting: {})", setting);
|
||||
pushInfoMap.put(group, setting);
|
||||
addPushTimer(group, setting);
|
||||
return "已在 " + group + " 开启定时推送功能。";
|
||||
}
|
||||
|
||||
/**
|
||||
* 重载推送列表
|
||||
*/
|
||||
@Command
|
||||
public static String loadPushList() {
|
||||
pushInfoMap.clear();
|
||||
if(!pushListFile.exists()) {
|
||||
log.warn("推送列表文件不存在, 跳过加载.");
|
||||
return "文件不存在, 跳过加载.";
|
||||
}
|
||||
|
||||
try (Reader reader = new BufferedReader(new FileReader(pushListFile))) {
|
||||
pushInfoMap.putAll(gson.fromJson(reader, new TypeToken<Map<Long, JsonObject>>(){}.getType()));
|
||||
loadAllPushTimer(false);
|
||||
return "列表重载完成";
|
||||
} catch (IOException e) {
|
||||
log.error("重载推送列表时发生错误", e);
|
||||
return "加载时发生异常";
|
||||
}
|
||||
}
|
||||
|
||||
@Command
|
||||
public static String savePushList() {
|
||||
try {
|
||||
if(!pushListFile.exists()) {
|
||||
pushListFile.createNewFile();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("PushList.json文件创建失败", e);
|
||||
return "保存失败!请检查控制台信息.";
|
||||
}
|
||||
|
||||
try (Writer writer = new FileWriter(pushListFile)) {
|
||||
writer.write(gson.toJson(pushInfoMap));
|
||||
return "保存成功.";
|
||||
} catch (IOException e) {
|
||||
log.error("写入PushList.json文件失败!", e);
|
||||
return "保存失败!请检查控制台信息.";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载所有推送Timer
|
||||
* @param flush 是否完全重载, 如为true则加载前会删除所有已加载的Timer
|
||||
*/
|
||||
public static void loadAllPushTimer(boolean flush) {
|
||||
if(flush) {
|
||||
RandomIntervalSendTimer.timerIdSet().forEach(id -> RandomIntervalSendTimer.getTimerById(id).destroy());
|
||||
} else {
|
||||
cleanPushTimer();
|
||||
}
|
||||
pushInfoMap.forEach(BotAdminCommandProcess::addPushTimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据设置增加Timer
|
||||
* @param id 群组id
|
||||
* @param setting jsonObject设置集
|
||||
*/
|
||||
private static void addPushTimer(long id, JsonObject setting) {
|
||||
try {
|
||||
RandomIntervalSendTimer.getTimerById(id);
|
||||
return;
|
||||
} catch(NoSuchElementException ignored) {
|
||||
}
|
||||
|
||||
int rankingStart = setting.has(RANKING_SETTING_RANKING_START) ? setting.get(RANKING_SETTING_RANKING_START).getAsInt() : 1;
|
||||
int rankingEnd = setting.has(RANKING_SETTING_RANKING_END) ? setting.get(RANKING_SETTING_RANKING_END).getAsInt() : 150;
|
||||
PixivURL.RankingMode rankingMode = PixivURL.RankingMode.MODE_DAILY;
|
||||
PixivURL.RankingContentType rankingContentType = PixivURL.RankingContentType.TYPE_ILLUST;
|
||||
PixivDownload.PageQuality pageQuality = PixivDownload.PageQuality.REGULAR;
|
||||
|
||||
if(rankingStart <= 0 || rankingStart > 500) {
|
||||
log.warn("群组 [{}] - 无效的RankingStart设定值, 将重置为默认设定值(1): {}", id, rankingStart);
|
||||
rankingStart = 1;
|
||||
} else if(rankingEnd > 500 || rankingEnd <= 0) {
|
||||
log.warn("群组 [{}] - 无效的RankingEnd设定值, 将重置为默认设定值(150): {}", id, rankingEnd);
|
||||
rankingEnd = 150;
|
||||
} else if(rankingStart > rankingEnd) {
|
||||
log.warn("群组 [{}] - 无效的排行榜选取范围, 将重置为默认设定值(1 ~ 150): start={}, end={}", id, rankingStart, rankingEnd);
|
||||
rankingStart = 1;
|
||||
rankingEnd = 150;
|
||||
}
|
||||
|
||||
if(setting.has(RANKING_SETTING_RANKING_MODE)) {
|
||||
String value = setting.get(RANKING_SETTING_RANKING_MODE).getAsString().trim().toUpperCase();
|
||||
try {
|
||||
rankingMode = PixivURL.RankingMode.valueOf(value.startsWith("MODE_") ? value : "MODE_" + value);
|
||||
} catch(IllegalArgumentException e) {
|
||||
log.warn("群组ID [{}] - 无效的RankingMode设定值, 将重置为默认值: {}", id, value);
|
||||
}
|
||||
}
|
||||
if(setting.has(RANKING_SETTING_RANKING_CONTENT_TYPE)) {
|
||||
String value = setting.get(RANKING_SETTING_RANKING_CONTENT_TYPE).getAsString().trim().toUpperCase();
|
||||
try {
|
||||
rankingContentType = PixivURL.RankingContentType.valueOf(value.startsWith("TYPE_") ? value : "TYPE_" + value);
|
||||
} catch(IllegalArgumentException e) {
|
||||
log.warn("群组ID [{}] - 无效的RankingContentType设定值: {}", id, value);
|
||||
}
|
||||
}
|
||||
|
||||
if(setting.has(RANKING_SETTING_PAGE_QUALITY)) {
|
||||
String value = setting.get(RANKING_SETTING_PAGE_QUALITY).getAsString().trim().toUpperCase();
|
||||
try {
|
||||
pageQuality = PixivDownload.PageQuality.valueOf(value);
|
||||
} catch(IllegalArgumentException e) {
|
||||
log.warn("群组ID [{}] - 无效的PageQuality设定值: {}", id, value);
|
||||
}
|
||||
}
|
||||
|
||||
AutoSender sender = new RandomRankingArtworksSender(
|
||||
MessageSenderBuilder.getMessageSender(MessageSource.Group, id),
|
||||
rankingStart,
|
||||
rankingEnd,
|
||||
rankingMode, rankingContentType,
|
||||
pageQuality
|
||||
);
|
||||
|
||||
RandomIntervalSendTimer timer = RandomIntervalSendTimer.createTimer(
|
||||
id,
|
||||
sender,
|
||||
setting.get("time.min").getAsLong(),
|
||||
setting.get("time.float").getAsInt(),
|
||||
true, true);
|
||||
log.info("群组 {} 已创建对应Timer: {}", id, Integer.toHexString(timer.hashCode()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除一个推送定时器
|
||||
* @param id 群号
|
||||
* @throws NoSuchElementException 当这个群号没有定时器的时候抛出异常
|
||||
*/
|
||||
@Command
|
||||
public static String removePushGroup(@Argument(name = "$fromGroup") long fromGroup, @Argument(name = "group", force = false) long id) {
|
||||
long group = id <= 0 ? fromGroup : id;
|
||||
RandomIntervalSendTimer.getTimerById(group).destroy();
|
||||
pushInfoMap.remove(group);
|
||||
return "已关闭群 " + group + " 的美图推送功能。";
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据已修改的pushInfoMap将已经被删除的Timer取消
|
||||
*/
|
||||
private static void cleanPushTimer() {
|
||||
RandomIntervalSendTimer.timerIdSet().forEach(id -> {
|
||||
if(!pushInfoMap.containsKey(id)) {
|
||||
RandomIntervalSendTimer.getTimerById(id).destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Command
|
||||
public static String getReportList() {
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH:mm:ss");
|
||||
Set<String> keys = BotCommandProcess.reportStore.keys();
|
||||
StringBuilder msgBuilder = new StringBuilder();
|
||||
msgBuilder.append("当前被报告的作品列表:\n");
|
||||
int count = 1;
|
||||
for(String key : keys) {
|
||||
String illustIdStr = key.substring(key.indexOf(".") + 1);
|
||||
JsonObject report = BotCommandProcess.reportStore.getCache(illustIdStr).getAsJsonObject();
|
||||
log.debug("{} - Report: {}", illustIdStr, report);
|
||||
String reason = report.get("reason").isJsonNull() ? "" : report.get("reason").getAsString();
|
||||
msgBuilder.append(count).append(". 作品Id: ").append(illustIdStr)
|
||||
.append("(").append(dateFormat.format(new Date(report.get("reportTime").getAsLong()))).append("):\n")
|
||||
.append("报告者QQ:").append(report.get("fromQQ").getAsLong()).append("\n")
|
||||
.append("报告所在群:").append(report.get("fromGroup").getAsLong()).append("\n")
|
||||
.append("报告原因:\n").append(reason).append("\n");
|
||||
}
|
||||
return msgBuilder.toString();
|
||||
}
|
||||
|
||||
@Command
|
||||
public static String unBanArtwork(@Argument(name = "id") int illustId) {
|
||||
if(illustId <= 0) {
|
||||
return "无效的作品id!";
|
||||
}
|
||||
boolean removeResult = BotCommandProcess.reportStore.remove(String.valueOf(illustId));
|
||||
return removeResult ? "作品已解封!" : "解封失败!可能该作品并未被封禁。";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -54,6 +54,14 @@ public class BotCode {
|
||||
return new BotCode(keys[0], keys[1], param);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取BotCode所使用的匹配正则表达式
|
||||
* @return 用于匹配BotCode的正则表达式对象
|
||||
*/
|
||||
public static Pattern getCodePattern() {
|
||||
return Pattern.compile(codePattern.pattern());
|
||||
}
|
||||
|
||||
private String platformName;
|
||||
private String functionName;
|
||||
private Hashtable<String, String> parameter = new Hashtable<>();
|
||||
|
@ -2,7 +2,6 @@ package net.lamgc.cgj.bot;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.base.Throwables;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import com.google.gson.*;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
@ -55,17 +54,8 @@ public class BotCommandProcess {
|
||||
new LocalHashCacheStore<>(), 3600000, 900000);
|
||||
private final static CacheStore<JsonElement> searchBodyCache = new JsonRedisCacheStore(BotEventHandler.redisServer, "searchBody", gson);
|
||||
private final static CacheStore<List<JsonObject>> rankingCache = new JsonObjectRedisListCacheStore(BotEventHandler.redisServer, "ranking", gson);
|
||||
private final static CacheStore<List<String>> pagesCache = new RedisPoolCacheStore<List<String>>(BotEventHandler.redisServer, "imagePages") {
|
||||
@Override
|
||||
protected String parse(List<String> dataObj) {
|
||||
return gson.toJson(dataObj);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> analysis(String dataStr) {
|
||||
return gson.fromJson(dataStr, new TypeToken<List<String>>(){}.getType());
|
||||
}
|
||||
};
|
||||
private final static CacheStore<List<String>> pagesCache = new StringListRedisCacheStore(BotEventHandler.redisServer, "imagePages");
|
||||
public final static CacheStore<JsonElement> reportStore = new JsonRedisCacheStore(BotEventHandler.redisServer, "report", gson);
|
||||
|
||||
/**
|
||||
* 图片异步缓存执行器
|
||||
@ -123,16 +113,69 @@ public class BotCommandProcess {
|
||||
public static String help() {
|
||||
StringBuilder helpStrBuilder = new StringBuilder();
|
||||
helpStrBuilder.append("CGJ Bot使用指南").append("\n");
|
||||
helpStrBuilder.append("使用方法:.cgj <命令> [参数...]").append("\n");
|
||||
helpStrBuilder.append("例如查询作品信息功能:").append("\n");
|
||||
helpStrBuilder.append(".cgj artwork -id 80846159").append("\n");
|
||||
helpStrBuilder.append("目前可用的命令:").append("\n");
|
||||
helpStrBuilder.append("\t").append("ranking - 获取今天或指定日期排行榜的前10名作品").append("\n");
|
||||
helpStrBuilder.append("\t\t").append("-date - 指定查询日期(年-月-日)").append("\n");
|
||||
helpStrBuilder.append("\t\t").append("-type - 排行榜类型(illust/插画, ugoira/动图, manga/漫画)").append("\n");
|
||||
|
||||
helpStrBuilder.append("\t").append("search - 搜索指定关键词并显示前10个作品").append("\n");
|
||||
helpStrBuilder.append("\t\t").append("-content - 搜索内容").append("\n");
|
||||
helpStrBuilder.append("\t").append("artworks - 获取作品的Pixiv页面").append("\n");
|
||||
|
||||
helpStrBuilder.append("\t").append("link - 获取作品的Pixiv页面").append("\n");
|
||||
helpStrBuilder.append("\t\t").append("-id - 作品id").append("\n");
|
||||
|
||||
helpStrBuilder.append("\t").append("info - 获取Pixiv作品信息").append("\n");
|
||||
helpStrBuilder.append("\t\t").append("-id - 作品id").append("\n");
|
||||
|
||||
helpStrBuilder.append("\t").append("image - 获取指定作品的图片").append("\n");
|
||||
helpStrBuilder.append("\t\t").append("-id - 作品id").append("\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();
|
||||
}
|
||||
|
||||
@Command(commandName = "info")
|
||||
public static String artworkInfo(@Argument(name = "id") int illustId) {
|
||||
if(illustId <= 0) {
|
||||
return "错误的作品id!";
|
||||
}
|
||||
|
||||
try {
|
||||
if(isNoSafe(illustId, globalProp, false) || isReported(illustId)) {
|
||||
return "阅览禁止:该作品已被封印!!";
|
||||
}
|
||||
|
||||
JsonObject illustPreLoadData = getIllustPreLoadData(illustId, false);
|
||||
StringBuilder builder = new StringBuilder("---------------- 作品信息 ----------------\n");
|
||||
builder.append("作品Id: ").append(illustId).append("\n");
|
||||
builder.append("作品标题:").append(illustPreLoadData.get("illustTitle").getAsString()).append("\n");
|
||||
builder.append("作者(作者Id):").append(illustPreLoadData.get("userName").getAsString())
|
||||
.append("(").append(illustPreLoadData.get("userId").getAsInt()).append(")\n");
|
||||
builder.append("点赞数:").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.LIKE.attrName).getAsInt()).append("\n");
|
||||
builder.append("收藏数:").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.BOOKMARK.attrName).getAsInt()).append("\n");
|
||||
builder.append("围观数:").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.VIEW.attrName).getAsInt()).append("\n");
|
||||
builder.append("评论数:").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.COMMENT.attrName).getAsInt()).append("\n");
|
||||
builder.append("页数:").append(illustPreLoadData.get(PreLoadDataComparator.Attribute.PAGE.attrName).getAsInt()).append("页\n");
|
||||
builder.append("---------------- 作品图片 ----------------\n");
|
||||
builder.append(getImageById(illustId, PixivDownload.PageQuality.REGULAR, 1)).append("\n");
|
||||
builder.append("使用 \".cgj image -id ")
|
||||
.append(illustId)
|
||||
.append("\" 获取原图。\n如有不当作品,可使用\".cgj report -id ")
|
||||
.append(illustId).append("\"向色图姬反馈。");
|
||||
return builder.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "尚未支持";
|
||||
}
|
||||
|
||||
@Command
|
||||
public static String ranking(
|
||||
@Argument(force = false, name = "date") Date queryTime,
|
||||
@ -158,18 +201,22 @@ public class BotCommandProcess {
|
||||
}
|
||||
}
|
||||
|
||||
PixivURL.RankingMode mode = PixivURL.RankingMode.MODE_DAILY;
|
||||
PixivURL.RankingMode mode;
|
||||
try {
|
||||
mode = PixivURL.RankingMode.valueOf("MODE_" + contentMode.toUpperCase());
|
||||
String rankingModeValue = contentMode.toUpperCase();
|
||||
mode = PixivURL.RankingMode.valueOf(rankingModeValue.startsWith("MODE_") ? rankingModeValue : "MODE_" + rankingModeValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("无效的RankingMode值: {}", contentMode);
|
||||
return "参数无效, 请查看帮助信息";
|
||||
}
|
||||
|
||||
PixivURL.RankingContentType type = PixivURL.RankingContentType.TYPE_ILLUST;
|
||||
PixivURL.RankingContentType type;
|
||||
try {
|
||||
type = PixivURL.RankingContentType.valueOf("TYPE_" + contentType.toUpperCase());
|
||||
String contentTypeValue = contentType.toUpperCase();
|
||||
type = PixivURL.RankingContentType.valueOf(contentTypeValue.startsWith("TYPE_") ? contentTypeValue : "TYPE_" + contentTypeValue);
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("无效的RankingContentType值: {}", contentType);
|
||||
return "参数无效, 请查看帮助信息";
|
||||
}
|
||||
|
||||
if(!type.isSupportedMode(mode)) {
|
||||
@ -221,7 +268,7 @@ public class BotCommandProcess {
|
||||
log.error("消息处理异常", e);
|
||||
return "排名榜获取失败!详情请查看机器人控制台。";
|
||||
}
|
||||
return resultBuilder.append("如查询当前时间获取到昨天时间,则今日排名榜未更新。").toString();
|
||||
return resultBuilder.append("如查询当前时间获取到昨天时间,则今日排名榜未更新。\n如有不当作品,可使用\".cgj report -id 作品id\"向色图姬反馈。").toString();
|
||||
}
|
||||
|
||||
@Command(commandName = "userArt")
|
||||
@ -230,7 +277,18 @@ public class BotCommandProcess {
|
||||
return "功能未完成";
|
||||
}
|
||||
|
||||
private final static Object searchCacheLock = new Object();
|
||||
/**
|
||||
* 搜索命令
|
||||
* @param content 搜索内容
|
||||
* @param type 搜索类型
|
||||
* @param area 搜索区域
|
||||
* @param includeKeywords 包括关键字
|
||||
* @param excludeKeywords 排除关键字
|
||||
* @param contentOption 搜索选项
|
||||
* @param pagesIndex 搜索页索引
|
||||
* @return 返回搜索内容消息
|
||||
* @throws IOException 当搜索发生异常时抛出
|
||||
*/
|
||||
@Command
|
||||
public static String search(@Argument(name = "content") String content,
|
||||
@Argument(name = "type", force = false) String type,
|
||||
@ -266,26 +324,26 @@ public class BotCommandProcess {
|
||||
|
||||
if (!Strings.isNullOrEmpty(includeKeywords)) {
|
||||
for (String keyword : includeKeywords.split(";")) {
|
||||
searchBuilder.removeExcludeKeyword(keyword);
|
||||
searchBuilder.addIncludeKeyword(keyword);
|
||||
searchBuilder.removeExcludeKeyword(keyword.trim());
|
||||
searchBuilder.addIncludeKeyword(keyword.trim());
|
||||
log.debug("已添加关键字: {}", keyword);
|
||||
}
|
||||
}
|
||||
if (!Strings.isNullOrEmpty(excludeKeywords)) {
|
||||
for (String keyword : excludeKeywords.split(";")) {
|
||||
searchBuilder.removeIncludeKeyword(keyword);
|
||||
searchBuilder.addExcludeKeyword(keyword);
|
||||
searchBuilder.removeIncludeKeyword(keyword.trim());
|
||||
searchBuilder.addExcludeKeyword(keyword.trim());
|
||||
log.debug("已添加排除关键字: {}", keyword);
|
||||
}
|
||||
}
|
||||
|
||||
log.info("正在搜索作品, 条件: {}", searchBuilder.getSearchCondition());
|
||||
|
||||
String requestUrl = searchBuilder.buildURL();
|
||||
String requestUrl = searchBuilder.buildURL().intern();
|
||||
log.debug("RequestUrl: {}", requestUrl);
|
||||
JsonObject resultBody = null;
|
||||
if(!searchBodyCache.exists(requestUrl)) {
|
||||
synchronized (searchCacheLock) {
|
||||
synchronized (requestUrl) {
|
||||
if (!searchBodyCache.exists(requestUrl)) {
|
||||
log.debug("searchBody缓存失效, 正在更新...");
|
||||
JsonObject jsonObject;
|
||||
@ -375,6 +433,9 @@ public class BotCommandProcess {
|
||||
if (isNoSafe(illustId, globalProp, true)) {
|
||||
log.warn("作品Id {} 为R-18作品, 跳过.", illustId);
|
||||
continue;
|
||||
} else if(isReported(illustId)) {
|
||||
log.warn("作品Id {} 被报告, 正在等待审核, 跳过该作品.", illustId);
|
||||
continue;
|
||||
}
|
||||
|
||||
result.append(searchArea.name()).append(" (").append(count).append(" / ").append(limit).append(")\n\t作品id: ").append(illustId)
|
||||
@ -388,7 +449,7 @@ public class BotCommandProcess {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Strings.nullToEmpty(result.toString()) + "预览图片并非原图,使用“.cgj image -id 作品id”获取原图";
|
||||
return Strings.nullToEmpty(result.toString()) + "预览图片并非原图,使用“.cgj image -id 作品id”获取原图\n如有不当作品,可使用\".cgj report -id 作品id\"向色图姬反馈。";
|
||||
}
|
||||
|
||||
@Command(commandName = "pages")
|
||||
@ -407,12 +468,15 @@ public class BotCommandProcess {
|
||||
}
|
||||
}
|
||||
|
||||
@Command(commandName = "artworks")
|
||||
@Command(commandName = "link")
|
||||
public static String artworksLink(@Argument(name = "id") int illustId) {
|
||||
try {
|
||||
if (isNoSafe(illustId, globalProp, false)) {
|
||||
log.warn("作品Id {} 已被屏蔽.", illustId);
|
||||
return "由于相关设置,该作品已被屏蔽!";
|
||||
} else if(isReported(illustId)) {
|
||||
log.warn("作品Id {} 被报告, 正在等待审核, 跳过该作品.", illustId);
|
||||
return "该作品暂时被封印,请等待色图姬进一步审核!";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error("获取作品信息失败!", e);
|
||||
@ -457,6 +521,9 @@ public class BotCommandProcess {
|
||||
if (isNoSafe(illustId, globalProp, false)) {
|
||||
log.warn("作品 {} 存在R-18内容且设置\"image.allowR18\"为false,将屏蔽该作品不发送.", illustId);
|
||||
return "(根据设置,该作品已被屏蔽!)";
|
||||
} else if(isReported(illustId)) {
|
||||
log.warn("作品Id {} 被报告, 正在等待审核, 跳过该作品.", illustId);
|
||||
return "(该作品已被封印)";
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.warn("作品信息无法获取!", e);
|
||||
@ -501,6 +568,12 @@ public class BotCommandProcess {
|
||||
return getImageToBotCode(imageCache.get(fileName), false).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过文件获取图片的BotCode代码
|
||||
* @param targetFile 图片文件
|
||||
* @param updateCache 是否刷新缓存(只是让机器人重新上传, 如果上传接口有重复检测的话是无法处理的)
|
||||
* @return 返回设定好参数的BotCode
|
||||
*/
|
||||
private static BotCode getImageToBotCode(File targetFile, boolean updateCache) {
|
||||
String fileName = targetFile.getName();
|
||||
BotCode code = BotCode.parse(CQCode.image(getImageStoreDir().getName() + "/" + fileName));
|
||||
@ -530,6 +603,34 @@ public class BotCommandProcess {
|
||||
log.warn("缓存删除完成.");
|
||||
}
|
||||
|
||||
/**
|
||||
* 举报某一作品
|
||||
* @param illustId 需要举报的作品id
|
||||
* @param reason 举报原因
|
||||
* @return 返回提示信息
|
||||
*/
|
||||
@Command
|
||||
public static String report(@Argument(name = "$fromGroup") long fromGroup, @Argument(name = "$fromQQ") long fromQQ, @Argument(name = "id") int illustId, @Argument(name = "msg", force = false) String reason) {
|
||||
log.warn("收到作品反馈(IllustId: {}, 原因: {})", illustId, reason);
|
||||
JsonObject reportJson = new JsonObject();
|
||||
reportJson.addProperty("illustId", illustId);
|
||||
reportJson.addProperty("reportTime", new Date().getTime());
|
||||
reportJson.addProperty("fromGroup", fromGroup);
|
||||
reportJson.addProperty("fromQQ", fromQQ);
|
||||
reportJson.addProperty("reason", reason);
|
||||
reportStore.update(String.valueOf(illustId), reportJson, 0);
|
||||
return "色图姬收到了你的报告,将屏蔽该作品并对作品违规情况进行核实,感谢你的反馈!";
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查某一作品是否被报告
|
||||
* @param illustId 作品Id
|
||||
* @return 如果被报告了, 返回true
|
||||
*/
|
||||
public static boolean isReported(int illustId) {
|
||||
return reportStore.exists(String.valueOf(illustId));
|
||||
}
|
||||
|
||||
/*
|
||||
下一目标:
|
||||
添加定时发图
|
||||
@ -537,7 +638,15 @@ public class BotCommandProcess {
|
||||
标签....标签支持搜索吧
|
||||
*/
|
||||
|
||||
private static boolean isNoSafe(int illustId, Properties settingProp, boolean returnRaw) throws IOException {
|
||||
/**
|
||||
* 检查指定作品是否为r18
|
||||
* @param illustId 作品Id
|
||||
* @param settingProp 配置项
|
||||
* @param returnRaw 是否返回原始值
|
||||
* @return 如果为true, 则不为全年龄
|
||||
* @throws IOException 获取数据时发生异常时抛出
|
||||
*/
|
||||
public static boolean isNoSafe(int illustId, Properties settingProp, boolean returnRaw) throws IOException {
|
||||
boolean rawValue = getIllustInfo(illustId, false).getAsJsonArray("tags").contains(new JsonPrimitive("R-18"));
|
||||
return returnRaw || settingProp == null ? rawValue : rawValue && !settingProp.getProperty("image.allowR18", "false").equalsIgnoreCase("true");
|
||||
}
|
||||
@ -668,7 +777,7 @@ public class BotCommandProcess {
|
||||
if(rankingResult.size() == 0) {
|
||||
log.info("数据获取失败, 将设置浮动有效时间以准备下次更新.");
|
||||
}
|
||||
result = new ArrayList<>(rankingResult).subList(start - 1, range);
|
||||
result = new ArrayList<>(rankingResult).subList(start - 1, start + range - 1);
|
||||
rankingCache.update(requestSign, rankingResult,
|
||||
rankingResult.size() == 0 ? 5400000 + expireTimeFloatRandom.nextInt(1800000) : 0);
|
||||
log.info("Ranking缓存更新完成.(RequestSign: {})", requestSign);
|
||||
|
153
src/main/java/net/lamgc/cgj/bot/RandomIntervalSendTimer.java
Normal file
153
src/main/java/net/lamgc/cgj/bot/RandomIntervalSendTimer.java
Normal file
@ -0,0 +1,153 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* 随机间隔发送器
|
||||
*/
|
||||
public class RandomIntervalSendTimer extends TimerTask {
|
||||
|
||||
private final static Timer timer = new Timer("Thread-RIST");
|
||||
private final static Logger log = LoggerFactory.getLogger("RandomIntervalSendTimer");
|
||||
private final static Map<Long, RandomIntervalSendTimer> timerMap = new HashMap<>();
|
||||
|
||||
private final long timerId;
|
||||
private final Random timeRandom = new Random();
|
||||
private final AutoSender sender;
|
||||
private final long time;
|
||||
private final int floatTime;
|
||||
private AtomicBoolean loop = new AtomicBoolean();
|
||||
private final AtomicBoolean start = new AtomicBoolean();
|
||||
|
||||
/**
|
||||
* 创建一个随机延迟发送器
|
||||
* @param timerId 该Timer的标识,
|
||||
* 标识必须是唯一的, 当使用相同id创建时, 将会返回该id所属的Timer对象
|
||||
* @param sender 自动发送器
|
||||
* @param time 最低时间(ms)
|
||||
* @param floatTime 浮动时间(ms)
|
||||
* @param startNow 现在开始
|
||||
* @param loop 是否循环
|
||||
*/
|
||||
public static RandomIntervalSendTimer createTimer(long timerId, AutoSender sender, long time, int floatTime, boolean startNow, boolean loop) {
|
||||
if(timerMap.containsKey(timerId)) {
|
||||
return timerMap.get(timerId);
|
||||
}
|
||||
|
||||
RandomIntervalSendTimer timer = new RandomIntervalSendTimer(timerId, sender, time, floatTime, startNow, loop);
|
||||
timerMap.put(timerId, timer);
|
||||
return timer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过Id获取Timer
|
||||
* @param id 待获取Timer对应的Id
|
||||
* @return 返回RandomIntervalSendTimer对象
|
||||
* @throws NoSuchElementException 当不存在Timer时抛出
|
||||
*/
|
||||
public static RandomIntervalSendTimer getTimerById(long id) {
|
||||
if(!timerMap.containsKey(id)) {
|
||||
throw new NoSuchElementException("id=" + id);
|
||||
}
|
||||
return timerMap.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有id
|
||||
* @return 所有TimerId的集合
|
||||
*/
|
||||
public static Set<Long> timerIdSet() {
|
||||
return new HashSet<>(timerMap.keySet());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个随机延迟发送器
|
||||
* @param timerId 该Timer的标识
|
||||
* @param sender 自动发送器
|
||||
* @param time 最低时间(ms)
|
||||
* @param floatTime 浮动时间(ms)
|
||||
* @param startNow 现在开始
|
||||
* @param loop 是否循环
|
||||
*/
|
||||
private RandomIntervalSendTimer(long timerId, AutoSender sender, long time, int floatTime, boolean startNow, boolean loop) {
|
||||
this.timerId = timerId;
|
||||
this.sender = sender;
|
||||
this.time = time;
|
||||
this.floatTime = floatTime;
|
||||
timerMap.put(timerId, this);
|
||||
if(startNow) {
|
||||
start(loop);
|
||||
}
|
||||
}
|
||||
|
||||
public void start() {
|
||||
start(this.loop.get());
|
||||
}
|
||||
|
||||
private final static DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-ss HH:mm:ss");
|
||||
|
||||
/**
|
||||
* 启动定时器
|
||||
* @param loop 是否循环, 如果为true, 则任务完成后会自动调用start方法继续循环, 直到被调用{@code #}或总定时器被销毁;
|
||||
*/
|
||||
public void start(boolean loop) {
|
||||
this.loop.set(loop);
|
||||
long nextDelay = time + timeRandom.nextInt(floatTime);
|
||||
Date nextDate = new Date();
|
||||
nextDate.setTime(nextDate.getTime() + nextDelay);
|
||||
log.info("定时器 {} 下一延迟: {}ms ({})", Integer.toHexString(this.hashCode()), nextDelay, dateFormat.format(nextDate));
|
||||
if(start.get()) {
|
||||
try {
|
||||
Field state = this.getClass().getSuperclass().getDeclaredField("state");
|
||||
state.setAccessible(true);
|
||||
state.setInt(this, 0);
|
||||
state.setAccessible(false);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
start.set(true);
|
||||
timer.schedule(this, nextDelay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sender.send();
|
||||
} catch (Exception e) {
|
||||
log.error("定时器 {} 执行时发生异常:\n{}", Integer.toHexString(this.hashCode()), Throwables.getStackTraceAsString(e));
|
||||
}
|
||||
if (this.loop.get()) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消该定时器
|
||||
* @return 取消成功返回true
|
||||
*/
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
start.set(false);
|
||||
loop.set(false);
|
||||
return super.cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁这个定时器
|
||||
*/
|
||||
public void destroy() {
|
||||
cancel();
|
||||
timerMap.remove(this.timerId);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.pixiv.PixivDownload;
|
||||
import net.lamgc.cgj.pixiv.PixivURL;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 推荐作品发送器
|
||||
*/
|
||||
public class RandomRankingArtworksSender extends AutoSender {
|
||||
|
||||
private final Logger log;
|
||||
private final int rankingStart;
|
||||
private final int rankingStop;
|
||||
private final PixivURL.RankingMode mode;
|
||||
private final PixivURL.RankingContentType contentType;
|
||||
private final PixivDownload.PageQuality quality;
|
||||
|
||||
/**
|
||||
* 构造一个推荐作品发送器
|
||||
* @param messageSender 消息发送器
|
||||
* @param rankingStart 排行榜开始范围(从1开始, 名次),如传入0或负数则为默认值,默认为1
|
||||
* @param rankingStop 排名榜结束范围(包括该名次),如传入0或负数则为默认值,默认为150
|
||||
* @param mode 排行榜模式
|
||||
* @param contentType 排行榜内容类型
|
||||
* @param quality 图片质量, 详见{@link PixivDownload.PageQuality}
|
||||
* @throws IndexOutOfBoundsException 当 rankingStart > rankingStop时抛出
|
||||
*/
|
||||
public RandomRankingArtworksSender(MessageSender messageSender, int rankingStart, int rankingStop, PixivURL.RankingMode mode, PixivURL.RankingContentType contentType, PixivDownload.PageQuality quality) {
|
||||
super(messageSender);
|
||||
this.mode = mode;
|
||||
this.contentType = contentType;
|
||||
log = LoggerFactory.getLogger("RecommendArtworksSender@" + Integer.toHexString(this.hashCode()));
|
||||
this.rankingStart = rankingStart > 0 ? rankingStart : 1;
|
||||
this.rankingStop = rankingStop > 0 ? rankingStop : 150;
|
||||
if(this.rankingStart > this.rankingStop) {
|
||||
throw new IndexOutOfBoundsException("rankingStart=" + this.rankingStart + ", rankingStop=" + this.rankingStop);
|
||||
}
|
||||
this.quality = quality == null ? PixivDownload.PageQuality.REGULAR : quality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send() {
|
||||
Date queryDate = new Date();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(queryDate);
|
||||
if(calendar.get(Calendar.HOUR_OF_DAY) < 12) {
|
||||
calendar.add(Calendar.DAY_OF_YEAR, -2);
|
||||
} else {
|
||||
calendar.add(Calendar.DAY_OF_YEAR, -1);
|
||||
}
|
||||
queryDate = calendar.getTime();
|
||||
|
||||
int selectRanking = rankingStart + new Random().nextInt(rankingStop - rankingStart + 1);
|
||||
try {
|
||||
List<JsonObject> rankingList = BotCommandProcess.getRankingInfoByCache(
|
||||
contentType,
|
||||
mode,
|
||||
queryDate,
|
||||
selectRanking,
|
||||
1, false);
|
||||
|
||||
log.info("RankingResult.size: {}", rankingList.size());
|
||||
if(rankingList.size() != 1) {
|
||||
log.error("排行榜选取失败!(获取到了多个结果)");
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject rankingInfo = rankingList.get(0);
|
||||
int illustId = rankingInfo.get("illust_id").getAsInt();
|
||||
if(BotCommandProcess.isNoSafe(illustId, BotCommandProcess.globalProp, false)) {
|
||||
log.warn("作品为r18作品, 取消本次发送.");
|
||||
return;
|
||||
} else if(BotCommandProcess.isReported(illustId)) {
|
||||
log.warn("作品Id {} 被报告, 正在等待审核, 跳过该作品.", illustId);
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder message = new StringBuilder();
|
||||
message.append("#美图推送 - 今日排行榜 第 ").append(rankingInfo.get("rank").getAsInt()).append(" 名\n");
|
||||
message.append("标题:").append(rankingInfo.get("title").getAsString()).append("(").append(illustId).append(")\n");
|
||||
message.append("作者:").append(rankingInfo.get("user_name").getAsString()).append("\n");
|
||||
message.append(BotCommandProcess.getImageById(illustId, quality, 1));
|
||||
message.append("\n如有不当作品,可使用\".cgj report -id ").append(illustId).append("\"向色图姬反馈。");
|
||||
getMessageSender().sendMessage(message.toString());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,10 +1,12 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
|
||||
import com.google.common.base.Throwables;
|
||||
import net.lamgc.cgj.bot.event.BotEventHandler;
|
||||
import net.lamgc.cgj.bot.event.VirtualLoadMessageEvent;
|
||||
import net.lamgc.cgj.pixiv.PixivURL;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@ -43,7 +45,7 @@ public class RankingUpdateTimer {
|
||||
}
|
||||
|
||||
public void now(Date queryDate) {
|
||||
log.info("当前时间 {}, 定时任务开始执行...", new Date());
|
||||
log.warn("当前时间 {}, 定时任务开始执行...", new Date());
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(queryDate == null ? new Date() : queryDate);
|
||||
|
||||
@ -65,14 +67,17 @@ public class RankingUpdateTimer {
|
||||
}
|
||||
log.info("当前排行榜类型: {}.{}, 正在更新...", rankingMode.name(), contentType.name());
|
||||
try {
|
||||
BotCommandProcess.getRankingInfoByCache(contentType, rankingMode, calendar.getTime(), 1, 0, true);
|
||||
//BotCommandProcess.getRankingInfoByCache(contentType, rankingMode, calendar.getTime(), 1, 0, true);
|
||||
BotEventHandler.executor.executorSync(
|
||||
new VirtualLoadMessageEvent(0,0,
|
||||
".cgj ranking -type=" + contentType.name() + " -mode=" + rankingMode.name()));
|
||||
log.info("排行榜 {}.{} 更新完成.", rankingMode.name(), contentType.name());
|
||||
} catch (IOException e) {
|
||||
log.error("排行榜 {}.{} 更新时发生异常", rankingMode.name(), contentType.name());
|
||||
log.error("异常信息如下", e);
|
||||
} catch (InterruptedException e) {
|
||||
log.error("排行榜 {}.{} 更新时发生异常. \n{}", rankingMode.name(), contentType.name(), Throwables.getStackTraceAsString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
log.warn("定时任务更新完成.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,7 +1,12 @@
|
||||
package net.lamgc.cgj.bot.cache;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 缓存库接口
|
||||
* @param <T> 缓存数据类型
|
||||
*/
|
||||
public interface CacheStore<T> {
|
||||
|
||||
/**
|
||||
@ -64,6 +69,19 @@ public interface CacheStore<T> {
|
||||
*/
|
||||
boolean clear();
|
||||
|
||||
/**
|
||||
* 获取key集合
|
||||
* @return 返回存储缓存库中所有缓存项key的集合
|
||||
*/
|
||||
Set<String> keys();
|
||||
|
||||
/**
|
||||
* 删除指定缓存项
|
||||
* @param key 缓存项key
|
||||
* @return 删除成功返回true
|
||||
*/
|
||||
boolean remove(String key);
|
||||
|
||||
/**
|
||||
* 是否支持持久化
|
||||
* @return 如果支持返回true
|
||||
|
@ -3,9 +3,7 @@ package net.lamgc.cgj.bot.cache;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.Random;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 具有继承性的热点数据缓存库
|
||||
@ -99,6 +97,21 @@ public class HotDataCacheStore<T> implements CacheStore<T> {
|
||||
return current.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keys() {
|
||||
Set<String> keys = new HashSet<>();
|
||||
keys.addAll(current.keys());
|
||||
keys.addAll(parent.keys());
|
||||
return keys;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(String key) {
|
||||
parent.remove(key);
|
||||
current.remove(key);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportedPersistence() {
|
||||
return current.supportedPersistence() || parent.supportedPersistence();
|
||||
|
@ -27,6 +27,7 @@ public class ImageCacheHandler implements EventHandler {
|
||||
|
||||
private final static Set<ImageCacheObject> cacheQueue = Collections.synchronizedSet(new HashSet<>());
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void getImageToCache(ImageCacheObject event) {
|
||||
if(cacheQueue.contains(event)) {
|
||||
log.info("图片 {} 已存在相同缓存任务, 跳过.", event.getStoreFile().getName());
|
||||
|
@ -5,8 +5,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Date;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 基于Hashtable的本地缓存库
|
||||
* @param <T> 缓存类型
|
||||
*/
|
||||
public class LocalHashCacheStore<T> implements CacheStore<T> {
|
||||
|
||||
private final Hashtable<String, CacheObject<T>> cache;
|
||||
@ -93,6 +98,16 @@ public class LocalHashCacheStore<T> implements CacheStore<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keys() {
|
||||
return cache.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(String key) {
|
||||
return cache.remove(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportedPersistence() {
|
||||
return false;
|
||||
|
@ -9,6 +9,7 @@ import redis.clients.jedis.*;
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@ -50,29 +51,27 @@ public abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
|
||||
|
||||
@Override
|
||||
public void update(String key, T value, Date expire) {
|
||||
Jedis jedis = jedisPool.getResource();
|
||||
jedis.set(keyPrefix + key, parse(value));
|
||||
if(expire != null) {
|
||||
jedis.pexpireAt(keyPrefix + key, expire.getTime());
|
||||
log.debug("已设置Key {} 的过期时间(Expire: {})", key, expire.getTime());
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
jedis.set(keyPrefix + key, parse(value));
|
||||
if(expire != null) {
|
||||
jedis.pexpireAt(keyPrefix + key, expire.getTime());
|
||||
log.debug("已设置Key {} 的过期时间(Expire: {})", key, expire.getTime());
|
||||
}
|
||||
}
|
||||
jedis.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getCache(String key) {
|
||||
Jedis jedis = jedisPool.getResource();
|
||||
T result = analysis(jedis.get(keyPrefix + key));
|
||||
jedis.close();
|
||||
return result;
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
return analysis(jedis.get(keyPrefix + key));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String key) {
|
||||
Jedis jedis = jedisPool.getResource();
|
||||
boolean result = jedis.exists(keyPrefix + key);
|
||||
jedis.close();
|
||||
return result;
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
return jedis.exists(keyPrefix + key);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -82,11 +81,25 @@ public abstract class RedisPoolCacheStore<T> implements CacheStore<T> {
|
||||
|
||||
@Override
|
||||
public boolean clear() {
|
||||
Jedis jedis = jedisPool.getResource();
|
||||
String result = jedis.flushDB();
|
||||
jedis.close();
|
||||
log.info("flushDB返回结果: {}", result);
|
||||
return true;
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
String result = jedis.flushDB();
|
||||
log.info("flushDB返回结果: {}", result);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keys() {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
return jedis.keys(keyPrefix + "*");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(String key) {
|
||||
try (Jedis jedis = jedisPool.getResource()) {
|
||||
return jedis.del(keyPrefix + key) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
30
src/main/java/net/lamgc/cgj/bot/cache/StringListRedisCacheStore.java
vendored
Normal file
30
src/main/java/net/lamgc/cgj/bot/cache/StringListRedisCacheStore.java
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
package net.lamgc.cgj.bot.cache;
|
||||
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public class StringListRedisCacheStore extends RedisListCacheStore<String> {
|
||||
public StringListRedisCacheStore(URI redisServerUri, String prefix) {
|
||||
super(redisServerUri, prefix);
|
||||
}
|
||||
|
||||
public StringListRedisCacheStore(URI redisServerUri, JedisPoolConfig config, int timeout, String password, String prefix) {
|
||||
super(redisServerUri, config, timeout, password, prefix);
|
||||
}
|
||||
|
||||
public StringListRedisCacheStore(JedisPool pool, String prefix) {
|
||||
super(pool, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseData(String dataObj) {
|
||||
return dataObj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String analysisData(String str) {
|
||||
return str;
|
||||
}
|
||||
}
|
@ -23,24 +23,23 @@ import redis.clients.jedis.JedisPool;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BotEventHandler implements EventHandler {
|
||||
|
||||
public final static String COMMAND_PREFIX = ".cgj";
|
||||
public final static String ADMIN_COMMAND_PREFIX = ".cgjadmin ";
|
||||
|
||||
private final ArgumentsRunner processRunner;
|
||||
private final ArgumentsRunner adminRunner;
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger("BotEventHandler@" + Integer.toHexString(this.hashCode()));
|
||||
private final static Logger log = LoggerFactory.getLogger("BotEventHandler");
|
||||
|
||||
/**
|
||||
* 所有缓存共用的JedisPool
|
||||
@ -95,6 +94,21 @@ public class BotEventHandler implements EventHandler {
|
||||
initialled = true;
|
||||
}
|
||||
|
||||
private final static AtomicBoolean preLoaded = new AtomicBoolean();
|
||||
/**
|
||||
* 预加载
|
||||
*/
|
||||
public synchronized static void preLoad() {
|
||||
if(preLoaded.get()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
BotAdminCommandProcess.loadPushList();
|
||||
} finally {
|
||||
preLoaded.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private BotEventHandler() {
|
||||
ArgumentsRunnerConfig runnerConfig = new ArgumentsRunnerConfig();
|
||||
runnerConfig.setUseDefaultValueInsteadOfException(true);
|
||||
@ -110,6 +124,11 @@ public class BotEventHandler implements EventHandler {
|
||||
BotCommandProcess.initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 以事件形式处理消息事件
|
||||
* @param event 消息事件对象
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void processMessage(MessageEvent event) {
|
||||
String msg = event.getMessage();
|
||||
log.debug(event.toString());
|
||||
@ -119,7 +138,7 @@ public class BotEventHandler implements EventHandler {
|
||||
|
||||
Pattern pattern = Pattern.compile("/\\s*(\".+?\"|[^:\\s])+((\\s*:\\s*(\".+?\"|[^\\s])+)|)|(\".+?\"|[^\"\\s])+");
|
||||
Matcher matcher = pattern.matcher(Strings.nullToEmpty(msg));
|
||||
ArrayList<String> argsList = new ArrayList<>();
|
||||
List<String> argsList = new ArrayList<>();
|
||||
while (matcher.find()) {
|
||||
String arg = matcher.group();
|
||||
int startIndex = 0;
|
||||
@ -141,17 +160,22 @@ public class BotEventHandler implements EventHandler {
|
||||
String[] args = new String[argsList.size()];
|
||||
argsList.toArray(args);
|
||||
log.debug("传入参数: {}", Arrays.toString(args));
|
||||
argsList.add("-$fromGroup");
|
||||
argsList.add(String.valueOf(event.getFromGroup()));
|
||||
argsList.add("-$fromQQ");
|
||||
argsList.add(String.valueOf(event.getFromQQ()));
|
||||
args = Arrays.copyOf(args, args.length + 4);
|
||||
argsList.toArray(args);
|
||||
|
||||
log.info("正在处理命令...");
|
||||
long time = System.currentTimeMillis();
|
||||
Object result;
|
||||
try {
|
||||
if(msg.toLowerCase().startsWith(COMMAND_PREFIX + "admin")) {
|
||||
if(msg.toLowerCase().startsWith(ADMIN_COMMAND_PREFIX)) {
|
||||
if(!String.valueOf(event.getFromQQ()).equals(BotCommandProcess.globalProp.getProperty("admin.adminId"))) {
|
||||
event.sendMessage("你没有执行该命令的权限!");
|
||||
return;
|
||||
result = "你没有执行该命令的权限!";
|
||||
} else {
|
||||
result = adminRunner.run(new BotAdminCommandProcess(), args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length));
|
||||
result = adminRunner.run(args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length));
|
||||
}
|
||||
} else {
|
||||
result = processRunner.run(args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length));
|
||||
@ -181,7 +205,7 @@ public class BotEventHandler implements EventHandler {
|
||||
* @return 如果为true则提交
|
||||
*/
|
||||
public static boolean match(String message) {
|
||||
return message.startsWith(COMMAND_PREFIX);
|
||||
return message.startsWith(COMMAND_PREFIX) || message.startsWith(ADMIN_COMMAND_PREFIX);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package net.lamgc.cgj.bot.event;
|
||||
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.utils.event.EventObject;
|
||||
|
||||
public abstract class MessageEvent implements EventObject {
|
||||
public abstract class MessageEvent implements EventObject, MessageSender {
|
||||
|
||||
private final long fromGroup;
|
||||
private final long fromQQ;
|
||||
@ -19,6 +20,7 @@ public abstract class MessageEvent implements EventObject {
|
||||
* @param message 消息内容
|
||||
* @return 成功返回MessageId, 如没有MessageId则返回0, 失败返回负数错误码
|
||||
*/
|
||||
@Override
|
||||
public abstract int sendMessage(final String message);
|
||||
|
||||
/**
|
||||
@ -28,12 +30,6 @@ public abstract class MessageEvent implements EventObject {
|
||||
*/
|
||||
public abstract String getImageUrl(String image);
|
||||
|
||||
/**
|
||||
* 获取原始消息对象.(不推荐使用)
|
||||
* @return 消息对象
|
||||
*/
|
||||
public abstract Object getRawMessage();
|
||||
|
||||
/**
|
||||
* 获取来源群组号
|
||||
* @return 如非群组消息, 返回0
|
||||
@ -60,7 +56,7 @@ public abstract class MessageEvent implements EventObject {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MessageEvent{" +
|
||||
return this.getClass().getSimpleName() + "{" +
|
||||
"fromGroup=" + getFromGroup() +
|
||||
", fromQQ=" + getFromQQ() +
|
||||
", message='" + getMessage() + '\'' +
|
||||
|
@ -0,0 +1,22 @@
|
||||
package net.lamgc.cgj.bot.event;
|
||||
|
||||
/**
|
||||
* 假负载消息事件, 一般用于预处理某个命令使用,可以增强在高峰期来临时的处理速度.
|
||||
*/
|
||||
public class VirtualLoadMessageEvent extends MessageEvent {
|
||||
|
||||
public VirtualLoadMessageEvent(long fromGroup, long fromQQ, String message) {
|
||||
super(fromGroup, fromQQ, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendMessage(String message) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageUrl(String image) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
package net.lamgc.cgj.bot.framework.coolq;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
import net.lz1998.cq.CQGlobal;
|
@ -1,7 +1,7 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
package net.lamgc.cgj.bot.framework.coolq;
|
||||
|
||||
import net.lamgc.cgj.bot.event.BotEventHandler;
|
||||
import net.lamgc.cgj.bot.event.SpringCQMessageEvent;
|
||||
import net.lamgc.cgj.bot.framework.coolq.message.SpringCQMessageEvent;
|
||||
import net.lamgc.utils.event.EventHandler;
|
||||
import net.lz1998.cq.event.message.CQDiscussMessageEvent;
|
||||
import net.lz1998.cq.event.message.CQGroupMessageEvent;
|
||||
@ -16,6 +16,8 @@ import org.springframework.stereotype.Component;
|
||||
public class CQPluginMain extends CQPlugin implements EventHandler {
|
||||
|
||||
public CQPluginMain() {
|
||||
// TODO(LamGC, 2020.04.21): SpringCQ无法适配MessageSenderBuilder
|
||||
BotEventHandler.preLoad();
|
||||
LoggerFactory.getLogger(this.toString())
|
||||
.info("BotEventHandler.COMMAND_PREFIX = {}", BotEventHandler.COMMAND_PREFIX);
|
||||
}
|
@ -1,6 +1,9 @@
|
||||
package net.lamgc.cgj.bot.event;
|
||||
package net.lamgc.cgj.bot.framework.coolq.message;
|
||||
|
||||
import net.lamgc.cgj.bot.BotCode;
|
||||
import net.lamgc.cgj.bot.event.MessageEvent;
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.lz1998.cq.event.message.CQDiscussMessageEvent;
|
||||
import net.lz1998.cq.event.message.CQGroupMessageEvent;
|
||||
import net.lz1998.cq.event.message.CQMessageEvent;
|
||||
@ -11,13 +14,8 @@ import java.util.Objects;
|
||||
|
||||
public class SpringCQMessageEvent extends MessageEvent {
|
||||
|
||||
private final static int TYPE_PRIVATE = 0;
|
||||
private final static int TYPE_GROUP = 1;
|
||||
private final static int TYPE_DISCUSS = 2;
|
||||
|
||||
private final CoolQ cq;
|
||||
private final int type;
|
||||
private final CQMessageEvent messageEvent;
|
||||
private final MessageSender messageSender;
|
||||
|
||||
public SpringCQMessageEvent(CoolQ cq, CQMessageEvent messageEvent) {
|
||||
super(messageEvent instanceof CQGroupMessageEvent ? (
|
||||
@ -26,27 +24,20 @@ public class SpringCQMessageEvent extends MessageEvent {
|
||||
((CQDiscussMessageEvent) messageEvent).getDiscussId() : 0,
|
||||
messageEvent.getUserId(), messageEvent.getMessage());
|
||||
this.cq = Objects.requireNonNull(cq);
|
||||
MessageSource source;
|
||||
if(messageEvent instanceof CQGroupMessageEvent) {
|
||||
type = TYPE_GROUP;
|
||||
source = MessageSource.Group;
|
||||
} else if (messageEvent instanceof CQDiscussMessageEvent) {
|
||||
type = TYPE_DISCUSS;
|
||||
source = MessageSource.Discuss;
|
||||
} else {
|
||||
type = TYPE_PRIVATE;
|
||||
source = MessageSource.Private;
|
||||
}
|
||||
this.messageEvent = messageEvent;
|
||||
messageSender = new SpringCQMessageSender(cq, source, source == MessageSource.Private ? getFromQQ() : getFromGroup());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendMessage(final String message) {
|
||||
switch(type) {
|
||||
case TYPE_PRIVATE:
|
||||
return cq.sendPrivateMsg(getFromQQ(), message, false).getData().getMessageId();
|
||||
case TYPE_GROUP:
|
||||
case TYPE_DISCUSS:
|
||||
return cq.sendGroupMsg(getFromGroup(), message, false).getData().getMessageId();
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return messageSender.sendMessage(message);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -70,8 +61,4 @@ public class SpringCQMessageEvent extends MessageEvent {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRawMessage() {
|
||||
return messageEvent;
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package net.lamgc.cgj.bot.framework.coolq.message;
|
||||
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.lz1998.cq.robot.CoolQ;
|
||||
|
||||
public class SpringCQMessageSender implements MessageSender {
|
||||
|
||||
private CoolQ coolQ;
|
||||
private MessageSource source;
|
||||
private long target;
|
||||
|
||||
public SpringCQMessageSender(CoolQ coolQ, MessageSource source, long target) {
|
||||
this.coolQ = coolQ;
|
||||
this.source = source;
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendMessage(String message) {
|
||||
switch (source) {
|
||||
case Private:
|
||||
return coolQ.sendPrivateMsg(target, message, false).getData().getMessageId();
|
||||
case Group:
|
||||
return coolQ.sendGroupMsg(target, message, false).getData().getMessageId();
|
||||
case Discuss:
|
||||
return coolQ.sendDiscussMsg(target, message, false).getData().getMessageId();
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package net.lamgc.cgj.bot.framework.coolq.message;
|
||||
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.bot.message.MessageSenderFactory;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.lz1998.cq.robot.CoolQ;
|
||||
|
||||
public class SpringCQMessageSenderFactory implements MessageSenderFactory {
|
||||
|
||||
private final CoolQ coolQ;
|
||||
|
||||
public SpringCQMessageSenderFactory(CoolQ coolQ) {
|
||||
this.coolQ = coolQ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSender createMessageSender(MessageSource source, long id) {
|
||||
return new SpringCQMessageSender(coolQ, source, id);
|
||||
}
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
package net.lamgc.cgj.bot;
|
||||
package net.lamgc.cgj.bot.framework.mirai;
|
||||
|
||||
import net.lamgc.cgj.bot.event.BotEventHandler;
|
||||
import net.lamgc.cgj.bot.event.MiraiMessageEvent;
|
||||
import net.lamgc.cgj.bot.framework.mirai.message.MiraiMessageEvent;
|
||||
import net.lamgc.cgj.bot.message.MessageSenderBuilder;
|
||||
import net.lamgc.cgj.bot.framework.mirai.message.MiraiMessageSenderFactory;
|
||||
import net.mamoe.mirai.Bot;
|
||||
import net.mamoe.mirai.BotFactoryJvm;
|
||||
import net.mamoe.mirai.japt.Events;
|
||||
import net.mamoe.mirai.message.FriendMessage;
|
||||
import net.mamoe.mirai.message.GroupMessage;
|
||||
import net.mamoe.mirai.qqandroid.QQAndroid;
|
||||
import net.mamoe.mirai.utils.BotConfiguration;
|
||||
import org.apache.commons.net.util.Base64;
|
||||
import org.slf4j.Logger;
|
||||
@ -24,6 +26,7 @@ public class MiraiMain implements Closeable {
|
||||
private final static Properties botProperties = new Properties();
|
||||
|
||||
public void init() {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(this::close));
|
||||
try {
|
||||
Class.forName(BotEventHandler.class.getName());
|
||||
} catch (ClassNotFoundException e) {
|
||||
@ -39,15 +42,19 @@ public class MiraiMain implements Closeable {
|
||||
return;
|
||||
}
|
||||
|
||||
bot = QQAndroid.INSTANCE.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")), Base64.decodeBase64(botProperties.getProperty("bot.password", "")), new BotConfiguration());
|
||||
bot = BotFactoryJvm.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")), Base64.decodeBase64(botProperties.getProperty("bot.password", "")), new BotConfiguration());
|
||||
Events.subscribeAlways(GroupMessage.class, (msg) -> BotEventHandler.executor.executor(new MiraiMessageEvent(msg)));
|
||||
Events.subscribeAlways(FriendMessage.class, (msg) -> BotEventHandler.executor.executor(new MiraiMessageEvent(msg)));
|
||||
bot.login();
|
||||
MessageSenderBuilder.setCurrentMessageSenderFactory(new MiraiMessageSenderFactory(bot));
|
||||
BotEventHandler.preLoad();
|
||||
bot.join();
|
||||
}
|
||||
|
||||
public void close() {
|
||||
log.warn("正在关闭机器人...");
|
||||
bot.close(null);
|
||||
log.warn("机器人已关闭.");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package net.lamgc.cgj.bot.framework.mirai.message;
|
||||
|
||||
import net.lamgc.cgj.bot.event.MessageEvent;
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.mamoe.mirai.message.ContactMessage;
|
||||
import net.mamoe.mirai.message.GroupMessage;
|
||||
import net.mamoe.mirai.message.data.MessageUtils;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class MiraiMessageEvent extends MessageEvent {
|
||||
|
||||
private final ContactMessage messageObject;
|
||||
private final MessageSender messageSender;
|
||||
|
||||
public MiraiMessageEvent(ContactMessage message) {
|
||||
super(message instanceof GroupMessage ? ((GroupMessage) message).getGroup().getId() : 0,
|
||||
message.getSender().getId(), message.getMessage().contentToString());
|
||||
this.messageObject = Objects.requireNonNull(message);
|
||||
if(message instanceof GroupMessage) {
|
||||
messageSender = new MiraiMessageSender(((GroupMessage) message).getGroup(), MessageSource.Group);
|
||||
} else {
|
||||
messageSender = new MiraiMessageSender(message.getSender(), MessageSource.Private);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sendMessage(final String message) {
|
||||
return messageSender.sendMessage(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageUrl(String imageId) {
|
||||
return messageObject.getBot().queryImageUrl(MessageUtils.newImage(imageId));
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package net.lamgc.cgj.bot.event;
|
||||
package net.lamgc.cgj.bot.framework.mirai.message;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import net.lamgc.cgj.bot.BotCode;
|
||||
@ -6,36 +6,54 @@ import net.lamgc.cgj.bot.cache.CacheStore;
|
||||
import net.lamgc.cgj.bot.cache.HotDataCacheStore;
|
||||
import net.lamgc.cgj.bot.cache.LocalHashCacheStore;
|
||||
import net.lamgc.cgj.bot.cache.StringRedisCacheStore;
|
||||
import net.mamoe.mirai.message.ContactMessage;
|
||||
import net.mamoe.mirai.message.FriendMessage;
|
||||
import net.mamoe.mirai.message.GroupMessage;
|
||||
import net.mamoe.mirai.message.data.CombinedMessage;
|
||||
import net.lamgc.cgj.bot.event.BotEventHandler;
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.mamoe.mirai.Bot;
|
||||
import net.mamoe.mirai.contact.Contact;
|
||||
import net.mamoe.mirai.message.data.Image;
|
||||
import net.mamoe.mirai.message.data.Message;
|
||||
import net.mamoe.mirai.message.data.MessageChain;
|
||||
import net.mamoe.mirai.message.data.MessageUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Objects;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MiraiMessageEvent extends MessageEvent {
|
||||
public class MiraiMessageSender implements MessageSender {
|
||||
|
||||
private final ContactMessage messageObject;
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(this.hashCode()));
|
||||
private final Contact member;
|
||||
private final MessageSource source;
|
||||
private final static Logger log = LoggerFactory.getLogger("MiraiMessageSender");
|
||||
private final static CacheStore<String> imageIdCache = new HotDataCacheStore<>(
|
||||
new StringRedisCacheStore(BotEventHandler.redisServer, "mirai.imageId"),
|
||||
new LocalHashCacheStore<>(),
|
||||
5400000, 1800000);
|
||||
|
||||
/**
|
||||
* 使用id构造发送器
|
||||
* @param bot 机器人对象
|
||||
* @param source 消息源类型
|
||||
* @param id id, 将会根据消息源类型判断为什么号(QQ号或群号)
|
||||
* @throws NoSuchElementException 当在机器人好友列表或群列表里没有这个好友或群的时候抛出
|
||||
*/
|
||||
public MiraiMessageSender(Bot bot, MessageSource source, long id) {
|
||||
this(source == MessageSource.Private ? bot.getFriend(id) : bot.getGroup(id), source);
|
||||
}
|
||||
|
||||
public MiraiMessageEvent(ContactMessage message) {
|
||||
super(message instanceof GroupMessage ? ((GroupMessage) message).getGroup().getId() : 0,
|
||||
message.getSender().getId(), message.getMessage().toString());
|
||||
this.messageObject = Objects.requireNonNull(message);
|
||||
/**
|
||||
* 通过联系人对象构造发送器
|
||||
* @param contact 联系人
|
||||
* @param source 消息源类型
|
||||
*/
|
||||
public MiraiMessageSender(Contact contact, MessageSource source) {
|
||||
this.member = contact;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -43,28 +61,11 @@ public class MiraiMessageEvent extends MessageEvent {
|
||||
log.debug("处理前的消息内容:\n{}", message);
|
||||
Message msgBody = processMessage(Objects.requireNonNull(message));
|
||||
log.debug("处理后的消息内容(可能出现乱序的情况, 但实际上顺序是没问题的):\n{}", msgBody);
|
||||
if(getFromGroup() == 0) {
|
||||
FriendMessage msgObject = (FriendMessage) messageObject;
|
||||
//FIXME(LamGC, 2020.04.10): 当前 Mirai 不支持私聊长文本, 所以发生异常是正常情况...
|
||||
msgObject.getSender().sendMessage(msgBody);
|
||||
} else {
|
||||
GroupMessage msgObject = (GroupMessage) messageObject;
|
||||
msgObject.getGroup().sendMessage(msgBody);
|
||||
}
|
||||
member.sendMessage(msgBody);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageUrl(String imageId) {
|
||||
return messageObject.getBot().queryImageUrl(MessageUtils.newImage(imageId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRawMessage() {
|
||||
return messageObject;
|
||||
}
|
||||
|
||||
private final static Pattern cqCodePattern = Pattern.compile("\\[.*?:.*?]");
|
||||
private final static Pattern cqCodePattern = BotCode.getCodePattern();
|
||||
private Message processMessage(final String message) {
|
||||
Matcher matcher = cqCodePattern.matcher(message);
|
||||
ArrayList<String> cqCode = new ArrayList<>();
|
||||
@ -79,7 +80,7 @@ public class MiraiMessageEvent extends MessageEvent {
|
||||
.replaceAll("&38", "&")
|
||||
.split("\\|");
|
||||
|
||||
CombinedMessage chain = MessageUtils.newChain().plus("");
|
||||
MessageChain messages = MessageUtils.newChain().plus("");
|
||||
int codeIndex = 0;
|
||||
for(String text : texts) {
|
||||
if(text.equals("{BotCode}")) {
|
||||
@ -90,32 +91,43 @@ public class MiraiMessageEvent extends MessageEvent {
|
||||
log.warn("解析待发送消息内的BotCode时发生异常, 请检查错误格式BotCode的来源并尽快排错!", e);
|
||||
continue;
|
||||
}
|
||||
chain = chain.plus(processBotCode(code));
|
||||
messages = messages.plus(processBotCode(code));
|
||||
} else {
|
||||
chain = chain.plus(text);
|
||||
messages = messages.plus(text);
|
||||
}
|
||||
}
|
||||
|
||||
return chain;
|
||||
return messages;
|
||||
}
|
||||
|
||||
private Message processBotCode(BotCode code) {
|
||||
switch(code.getFunctionName().toLowerCase()) {
|
||||
case "image":
|
||||
Image img;
|
||||
if(code.containsParameter("id")) {
|
||||
return MessageUtils.newImage(code.getParameter("id"));
|
||||
img = MessageUtils.newImage(code.getParameter("id"));
|
||||
} else if(code.containsParameter("absolutePath")) {
|
||||
return uploadImage(code);
|
||||
img = uploadImage(code);
|
||||
} else {
|
||||
return MessageUtils.newChain("(参数不存在)");
|
||||
}
|
||||
if(code.getParameter("flashImage").equalsIgnoreCase("true")) {
|
||||
return MessageUtils.flash(img);
|
||||
} else {
|
||||
return img;
|
||||
}
|
||||
default:
|
||||
log.warn("解析到不支持的BotCode: {}", code);
|
||||
return MessageUtils.newChain("(不支持的BotCode)");
|
||||
}
|
||||
}
|
||||
|
||||
private Image uploadImage(BotCode code) {
|
||||
/**
|
||||
* 存在缓存的上传图片.
|
||||
* @param code 图片BotCode
|
||||
* @return Image对象
|
||||
*/
|
||||
public Image uploadImage(BotCode code) {
|
||||
log.debug("传入BotCode信息:\n{}", code);
|
||||
String absolutePath = code.getParameter("absolutePath");
|
||||
if(Strings.isNullOrEmpty(absolutePath)) {
|
||||
@ -125,20 +137,16 @@ public class MiraiMessageEvent extends MessageEvent {
|
||||
String imageName = code.getParameter("imageName");
|
||||
if(!Strings.isNullOrEmpty(imageName)) {
|
||||
Image image = null;
|
||||
imageName = (getMessageSource() + "." + imageName).intern();
|
||||
imageName = (source + "." + imageName).intern();
|
||||
if(!imageIdCache.exists(imageName) ||
|
||||
Strings.nullToEmpty(code.getParameter("updateCache")).equalsIgnoreCase("true")) {
|
||||
Strings.nullToEmpty(code.getParameter("updateCache")).equalsIgnoreCase("true")) {
|
||||
synchronized (imageName) {
|
||||
if(!imageIdCache.exists(imageName) ||
|
||||
Strings.nullToEmpty(code.getParameter("updateCache")) .equalsIgnoreCase("true")) {
|
||||
Strings.nullToEmpty(code.getParameter("updateCache")) .equalsIgnoreCase("true")) {
|
||||
log.debug("imageName [{}] 缓存失效或强制更新, 正在更新缓存...", imageName);
|
||||
image = uploadImage0(new File(absolutePath));
|
||||
if(Objects.isNull(image)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cacheExpireAt;
|
||||
long expireTime = 0;
|
||||
long expireTime = 864000000; // 10d
|
||||
if(!Strings.isNullOrEmpty(cacheExpireAt = code.getParameter("cacheExpireAt"))) {
|
||||
try {
|
||||
expireTime = Integer.parseInt(cacheExpireAt);
|
||||
@ -169,25 +177,7 @@ public class MiraiMessageEvent extends MessageEvent {
|
||||
}
|
||||
|
||||
private Image uploadImage0(File imageFile) {
|
||||
if(messageObject instanceof FriendMessage) {
|
||||
return messageObject.getSender().uploadImage(imageFile);
|
||||
} else if(messageObject instanceof GroupMessage) {
|
||||
return ((GroupMessage) messageObject).getGroup().uploadImage(imageFile);
|
||||
} else {
|
||||
log.warn("未知的ContactMessage类型: " + messageObject.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getMessageSource() {
|
||||
if(messageObject instanceof FriendMessage) {
|
||||
return "Private";
|
||||
} else if(messageObject instanceof GroupMessage) {
|
||||
return "Group";
|
||||
} else {
|
||||
log.warn("未知的ContactMessage类型: " + messageObject.toString());
|
||||
return "Unknown";
|
||||
}
|
||||
return member.uploadImage(imageFile);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package net.lamgc.cgj.bot.framework.mirai.message;
|
||||
|
||||
import net.lamgc.cgj.bot.message.MessageSender;
|
||||
import net.lamgc.cgj.bot.message.MessageSenderFactory;
|
||||
import net.lamgc.cgj.bot.message.MessageSource;
|
||||
import net.mamoe.mirai.Bot;
|
||||
|
||||
public class MiraiMessageSenderFactory implements MessageSenderFactory {
|
||||
|
||||
private final Bot bot;
|
||||
|
||||
public MiraiMessageSenderFactory(Bot bot) {
|
||||
this.bot = bot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageSender createMessageSender(MessageSource source, long id) throws Exception {
|
||||
switch(source) {
|
||||
case Group:
|
||||
case Discuss:
|
||||
return new MiraiMessageSender(bot.getGroup(id), source);
|
||||
case Private:
|
||||
return new MiraiMessageSender(bot.getFriend(id), source);
|
||||
default:
|
||||
throw new NoSuchFieldException(source.toString());
|
||||
}
|
||||
}
|
||||
}
|
15
src/main/java/net/lamgc/cgj/bot/message/MessageSender.java
Normal file
15
src/main/java/net/lamgc/cgj/bot/message/MessageSender.java
Normal file
@ -0,0 +1,15 @@
|
||||
package net.lamgc.cgj.bot.message;
|
||||
|
||||
public interface MessageSender {
|
||||
|
||||
/**
|
||||
* 发送消息并返回消息id
|
||||
* @param message 消息内容
|
||||
* @return 返回非负数则发送成功,
|
||||
* 返回0则发送器不支持消息Id,
|
||||
* 返回非0正整数则为消息Id,
|
||||
* 返回负数则为错误.
|
||||
*/
|
||||
int sendMessage(final String message);
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package net.lamgc.cgj.bot.message;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 消息发送器构造
|
||||
*/
|
||||
public final class MessageSenderBuilder {
|
||||
|
||||
private final static AtomicReference<MessageSenderFactory> currentFactory = new AtomicReference<>();
|
||||
|
||||
private MessageSenderBuilder() {}
|
||||
|
||||
/**
|
||||
* 获取消息发送器
|
||||
* @param source 消息源类型
|
||||
* @param id 消息源Id
|
||||
* @return 返回新建的发送器
|
||||
*/
|
||||
public static MessageSender getMessageSender(MessageSource source, long id) {
|
||||
MessageSenderFactory messageSenderFactory = currentFactory.get();
|
||||
if(messageSenderFactory == null) {
|
||||
throw new IllegalStateException("The factory is not ready");
|
||||
}
|
||||
try {
|
||||
return messageSenderFactory.createMessageSender(source, id);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置消息发送器工厂
|
||||
* @param factory 工厂对象
|
||||
*/
|
||||
public static void setCurrentMessageSenderFactory(MessageSenderFactory factory) {
|
||||
if(currentFactory.get() != null) {
|
||||
throw new IllegalStateException("Factory already exists");
|
||||
}
|
||||
currentFactory.set(factory);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package net.lamgc.cgj.bot.message;
|
||||
|
||||
public interface MessageSenderFactory {
|
||||
|
||||
/**
|
||||
* 通过Id创建发送器
|
||||
* @param source 消息源
|
||||
* @param id 消息源id
|
||||
* @return 如果成功返回MessageSender
|
||||
*/
|
||||
MessageSender createMessageSender(MessageSource source, long id) throws Exception;
|
||||
|
||||
}
|
23
src/main/java/net/lamgc/cgj/bot/message/MessageSource.java
Normal file
23
src/main/java/net/lamgc/cgj/bot/message/MessageSource.java
Normal file
@ -0,0 +1,23 @@
|
||||
package net.lamgc.cgj.bot.message;
|
||||
|
||||
/**
|
||||
* 消息来源
|
||||
*/
|
||||
public enum MessageSource {
|
||||
/**
|
||||
* 私聊消息
|
||||
*/
|
||||
Private,
|
||||
/**
|
||||
* 群组消息
|
||||
*/
|
||||
Group,
|
||||
/**
|
||||
* 讨论组消息
|
||||
*/
|
||||
Discuss,
|
||||
/**
|
||||
* 未知来源
|
||||
*/
|
||||
Unknown
|
||||
}
|
@ -71,7 +71,7 @@ public class PreLoadDataComparator implements Comparator<JsonElement> {
|
||||
VIEW("viewCount"),
|
||||
;
|
||||
|
||||
final String attrName;
|
||||
public final String attrName;
|
||||
|
||||
Attribute(String attrName) {
|
||||
this.attrName = attrName;
|
||||
|
33
src/main/java/net/lamgc/cgj/pixiv/AdultContentDetector.java
Normal file
33
src/main/java/net/lamgc/cgj/pixiv/AdultContentDetector.java
Normal file
@ -0,0 +1,33 @@
|
||||
package net.lamgc.cgj.pixiv;
|
||||
|
||||
public interface AdultContentDetector {
|
||||
|
||||
/**
|
||||
* 检查某一作品的成人内容判断指数
|
||||
* @param illustId 作品Id
|
||||
* @param isUgoira 是否为动图
|
||||
* @param pageIndex 指定页数, 设为0或负数则视为单页面作品
|
||||
* @return 返回成人作品判断指数(0 ~ 1), 需按照情况设置阀值.
|
||||
*/
|
||||
double detect(int illustId, boolean isUgoira, int pageIndex) throws Exception;
|
||||
|
||||
/**
|
||||
* 检查某一作品是否为成人内容
|
||||
* @param illustId 作品Id
|
||||
* @param isUgoira 是否为动图
|
||||
* @param pageIndex 指定页数, 设为0或负数则视为单页面作品
|
||||
* @return 如果为true则为成人作品, 该方法将由检测器决定如何为成人作品.
|
||||
*/
|
||||
boolean isAdultContent(int illustId, boolean isUgoira, int pageIndex) throws Exception;
|
||||
|
||||
/**
|
||||
* 检查某一作品是否为成人内容
|
||||
* @param illustId 作品Id
|
||||
* @param isUgoira 是否为动图
|
||||
* @param pageIndex 指定页数, 设为0或负数则视为单页面作品
|
||||
* @param threshold 指数阀值, 当等于或大于该阀值时返回true.
|
||||
* @return 如果为true则为成人作品, 该方法将由 threshold 参数决定是否为成人作品.
|
||||
*/
|
||||
boolean isAdultContent(int illustId, boolean isUgoira, int pageIndex, double threshold) throws Exception;
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package net.lamgc.cgj.pixiv;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 使用ModerateContent服务开发的检测器.<br/>
|
||||
* ModerateContent: www.moderatecontent.com
|
||||
*/
|
||||
public class ModerateContentDetector implements AdultContentDetector {
|
||||
|
||||
private final static HttpClient httpClient = HttpClientBuilder.create().build();
|
||||
private final static Gson gson = new Gson();
|
||||
private final String requestUrl;
|
||||
|
||||
private final static String API_URL = "https://www.moderatecontent.com/api/v2?key={key}&url=https://pixiv.cat/";
|
||||
|
||||
/**
|
||||
* 创建一个使用ModerateContent鉴黄服务的检测器
|
||||
* @param apiKey API密钥
|
||||
*/
|
||||
public ModerateContentDetector(String apiKey) {
|
||||
requestUrl = API_URL.replace("{key}", apiKey);
|
||||
}
|
||||
|
||||
private JsonObject accessInterface(int illustId, boolean isUgoira, int pageIndex) throws IOException {
|
||||
HttpResponse response;
|
||||
if(pageIndex <= 0) {
|
||||
response = httpClient.execute(new HttpGet(requestUrl + illustId + (isUgoira ? ".gif" : ".jpg")));
|
||||
} else {
|
||||
response = httpClient.execute(new HttpGet(requestUrl + illustId + "-" + pageIndex + (isUgoira ? ".gif" : ".jpg")));
|
||||
}
|
||||
if(response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new IOException("Http response error: " + response.getStatusLine());
|
||||
}
|
||||
|
||||
JsonObject result = gson.fromJson(EntityUtils.toString(response.getEntity()), JsonObject.class);
|
||||
if (result.get("error_code").getAsInt() != 0) {
|
||||
throw new IOException("Interface result error: " + (result.has("error") ? result.get("error").getAsString() : "(error message is empty)"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double detect(int illustId, boolean isUgoira, int pageIndex) throws IOException {
|
||||
return accessInterface(illustId, isUgoira, pageIndex).getAsJsonObject("predictions").get("adult").getAsDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdultContent(int illustId, boolean isUgoira, int pageIndex) throws IOException {
|
||||
return accessInterface(illustId, isUgoira, pageIndex).get("rating_index").getAsInt() == 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAdultContent(int illustId, boolean isUgoira, int pageIndex, double threshold) throws IOException {
|
||||
return detect(illustId, isUgoira, pageIndex) >= threshold;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpHost;
|
||||
@ -256,8 +257,8 @@ public class PixivDownload {
|
||||
public static List<JsonObject> getRanking(List<JsonObject> rankingList, int rankStart, int range) {
|
||||
log.debug("正在读取JsonArray...(rankStart: {}, range: {})", rankStart, range);
|
||||
ArrayList<JsonObject> results = new ArrayList<>(rankingList.size());
|
||||
for (int rankIndex = rankStart; rankIndex < rankingList.size() && rankIndex < range; rankIndex++) {
|
||||
JsonElement jsonElement = rankingList.get(rankIndex);
|
||||
for (int rankIndex = rankStart; rankIndex < rankStart + range; rankIndex++) {
|
||||
JsonElement jsonElement = rankingList.get(rankIndex - rankStart);
|
||||
JsonObject rankInfo = jsonElement.getAsJsonObject();
|
||||
int rank = rankInfo.get("rank").getAsInt();
|
||||
int illustId = rankInfo.get("illust_id").getAsInt();
|
||||
@ -279,21 +280,8 @@ public class PixivDownload {
|
||||
* @return 返回List对象
|
||||
*/
|
||||
public static List<JsonObject> getRanking(JsonArray rankingArray, int rankStart, int range) {
|
||||
log.debug("正在读取JsonArray...(rankStart: {}, range: {})", rankStart, range);
|
||||
ArrayList<JsonObject> results = new ArrayList<>(rankingArray.size());
|
||||
for (int rankIndex = rankStart; rankIndex < rankingArray.size() && rankIndex < range; rankIndex++) {
|
||||
JsonElement jsonElement = rankingArray.get(rankIndex);
|
||||
JsonObject rankInfo = jsonElement.getAsJsonObject();
|
||||
int rank = rankInfo.get("rank").getAsInt();
|
||||
int illustId = rankInfo.get("illust_id").getAsInt();
|
||||
int authorId = rankInfo.get("user_id").getAsInt();
|
||||
String authorName = rankInfo.get("user_name").getAsString();
|
||||
String title = rankInfo.get("title").getAsString();
|
||||
log.debug("Array-当前到第 {}/{} 名(总共 {} 名), IllustID: {}, Author: ({}) {}, Title: {}", rank, rankStart + range, range, illustId, authorId, authorName, title);
|
||||
results.add(rankInfo);
|
||||
}
|
||||
log.debug("JsonArray读取完成.");
|
||||
return results;
|
||||
List<JsonObject> list = new Gson().fromJson(rankingArray, new TypeToken<List<JsonObject>>(){}.getType());
|
||||
return getRanking(list, rankStart, range);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -304,65 +292,47 @@ public class PixivDownload {
|
||||
* @param rankStart 开始排名, 从1开始
|
||||
* @param range 取范围
|
||||
* @return 成功返回有值List, 失败且无异常返回空
|
||||
* @throws IOException 获取异常时抛出
|
||||
* @throws IllegalArgumentException 当{@linkplain net.lamgc.cgj.pixiv.PixivURL.RankingContentType RankingContentType}
|
||||
* 与{@linkplain net.lamgc.cgj.pixiv.PixivURL.RankingMode RankingMode}互不兼容时抛出
|
||||
* @throws IndexOutOfBoundsException 当排行榜选取范围超出排行榜范围时抛出(排行榜范围为 1 ~ 500 名)
|
||||
* @throws IOException 当Http请求发生异常时抛出, 或Http请求响应码非200时抛出
|
||||
*/
|
||||
public List<JsonObject> getRanking(PixivURL.RankingContentType contentType, PixivURL.RankingMode mode,
|
||||
Date time, int rankStart, int range) throws IOException {
|
||||
if(rankStart <= 0) {
|
||||
throw new IllegalArgumentException("rankStart cannot be less than or equal to zero");
|
||||
}
|
||||
if(range <= 0) {
|
||||
throw new IllegalArgumentException("range cannot be less than or equal to zero");
|
||||
}
|
||||
|
||||
if(!contentType.isSupportedMode(mode)) {
|
||||
Date time, int rankStart, int range) throws IOException {
|
||||
Objects.requireNonNull(time);
|
||||
if(!Objects.requireNonNull(contentType).isSupportedMode(Objects.requireNonNull(mode))) {
|
||||
throw new IllegalArgumentException("ContentType不支持指定的RankingMode: ContentType: " + contentType.name() + ", Mode: " + mode.name());
|
||||
} else if(rankStart <= 0) {
|
||||
throw new IndexOutOfBoundsException("rankStart cannot be less than or equal to zero: " + rankStart);
|
||||
} else if(range <= 0) {
|
||||
throw new IndexOutOfBoundsException("range cannot be less than or equal to zero:" + range);
|
||||
} else if(rankStart + range - 1 > 500) {
|
||||
throw new IndexOutOfBoundsException("排名选取范围超出了排行榜范围: rankStart=" + rankStart + ", range=" + range + ", length:" + (rankStart + range - 1));
|
||||
}
|
||||
|
||||
int startPage = (int) Math.ceil(rankStart / 50F);
|
||||
int requestFrequency = (int) Math.ceil((rankStart + (range - 1)) / 50F);
|
||||
int surplusQuantity = range;
|
||||
boolean firstRequest = true;
|
||||
int startPages = (int) Math.max(1, Math.floor(rankStart / 50F));
|
||||
int endPages = (int) Math.min(10, Math.ceil((rankStart + range) / 50F));
|
||||
int startIndex = rankStart - 1;
|
||||
int count = 0;
|
||||
Gson gson = new Gson();
|
||||
ArrayList<JsonObject> results = new ArrayList<>();
|
||||
for (int requestCount = startPage; requestCount <= requestFrequency && requestCount <= 10; requestCount++) {
|
||||
int rangeStart = (requestCount - 1) * 50 + 1;
|
||||
log.debug("正在请求第 {} 到 {} 位排名榜 (第{}次请求, 共 {} 次)", rangeStart, rangeStart + 49, requestCount - startPage + 1, requestFrequency - startPage + 1);
|
||||
HttpGet request = createHttpGetRequest(PixivURL.getRankingLink(contentType, mode, time, requestCount, true));
|
||||
log.debug("Request URL: {}", request.getURI());
|
||||
ArrayList<JsonObject> results = new ArrayList<>(range);
|
||||
for (int pageIndex = startPages; pageIndex <= endPages && count < range; pageIndex++) {
|
||||
HttpGet request = createHttpGetRequest(PixivURL.getRankingLink(contentType, mode, time, pageIndex, true));
|
||||
log.debug("RequestUri: {}", request.getURI());
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
String content = EntityUtils.toString(response.getEntity());
|
||||
log.debug("Content: " + content);
|
||||
JsonObject contentObject = gson.fromJson(content, JsonObject.class);
|
||||
if(contentObject.has("error")) {
|
||||
log.warn("接口报错, 返回信息: {}", contentObject.get("error").getAsString());
|
||||
break;
|
||||
String responseBody = EntityUtils.toString(response.getEntity());
|
||||
log.debug("ResponseBody: {}", responseBody);
|
||||
if(response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new IOException("Http Response Error: '" + response.getStatusLine() + "', ResponseBody: '" + responseBody + '\'');
|
||||
}
|
||||
JsonArray rankingArray = contentObject.getAsJsonArray("contents");
|
||||
log.debug("正在解析数据...");
|
||||
|
||||
//需要添加一个总量, 否则会完整跑完一次.
|
||||
//检查是否为最后一次请求,和剩余量有多少
|
||||
int firstRequestStartIndex = (rankStart % 50) - 1;
|
||||
for (int rankIndex = firstRequest ? firstRequestStartIndex : 0; rankIndex < rankingArray.size() && surplusQuantity > 0; rankIndex++, surplusQuantity--) {
|
||||
JsonElement jsonElement = rankingArray.get(rankIndex);
|
||||
JsonObject rankInfo = jsonElement.getAsJsonObject();
|
||||
int rank = rankInfo.get("rank").getAsInt();
|
||||
int illustId = rankInfo.get("illust_id").getAsInt();
|
||||
int authorId = rankInfo.get("user_id").getAsInt();
|
||||
String authorName = rankInfo.get("user_name").getAsString();
|
||||
String title = rankInfo.get("title").getAsString();
|
||||
log.debug("Download-当前到第 {}/{} 名(总共 {} 名), IllustID: {}, Author: ({}) {}, Title: {}", rank, rankStart + range - 1, range, illustId, authorId, authorName, title);
|
||||
results.add(rankInfo);
|
||||
JsonArray resultArray = gson.fromJson(responseBody, JsonObject.class).getAsJsonArray("contents");
|
||||
for (int resultIndex = startIndex; resultIndex < resultArray.size() && count < range; resultIndex++, count++) {
|
||||
results.add(resultArray.get(resultIndex).getAsJsonObject());
|
||||
}
|
||||
firstRequest = false;
|
||||
log.debug("第 {} 到 {} 位排名榜完成. (第{}次请求)", rangeStart, rangeStart + 49, requestCount);
|
||||
// 重置索引
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
if(requestFrequency > 10) {
|
||||
log.warn("请求的排名榜范围超出所支持的范围, 已终止请求.");
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@ -463,13 +433,14 @@ public class PixivDownload {
|
||||
THUMB_MINI
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取帐号所有的收藏插画,并以输入流形式提供
|
||||
* @return 获取所有链接的InputStream, 请注意关闭InputStream
|
||||
* @throws IOException 当获取时发生异常则直接抛出
|
||||
* @deprecated 该方法可能会导致已经打开的InputStream超时, 使图片获取失败,
|
||||
* 请直接使用{@linkplain #getCollectionAsInputStream(PageQuality, BiConsumer)}
|
||||
*/
|
||||
@Deprecated
|
||||
public Set<Map.Entry<String, InputStream>> getCollectionAsInputStream(PageQuality quality) throws IOException {
|
||||
HashSet<Map.Entry<String, InputStream>> illustInputStreamSet = new HashSet<>();
|
||||
getCollectionAsInputStream(quality, (link, inputStream) -> illustInputStreamSet.add(new AbstractMap.SimpleEntry<>(link, inputStream)));
|
||||
@ -630,12 +601,29 @@ public class PixivDownload {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过预加载数据获取作品类型
|
||||
* @param illustId 作品Id
|
||||
* @param preLoadDataObject 预加载数据(IllustInfo也可以)
|
||||
* @return 如果存在illustType属性, 则返回对应项, 如没有, 或数据内不存在指定作品id的数据, 返回null
|
||||
*/
|
||||
public static PixivIllustType getIllustTypeByPreLoadData(int illustId, JsonObject preLoadDataObject) {
|
||||
JsonObject illustData;
|
||||
JsonObject illustData = null;
|
||||
if(preLoadDataObject.has("illust")) {
|
||||
illustData = preLoadDataObject.getAsJsonObject("illust").getAsJsonObject(String.valueOf(illustId));
|
||||
} else if(preLoadDataObject.has(String.valueOf(illustId))) {
|
||||
illustData = preLoadDataObject.getAsJsonObject(String.valueOf(illustId));
|
||||
} else if(preLoadDataObject.has("body")) { // 解析IllustInfo
|
||||
for (JsonElement jsonElement : preLoadDataObject.getAsJsonObject("body").getAsJsonArray("illusts")) {
|
||||
JsonObject illustInfo = jsonElement.getAsJsonObject();
|
||||
if (illustInfo.get("illustId").getAsInt() == illustId) {
|
||||
illustData = illustInfo;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(illustData == null) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
illustData = preLoadDataObject;
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ public class PixivURL {
|
||||
public static String getRankingLink(RankingContentType contentType, RankingMode mode, Date time, int pageIndex, boolean json){
|
||||
StringBuilder linkBuilder = new StringBuilder(PIXIV_RANKING_LINK);
|
||||
linkBuilder.append("mode=").append(mode == null ? RankingMode.MODE_DAILY.modeParam : mode.modeParam);
|
||||
if(contentType != null && !contentType.equals(RankingContentType.ALL)){
|
||||
if(contentType != null && !contentType.equals(RankingContentType.TYPE_ALL)){
|
||||
linkBuilder.append("&content=").append(contentType.typeName);
|
||||
}
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
|
||||
@ -278,7 +278,7 @@ public class PixivURL {
|
||||
* 排名榜类型
|
||||
*/
|
||||
public enum RankingContentType{
|
||||
ALL("", RankingMode.values()),
|
||||
TYPE_ALL("", RankingMode.values()),
|
||||
/**
|
||||
* 插画
|
||||
* 支持的时间类型: 每天, 每周, 每月, 新人
|
||||
@ -336,7 +336,7 @@ public class PixivURL {
|
||||
* @return 如果支持返回true
|
||||
*/
|
||||
public boolean isSupportedMode(RankingMode mode) {
|
||||
return Arrays.binarySearch(supportedMode, mode) != -1;
|
||||
return Arrays.binarySearch(supportedMode, mode) >= 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -135,7 +135,7 @@ public final class PixivUgoiraBuilder {
|
||||
image.getRGB(0, 0, image.getWidth(), image.getHeight(), rgb, 0, image.getWidth());
|
||||
log.trace("帧解析完成, 正在插入...");
|
||||
encoder.addImage(Image.fromRgb(rgb, image.getWidth()), new ImageOptions().setDelay(frameInfo.get("delay").getAsLong(), TimeUnit.MILLISECONDS));
|
||||
log.debug("帧 {} 插入完成.", frameFileName);
|
||||
log.trace("帧 {} 插入完成.", frameFileName);
|
||||
} catch (IOException e) {
|
||||
log.error("解析帧图片数据时发生异常", e);
|
||||
}
|
||||
|
39
src/main/resources/log4j2-test.xml
Normal file
39
src/main/resources/log4j2-test.xml
Normal file
@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration status="WARN">
|
||||
<properties>
|
||||
<property name="logStorePath">./logs</property>
|
||||
<property name="charset">UTF-8</property>
|
||||
<property name="pattern">[%-d{HH:mm:ss.SSS} %5level][%logger.%method():%-3L][%thread]: %msg%n</property>
|
||||
</properties>
|
||||
|
||||
<Appenders>
|
||||
<Console name="CONSOLE_STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${pattern}" charset="${charset}"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
<Console name="CONSOLE_STDERR" target="SYSTEM_ERR">
|
||||
<PatternLayout pattern="${pattern}" charset="${charset}"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="DENY"/>
|
||||
</Filters>
|
||||
</Console>
|
||||
|
||||
<RollingFile name="rollingFile" fileName="logs/latest.log" filePattern="logs/running.%-d{yyyy-MM-dd_HH-mm-ss}.log.gz">
|
||||
<PatternLayout pattern="${pattern}" charset="${charset}"/>
|
||||
<Policies>
|
||||
<OnStartupTriggeringPolicy />
|
||||
</Policies>
|
||||
</RollingFile>
|
||||
</Appenders>
|
||||
|
||||
<Loggers>
|
||||
<Logger level="INFO" name="org.apache.http"/>
|
||||
<Root level="TRACE">
|
||||
<AppenderRef ref="CONSOLE_STDOUT"/>
|
||||
<AppenderRef ref="CONSOLE_STDERR"/>
|
||||
<AppenderRef ref="rollingFile"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</configuration>
|
@ -10,7 +10,7 @@
|
||||
<Console name="CONSOLE_STDOUT" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${pattern}" charset="${charset}"/>
|
||||
<Filters>
|
||||
<ThresholdFilter level="WARN" onMatch="DENY" onMismatch="ACCEPT"/>
|
||||
<LevelRangeFilter minLevel="INFO" maxLevel="INFO" />
|
||||
</Filters>
|
||||
</Console>
|
||||
<Console name="CONSOLE_STDERR" target="SYSTEM_ERR">
|
||||
@ -30,7 +30,7 @@
|
||||
|
||||
<Loggers>
|
||||
<Logger level="INFO" name="org.apache.http"/>
|
||||
<Root level="DEBUG">
|
||||
<Root level="TRACE">
|
||||
<AppenderRef ref="CONSOLE_STDOUT"/>
|
||||
<AppenderRef ref="CONSOLE_STDERR"/>
|
||||
<AppenderRef ref="rollingFile"/>
|
||||
|
@ -0,0 +1,22 @@
|
||||
package net.lamgc.cgj.pixiv;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@Ignore
|
||||
public class ModerateContentDetectorTest {
|
||||
|
||||
private final static AdultContentDetector acd = new ModerateContentDetector("d91b6c3fa2bba9ee8f9e68827ba0d937");
|
||||
private final static Logger log = LoggerFactory.getLogger(ModerateContentDetector.class);
|
||||
|
||||
@Test
|
||||
public void checkTest() throws Exception {
|
||||
log.info("Detect: {}, isAdult: {}",
|
||||
acd.detect(80840411, false, 0),
|
||||
acd.isAdultContent(80840411, false, 0));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,8 +1,15 @@
|
||||
package net.lamgc.cgj.pixiv;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpHost;
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Ignore;
|
||||
@ -202,4 +209,17 @@ public class PixivDownloadTest {
|
||||
log.info(new PixivDownload(cookieStore, proxy).getIllustPreLoadDataById(64076261).toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void illustInfoTest() throws IOException {
|
||||
CloseableHttpClient httpClient = HttpClientBuilder.create().setProxy(new HttpHost("127.0.0.1", 1001)).build();
|
||||
HttpGet request = new HttpGet(PixivURL.getPixivIllustInfoAPI(80880547));
|
||||
CloseableHttpResponse response = httpClient.execute(request);
|
||||
String body = EntityUtils.toString(response.getEntity());
|
||||
JsonObject illustInfoResult = new Gson().fromJson(body, JsonObject.class);
|
||||
log.info("IllustInfoJsonResult: {}", illustInfoResult);
|
||||
PixivDownload.PixivIllustType illustType = PixivDownload.PixivIllustType.getIllustTypeByPreLoadData(80880547, illustInfoResult);
|
||||
log.info("IllustType: {}", illustType);
|
||||
Assert.assertEquals(PixivDownload.PixivIllustType.UGOIRA, illustType);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user