Merge tag '0.2.0@mvn(c3)' into release

This commit is contained in:
A.C.Sukazyo Eyre 2021-12-24 18:29:52 +08:00
commit 7dee5a9d40
Signed by: Eyre_S
GPG Key ID: EFB47D98FE082FAD
8 changed files with 217 additions and 7 deletions

View File

@ -1,9 +1,18 @@
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.github.jengelman.gradle.plugins:shadow:5.0.0'
}
}
plugins {
id 'java'
}
group 'cc.sukazyo'
version '0.1.2'
version '0.2.0'
repositories {
mavenCentral()
@ -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)
}

View File

@ -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;
@ -11,19 +11,23 @@ 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) {
logger.info(MornyHello.MORNY_PREVIEW_IMAGE_ASCII);
logger.info("System Starting");
configureSafeExit();
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.");
TrackerDataManager.init();
EventListeners.registerAllListeners();
account.setUpdatesListener(OnUpdate::onNormalUpdate);
@ -31,13 +35,25 @@ 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...");
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);

View File

@ -1,4 +1,4 @@
package cc.sukazyo.cono.morny.data;
package cc.sukazyo.cono.morny;
@SuppressWarnings("all")
public class MornyHello {

View File

@ -25,9 +25,13 @@ public class EventListenerManager {
@Override
public void run () {
for (EventListener x : listeners) {
try {
if (exec.apply(x)) return;
} catch (Exception e) {
logger.error("Event Error!");
e.printStackTrace(System.out);
}
}
logger.info("event exited undone");
}
}

View File

@ -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
);
}

View File

@ -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);
}
}

View File

@ -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:

View File

@ -0,0 +1,127 @@
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<Long, HashMap<Long, TreeSet<Long>>> 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 {
lastWaitTimestamp += 10 * 60 * 1000;
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!");
}
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<Long, TreeSet<Long>> chatUsers = record.get(chat);
if (!chatUsers.containsKey(user)) chatUsers.put(user, new TreeSet<>());
TreeSet<Long> userRecords = chatUsers.get(user);
userRecords.add(timestamp);
recordLock.unlock();
}
public static void init () {
DAEMON.start();
}
private static HashMap<Long, HashMap<Long, TreeSet<Long>>> reset () {
recordLock.lock();
HashMap<Long, HashMap<Long, TreeSet<Long>>> recordOld = record;
record = new HashMap<>();
recordLock.unlock();
return recordOld;
}
private static void save (HashMap<Long, HashMap<Long, TreeSet<Long>>> record) {
{
if (!record.containsKey(0L)) record.put(0L, new HashMap<>());
HashMap<Long, TreeSet<Long>> chatUsers = record.get(0L);
if (!chatUsers.containsKey(0L)) chatUsers.put(0L, new TreeSet<>());
TreeSet<Long> userRecords = chatUsers.get(0L);
userRecords.add(System.currentTimeMillis());
}
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();
}
}