diff --git a/src/main/java/net/lamgc/cgj/bot/AutoSender.java b/src/main/java/net/lamgc/cgj/bot/AutoSender.java new file mode 100644 index 0000000..e450a84 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/AutoSender.java @@ -0,0 +1,30 @@ +package net.lamgc.cgj.bot; + +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(); + +} diff --git a/src/main/java/net/lamgc/cgj/bot/RandomIntervalSendTimer.java b/src/main/java/net/lamgc/cgj/bot/RandomIntervalSendTimer.java new file mode 100644 index 0000000..5729650 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/RandomIntervalSendTimer.java @@ -0,0 +1,70 @@ +package net.lamgc.cgj.bot; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.util.Random; +import java.util.Timer; +import java.util.TimerTask; +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 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(); + + + public RandomIntervalSendTimer(AutoSender sender, long time, int floatTime) { + this.sender = sender; + this.time = time; + this.floatTime = floatTime; + } + + public void start() { + start(this.loop.get()); + } + + public void start(boolean loop) { + this.loop.set(loop); + long nextDelay = time + timeRandom.nextInt(floatTime); + log.info("定时器 {} 下一延迟: {}ms", Integer.toHexString(this.hashCode()), nextDelay); + 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() { + sender.send(); + if (this.loop.get()) { + start(); + } + } + + @Override + public boolean cancel() { + start.set(false); + return super.cancel(); + } + +} diff --git a/src/main/java/net/lamgc/cgj/bot/RandomRankingArtworksSender.java b/src/main/java/net/lamgc/cgj/bot/RandomRankingArtworksSender.java new file mode 100644 index 0000000..d1784f1 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/RandomRankingArtworksSender.java @@ -0,0 +1,85 @@ +package net.lamgc.cgj.bot; + +import com.google.gson.JsonObject; +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 PixivDownload.PageQuality quality; + + /** + * 构造一个推荐作品发送器 + * @param messageSender 消息发送器 + * @param rankingStart 排行榜开始范围(从1开始, 名次) + * @param rankingStop 排名榜结束范围(包括该名次) + * @param quality 图片质量, 详见{@link net.lamgc.cgj.pixiv.PixivDownload.PageQuality} + */ + public RandomRankingArtworksSender(MessageSender messageSender, int rankingStart, int rankingStop, PixivDownload.PageQuality quality) { + super(messageSender); + log = LoggerFactory.getLogger("RecommendArtworksSender@" + Integer.toHexString(this.hashCode())); + this.rankingStart = rankingStart > 0 ? rankingStart : 1; + this.rankingStop = rankingStop > 0 ? rankingStop : 150; + 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 + 1); + try { + List rankingList = BotCommandProcess.getRankingInfoByCache( + PixivURL.RankingContentType.TYPE_ILLUST, + PixivURL.RankingMode.MODE_DAILY, + 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; + } + + 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)); + + getMessageSender().sendMessage(message.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/net/lamgc/cgj/pixiv/PixivDownload.java b/src/main/java/net/lamgc/cgj/pixiv/PixivDownload.java index 9614821..5f158d0 100644 --- a/src/main/java/net/lamgc/cgj/pixiv/PixivDownload.java +++ b/src/main/java/net/lamgc/cgj/pixiv/PixivDownload.java @@ -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 getRanking(List rankingList, int rankStart, int range) { log.debug("正在读取JsonArray...(rankStart: {}, range: {})", rankStart, range); ArrayList 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 getRanking(JsonArray rankingArray, int rankStart, int range) { - log.debug("正在读取JsonArray...(rankStart: {}, range: {})", rankStart, range); - ArrayList 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 list = new Gson().fromJson(rankingArray, new TypeToken>(){}.getType()); + return getRanking(list, rankStart, range); } /** @@ -304,65 +292,44 @@ 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 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 { + 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 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 results = new ArrayList<>(range); + for (int pageIndex = startPages; pageIndex <= endPages && count < range; pageIndex++) { + HttpGet request = createHttpGetRequest(PixivURL.getRankingLink(contentType, mode, time, pageIndex, true)); 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; + + if(response.getStatusLine().getStatusCode() != 200) { + throw new IOException("Http Response Error: " + response.getStatusLine()); } - 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(EntityUtils.toString(response.getEntity()), 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; }