diff --git a/src/main/java/net/lamgc/cgj/Main.java b/src/main/java/net/lamgc/cgj/Main.java index dcb9cd1..cafb055 100644 --- a/src/main/java/net/lamgc/cgj/Main.java +++ b/src/main/java/net/lamgc/cgj/Main.java @@ -7,6 +7,8 @@ import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import net.lamgc.cgj.bot.CQConfig; +import net.lamgc.cgj.bot.MiraiMain; import net.lamgc.cgj.pixiv.*; import net.lamgc.cgj.proxy.PixivAccessProxyServer; import net.lamgc.cgj.proxy.PixivLoginProxyServer; @@ -49,12 +51,6 @@ public class Main { public static HttpHost proxy; - static { - if(!storeDir.exists() && !storeDir.mkdirs()) { - log.error("创建文件夹失败!"); - } - } - public static void main(String[] args) throws IOException, ClassNotFoundException { ArgumentsProperties argsProp = new ArgumentsProperties(args); if(argsProp.containsKey("proxy")) { @@ -65,11 +61,16 @@ public class Main { proxy = null; } + if(!storeDir.exists() && !storeDir.mkdirs()) { + log.error("创建文件夹失败!"); + } + + // TODO: 需要修改参数名了, 大概改成类似于 workerDir这样的吧 if(argsProp.containsKey("cqRootDir")) { log.info("cqRootDir: {}", argsProp.getValue("cqRootDir")); System.setProperty("cgj.cqRootDir", argsProp.getValue("cqRootDir")); } else { - log.info("未设置cqRootDir, 当前运行目录将作为酷Q机器人所在目录."); + log.warn("未设置cqRootDir, 当前运行目录将作为酷Q机器人所在目录."); System.setProperty("cgj.cqRootDir", "./"); } @@ -97,6 +98,11 @@ public class Main { ArgumentsRunner.run(Main.class, args); } + @Command + public static void botMode(@Argument(name = "args", force = false) String argsStr) { + new MiraiMain().init(); + } + @Command public static void pluginMode(@Argument(name = "args", force = false) String argsStr) { if(!System.getProperty("cgj.cqRootDir").endsWith("\\") && !System.getProperty("cgj.cqRootDir").endsWith("/")) { diff --git a/src/main/java/net/lamgc/cgj/bot/BotCode.java b/src/main/java/net/lamgc/cgj/bot/BotCode.java new file mode 100644 index 0000000..62c3243 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/BotCode.java @@ -0,0 +1,158 @@ +package net.lamgc.cgj.bot; + +import com.google.common.base.Strings; + +import java.util.*; +import java.util.regex.Pattern; + +public class BotCode { + + public static BotCode build(String platformName, String functionName) { + return build(platformName, functionName, null); + } + + public static BotCode build(String platformName, String functionName, Map parameter) { + if(Strings.isNullOrEmpty(platformName)) { + throw new IllegalArgumentException("platformName is Null or Empty."); + } else if(Strings.isNullOrEmpty(functionName)) { + throw new IllegalArgumentException("functionName is Null or Empty."); + } + + return new BotCode(platformName, functionName, parameter); + } + + private final static Pattern codePattern = Pattern.compile("\\[.*?:.*?]"); + public static BotCode parse(String str) { + if (!codePattern.matcher(str).matches()) { + throw new IllegalArgumentException("invalid string input: " + str); + } + + String text = str.substring(1, str.length() - 1); + String[] texts = text.split(","); + if(texts.length <= 0) { + throw new IllegalArgumentException("invalid string input: " + str); + } + + String[] keys = texts[0].split(":", 2); + if(keys.length != 2) { + throw new IllegalArgumentException("invalid string input: " + str); + } + if(Strings.isNullOrEmpty(keys[0]) || Strings.isNullOrEmpty(keys[1])) { + throw new IllegalArgumentException("invalid string input: " + str); + } + + HashMap param = new HashMap<>(texts.length - 1); + for (int i = 1; i < texts.length; i++) { + String[] items = texts[i].split("="); + if(items.length != 2) { + continue; + } + + param.put(items[0].trim(), items[1]); + } + + return new BotCode(keys[0], keys[1], param); + } + + private String platformName; + private String functionName; + private Hashtable parameter = new Hashtable<>(); + + /** + * 构造一个机器功能码 + * @param platformName 平台代码 + * @param functionName 功能名 + * @param parameter 参数Map + */ + private BotCode(String platformName, String functionName, Map parameter) { + this.platformName = platformName; + this.functionName = functionName; + if(parameter != null && !parameter.isEmpty()) { + this.parameter.putAll(parameter); + } + } + + /** + * 设置平台代码 + * @param platformName 欲设置的平台代码 + */ + public void setPlatformName(String platformName) { + this.platformName = platformName; + } + + /** + * 获取平台代码 + * @return 当前设置的平台代码 + */ + public String getPlatformName() { + return platformName; + } + + /** + * 设置功能名 + * @param functionName 欲设置的新功能名 + */ + public void setFunctionName(String functionName) { + this.functionName = functionName; + } + + /** + * 获取功能名 + * @return 返回当前设置的功能名 + */ + public String getFunctionName() { + return functionName; + } + + /** + * 添加参数 + * @param key 参数键 + * @param value 参数值 + */ + public void addParameter(String key, String value) { + parameter.put(key.trim(), value); + } + + /** + * 检查一个参数项是否存在 + * @param key 欲查询其存在的参数项所属键 + * @return 返回true则参数项存在 + */ + public boolean containsParameter(String key) { + return parameter.containsKey(key.trim()); + } + + /** + * 获取参数项的值 + * @param key 欲获取参数值的参数项所属键 + * @return 返回参数项的参数值 + */ + public String getParameter(String key) { + return parameter.get(key.trim()); + } + + /** + * 获取所有参数项的参数键 + * @return 返回存储了所有参数键的Set对象 + */ + public Set parameterKeys() { + return new HashSet<>(parameter.keySet()); + } + + /** + * 将BotCode对象转为功能代码文本 + * 格式: + *
[Platform:Function, parameter...]
+ * @return 功能代码文本 + */ + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[" + platformName + ":" + functionName); + if(!parameter.isEmpty()) { + builder.append(", "); + parameter.forEach((key, value) -> builder.append(key).append("=").append(value).append(", ")); + builder.replace(builder.length() - 2, builder.length(), ""); + } + return builder.append("]").toString(); + } +} diff --git a/src/main/java/net/lamgc/cgj/bot/MiraiMain.java b/src/main/java/net/lamgc/cgj/bot/MiraiMain.java new file mode 100644 index 0000000..e64ace8 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/MiraiMain.java @@ -0,0 +1,52 @@ +package net.lamgc.cgj.bot; + +import net.lamgc.cgj.bot.event.BotEventHandler; +import net.lamgc.cgj.bot.event.MiraiMessageEvent; +import net.mamoe.mirai.Bot; +import net.mamoe.mirai.japt.Events; +import net.mamoe.mirai.message.FriendMessage; +import net.mamoe.mirai.message.GroupMessage; +import net.mamoe.mirai.qqandroid.QQAndroid; +import net.mamoe.mirai.utils.BotConfiguration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.Properties; + +public class MiraiMain implements Closeable { + + private final Logger log = LoggerFactory.getLogger(this.toString()); + + private Bot bot; + + private final static Properties botProperties = new Properties(); + + public void init() { + try { + Class.forName(BotEventHandler.class.getName()); + } catch (ClassNotFoundException e) { + log.error("加载BotEventHandler时发生异常", e); + return; + } + + File botPropFile = new File("./bot.properties"); + try (Reader reader = new BufferedReader(new FileReader(botPropFile))) { + botProperties.load(reader); + } catch (IOException e) { + log.error("机器人配置文件读取失败!", e); + return; + } + + bot = QQAndroid.INSTANCE.newBot(Long.parseLong(botProperties.getProperty("bot.qq", "0")), botProperties.getProperty("bot.password", ""), new BotConfiguration()); + Events.subscribeAlways(GroupMessage.class, (msg) -> BotEventHandler.executor.executor(new MiraiMessageEvent(msg))); + Events.subscribeAlways(FriendMessage.class, (msg) -> BotEventHandler.executor.executor(new MiraiMessageEvent(msg))); + bot.login(); + bot.join(); + } + + public void close() { + bot.close(null); + } + +} diff --git a/src/main/java/net/lamgc/cgj/bot/event/BotEventHandler.java b/src/main/java/net/lamgc/cgj/bot/event/BotEventHandler.java new file mode 100644 index 0000000..c7a177d --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/event/BotEventHandler.java @@ -0,0 +1,174 @@ +package net.lamgc.cgj.bot.event; + +import com.google.common.base.Strings; +import com.google.common.base.Throwables; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import net.lamgc.cgj.bot.BotAdminCommandProcess; +import net.lamgc.cgj.bot.BotCommandProcess; +import net.lamgc.cgj.util.DateParser; +import net.lamgc.cgj.util.PagesQualityParser; +import net.lamgc.utils.base.runner.ArgumentsRunner; +import net.lamgc.utils.base.runner.ArgumentsRunnerConfig; +import net.lamgc.utils.base.runner.exception.DeveloperRunnerException; +import net.lamgc.utils.base.runner.exception.NoSuchCommandException; +import net.lamgc.utils.base.runner.exception.ParameterNoFoundException; +import net.lamgc.utils.event.EventExecutor; +import net.lamgc.utils.event.EventHandler; +import net.lamgc.utils.event.EventObject; +import net.lamgc.utils.event.EventUncaughtExceptionHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import redis.clients.jedis.JedisPool; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.Method; +import java.net.URI; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BotEventHandler implements EventHandler { + + public final static String COMMAND_PREFIX = ".cgj"; + + private final ArgumentsRunner processRunner; + private final ArgumentsRunner adminRunner; + + private final Logger log = LoggerFactory.getLogger("BotEventHandler@" + Integer.toHexString(this.hashCode())); + + /** + * 所有缓存共用的JedisPool + */ + private final static URI redisServerUri = URI.create("redis://" + System.getProperty("cgj.redisAddress")); + public final static JedisPool redisServer = new JedisPool(redisServerUri.getHost(), redisServerUri.getPort() == -1 ? 6379 : redisServerUri.getPort()); + + /** + * 消息事件执行器 + */ + public final static EventExecutor executor = new EventExecutor(new ThreadPoolExecutor( + (int) Math.ceil(Runtime.getRuntime().availableProcessors() / 2F), + Runtime.getRuntime().availableProcessors(), + 30L, + TimeUnit.SECONDS, + new LinkedBlockingQueue<>(1536), + new ThreadFactoryBuilder() + .setNameFormat("CommandProcess-%d") + .build() + )); + + static { + executor.setEventUncaughtExceptionHandler(new EventUncaughtExceptionHandler() { + private final Logger log = LoggerFactory.getLogger("EventUncaughtExceptionHandler"); + @Override + public void exceptionHandler(Thread executeThread, EventHandler handler, Method handlerMethod, EventObject event, Throwable cause) { + log.error("发生未捕获异常:\nThread:{}, EventHandler: {}, HandlerMethod: {}, EventObject: {}\n{}", + executeThread.getName(), + handler.toString(), + handlerMethod.getName(), + event.toString(), + Throwables.getStackTraceAsString(cause)); + } + }); + try { + executor.addHandler(new BotEventHandler()); + executor.addHandler(new TestEventHandler()); + } catch (IllegalAccessException e) { + LoggerFactory.getLogger("BotEventHandler@Static").error("添加Handler时发生异常", e); + } + } + + private BotEventHandler() { + ArgumentsRunnerConfig runnerConfig = new ArgumentsRunnerConfig(); + runnerConfig.setUseDefaultValueInsteadOfException(true); + runnerConfig.setCommandIgnoreCase(true); + runnerConfig.addStringParameterParser(new DateParser(new SimpleDateFormat("yyyy-MM-dd"))); + runnerConfig.addStringParameterParser(new PagesQualityParser()); + + log.debug("DateParser添加情况: {}", runnerConfig.hasStringParameterParser(Date.class)); + + processRunner = new ArgumentsRunner(BotCommandProcess.class, runnerConfig); + adminRunner = new ArgumentsRunner(BotAdminCommandProcess.class, runnerConfig); + + BotCommandProcess.initialize(); + } + + public void processMessage(MessageEvent event) { + String msg = event.getMessage(); + if(!match(msg)) { + return; + } + + Pattern pattern = Pattern.compile("/\\s*(\".+?\"|[^:\\s])+((\\s*:\\s*(\".+?\"|[^\\s])+)|)|(\".+?\"|[^\"\\s])+"); + Matcher matcher = pattern.matcher(Strings.nullToEmpty(msg)); + ArrayList argsList = new ArrayList<>(); + while (matcher.find()) { + String arg = matcher.group(); + int startIndex = 0; + int endIndex = arg.length(); + if(arg.startsWith("\"")) { + while(arg.indexOf("\"", startIndex) == startIndex) { + startIndex++; + } + } + + if(arg.endsWith("\"")) { + while(arg.charAt(endIndex - 1) == '\"') { + endIndex--; + } + } + + argsList.add(arg.substring(startIndex, endIndex)); + } + String[] args = new String[argsList.size()]; + argsList.toArray(args); + log.debug("传入参数: {}", Arrays.toString(args)); + + log.info("正在处理命令..."); + long time = System.currentTimeMillis(); + Object result; + try { + if(msg.toLowerCase().startsWith(COMMAND_PREFIX + "admin")) { + if(!String.valueOf(event.getFromQQ()).equals(BotCommandProcess.globalProp.getProperty("admin.adminId"))) { + event.sendMessage("你没有执行该命令的权限!"); + return; + } else { + result = adminRunner.run(new BotAdminCommandProcess(), args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length)); + } + } else { + result = processRunner.run(args.length <= 1 ? new String[0] : Arrays.copyOfRange(args, 1, args.length)); + } + } catch(NoSuchCommandException e) { + result = "没有这个命令!请使用“.cgj”查看帮助说明!"; + } catch(ParameterNoFoundException e) { + result = "命令缺少参数: " + e.getParameterName(); + } catch(DeveloperRunnerException e) { + log.error("执行命令时发生异常", e); + result = "命令执行时发生错误,无法完成!"; + } + log.info("命令处理完成.(耗时: {}ms)", System.currentTimeMillis() - time); + if(Objects.requireNonNull(result) instanceof String) { + try { + event.sendMessage((String) result); + } catch (Exception e) { + log.error("发送消息时发生异常", e); + } + } + log.info("命令反馈完成.(耗时: {}ms)", System.currentTimeMillis() - time); + } + + /** + * 检查消息是否需要提交 + * @param message 要检查的消息 + * @return 如果为true则提交 + */ + public static boolean match(String message) { + return message.startsWith(COMMAND_PREFIX); + } + +} diff --git a/src/main/java/net/lamgc/cgj/bot/event/MessageEvent.java b/src/main/java/net/lamgc/cgj/bot/event/MessageEvent.java new file mode 100644 index 0000000..1e953cc --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/event/MessageEvent.java @@ -0,0 +1,33 @@ +package net.lamgc.cgj.bot.event; + +import net.lamgc.utils.event.EventObject; + +public abstract class MessageEvent implements EventObject { + + private final long fromGroup; + private final long fromQQ; + private final String message; + + public MessageEvent(long fromGroup, long fromQQ, String message) { + this.fromGroup = fromGroup; + this.fromQQ = fromQQ; + this.message = message; + } + + public abstract int sendMessage(final String message); + + public abstract Object getRawMessage(); + + public long getFromGroup() { + return fromGroup; + } + + public long getFromQQ() { + return fromQQ; + } + + public String getMessage() { + return message; + } + +} diff --git a/src/main/java/net/lamgc/cgj/bot/event/MiraiMessageEvent.java b/src/main/java/net/lamgc/cgj/bot/event/MiraiMessageEvent.java new file mode 100644 index 0000000..b717db1 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/event/MiraiMessageEvent.java @@ -0,0 +1,184 @@ +package net.lamgc.cgj.bot.event; + +import com.google.common.base.Strings; +import net.lamgc.cgj.bot.BotCode; +import net.lamgc.cgj.bot.cache.CacheStore; +import net.lamgc.cgj.bot.cache.HotDataCacheStore; +import net.lamgc.cgj.bot.cache.LocalHashCacheStore; +import net.lamgc.cgj.bot.cache.StringRedisCacheStore; +import net.mamoe.mirai.message.ContactMessage; +import net.mamoe.mirai.message.FriendMessage; +import net.mamoe.mirai.message.GroupMessage; +import net.mamoe.mirai.message.data.CombinedMessage; +import net.mamoe.mirai.message.data.Image; +import net.mamoe.mirai.message.data.Message; +import net.mamoe.mirai.message.data.MessageUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class MiraiMessageEvent extends MessageEvent { + + private final ContactMessage messageObject; + private final Logger log = LoggerFactory.getLogger(this.getClass().getSimpleName() + "@" + Integer.toHexString(this.hashCode())); + private final static CacheStore imageIdCache = new HotDataCacheStore<>( + new StringRedisCacheStore(BotEventHandler.redisServer, "mirai.imageId"), + new LocalHashCacheStore<>(), + 5400000, 1800000); + + + public MiraiMessageEvent(ContactMessage message) { + super(message instanceof GroupMessage ? ((GroupMessage) message).getGroup().getId() : 0, + message.getSender().getId(), message.getMessage().toString()); + this.messageObject = Objects.requireNonNull(message); + } + + @Override + public int sendMessage(final String message) { + log.debug("处理前的消息内容:\n{}", message); + Message msgBody = processMessage(Objects.requireNonNull(message)); + log.debug("处理后的消息内容(可能出现乱序的情况, 但实际上顺序是没问题的):\n{}", msgBody); + if(getFromGroup() == 0) { + FriendMessage msgObject = (FriendMessage) messageObject; + //FIXME(LamGC, 2020.04.10): 当前 Mirai 不支持私聊长文本, 所以发生异常是正常情况... + msgObject.getSender().sendMessage(msgBody); + } else { + GroupMessage msgObject = (GroupMessage) messageObject; + msgObject.getGroup().sendMessage(msgBody); + } + return 0; + } + + @Override + public Object getRawMessage() { + return messageObject; + } + + private final static Pattern cqCodePattern = Pattern.compile("\\[.*?:.*?]"); + private Message processMessage(final String message) { + Matcher matcher = cqCodePattern.matcher(message); + ArrayList cqCode = new ArrayList<>(); + while (matcher.find()) { + cqCode.add(matcher.group()); + } + String[] texts = message + .replaceAll("&", "&38") + .replaceAll("\\{", "&" + Character.getNumericValue('{')) + .replaceAll(cqCodePattern.pattern(), "|{BotCode}|") + .replaceAll("&" + Character.getNumericValue('{'), "{") + .replaceAll("&38", "&") + .split("\\|"); + + CombinedMessage chain = MessageUtils.newChain().plus(""); + int codeIndex = 0; + for(String text : texts) { + if(text.equals("{BotCode}")) { + BotCode code; + try { + code = BotCode.parse(cqCode.get(codeIndex++)); + } catch(IllegalArgumentException e) { + log.warn("解析待发送消息内的BotCode时发生异常, 请检查错误格式BotCode的来源并尽快排错!", e); + continue; + } + chain = chain.plus(processBotCode(code)); + } else { + chain = chain.plus(text); + } + } + + return chain; + } + + private Message processBotCode(BotCode code) { + switch(code.getFunctionName().toLowerCase()) { + case "image": + if(code.containsParameter("id")) { + return MessageUtils.newImage(code.getParameter("id")); + } else if(code.containsParameter("absolutePath")) { + return uploadImage(code); + } else { + return MessageUtils.newChain("(参数不存在)"); + } + default: + log.warn("解析到不支持的BotCode: {}", code); + return MessageUtils.newChain("(不支持的BotCode)"); + } + } + + private Image uploadImage(BotCode code) { + log.debug("传入BotCode信息:\n{}", code); + String absolutePath = code.getParameter("absolutePath"); + if(Strings.isNullOrEmpty(absolutePath)) { + throw new IllegalArgumentException("BotCode does not contain the absolutePath parameter"); + } + + String imageName = code.getParameter("imageName"); + if(!Strings.isNullOrEmpty(imageName)) { + Image image = null; + imageName = (getMessageSource() + "." + imageName).intern(); + if(!imageIdCache.exists(imageName) || + Strings.nullToEmpty(code.getParameter("updateCache")).equalsIgnoreCase("true")) { + synchronized (imageName) { + if(!imageIdCache.exists(imageName) || + Strings.nullToEmpty(code.getParameter("updateCache")) .equalsIgnoreCase("true")) { + log.debug("imageName [{}] 缓存失效或强制更新, 正在更新缓存...", imageName); + image = uploadImage0(new File(absolutePath)); + if(Objects.isNull(image)) { + return null; + } + + String cacheExpireAt; + long expireTime = 0; + if(!Strings.isNullOrEmpty(cacheExpireAt = code.getParameter("cacheExpireAt"))) { + try { + expireTime = Integer.parseInt(cacheExpireAt); + } catch (NumberFormatException e) { + log.warn("BotCode中的cacheExpireAt参数无效: {}", cacheExpireAt); + } + } + imageIdCache.update(imageName, image.getImageId(), expireTime); + log.info("imageName [{}] 缓存更新完成.(有效时间: {})", imageName, expireTime); + } else { + log.debug("ImageName: [{}] 缓存命中.", imageName); + } + } + } else { + log.debug("ImageName: [{}] 缓存命中.", imageName); + } + + String cache = imageIdCache.getCache(imageName); + return image != null ? image : MessageUtils.newImage(cache); + } else { + log.debug("未设置imageName, 无法使用缓存."); + return uploadImage0(new File(absolutePath)); + } + } + + private Image uploadImage0(File imageFile) { + if(messageObject instanceof FriendMessage) { + return messageObject.getSender().uploadImage(imageFile); + } else if(messageObject instanceof GroupMessage) { + return ((GroupMessage) messageObject).getGroup().uploadImage(imageFile); + } else { + log.warn("未知的ContactMessage类型: " + messageObject.toString()); + return null; + } + } + + private String getMessageSource() { + if(messageObject instanceof FriendMessage) { + return "Private"; + } else if(messageObject instanceof GroupMessage) { + return "Group"; + } else { + log.warn("未知的ContactMessage类型: " + messageObject.toString()); + return "Unknown"; + } + } + +} diff --git a/src/main/java/net/lamgc/cgj/bot/event/SpringCQMessageEvent.java b/src/main/java/net/lamgc/cgj/bot/event/SpringCQMessageEvent.java new file mode 100644 index 0000000..1300066 --- /dev/null +++ b/src/main/java/net/lamgc/cgj/bot/event/SpringCQMessageEvent.java @@ -0,0 +1,54 @@ +package net.lamgc.cgj.bot.event; + +import net.lz1998.cq.event.message.CQDiscussMessageEvent; +import net.lz1998.cq.event.message.CQGroupMessageEvent; +import net.lz1998.cq.event.message.CQMessageEvent; +import net.lz1998.cq.robot.CoolQ; + +import java.util.Objects; + +public class SpringCQMessageEvent extends MessageEvent { + + private final static int TYPE_PRIVATE = 0; + private final static int TYPE_GROUP = 1; + private final static int TYPE_DISCUSS = 2; + + private final CoolQ cq; + private final int type; + private final CQMessageEvent messageEvent; + + public SpringCQMessageEvent(CoolQ cq, CQMessageEvent messageEvent) { + super(messageEvent instanceof CQGroupMessageEvent ? ( + (CQGroupMessageEvent) messageEvent).getGroupId() : + messageEvent instanceof CQDiscussMessageEvent ? + ((CQDiscussMessageEvent) messageEvent).getDiscussId() : 0, + messageEvent.getUserId(), messageEvent.getMessage()); + this.cq = Objects.requireNonNull(cq); + if(messageEvent instanceof CQGroupMessageEvent) { + type = TYPE_GROUP; + } else if (messageEvent instanceof CQDiscussMessageEvent) { + type = TYPE_DISCUSS; + } else { + type = TYPE_PRIVATE; + } + this.messageEvent = messageEvent; + } + + @Override + public int sendMessage(final String message) { + switch(type) { + case TYPE_PRIVATE: + return cq.sendPrivateMsg(getFromQQ(), message, false).getData().getMessageId(); + case TYPE_GROUP: + case TYPE_DISCUSS: + return cq.sendGroupMsg(getFromGroup(), message, false).getData().getMessageId(); + default: + return -1; + } + } + + @Override + public Object getRawMessage() { + return messageEvent; + } +}