Merge remote-tracking branch 'origin/master'

This commit is contained in:
LamGC 2020-05-22 20:34:32 +08:00
commit 33d18cef6b
13 changed files with 202 additions and 92 deletions

14
.gitignore vendored
View File

@ -1,6 +1,16 @@
# Ignore test date folder
/pluginData/ /pluginData/
/logs/ /logs/
/.idea/
/CGJ_2.iml
/cookies.store /cookies.store
/target/ /target/
# Ignore Idea files
/.idea/
/CGJ_2.iml
# Ignore Visual Studio Code files
.classpath
.factorypath
.project
/.settings/
/.vscode/

35
README.md Normal file
View File

@ -0,0 +1,35 @@
# ContentGrabbingJi
Pixiv爬虫一只同时也是一个机器人/插件!
## 支持的机器人平台 ##
- [Mirai](https://github.com/mamoe/mirai)
- [CoolQ](https://cqp.cc)(基于`SpringCQ`, 不支持多账号使用, 需要使用`CQHttp`插件)
## Usage ##
> 注意: 运行色图姬前, 你需要准备一个Pixiv账号的会话Cookie存储文件, 否则色图姬将无法运行.
### Arguments ###
- 通用参数
- `proxy` / `CGJ_PROXY`: 设置代理
- 格式: `协议://地址:端口`
- 示例: `socks5://127.0.0.1:1080`
- 机器人参数
- `botDataDir` / `CGJ_BOT_DATA_DIR`: 设置`botMode`运行模式下机器人数据存储目录
- 格式: `路径`
- 示例: `./data`
- 默认: `./`
- `redisAddress` / `CGJ_REDIS_URI`: Redis服务器地址
- 格式: `地址:端口`
- 示例: `127.0.0.1:6379`
### Commands ###
- `pluginMode`: CoolQ插件模式
- `botMode`: Mirai独立模式
- `collectionDownload`: 收藏下载, 以原图画质下载Cookie所属账号的所有收藏作品
- `getRecommends`: 将访问主页获得的推荐作品全部以原图画质下载
- `getRankingIllust`: 以原图画质下载指定排行榜类型的全部作品
- `search`: 搜索指定内容并获取相关作品信息(不下载)

View File

@ -6,7 +6,7 @@
<groupId>net.lamgc</groupId> <groupId>net.lamgc</groupId>
<artifactId>ContentGrabbingJi</artifactId> <artifactId>ContentGrabbingJi</artifactId>
<version>2.5.2-20200517.1-SNAPSHOT</version> <version>2.5.2-20200520.1-SNAPSHOT</version>
<repositories> <repositories>
<repository> <repository>

View File

@ -86,13 +86,15 @@ public class Main {
File cookieStoreFile = new File(System.getProperty("cgj.botDataDir"), "cookies.store"); File cookieStoreFile = new File(System.getProperty("cgj.botDataDir"), "cookies.store");
if(!cookieStoreFile.exists()) { if(!cookieStoreFile.exists()) {
log.warn("未找到cookies.store文件, 是否启动PixivLoginProxyServer? (yes/no)"); log.warn("未找到cookies.store文件, 是否启动PixivLoginProxyServer? (yes/no)");
Scanner scanner = new Scanner(System.in); try(Scanner scanner = new Scanner(System.in)) {
if(scanner.nextLine().trim().equalsIgnoreCase("yes")) { if(scanner.nextLine().trim().equalsIgnoreCase("yes")) {
startPixivLoginProxyServer(); startPixivLoginProxyServer();
} else { } else {
System.exit(1); System.exit(1);
return; return;
}
} }
} }
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(cookieStoreFile)); ObjectInputStream ois = new ObjectInputStream(new FileInputStream(cookieStoreFile));
cookieStore = (CookieStore) ois.readObject(); cookieStore = (CookieStore) ois.readObject();
@ -137,7 +139,9 @@ public class Main {
@Command @Command
public static void botMode(@Argument(name = "args", force = false) String argsStr) { public static void botMode(@Argument(name = "args", force = false) String argsStr) {
new MiraiMain().init(); MiraiMain main = new MiraiMain();
main.init();
main.close();
} }
@Command @Command
@ -423,23 +427,25 @@ public class Main {
proxyServerStartThread.setName("LoginProxyServerThread"); proxyServerStartThread.setName("LoginProxyServerThread");
proxyServerStartThread.start(); proxyServerStartThread.start();
//System.console().readLine(); //System.console().readLine();
Scanner scanner = new Scanner(System.in);
log.info("登录完成后, 使用\"done\"命令结束登录过程."); log.info("登录完成后, 使用\"done\"命令结束登录过程.");
while(true) { try(Scanner scanner = new Scanner(System.in)) {
if (scanner.nextLine().equalsIgnoreCase("done")) { while(true) {
log.info("关闭PLPS服务器..."); if (scanner.nextLine().equalsIgnoreCase("done")) {
proxyServer.close(); log.info("关闭PLPS服务器...");
cookieStore = proxyServer.getCookieStore(); proxyServer.close();
try { cookieStore = proxyServer.getCookieStore();
log.info("正在保存CookieStore..."); try {
saveCookieStoreToFile(); log.info("正在保存CookieStore...");
log.info("CookieStore保存完成."); saveCookieStoreToFile();
} catch (IOException e) { log.info("CookieStore保存完成.");
log.error("CookieStore保存时发生异常, 本次CookieStore仅可在本次运行使用.", e); } catch (IOException e) {
log.error("CookieStore保存时发生异常, 本次CookieStore仅可在本次运行使用.", e);
}
break;
} else {
log.warn("要结束登录过程, 请使用\"done\"命令.");
} }
break;
} else {
log.warn("要结束登录过程, 请使用\"done\"命令.");
} }
} }
} }

View File

@ -1,57 +0,0 @@
package net.lamgc.cgj.bot;
import net.lz1998.cq.robot.CoolQ;
import org.apache.http.client.methods.HttpGet;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class AutoArtworksSender {
private final CoolQ CQ;
private final ReceiveType receiveType;
private final long targetReceiveId;
private Timer timer = new Timer();
private TimerTask task = new TimerTask() {
@Override
public void run() {
HttpGet request = new HttpGet();
// https://api.imjad.cn/pixiv/v2/?type=tags
}
};
public AutoArtworksSender(CoolQ cq, ReceiveType receiveType, long receiveId) {
this.CQ = cq;
this.receiveType = receiveType;
this.targetReceiveId = receiveId;
}
public void reset(long time) {
if(time <= 0) {
timer.schedule(task, new Random().nextInt(10 * 60 * 60 * 1000) + 7200000L); //2H ~ 12H
} else {
timer.schedule(task, time);
}
}
public void sendMessage(String message, boolean auto_escape) {
switch (receiveType) {
case GROUP:
CQ.sendGroupMsg(targetReceiveId, message, auto_escape);
break;
case Discuss:
CQ.sendDiscussMsg(targetReceiveId, message, auto_escape);
break;
case PRIVATE:
CQ.sendPrivateMsg(targetReceiveId, message, auto_escape);
break;
}
}
public enum ReceiveType {
PRIVATE, GROUP, Discuss
}
}

View File

@ -7,10 +7,14 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import net.lamgc.cgj.Main; import net.lamgc.cgj.Main;
import net.lamgc.cgj.bot.cache.*; import net.lamgc.cgj.bot.cache.*;
import net.lamgc.cgj.bot.event.BotEventHandler; import net.lamgc.cgj.bot.event.BotEventHandler;
import net.lamgc.cgj.bot.event.BufferMessageEvent;
import net.lamgc.cgj.bot.sort.PreLoadDataComparator; import net.lamgc.cgj.bot.sort.PreLoadDataComparator;
import net.lamgc.cgj.pixiv.PixivDownload; import net.lamgc.cgj.pixiv.PixivDownload;
import net.lamgc.cgj.pixiv.PixivSearchBuilder; import net.lamgc.cgj.pixiv.PixivSearchBuilder;
import net.lamgc.cgj.pixiv.PixivURL; import net.lamgc.cgj.pixiv.PixivURL;
import net.lamgc.cgj.pixiv.PixivDownload.PageQuality;
import net.lamgc.cgj.pixiv.PixivURL.RankingContentType;
import net.lamgc.cgj.pixiv.PixivURL.RankingMode;
import net.lamgc.cgj.util.URLs; import net.lamgc.cgj.util.URLs;
import net.lamgc.utils.base.runner.Argument; import net.lamgc.utils.base.runner.Argument;
import net.lamgc.utils.base.runner.Command; import net.lamgc.utils.base.runner.Command;
@ -296,6 +300,18 @@ public class BotCommandProcess {
return "功能未完成"; return "功能未完成";
} }
@Command(commandName = "st")
public static String r18Image() {
BufferMessageEvent event = new BufferMessageEvent();
RandomRankingArtworksSender artworksSender =
new RandomRankingArtworksSender(event, 1, 200,
RankingMode.MODE_MALE,
RankingContentType.TYPE_ALL,
PageQuality.ORIGINAL);
artworksSender.send();
return event.getBufferMessage();
}
/** /**
* 搜索命令 * 搜索命令
* @param fromGroup 来源群(系统提供) * @param fromGroup 来源群(系统提供)

View File

@ -26,13 +26,19 @@ public enum MessageEventExecutionDebugger {
try { try {
rotation = Integer.parseInt(properties.getProperty("debug.pm.rotation", "5")); rotation = Integer.parseInt(properties.getProperty("debug.pm.rotation", "5"));
} catch(NumberFormatException ignored) {} } catch(NumberFormatException e) {
log.warn("配置项 {} 值无效, 将使用默认值.({})", "debug.pm.rotation", rotation);
}
try { try {
number = Integer.parseInt(properties.getProperty("debug.pm.number", "50")); number = Integer.parseInt(properties.getProperty("debug.pm.number", "50"));
} catch(NumberFormatException ignored) {} } catch(NumberFormatException e) {
log.warn("配置项 {} 值无效, 将使用默认值.({})", "debug.pm.number", number);
}
try { try {
interval = Integer.parseInt(properties.getProperty("debug.pm.interval", "2500")); interval = Integer.parseInt(properties.getProperty("debug.pm.interval", "2500"));
} catch(NumberFormatException ignored) {} } catch(NumberFormatException e) {
log.warn("配置项 {} 值无效, 将使用默认值.({})", "debug.pm.interval", interval);
}
boolean interrupted = false; boolean interrupted = false;
Thread currentThread = Thread.currentThread(); Thread currentThread = Thread.currentThread();

View File

@ -8,7 +8,6 @@ import java.util.Hashtable;
import java.util.Map; import java.util.Map;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
public final class ImageCacheStore { public final class ImageCacheStore {
@ -102,8 +101,6 @@ public final class ImageCacheStore {
public final AtomicReference<TaskState> taskState = new AtomicReference<>(TaskState.READY); public final AtomicReference<TaskState> taskState = new AtomicReference<>(TaskState.READY);
public final Condition condition = lock.newCondition();
} }
} }

View File

@ -12,19 +12,45 @@ import java.util.concurrent.atomic.AtomicReference;
* 基于Hashtable的本地缓存库 * 基于Hashtable的本地缓存库
* @param <T> 缓存类型 * @param <T> 缓存类型
*/ */
public class LocalHashCacheStore<T> implements CacheStore<T> { public class LocalHashCacheStore<T> implements CacheStore<T>, Cleanable {
private final Hashtable<String, CacheObject<T>> cache; private final Hashtable<String, CacheObject<T>> cache;
/**
* 构造一个基于Hashtable的本地缓存库
* @see Hashtable
*/
public LocalHashCacheStore() { public LocalHashCacheStore() {
this(0); this(0);
} }
/**
* 构造一个基于Hashtable的本地缓存库
* @param initialCapacity 初始容量
* @see Hashtable
*/
public LocalHashCacheStore(int initialCapacity) { public LocalHashCacheStore(int initialCapacity) {
this(initialCapacity, 0F); this(initialCapacity, 0F);
} }
/**
* 构造一个基于Hashtable的本地缓存库
* @param initialCapacity 初始容量
* @param loadFactor 重载因子
* @see Hashtable
*/
public LocalHashCacheStore(int initialCapacity, float loadFactor) { public LocalHashCacheStore(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, false);
}
/**
* 构造一个基于Hashtable的本地缓存库
* @param initialCapacity 初始容量
* @param loadFactor 重载因子
* @param autoClean 是否自动清理
* @see Hashtable
*/
public LocalHashCacheStore(int initialCapacity, float loadFactor, boolean autoClean) {
if(initialCapacity != 0) { if(initialCapacity != 0) {
if(loadFactor <= 0F) { if(loadFactor <= 0F) {
cache = new Hashtable<>(initialCapacity); cache = new Hashtable<>(initialCapacity);
@ -34,6 +60,10 @@ public class LocalHashCacheStore<T> implements CacheStore<T> {
} else { } else {
cache = new Hashtable<>(); cache = new Hashtable<>();
} }
if(autoClean) {
AutoCleanTimer.add(this);
}
} }
@Override @Override
@ -118,6 +148,15 @@ public class LocalHashCacheStore<T> implements CacheStore<T> {
return false; return false;
} }
@Override
public void clean() throws Exception {
Date currentDate = new Date();
cache.forEach((key, value) -> {
if(value.isExpire(currentDate)) {
cache.remove(key);
}
});
}
public static class CacheObject<T> implements Comparable<CacheObject<T>> { public static class CacheObject<T> implements Comparable<CacheObject<T>> {

View File

@ -224,7 +224,7 @@ public class BotEventHandler implements EventHandler {
} }
} }
long processTime = System.currentTimeMillis() - time; long processTime = System.currentTimeMillis() - time;
if(Objects.requireNonNull(result) instanceof String && !isMute(event.getFromGroup())) { if(!Objects.isNull(result) && result instanceof String && !isMute(event.getFromGroup())) {
try { try {
event.sendMessage((String) result); event.sendMessage((String) result);
} catch (Exception e) { } catch (Exception e) {

View File

@ -0,0 +1,59 @@
package net.lamgc.cgj.bot.event;
import java.util.Objects;
public class BufferMessageEvent extends MessageEvent {
StringBuffer buffer = new StringBuffer();
public final MessageEvent parent;
/**
* 以空消息空Id生成BufferMessageEvent
*/
public BufferMessageEvent() {
super(0, 0, "");
parent = null;
}
/**
* 提供消息内容构造BufferMessageEvent
* @param message 传入的消息内容
*/
public BufferMessageEvent(String message) {
super(0, 0, message);
parent = null;
}
/**
* 使用事件构造BufferMessageEvent
* @param parentEvent 父级消息事件对象
*/
public BufferMessageEvent(MessageEvent parentEvent) {
super(parentEvent.getFromGroup(), parentEvent.getFromQQ(), parentEvent.getMessage());
parent = parentEvent;
}
@Override
public int sendMessage(String message) {
buffer.append(message);
return 0;
}
/**
* 当提供了父级消息事件时, 本方法调用父级消息事件对象的{@code getImageUrl(String)}, 如果没有, 返回{@code null}
*/
@Override
public String getImageUrl(String image) {
return Objects.isNull(this.parent) ? null : this.parent.getImageUrl(image);
}
/**
* 获取缓冲区消息内容
* @return 消息内容
*/
public String getBufferMessage() {
return buffer.toString();
}
}

View File

@ -99,7 +99,7 @@ public class PixivAccessProxyServer {
}*/ }*/
log.info("Response Cookie: " + value); log.info("Response Cookie: " + value);
BasicClientCookie cookie = parseRawCookie(value); BasicClientCookie cookie = parseRawCookie(value);
cookieStore.addCookie(null); cookieStore.addCookie(cookie);
}); });
httpResponse.headers().remove(HttpHeaderNames.SET_COOKIE); httpResponse.headers().remove(HttpHeaderNames.SET_COOKIE);
super.afterResponse(clientChannel, proxyChannel, httpResponse, pipeline); super.afterResponse(clientChannel, proxyChannel, httpResponse, pipeline);

View File

@ -1,7 +1,6 @@
package net.lamgc.cgj.util; package net.lamgc.cgj.util;
import net.lamgc.cgj.pixiv.PixivDownload; import net.lamgc.cgj.pixiv.PixivDownload;
import net.lamgc.cgj.pixiv.PixivURL;
import net.lamgc.utils.base.runner.StringParameterParser; import net.lamgc.utils.base.runner.StringParameterParser;
public class PagesQualityParser implements StringParameterParser<PixivDownload.PageQuality> { public class PagesQualityParser implements StringParameterParser<PixivDownload.PageQuality> {