added --dinner-chat startup param, code quality optimization.

- added `--dinner-chat`/`-chd` startup param for setup dinner chat id
- fixed some unclosed resource
  - TrackerDataManager changed method to get FileChannel (I don't know if it works)
  - MornyCLI scanner uses try-with-resources
- declared directly used dependencies
  - okhttp: 4.11.0
  - gson: 2.10.1
- some minor refactors
  - MornyCoeur.main rename to .init
  - added package `internal` and move @BuildConfigField to it
  - added new util class OkHttpPublic with MediaTypes in use
  - added javadoc for TrackerDataManager
  - and so on...
This commit is contained in:
A.C.Sukazyo Eyre 2023-08-20 23:11:10 +08:00
parent 69a33933f5
commit 7589e8661d
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
14 changed files with 133 additions and 69 deletions

View File

@ -4,7 +4,7 @@ plugins {
id 'application'
id 'maven-publish'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'com.github.gmazzo.buildconfig' version '3.1.0'
id 'com.github.gmazzo.buildconfig' version '4.1.2'
id 'org.ajoberstar.grgit' version '5.2.0'
}
@ -73,15 +73,16 @@ repositories {
dependencies {
compileOnlyApi "com.github.spotbugs:spotbugs-annotations:${libSpotbugsVersion}"
compileOnlyApi "com.github.spotbugs:spotbugs-annotations:${lib_spotbugs_v}"
api "cc.sukazyo:messiva:${libMessivaVersion}"
api "cc.sukazyo:messiva:${lib_messiva_v}"
implementation "com.github.pengrad:java-telegram-bot-api:${libJavaTelegramBotApiVersion}"
implementation "com.github.pengrad:java-telegram-bot-api:${lib_javatelegramapi_v}"
implementation "com.squareup.okhttp3:okhttp:${lib_okhttp_v}"
implementation "com.google.code.gson:gson:${lib_gson_v}"
testImplementation "org.junit.jupiter:junit-jupiter-api:${libJunitVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${libJunitVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${libJunitVersion}"
testImplementation platform("org.junit:junit-bom:${lib_junit_v}")
testImplementation "org.junit.jupiter:junit-jupiter"
}
@ -91,6 +92,9 @@ application {
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
java {

View File

@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
VERSION = 1.0.0-RC3.8
VERSION = 1.0.0-RC3.9
USE_DELTA = false
VERSION_DELTA =
@ -14,10 +14,13 @@ CODENAME = beiping
# dependencies
libSpotbugsVersion = 4.7.3
lib_spotbugs_v = 4.7.3
libMessivaVersion = 0.1.1
lib_messiva_v = 0.1.1
libJavaTelegramBotApiVersion = 6.2.0
lib_javatelegramapi_v = 6.2.0
libJunitVersion = 5.9.0
lib_okhttp_v = 4.11.0
lib_gson_v = 2.10.1
lib_junit_v = 5.10.0

View File

@ -118,7 +118,7 @@ public class MornyCoeur {
* @see #MornyCoeur 程序初始化方法
* @param config morny 实例的配置选项数据
*/
public static void main (MornyConfig config) {
public static void init (MornyConfig config) {
if (INSTANCE == null) {
logger.info("Coeur Starting");

View File

@ -169,12 +169,12 @@ public class MornyConfig {
public long eventOutdatedTimestamp = -1;
public boolean commandLoginRefresh = false;
public boolean commandLogoutClear = false;
@Nonnull public Set<Long> dinnerTrustedReaders = new HashSet<>();
@Nonnull public final Set<Long> dinnerTrustedReaders = new HashSet<>();
public long dinnerChatId = -1001707106392L;
public long reportToChat = -1001650050443L;
public long medicationNotifyToChat = -1001729016815L;
@Nonnull public ZoneOffset medicationTimerUseTimezone = ZoneOffset.UTC;
@Nonnull public Set<Integer> medicationNotifyAt = new HashSet<>();
@Nonnull public final Set<Integer> medicationNotifyAt = new HashSet<>();
}

View File

@ -1,7 +1,7 @@
package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.daemon.MornyReport;
import cc.sukazyo.cono.morny.util.BuildConfigField;
import cc.sukazyo.cono.morny.internal.BuildConfigField;
import cc.sukazyo.cono.morny.util.FileUtils;
import javax.annotation.Nonnull;

View File

@ -79,7 +79,7 @@ public class ServerMain {
* <b> {@code 0.5.0.4}旧的直接通过参数为 bot token & username 赋值的方式已被删除</b>
* 使用参数所进行取值的 token username 已被转移至 {@code --token} {@code --username} 参数<br>
*
* @see MornyCoeur#main
* @see MornyCoeur#init
* @since 0.4.0.0
* @param args 参数组
*/
@ -143,12 +143,17 @@ public class ServerMain {
config.trustedChat = Long.parseLong(args[i]);
continue;
}
//noinspection SpellCheckingInspection
// noinspection SpellCheckingInspection
case "--trusted-reader-dinner", "-trsd" -> {
i++;
config.dinnerTrustedReaders.add(Long.parseLong(args[i]));
continue;
}
case "--dinner-chat", "-chd" -> {
i++;
config.dinnerChatId = Long.parseLong(args[i]);
continue;
}
case "--auto-cmd", "-cmd", "-c" -> {
config.commandLoginRefresh = true;
config.commandLogoutClear = true;
@ -278,7 +283,7 @@ public class ServerMain {
Thread.currentThread().setName(THREAD_MORNY_INIT);
try {
MornyCoeur.main(new MornyConfig(config));
MornyCoeur.init(new MornyConfig(config));
} catch (MornyConfig.CheckFailure.NullTelegramBotKey ignore) {
logger.info("Parameter required has no value:\n --token.");
} catch (MornyConfig.CheckFailure e) {

View File

@ -37,50 +37,57 @@ public class EventHack implements ITelegramCommand {
@Override
public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
boolean isOk = false;
enum Status {
OK,
FORBIDDEN_FOR_ANY
}
Status status;
String x_mode = "";
if (command.hasArgs()) {
x_mode = command.getArgs()[0];
}
switch (x_mode) {
case "any":
case "any" -> {
if (MornyCoeur.trustedInstance().isTrusted(event.message().from().id())) {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.ANY
);isOk = true;
);
status = Status.OK;
} else {
status = Status.FORBIDDEN_FOR_ANY;
}
break;
case "group":
}
case "group" -> {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.GROUP
);isOk = true;
break;
default:
);
status = Status.OK;
}
default -> {
OnEventHackHandle.registerHack(
event.message().messageId(),
event.message().from().id(),
event.message().chat().id(),
OnEventHackHandle.HackType.USER
);isOk = true;
break;
);
status = Status.OK;
}
}
if (isOk) {
MornyCoeur.extra().exec(new SendSticker(
switch (status) {
case OK -> MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_WAITING
).replyToMessageId(event.message().messageId())
);
} else {
MornyCoeur.extra().exec(new SendSticker(
case FORBIDDEN_FOR_ANY -> MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_403
).replyToMessageId(event.message().messageId())

View File

@ -4,10 +4,10 @@ import cc.sukazyo.cono.morny.bot.api.EventListener;
public class OnRandomlyTriggered extends EventListener {
/**
* function CODE_IK0XA1
*/
// @Override
// /**
// * function CODE_IK0XA1
// */
// // @Override
// public boolean onMessage (@Nonnull Update update) {
//
// if (update.message().text() == null) return false;

View File

@ -1,11 +1,11 @@
package cc.sukazyo.cono.morny.daemon;
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.nio.file.StandardOpenOption;
import java.util.HashMap;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;
@ -15,9 +15,11 @@ import static cc.sukazyo.cono.morny.Log.logger;
public class TrackerDataManager {
/** {@link TrackerDaemon} 的锁。保证在程序中只有一个 TrackerDaemon 会运行。 */
public static final ReentrantLock trackingLock = new ReentrantLock();
/** {@link #record Tracker 缓存}的锁 <p> 为保证对 Tracker 缓存的操作不会造成线程冲突,在操作缓存数据前应先取得此锁。 */
private static final ReentrantLock recordLock = new ReentrantLock();
/** Tracker 数据的内存缓存 <p> 进行数据操作前请先取得对应的{@link #recordLock 锁} */
private static HashMap<Long, HashMap<Long, TreeSet<Long>>> record = new HashMap<>();
public static final TrackerDaemon DAEMON = new TrackerDaemon();
@ -55,6 +57,15 @@ public class TrackerDataManager {
}
/**
* Tracker 缓存写入一条 tracker 数据.
* <p>
* <font color=green>这个方法对于 Tracker 缓存是原子化的</font>
*
* @param chat tracker 所属的 Telegram Chat ID
* @param user tracker 所记录的 Telegram User ID
* @param timestamp tracker 被生成时的 UTC 时间戳
*/
public static void record (long chat, long user, long timestamp) {
recordLock.lock();
if (!record.containsKey(chat)) record.put(chat, new HashMap<>());
@ -65,16 +76,36 @@ public class TrackerDataManager {
recordLock.unlock();
}
/**
* 开启 {@link TrackerDaemon}.
* <p>
* <font color=orange>由于 Tracker 已废弃这个方法已无作用</font>
*/
@SuppressWarnings("unused")
public static void init () {
DAEMON.start();
}
/**
* 执行 Tracker 的保存逻辑.
* @see #reset() 弹出 Tracker 缓存
* @see #save(HashMap) 执行硬盘写入操作
*/
public static void save () {
logger.info("start writing tracker data.");
save(reset());
logger.info("done writing tracker data.");
}
/**
* Tracker 的缓存数据弹出.
* <p>
* 这个方法将返回现在 Tracker 的所有缓存数据然后清除缓存
* <p>
* <font color=green>这个方法对于 Tracker 缓存是原子化的</font>
*
* @return 当前 Tracker 所包含的内容
*/
private static HashMap<Long, HashMap<Long, TreeSet<Long>>> reset () {
recordLock.lock();
HashMap<Long, HashMap<Long, TreeSet<Long>>> recordOld = record;
@ -83,6 +114,11 @@ public class TrackerDataManager {
return recordOld;
}
/**
* Tracker 数据写入到硬盘.
*
* @param record 需要保存的 Tracker 数据集
*/
private static void save (HashMap<Long, HashMap<Long, TreeSet<Long>>> record) {
{
@ -109,10 +145,12 @@ public class TrackerDataManager {
}
assert channelCurrent != null;
channelCurrent.write(ByteBuffer.wrap(
final int result = channelCurrent.write(ByteBuffer.wrap(
String.format("%d\n", timestamp).getBytes(StandardCharsets.UTF_8)
));
if (result == 0) logger.warn("writing tracker data %d/%d/%d: write only 0 bytes! is anything wrong?");
} catch (Exception e) {
final String message = String.format("exception in write tracker data: %d/%d/%d", chat, user, timestamp);
logger.error(message);
@ -130,7 +168,7 @@ public class TrackerDataManager {
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();
return FileChannel.open(file.toPath(), StandardOpenOption.APPEND);
}
}

View File

@ -4,7 +4,7 @@ import java.io.IOException;
import com.google.gson.Gson;
import okhttp3.MediaType;
import cc.sukazyo.cono.morny.util.OkHttpPublic.MediaTypes;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
@ -23,21 +23,18 @@ public class NbnhhshQuery {
public Word[] words;
}
public record GuessReq (String text) {
}
public record GuessReq (String text) {}
public static final String API_URL = "https://lab.magiconch.com/api/nbnhhsh/";
public static final String API_GUESS_METHOD = "guess/";
public static final String API_GUESS_DATA_TEMPLATE = "{ \"text\": \"%s\" }";
private static final OkHttpClient httpClient = new OkHttpClient();
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
public static GuessResult sendGuess (String text) throws IOException {
final String reqJsonText = new Gson().toJson(new GuessReq(text));
Request request = new Request.Builder()
.url(API_URL + API_GUESS_METHOD)
.post(RequestBody.create(JSON, reqJsonText))
.post(RequestBody.create(reqJsonText, MediaTypes.JSON))
.build();
try (Response response = httpClient.newCall(request).execute()) {
final ResponseBody body = response.body();

View File

@ -1,4 +1,4 @@
package cc.sukazyo.cono.morny.util;
package cc.sukazyo.cono.morny.internal;
import java.lang.annotation.Documented;

View File

@ -0,0 +1,13 @@
package cc.sukazyo.cono.morny.util;
import okhttp3.MediaType;
public class OkHttpPublic {
public static class MediaTypes {
public static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
}
}

View File

@ -8,9 +8,9 @@ public class MornyCLI {
public static void main (String[] args) {
Scanner line = new Scanner(System.in);
System.out.print("$ java -jar morny-coeur-"+MornySystem.VERSION_FULL+".jar " );
String x = line.nextLine();
String x;
try (Scanner line = new Scanner(System.in)) { x = line.nextLine(); }
ServerMain.main(UniversalCommand.format(x));
}

View File

@ -1,16 +1,12 @@
package cc.sukazyo.cono.morny.util;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import org.junit.jupiter.params.provider.EnumSource;
import static cc.sukazyo.cono.morny.util.CommonConvert.byteArrayToHex;
import static cc.sukazyo.cono.morny.util.CommonConvert.byteToHex;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.params.provider.Arguments.arguments;
public class TestCommonConvert {
@ -30,21 +26,22 @@ public class TestCommonConvert {
assertEquals(expected, byteToHex(source));
}
public static Stream<Arguments> testByteArrayToHexProvider () {
return Stream.of(
arguments(new byte[]{0x00}, "00"),
arguments(new byte[]{(byte)0xff}, "ff"),
arguments(new byte[]{(byte)0xc3}, "c3"),
arguments(new byte[]{}, ""),
arguments(new byte[]{0x30,0x0a,0x00,0x04,(byte)0xb0,0x00}, "300a0004b000"),
arguments(new byte[]{0x00,0x00,0x0a,(byte)0xff,(byte)0xfc,(byte)0xab,(byte)0x00,0x04}, "00000afffcab0004"),
arguments(new byte[]{0x00,0x7c,0x11,0x28,(byte)0x88,(byte)0xa6,(byte)0xfc,0x30}, "007c112888a6fc30")
);
public enum TestByteArrayToHexSource {
$1(new T(new byte[]{0x00}, "00")),
$2(new T(new byte[]{(byte)0xff}, "ff")),
$3(new T(new byte[]{(byte)0xc3}, "c3")),
$4(new T(new byte[]{}, "")),
$5(new T(new byte[]{0x30,0x0a,0x00,0x04,(byte)0xb0,0x00}, "300a0004b000")),
$6(new T(new byte[]{0x00,0x00,0x0a,(byte)0xff,(byte)0xfc,(byte)0xab,(byte)0x00,0x04}, "00000afffcab0004")),
$7(new T(new byte[]{0x00,0x7c,0x11,0x28,(byte)0x88,(byte)0xa6,(byte)0xfc,0x30}, "007c112888a6fc30"));
public record T (byte[] raw, String expected) {}
public final T value;
TestByteArrayToHexSource (T value) { this.value = value; }
}
@ParameterizedTest
@MethodSource("testByteArrayToHexProvider")
void testByteArrayToHex (byte[] raw, String expected) {
assertEquals(expected, byteArrayToHex(raw));
@EnumSource
void testByteArrayToHex (TestByteArrayToHexSource source) {
assertEquals(source.value.expected, byteArrayToHex(source.value.raw));
}
}