diff --git a/pom.xml b/pom.xml
index 8f026e4..546cdbe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,11 @@
commons-configuration2
2.9.0
+
+ org.jline
+ jline
+ 3.21.0
+
commons-beanutils
commons-beanutils
diff --git a/src/main/java/com/lingoutil/workcopilot/WorkCopilotApplication.java b/src/main/java/com/lingoutil/workcopilot/WorkCopilotApplication.java
index 16a2d9f..412e9a3 100644
--- a/src/main/java/com/lingoutil/workcopilot/WorkCopilotApplication.java
+++ b/src/main/java/com/lingoutil/workcopilot/WorkCopilotApplication.java
@@ -1,7 +1,15 @@
package com.lingoutil.workcopilot;
import com.lingoutil.workcopilot.handler.CommandHandler;
+import com.lingoutil.workcopilot.runner.CommandRunner;
import com.lingoutil.workcopilot.util.LogUtil;
+import org.jline.reader.EndOfFileException;
+import org.jline.reader.LineReader;
+import org.jline.reader.LineReaderBuilder;
+import org.jline.reader.UserInterruptException;
+import org.jline.reader.impl.history.DefaultHistory;
+import org.jline.terminal.Terminal;
+import org.jline.terminal.TerminalBuilder;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
@@ -9,19 +17,22 @@
import static com.lingoutil.workcopilot.constant.Constant.LOG_MODE;
import static com.lingoutil.workcopilot.constant.Constant.MODE_VERBOSE;
+import static com.lingoutil.workcopilot.util.LogUtil.RESET;
import static com.lingoutil.workcopilot.util.LogUtil.YELLOW;
public class WorkCopilotApplication {
public static void main(String[] args) {
- Scanner sc = new Scanner(System.in);
-
// 设置控制台输出为UTF-8编码
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
if (args.length == 1) {
- // just j command
- runWithMultiMode(sc);
+ if (CommandRunner.getOsType().equals(CommandRunner.MAC)) {
+ runWithMultiModeOnUnix();
+ }
+ else {
+ runWithMultiModeOnWin();
+ }
}
else {
// j another argument
@@ -52,7 +63,58 @@ private static void runWithSingleMode(String[] args) {
}
}
- private static void runWithMultiMode(Scanner sc) {
+ private static void runWithMultiModeOnUnix() {
+ try {
+ // 创建 JLine 终端和读取器
+ Terminal terminal = TerminalBuilder.builder().system(true).build();
+ LineReader reader = LineReaderBuilder.builder()
+ .terminal(terminal)
+ .history(new DefaultHistory())
+ .build();
+
+ LogUtil.info("Welcome to use work copilot \uD83D\uDE80 ~");
+ String prompt = YELLOW + "copilot > " + RESET; // 设置为亮黄色
+ while (true) {
+ try {
+ // 显示提示符并读取输入
+ String input = reader.readLine(prompt);
+ String[] args = ("j " + input).split("\\s+(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)");
+
+ boolean verboseMode = LOG_MODE.equals(MODE_VERBOSE);
+ if (verboseMode) {
+ LogUtil.log("verbose mode is start: %s", verboseMode);
+ long startTime = System.currentTimeMillis();
+ long endTime = 0;
+ if (!isValidArgsNum(args)) {
+ continue;
+ }
+ String command = args[1];
+ CommandHandler.execute(command, args);
+ endTime = System.currentTimeMillis();
+ LogUtil.log("duration: %s ms", endTime - startTime);
+ }
+ else {
+ if (!isValidArgsNum(args)) {
+ continue;
+ }
+ String command = args[1];
+ CommandHandler.execute(command, args);
+ }
+ System.out.println();
+ } catch (UserInterruptException e) {
+ LogUtil.info("\nProgram interrupted. Use 'exit' to quit.");
+ } catch (EndOfFileException e) {
+ LogUtil.info("\nGoodbye!");
+ break;
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void runWithMultiModeOnWin() {
+ Scanner sc = new Scanner(System.in);
String[] args;
LogUtil.info("Welcome to use work copilot \uD83D\uDE80 ~");
while (true) {
diff --git a/src/main/java/com/lingoutil/workcopilot/config/YamlConfig.java b/src/main/java/com/lingoutil/workcopilot/config/YamlConfig.java
index 4fc05b1..2d85a09 100644
--- a/src/main/java/com/lingoutil/workcopilot/config/YamlConfig.java
+++ b/src/main/java/com/lingoutil/workcopilot/config/YamlConfig.java
@@ -121,6 +121,7 @@ public static void addNestedProperty(String parentKey, String childKey, String v
String fullKey = parentKey + "." + childKey;
LogUtil.log("添加嵌套键值对: " + fullKey + " = " + value);
config.setProperty(fullKey, value);
+ // todo
propertyCache.put(fullKey, value);
propertiesMapCache.clear();
saveConfig();
diff --git a/src/main/java/com/lingoutil/workcopilot/constant/Constant.java b/src/main/java/com/lingoutil/workcopilot/constant/Constant.java
index f3fd36a..4485246 100644
--- a/src/main/java/com/lingoutil/workcopilot/constant/Constant.java
+++ b/src/main/java/com/lingoutil/workcopilot/constant/Constant.java
@@ -94,7 +94,7 @@ public class Constant {
public static final String CATEGORY_BROWSER = "browser";
public static final String CATEGORY_EDITOR = "editor";
public static final String CATEGORY_VPN = "vpn";
- public static final String CATEGORY_OUTER_URL = "outer-url";
+ public static final String CATEGORY_OUTER_URL = "outer_url";
public static String LOG_MODE = YamlConfig.initializeProperty(LOG, MODE);
diff --git a/src/main/java/com/lingoutil/workcopilot/handler/CommandHandler.java b/src/main/java/com/lingoutil/workcopilot/handler/CommandHandler.java
index 48be83b..aebd061 100644
--- a/src/main/java/com/lingoutil/workcopilot/handler/CommandHandler.java
+++ b/src/main/java/com/lingoutil/workcopilot/handler/CommandHandler.java
@@ -4,6 +4,7 @@
import com.lingoutil.workcopilot.scanner.CommandHandlerScanner;
import com.lingoutil.workcopilot.util.LogUtil;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -63,6 +64,25 @@ protected final boolean checkArgs(String[] argv, int expectedNum, Consumer errorHandler, int... expectedNums) {
+ int length = argv.length;
+ if (!containInArray(expectedNums,length)) {
+ LogUtil.error("expected argument num is %s, but got %d", Arrays.toString(expectedNums), length);
+ errorHandler.accept(argv);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean containInArray(int[] expected, int target) {
+ for (int i : expected) {
+ if (i == target) {
+ return true;
+ }
+ }
+ return false;
+ }
+
protected final boolean checkArgs(String[] argv, int expectedNum) {
return checkArgs(argv, expectedNum, this::hint);
}
diff --git a/src/main/java/com/lingoutil/workcopilot/handler/NoteCommandHandler.java b/src/main/java/com/lingoutil/workcopilot/handler/NoteCommandHandler.java
index eccf95c..55e170f 100644
--- a/src/main/java/com/lingoutil/workcopilot/handler/NoteCommandHandler.java
+++ b/src/main/java/com/lingoutil/workcopilot/handler/NoteCommandHandler.java
@@ -16,8 +16,6 @@ protected List loadCommandList() {
@Override
protected void process(String[] argv) {
String alias = argv[2];
-
- String path = YamlConfig.getProperty(PATH, alias);
if (!YamlConfig.containProperty(PATH, alias)
&& !YamlConfig.containProperty(INNER_URL, alias)
&& !YamlConfig.containProperty(OUTER_URL, alias)) {
@@ -25,6 +23,8 @@ protected void process(String[] argv) {
return;
}
+ String path = YamlConfig.getProperty(PATH, alias);
+
String category = argv[3];
switch (category) {
case CATEGORY_BROWSER -> {
@@ -40,7 +40,9 @@ protected void process(String[] argv) {
LogUtil.info("Add alias %s to VPN successfully", alias);
}
case CATEGORY_OUTER_URL -> {
+ path = YamlConfig.getProperty(INNER_URL, alias);
YamlConfig.addNestedProperty(category, alias, path);
+ YamlConfig.removeNestedProperty(INNER_URL, alias);
LogUtil.info("Add alias %s to OUTER_URL successfully", alias);
}
case SCRIPT -> {
diff --git a/src/main/java/com/lingoutil/workcopilot/handler/ReportCommandHandler.java b/src/main/java/com/lingoutil/workcopilot/handler/ReportCommandHandler.java
index 1ff8ef0..a8e490c 100644
--- a/src/main/java/com/lingoutil/workcopilot/handler/ReportCommandHandler.java
+++ b/src/main/java/com/lingoutil/workcopilot/handler/ReportCommandHandler.java
@@ -22,111 +22,134 @@
import static com.lingoutil.workcopilot.constant.Constant.*;
public class ReportCommandHandler extends CommandHandler {
- @Override
- protected List loadCommandList() {
- return reportCommands;
- }
-
- private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd");
-
- private Instant parseDate(String dateStr) throws ParseException {
- return LocalDate.parse(dateStr, DATE_FORMATTER).atStartOfDay(ZoneId.systemDefault()).toInstant();
- }
-
- private void updateWeekConfig(String reportPath, int weekNum, Date nextLastDayOfWeek) {
- try {
- String nextLastDayOfWeekStr = new SimpleDateFormat("yyyy.MM.dd").format(nextLastDayOfWeek);
- YamlConfig.addNestedProperty(REPORT, WEEK_NUM, String.valueOf(weekNum));
- YamlConfig.addNestedProperty(REPORT, LAST_DAY_OF_WEEK, nextLastDayOfWeekStr);
- LogUtil.info("✅ 更新配置文件成功:周数 = %d, 周结束日期 = %s", weekNum, nextLastDayOfWeekStr);
- }
- catch (Exception e) {
- LogUtil.error("❌ 更新配置文件时出错: %s", e.getMessage());
- }
- }
-
- @Override
- protected void process(String[] argv) {
- if (argv.length < 3) {
- LogUtil.error("❌ 缺少必要参数,请提供脚本名、命令和内容。");
- return;
- }
-
- String content = argv[2].trim();
-
- // 如果 content 被引号包围 "",去除引号
- if (content.startsWith("\"") && content.endsWith("\"")) {
- content = content.substring(1, content.length() - 1);
- }
-
- if (content.isEmpty()) {
- LogUtil.error("⚠️ 内容为空,无法写入。");
- return;
- }
-
- String reportPath = YamlConfig.getProperty(REPORT, WEEK_REPORT);
- LogUtil.info("📂 从配置文件中读取到路径:%s", reportPath);
-
- int weekNum = Integer.parseInt(YamlConfig.getProperty(REPORT, WEEK_NUM));
- String lastDayOfWeekStr = YamlConfig.getProperty(REPORT, LAST_DAY_OF_WEEK);
-
- File file = new File(reportPath);
- if (!file.exists()) {
- LogUtil.error("❌ 路径不存在:%s", reportPath);
- return;
- }
-
- Date now = new Date();
- try {
- Date lastDayOfWeek = new SimpleDateFormat("yyyy.MM.dd").parse(lastDayOfWeekStr);
-
- if (now.after(addOneDay(lastDayOfWeek))) {
- Calendar calendar = Calendar.getInstance();
- calendar.setTime(now);
- calendar.add(Calendar.DAY_OF_MONTH, 6);
- Date nextLastDayOfWeek = calendar.getTime();
- String newWeekTitle = String.format("# Week%d[%s-%s]\n", weekNum,
- DATE_FORMATTER.format(now.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()),
- DATE_FORMATTER.format(nextLastDayOfWeek.toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
- );
- weekNum++;
- updateWeekConfig(reportPath, weekNum, nextLastDayOfWeek);
- appendToFile(reportPath, newWeekTitle);
- }
-
- String todayStr = new SimpleDateFormat("yyyy/MM/dd").format(now);
- String logEntry = String.format("- 【%s】 %s\n", todayStr, content);
- appendToFile(reportPath, logEntry);
- LogUtil.info("✅ 成功将内容写入:%s", reportPath);
- }
- catch (Exception e) {
- LogUtil.error("❌ 操作时发生错误: %s", e.getMessage(), e);
- }
- }
-
- // 使用 UTF-8 编码的文件追加方法
- private void appendToFile(String filePath, String content) throws IOException {
- try (FileChannel channel = new FileOutputStream(filePath, true).getChannel()) {
- byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
- ByteBuffer buffer = ByteBuffer.wrap(bytes);
- channel.write(buffer);
- }
- }
-
- private Date addOneDay(Date date) {
- Calendar calendar = Calendar.getInstance();
- calendar.setTime(date);
- calendar.add(Calendar.DAY_OF_MONTH, 1);
- return calendar.getTime();
- }
-
- @Override
- protected boolean checkArgs(String[] argv) {
- return checkArgs(argv, 3, this::hint);
- }
-
- @Override
- protected void hint(String[] argv) {
- LogUtil.usage("%s %s ", argv[0], argv[1]);
- }
+ @Override
+ protected List loadCommandList() {
+ return reportCommands;
+ }
+
+ private final static String NEW_WEEK_CONFIG_UPDATE = "new";
+
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.MM.dd");
+
+ private Instant parseDate(String dateStr) throws ParseException {
+ return LocalDate.parse(dateStr, DATE_FORMATTER).atStartOfDay(ZoneId.systemDefault()).toInstant();
+ }
+
+ private void updateWeekConfig(int weekNum, Date nextLastDayOfWeek) {
+ try {
+ String nextLastDayOfWeekStr = new SimpleDateFormat("yyyy.MM.dd").format(nextLastDayOfWeek);
+ YamlConfig.addNestedProperty(REPORT, WEEK_NUM, String.valueOf(weekNum));
+ YamlConfig.addNestedProperty(REPORT, LAST_DAY_OF_WEEK, nextLastDayOfWeekStr);
+ LogUtil.info("✅ 更新配置文件成功:周数 = %d, 周结束日期 = %s", weekNum, nextLastDayOfWeekStr);
+ }
+ catch (Exception e) {
+ LogUtil.error("❌ 更新配置文件时出错: %s", e.getMessage());
+ }
+ }
+
+ @Override
+ protected void process(String[] argv) {
+ if (argv.length < 3) {
+ LogUtil.error("❌ 缺少必要参数,请提供脚本名、命令和内容。");
+ return;
+ }
+
+ String content = argv[2].trim();
+
+ // 如果 content 被引号包围 "",去除引号
+ if (content.startsWith("\"") && content.endsWith("\"")) {
+ content = content.substring(1, content.length() - 1);
+ }
+
+ if (content.isEmpty()) {
+ LogUtil.error("⚠️ 内容为空,无法写入。");
+ return;
+ }
+
+ int weekNum = Integer.parseInt(YamlConfig.getProperty(REPORT, WEEK_NUM));
+ String lastDayOfWeekStr = YamlConfig.getProperty(REPORT, LAST_DAY_OF_WEEK);
+
+ if (content.equals(NEW_WEEK_CONFIG_UPDATE)) {
+ String dataStr = argv.length == 4 ? argv[3] : lastDayOfWeekStr;
+ Date lastDayOfWeek = null;
+ Date nextLastDayOfWeek = null;
+
+ try {
+ lastDayOfWeek = new SimpleDateFormat("yyyy.MM.dd").parse(dataStr);
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(lastDayOfWeek);
+ calendar.add(Calendar.DAY_OF_MONTH, 7);
+ nextLastDayOfWeek = calendar.getTime();
+ }
+ catch (ParseException e) {
+ LogUtil.error("更新周数失败,请检查日期字符串是否有误");
+ return;
+ }
+ updateWeekConfig(weekNum + 1, nextLastDayOfWeek);
+ return;
+ }
+
+ String reportPath = YamlConfig.getProperty(REPORT, WEEK_REPORT);
+ LogUtil.info("📂 从配置文件中读取到路径:%s", reportPath);
+
+ File file = new File(reportPath);
+ if (!file.exists()) {
+ LogUtil.error("❌ 路径不存在:%s", reportPath);
+ return;
+ }
+
+ Date now = new Date();
+ try {
+ Date lastDayOfWeek = new SimpleDateFormat("yyyy.MM.dd").parse(lastDayOfWeekStr);
+
+ if (now.after(addOneDay(lastDayOfWeek))) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(now);
+ calendar.add(Calendar.DAY_OF_MONTH, 6);
+ Date nextLastDayOfWeek = calendar.getTime();
+ String newWeekTitle = String.format("# Week%d[%s-%s]\n", weekNum,
+ DATE_FORMATTER.format(now.toInstant().atZone(ZoneId.systemDefault()).toLocalDate()),
+ DATE_FORMATTER.format(nextLastDayOfWeek.toInstant().atZone(ZoneId.systemDefault()).toLocalDate())
+ );
+ weekNum++;
+ updateWeekConfig(weekNum, nextLastDayOfWeek);
+ appendToFile(reportPath, newWeekTitle);
+ }
+
+ String todayStr = new SimpleDateFormat("yyyy/MM/dd").format(now);
+ String logEntry = String.format("- 【%s】 %s\n", todayStr, content);
+ appendToFile(reportPath, logEntry);
+ LogUtil.info("✅ 成功将内容写入:%s", reportPath);
+ }
+ catch (Exception e) {
+ LogUtil.error("❌ 操作时发生错误: %s", e.getMessage(), e);
+ }
+ }
+
+ // 使用 UTF-8 编码的文件追加方法
+ private void appendToFile(String filePath, String content) throws IOException {
+ try (FileChannel channel = new FileOutputStream(filePath, true).getChannel()) {
+ byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
+ ByteBuffer buffer = ByteBuffer.wrap(bytes);
+ channel.write(buffer);
+ }
+ }
+
+ private Date addOneDay(Date date) {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ calendar.add(Calendar.DAY_OF_MONTH, 1);
+ return calendar.getTime();
+ }
+
+ @Override
+ protected boolean checkArgs(String[] argv) {
+ return checkArgs(argv, this::hint, 3, 4);
+ }
+
+ @Override
+ protected void hint(String[] argv) {
+ LogUtil.usage("%s %s ", argv[0], argv[1]);
+ LogUtil.usage("%s new [ in pattern yyyy.MM.dd]", argv[0], argv[1]);
+ }
}