From 976d106de92ca958e99cc13fd711a50b96509f5e Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Mon, 8 Nov 2021 20:17:42 +0800 Subject: [PATCH 1/4] =?UTF-8?q?@id=20=E5=91=BD=E4=BB=A4=E6=94=AF=E6=8C=81?= =?UTF-8?q?=EF=BC=88=E7=9B=B8=E5=BD=93=E6=9A=B4=E5=8A=9B=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=EF=BC=89=EF=BC=8C=E9=99=90=E5=88=B6=20@id=EF=BC=88?= =?UTF-8?q?=E4=B8=BA=E4=BA=86=E5=89=8D=E9=9D=A2=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java | 8 ++++++-- .../java/cc/sukazyo/cono/morny/{data => }/MornyHello.java | 2 +- .../cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java | 4 ++++ 3 files changed, 11 insertions(+), 3 deletions(-) rename src/main/java/cc/sukazyo/cono/morny/{data => }/MornyHello.java (99%) diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index 1b339ff..26859e4 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -11,6 +11,7 @@ import static cc.sukazyo.cono.morny.Logger.logger; public class MornyCoeur { private static TelegramBot account; + public static final String USERNAME = "morny_cono_annie_bot"; public static void main (String[] args) { @@ -20,7 +21,7 @@ public class MornyCoeur { logger.info("args key:\n " + args[0]); try { account = login(args[0]); } - catch (Exception e) { logger.error("Cannot login to bot/api."); System.exit(-1); } + catch (Exception e) { logger.error("Cannot login to bot/api. :\n " + e.getMessage()); System.exit(-1); } logger.info("Bot login succeed."); @@ -37,7 +38,10 @@ public class MornyCoeur { for (int i = 1; i < 4; i++) { if (i != 1) logger.info("retrying..."); try { - logger.info("Succeed login to @" + account.execute(new GetMe()).user().username()); + String username = account.execute(new GetMe()).user().username(); + if (!USERNAME.equals(username)) + throw new RuntimeException("Required the bot @"+USERNAME + " but @"+username + "logged in!"); + logger.info("Succeed login to @" + username); return account; } catch (Exception e) { e.printStackTrace(System.out); diff --git a/src/main/java/cc/sukazyo/cono/morny/data/MornyHello.java b/src/main/java/cc/sukazyo/cono/morny/MornyHello.java similarity index 99% rename from src/main/java/cc/sukazyo/cono/morny/data/MornyHello.java rename to src/main/java/cc/sukazyo/cono/morny/MornyHello.java index f8be6e3..4004403 100644 --- a/src/main/java/cc/sukazyo/cono/morny/data/MornyHello.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyHello.java @@ -1,4 +1,4 @@ -package cc.sukazyo.cono.morny.data; +package cc.sukazyo.cono.morny; @SuppressWarnings("all") public class MornyHello { diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java index 4f2b9ea..a6653d2 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java @@ -22,13 +22,17 @@ public class OnCommandExecute extends EventListener { } switch (event.message().text()) { case "/o": + case "/o@" + MornyCoeur.USERNAME: onCommandOnExec(event); break; case "/hi": + case "/hi@" + MornyCoeur.USERNAME: case "/hello": + case "/hello@" + MornyCoeur.USERNAME: onCommandHelloExec(event); break; case "/exit": + case "/exit@" + MornyCoeur.USERNAME: onCommandExitExec(event); break; default: From 66866572c0efe7bd25eb7f1b9f065d1ccb3cab7d Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Tue, 9 Nov 2021 17:27:11 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E7=AE=80=E5=8D=95?= =?UTF-8?q?=E7=9A=84=E7=BE=A4=E7=BB=84=E6=B4=BB=E8=B7=83=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E5=99=A8=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=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(); + } + +} From b06f7cb365e82edc67018cd402a479c65decf5cb Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Tue, 9 Nov 2021 17:33:56 +0800 Subject: [PATCH 3/4] shadowjar --- build.gradle | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/build.gradle b/build.gradle index 20af079..140c3c5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,3 +1,12 @@ +buildscript { + repositories { + mavenCentral() + } + dependencies { + classpath 'com.github.jengelman.gradle.plugins:shadow:5.0.0' + } +} + plugins { id 'java' } @@ -21,3 +30,26 @@ dependencies { test { useJUnitPlatform() } + +tasks.withType(JavaCompile) { + options.encoding = "UTF-8" +} + +tasks.withType(Javadoc) { + options.encoding = 'UTF-8' + options.docEncoding = 'UTF-8' + options.charSet = 'UTF-8' +} + +tasks.test { + useJUnitPlatform() +} + +apply plugin: 'com.github.johnrengelman.shadow' +apply plugin: 'application' + +shadowJar { + mainClassName = 'cc.sukazyo.cono.morny.MornyCoeur' + archiveBaseName.set("Morny_Coeur") + archiveVersion.set(version) +} From 14d3f76ac1f3be157a144f524261ad6c1c008524 Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Tue, 9 Nov 2021 18:16:30 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=BF=98=E8=AE=B0?= =?UTF-8?q?=E5=86=99=E7=9A=84=20tracker/0/0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cono/morny/data/tracker/TrackerDataManager.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 index 6680b9f..1695a24 100644 --- a/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java +++ b/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java @@ -30,6 +30,7 @@ public class TrackerDataManager { long lastWaitTimestamp = System.currentTimeMillis(); boolean postProcess = false; do { + lastWaitTimestamp += 10 * 60 * 1000; long sleeping = lastWaitTimestamp - System.currentTimeMillis(); if (sleeping > 0) { try { Thread.sleep(sleeping); } catch (InterruptedException e) { interrupt(); } @@ -41,7 +42,6 @@ public class TrackerDataManager { 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()); @@ -78,6 +78,14 @@ public class TrackerDataManager { private static void save (HashMap>> record) { + { + if (!record.containsKey(0L)) record.put(0L, new HashMap<>()); + HashMap> chatUsers = record.get(0L); + if (!chatUsers.containsKey(0L)) chatUsers.put(0L, new TreeSet<>()); + TreeSet userRecords = chatUsers.get(0L); + userRecords.add(System.currentTimeMillis()); + } + record.forEach((chat, chatUsers) -> chatUsers.forEach((user, userRecords) -> { long dayCurrent = -1; @@ -103,6 +111,7 @@ public class TrackerDataManager { e.printStackTrace(System.out); } } + })); }