From 66866572c0efe7bd25eb7f1b9f065d1ccb3cab7d Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Tue, 9 Nov 2021 17:27:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AE=80=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E7=BE=A4=E7=BB=84=E6=B4=BB=E8=B7=83=E7=8A=B6=E6=80=81=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=99=A8=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=A8=8B=E5=BA=8F?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E7=BB=93=E6=9D=9F=E9=92=A9=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ./data/tracker///.txt - 每行一个发言时的timestamp - 0/0/currentTime 用于记录tracker的在线状态 --- build.gradle | 2 +- .../cc/sukazyo/cono/morny/MornyCoeur.java | 14 ++- .../morny/bot/api/EventListenerManager.java | 8 +- .../cono/morny/bot/event/EventListeners.java | 2 + .../morny/bot/event/OnActivityRecord.java | 25 ++++ .../data/tracker/TrackerDataManager.java | 118 ++++++++++++++++++ 6 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java create mode 100644 src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java diff --git a/build.gradle b/build.gradle index 8eb1d4e..20af079 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { } group 'cc.sukazyo' -version '0.1.2' +version '0.2.0' repositories { mavenCentral() diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index 26859e4..03ca922 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -2,7 +2,7 @@ package cc.sukazyo.cono.morny; import cc.sukazyo.cono.morny.bot.api.OnUpdate; import cc.sukazyo.cono.morny.bot.event.EventListeners; -import cc.sukazyo.cono.morny.data.MornyHello; +import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager; import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.request.GetMe; @@ -18,6 +18,8 @@ public class MornyCoeur { logger.info(MornyHello.MORNY_PREVIEW_IMAGE_ASCII); logger.info("System Starting"); + configureSafeExit(); + logger.info("args key:\n " + args[0]); try { account = login(args[0]); } @@ -25,6 +27,7 @@ public class MornyCoeur { logger.info("Bot login succeed."); + TrackerDataManager.init(); EventListeners.registerAllListeners(); account.setUpdatesListener(OnUpdate::onNormalUpdate); @@ -32,6 +35,15 @@ public class MornyCoeur { } + private static void exitCleanup () { + TrackerDataManager.DAEMON.interrupt(); + TrackerDataManager.trackingLock.lock(); + } + + private static void configureSafeExit () { + Runtime.getRuntime().addShutdownHook(new Thread(MornyCoeur::exitCleanup)); + } + public static TelegramBot login (String key) { TelegramBot account = new TelegramBot(key); logger.info("Trying to login..."); diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java index 5b1eeba..9b59bb1 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java @@ -25,9 +25,13 @@ public class EventListenerManager { @Override public void run () { for (EventListener x : listeners) { - if (exec.apply(x)) return; + try { + if (exec.apply(x)) return; + } catch (Exception e) { + logger.error("Event Error!"); + e.printStackTrace(System.out); + } } - logger.info("event exited undone"); } } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java index aaeefb4..bd26c6b 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java @@ -5,9 +5,11 @@ import cc.sukazyo.cono.morny.bot.api.EventListenerManager; public class EventListeners { public static final OnCommandExecute COMMANDS_LISTENER = new OnCommandExecute(); + public static final OnActivityRecord ACTIVITY_RECORDER = new OnActivityRecord(); public static void registerAllListeners () { EventListenerManager.addListener( + ACTIVITY_RECORDER, COMMANDS_LISTENER ); } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java new file mode 100644 index 0000000..adbd3b3 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java @@ -0,0 +1,25 @@ +package cc.sukazyo.cono.morny.bot.event; + +import cc.sukazyo.cono.morny.bot.api.EventListener; +import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager; +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.Update; + +public class OnActivityRecord extends EventListener { + + @Override + public boolean onMessage (Update update) { + if ( + update.message().chat().type() == Chat.Type.supergroup || + update.message().chat().type() == Chat.Type.group + ) { + TrackerDataManager.record( + update.message().chat().id(), + update.message().from().id(), + (long)update.message().date() * 1000 + ); + } + return super.onMessage(update); + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java b/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java new file mode 100644 index 0000000..6680b9f --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java @@ -0,0 +1,118 @@ +package cc.sukazyo.cono.morny.data.tracker; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.TreeSet; +import java.util.concurrent.locks.ReentrantLock; + +import static cc.sukazyo.cono.morny.Logger.logger; + +public class TrackerDataManager { + + public static final ReentrantLock trackingLock = new ReentrantLock(); + + private static final ReentrantLock recordLock = new ReentrantLock(); + private static HashMap>> record = new HashMap<>(); + + public static final TrackerDaemon DAEMON = new TrackerDaemon(); + public static class TrackerDaemon extends Thread { + + public TrackerDaemon () { this.setName("TRACKER"); } + + @Override + public void run () { + trackingLock.lock(); + long lastWaitTimestamp = System.currentTimeMillis(); + boolean postProcess = false; + do { + long sleeping = lastWaitTimestamp - System.currentTimeMillis(); + if (sleeping > 0) { + try { Thread.sleep(sleeping); } catch (InterruptedException e) { interrupt(); } + } else { + logger.warn("Tracker may be too busy to process data!!"); + lastWaitTimestamp = System.currentTimeMillis(); + } + if (interrupted()) { + postProcess = true; + logger.warn("last tracker write in this processor!"); + } + lastWaitTimestamp += 10 * 60 * 1000; + if (record.size() != 0) { + logger.info("start writing tracker data."); + save(reset()); + logger.info("done writing tracker data."); + } + else logger.info("nothing to do yet"); + } while (!postProcess); + trackingLock.unlock(); + } + + } + + public static void record (long chat, long user, long timestamp) { + recordLock.lock(); + if (!record.containsKey(chat)) record.put(chat, new HashMap<>()); + HashMap> chatUsers = record.get(chat); + if (!chatUsers.containsKey(user)) chatUsers.put(user, new TreeSet<>()); + TreeSet userRecords = chatUsers.get(user); + userRecords.add(timestamp); + recordLock.unlock(); + } + + public static void init () { + DAEMON.start(); + } + + private static HashMap>> reset () { + recordLock.lock(); + HashMap>> recordOld = record; + record = new HashMap<>(); + recordLock.unlock(); + return recordOld; + } + + private static void save (HashMap>> record) { + + record.forEach((chat, chatUsers) -> chatUsers.forEach((user, userRecords) -> { + + long dayCurrent = -1; + FileChannel channelCurrent = null; + + for (long timestamp : userRecords) { + try { + + long day = timestamp / (24 * 60 * 60 * 1000); + if (dayCurrent != day) { + if (channelCurrent != null) channelCurrent.close(); + channelCurrent = openFile(chat, user, day); + dayCurrent = day; + } + + assert channelCurrent != null; + channelCurrent.write(ByteBuffer.wrap( + String.format("%d\n", timestamp).getBytes(StandardCharsets.UTF_8) + )); + + } catch (Exception e) { + logger.error(String.format("exception in write tracker data: %d/%d/%d", chat, user, timestamp)); + e.printStackTrace(System.out); + } + } + })); + + } + + private static FileChannel openFile (long chat, long user, long day) throws IOException { + File data = new File(String.format("./data/tracker/%d/%d", chat, user)); + if (!data.isDirectory()) if (!data.mkdirs()) throw new IOException("Cannot create file directory " + data.getPath()); + File file = new File(data, String.valueOf(day)); + if (!file.isFile()) if (!file.createNewFile()) throw new IOException("Cannot create file " + file.getPath()); + return new FileOutputStream(file, true).getChannel(); + } + +}