diff --git a/.run/RunMiraiKt.run.xml b/.run/RunMiraiKt.run.xml
deleted file mode 100644
index ce3fef7..0000000
--- a/.run/RunMiraiKt.run.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index c125345..7f194de 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,5 +1,5 @@
plugins {
- val kotlinVersion = "1.6.10"
+ val kotlinVersion = "1.6.21"
kotlin("jvm") version kotlinVersion
kotlin("plugin.serialization") version kotlinVersion
@@ -9,9 +9,17 @@ plugins {
group = "ltd.guimc.mirai-group-verify"
version = "0.1.0"
+dependencies {
+ implementation("org.json:json:20200518")
+}
+
repositories {
if (System.getenv("CI")?.toBoolean() != true) {
maven("https://maven.aliyun.com/repository/public") // 阿里云国内代理仓库
}
mavenCentral()
}
+
+mirai {
+ jvmTarget = JavaVersion.VERSION_11
+}
diff --git a/src/main/java/org/example/mirai/plugin/JavaPluginMain.java b/src/main/java/org/example/mirai/plugin/JavaPluginMain.java
deleted file mode 100644
index a32d52e..0000000
--- a/src/main/java/org/example/mirai/plugin/JavaPluginMain.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.example.mirai.plugin;
-
-import kotlin.Lazy;
-import kotlin.LazyKt;
-import net.mamoe.mirai.console.permission.*;
-import net.mamoe.mirai.console.plugin.jvm.JavaPlugin;
-import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription;
-import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescriptionBuilder;
-import net.mamoe.mirai.contact.Member;
-import net.mamoe.mirai.contact.User;
-import net.mamoe.mirai.event.Event;
-import net.mamoe.mirai.event.EventChannel;
-import net.mamoe.mirai.event.GlobalEventChannel;
-import net.mamoe.mirai.event.events.FriendMessageEvent;
-import net.mamoe.mirai.event.events.GroupMessageEvent;
-
-
-/**
- * 使用 Java 请把
- * {@code /src/main/resources/META-INF.services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin}
- * 文件内容改成 {@code org.example.mirai.plugin.JavaPluginMain}
- * 也就是当前主类全类名
- *
- * 使用 Java 可以把 kotlin 源集删除且不会对项目有影响
- *
- * 在 {@code settings.gradle.kts} 里改构建的插件名称、依赖库和插件版本
- *
- * 在该示例下的 {@link JvmPluginDescription} 修改插件名称,id 和版本等
- *
- * 可以使用 {@code src/test/kotlin/RunMirai.kt} 在 IDE 里直接调试,
- * 不用复制到 mirai-console-loader 或其他启动器中调试
- */
-
-public final class JavaPluginMain extends JavaPlugin {
- public static final JavaPluginMain INSTANCE = new JavaPluginMain();
-
- private JavaPluginMain() {
- super(new JvmPluginDescriptionBuilder("org.example.mirai-example", "0.1.0")
- .info("EG")
- .build());
- }
-
- @Override
- public void onEnable() {
- getLogger().info("日志");
- EventChannel eventChannel = GlobalEventChannel.INSTANCE.parentScope(this);
- eventChannel.subscribeAlways(GroupMessageEvent.class, g -> {
- //监听群消息
- getLogger().info(g.getMessage().contentToString());
-
- });
- eventChannel.subscribeAlways(FriendMessageEvent.class, f -> {
- //监听好友消息
- getLogger().info(f.getMessage().contentToString());
- });
-
- myCustomPermission.getValue(); // 注册权限
- }
-
- // region mirai-console 权限系统示例
- public static final Lazy myCustomPermission = LazyKt.lazy(() -> { // Lazy: Lazy 是必须的, console 不允许提前访问权限系统
- // 注册一条权限节点 org.example.mirai-example:my-permission
- // 并以 org.example.mirai-example:* 为父节点
-
-
- // @param: parent: 父权限
- // 在 Console 内置权限系统中, 如果某人拥有父权限
- // 那么意味着此人也拥有该权限 (org.example.mirai-example:my-permission)
- // @func: PermissionIdNamespace.permissionId: 根据插件 id 确定一条权限 id
- try {
- return PermissionService.getInstance().register(
- INSTANCE.permissionId("my-permission"),
- "一条自定义权限",
- INSTANCE.getParentPermission()
- );
- } catch (PermissionRegistryConflictException e) {
- throw new RuntimeException(e);
- }
- });
-
- public static boolean hasCustomPermission(User usr) {
- PermitteeId pid;
- if (usr instanceof Member) {
- pid = new AbstractPermitteeId.ExactMember(((Member) usr).getGroup().getId(), usr.getId());
- } else {
- pid = new AbstractPermitteeId.ExactUser(usr.getId());
- }
- return PermissionService.hasPermission(pid, myCustomPermission.getValue());
- }
- // endregion
-}
diff --git a/src/main/kotlin/PluginMain.kt b/src/main/kotlin/PluginMain.kt
deleted file mode 100644
index c5fcbab..0000000
--- a/src/main/kotlin/PluginMain.kt
+++ /dev/null
@@ -1,118 +0,0 @@
-package org.example.mirai.plugin
-
-import net.mamoe.mirai.console.permission.AbstractPermitteeId
-import net.mamoe.mirai.console.permission.PermissionService
-import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission
-import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
-import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
-import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.contact.User
-import net.mamoe.mirai.event.GlobalEventChannel
-import net.mamoe.mirai.event.events.BotInvitedJoinGroupRequestEvent
-import net.mamoe.mirai.event.events.FriendMessageEvent
-import net.mamoe.mirai.event.events.GroupMessageEvent
-import net.mamoe.mirai.event.events.NewFriendRequestEvent
-import net.mamoe.mirai.message.data.Image
-import net.mamoe.mirai.message.data.Image.Key.queryUrl
-import net.mamoe.mirai.message.data.PlainText
-import net.mamoe.mirai.utils.info
-
-/**
- * 使用 kotlin 版请把
- * `src/main/resources/META-INF.services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin`
- * 文件内容改成 `org.example.mirai.plugin.PluginMain` 也就是当前主类全类名
- *
- * 使用 kotlin 可以把 java 源集删除不会对项目有影响
- *
- * 在 `settings.gradle.kts` 里改构建的插件名称、依赖库和插件版本
- *
- * 在该示例下的 [JvmPluginDescription] 修改插件名称,id和版本,etc
- *
- * 可以使用 `src/test/kotlin/RunMirai.kt` 在 ide 里直接调试,
- * 不用复制到 mirai-console-loader 或其他启动器中调试
- */
-
-object PluginMain : KotlinPlugin(
- JvmPluginDescription(
- id = "org.example.mirai-example",
- name = "插件示例",
- version = "0.1.0"
- ) {
- author("作者名称或联系方式")
- info(
- """
- 这是一个测试插件,
- 在这里描述插件的功能和用法等.
- """.trimIndent()
- )
- // author 和 info 可以删除.
- }
-) {
- override fun onEnable() {
- logger.info { "Plugin loaded" }
- //配置文件目录 "${dataFolder.absolutePath}/"
- val eventChannel = GlobalEventChannel.parentScope(this)
- eventChannel.subscribeAlways {
- //群消息
- //复读示例
- if (message.contentToString().startsWith("复读")) {
- group.sendMessage(message.contentToString().replace("复读", ""))
- }
- if (message.contentToString() == "hi") {
- //群内发送
- group.sendMessage("hi")
- //向发送者私聊发送消息
- sender.sendMessage("hi")
- //不继续处理
- return@subscribeAlways
- }
- //分类示例
- message.forEach {
- //循环每个元素在消息里
- if (it is Image) {
- //如果消息这一部分是图片
- val url = it.queryUrl()
- group.sendMessage("图片,下载地址$url")
- }
- if (it is PlainText) {
- //如果消息这一部分是纯文本
- group.sendMessage("纯文本,内容:${it.content}")
- }
- }
- }
- eventChannel.subscribeAlways {
- //好友信息
- sender.sendMessage("hi")
- }
- eventChannel.subscribeAlways {
- //自动同意好友申请
- accept()
- }
- eventChannel.subscribeAlways {
- //自动同意加群申请
- accept()
- }
-
- myCustomPermission // 注册权限
- }
-
- // region console 权限系统示例
- private val myCustomPermission by lazy { // Lazy: Lazy 是必须的, console 不允许提前访问权限系统
- // 注册一条权限节点 org.example.mirai-example:my-permission
- // 并以 org.example.mirai-example:* 为父节点
-
- // @param: parent: 父权限
- // 在 Console 内置权限系统中, 如果某人拥有父权限
- // 那么意味着此人也拥有该权限 (org.example.mirai-example:my-permission)
- // @func: PermissionIdNamespace.permissionId: 根据插件 id 确定一条权限 id
- PermissionService.INSTANCE.register(permissionId("my-permission"), "一条自定义权限", parentPermission)
- }
-
- public fun hasCustomPermission(sender: User): Boolean {
- return when (sender) {
- is Member -> AbstractPermitteeId.ExactMember(sender.group.id, sender.id)
- else -> AbstractPermitteeId.ExactUser(sender.id)
- }.hasPermission(myCustomPermission)
- }
- // endregion
-}
diff --git a/src/main/kotlin/ltd/guimc/mirai/groupverify/PluginMain.kt b/src/main/kotlin/ltd/guimc/mirai/groupverify/PluginMain.kt
new file mode 100644
index 0000000..b5a27b3
--- /dev/null
+++ b/src/main/kotlin/ltd/guimc/mirai/groupverify/PluginMain.kt
@@ -0,0 +1,44 @@
+package ltd.guimc.mirai.groupverify
+
+import ltd.guimc.mirai.groupverify.listener.MemberListener
+import net.mamoe.mirai.console.permission.Permission
+import net.mamoe.mirai.console.permission.PermissionId
+import net.mamoe.mirai.console.permission.PermissionService
+import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
+import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
+import net.mamoe.mirai.event.events.MemberJoinEvent
+import net.mamoe.mirai.event.globalEventChannel
+
+object PluginMain : KotlinPlugin(
+ JvmPluginDescription(
+ id = "ltd.guimc.mirai.groupverify",
+ name = "Mirai Group Verify",
+ version = "0.1.0"
+ ) {
+ author("BakaBotTeam")
+ }
+) {
+ lateinit var rootPerm: Permission
+ lateinit var enablePerm: Permission
+
+ override fun onEnable() {
+ logger.info("Mirai Group Verify 正在加载的路上了喵~")
+ registerPerms()
+ registerEvents()
+ logger.info("Mirai Group Verify 加载好啦喵~")
+ }
+
+ override fun onDisable() {
+ logger.info("Mirai Group Verify 正在关闭呢...")
+ logger.info("Mirai Group Verify 已关闭了喵")
+ }
+
+ private fun registerPerms() = PermissionService.INSTANCE.run {
+ rootPerm = register(PermissionId("ltd.guimc.mirai.groupverify", "*"), "Root Permission")
+ enablePerm = register(PermissionId("ltd.guimc.mirai.groupverify", "enable"), "启用插件 (群)")
+ }
+
+ private fun registerEvents() = this.globalEventChannel().run {
+ subscribeAlways { event -> MemberListener.onEvent(event) }
+ }
+}
diff --git a/src/main/kotlin/ltd/guimc/mirai/groupverify/config/Config.kt b/src/main/kotlin/ltd/guimc/mirai/groupverify/config/Config.kt
new file mode 100644
index 0000000..a7b99ba
--- /dev/null
+++ b/src/main/kotlin/ltd/guimc/mirai/groupverify/config/Config.kt
@@ -0,0 +1,12 @@
+package ltd.guimc.mirai.groupverify.config
+
+import net.mamoe.mirai.console.data.AutoSavePluginConfig
+import net.mamoe.mirai.console.data.value
+
+object Config : AutoSavePluginConfig("config") {
+ var backendUrl by value("http://example.com:5566/verify")
+
+ var secret by value("xxx")
+
+ var appid by value("xxx_xxx_xxxxxxxxxxxx")
+}
\ No newline at end of file
diff --git a/src/main/kotlin/ltd/guimc/mirai/groupverify/listener/MemberListener.kt b/src/main/kotlin/ltd/guimc/mirai/groupverify/listener/MemberListener.kt
new file mode 100644
index 0000000..8473ba8
--- /dev/null
+++ b/src/main/kotlin/ltd/guimc/mirai/groupverify/listener/MemberListener.kt
@@ -0,0 +1,69 @@
+package ltd.guimc.mirai.groupverify.listener
+
+import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import ltd.guimc.mirai.groupverify.PluginMain
+import ltd.guimc.mirai.groupverify.config.Config
+import ltd.guimc.mirai.groupverify.utils.APIUtils
+import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission
+import net.mamoe.mirai.console.permission.PermitteeId.Companion.permitteeId
+import net.mamoe.mirai.contact.Member
+import net.mamoe.mirai.contact.MemberPermission
+import net.mamoe.mirai.contact.NormalMember
+import net.mamoe.mirai.contact.checkBotPermission
+import net.mamoe.mirai.event.events.MemberJoinEvent
+import net.mamoe.mirai.message.data.At
+import net.mamoe.mirai.message.data.PlainText
+import java.util.*
+
+object MemberListener {
+ suspend fun onEvent(event: MemberJoinEvent) {
+ if (event.group.permitteeId.hasPermission(PluginMain.enablePerm)) {
+ event.group.checkBotPermission(MemberPermission.ADMINISTRATOR) { "权限不足" }
+ event.member.mute(2592000) // 30 Days
+
+ val session = APIUtils.newSession(event.member)
+ event.group.sendMessage(
+ At(event.member)+
+ PlainText(" 你好呀! 进入本群,你需要先完成以下的人机验证!\n" +
+ "请点击下面的链接来开始验证\n\n" +
+ "${Config.backendUrl}?session=$session")
+ )
+ statusListenerRegister(event.member, session)
+ }
+ }
+
+ @OptIn(DelicateCoroutinesApi::class)
+ private fun statusListenerRegister(member: Member, session: String) {
+ Timer().schedule(
+ object : TimerTask() {
+ override fun run() {
+ GlobalScope.launch {
+ if (statusTimerTask(member, session)) cancel()
+ }
+ }
+ }, 2, 2
+ )
+ }
+
+ private suspend fun statusTimerTask(member: Member, session: String): Boolean {
+ val status = APIUtils.getStatus(session)
+ if (status == 1) {
+ member.group.sendMessage(
+ At(member)+
+ PlainText(" 你已经完成了验证 现在可以正常发言了!")
+ )
+ (member as NormalMember).unmute()
+ return true
+ } else if (status == 2) {
+ member.group.sendMessage(
+ At(member)+
+ PlainText(" 你的验证超时 请重新进群")
+ )
+ (member as NormalMember).kick("人机验证超时")
+ return true
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/ltd/guimc/mirai/groupverify/utils/APIUtils.kt b/src/main/kotlin/ltd/guimc/mirai/groupverify/utils/APIUtils.kt
new file mode 100644
index 0000000..21f71db
--- /dev/null
+++ b/src/main/kotlin/ltd/guimc/mirai/groupverify/utils/APIUtils.kt
@@ -0,0 +1,53 @@
+package ltd.guimc.mirai.groupverify.utils
+
+import ltd.guimc.mirai.groupverify.config.Config
+import ltd.guimc.mirai.groupverify.utils.HashUtils.sha256
+import net.mamoe.mirai.contact.Member
+import org.json.JSONObject
+import java.net.URI
+import java.net.http.HttpClient
+import java.net.http.HttpRequest
+import java.net.http.HttpResponse
+
+object APIUtils {
+ val httpclient = HttpClient.newBuilder().build();
+
+ fun newSession(member: Member): String {
+ val time = System.currentTimeMillis()
+ val url = "${Config.backendUrl}?action=new&appid=${Config.appid}&qqid=${member.id}&group=${member.group.id}&time=${time}&sign=${sign(member, time)}"
+ return JSONObject(get(url)).getString("session")!!
+ }
+
+ fun getStatus(session: String): Int {
+ val url = "${Config.backendUrl}?action=status&session=$session"
+ val success = JSONObject(get(url)).getBoolean("success")
+ val timedout = JSONObject(get(url)).getBoolean("timeout")
+
+ return if (success) {
+ Status.FINISHED
+ } else if (timedout) {
+ Status.TIMEDOUT
+ } else {
+ Status.INPROGRESS
+ }
+ }
+
+ fun get(url: String): String {
+ val request = HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .build();
+
+ val response = httpclient.send(request, HttpResponse.BodyHandlers.ofString());
+ return response.body()!!
+ }
+
+ object Status {
+ const val INPROGRESS = 0
+ const val FINISHED = 1
+ const val TIMEDOUT = 2
+ }
+
+ private fun sign(member: Member, time: Long): String = "${Config.appid}:$secret:${member.group.id}:${member.id}:$time".sha256()
+ private val secret: String
+ get() = "${Config.appid}:${Config.secret}".sha256()
+}
\ No newline at end of file
diff --git a/src/main/kotlin/ltd/guimc/mirai/groupverify/utils/HashUtils.kt b/src/main/kotlin/ltd/guimc/mirai/groupverify/utils/HashUtils.kt
new file mode 100644
index 0000000..cb3603f
--- /dev/null
+++ b/src/main/kotlin/ltd/guimc/mirai/groupverify/utils/HashUtils.kt
@@ -0,0 +1,11 @@
+package ltd.guimc.mirai.groupverify.utils
+
+import java.math.BigInteger
+import java.security.MessageDigest
+
+object HashUtils {
+ fun String.sha256(): String {
+ val md = MessageDigest.getInstance("SHA-256")
+ return BigInteger(1, md.digest(toByteArray())).toString(16).padStart(32, '0')
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin b/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin
index 0bed70b..847e297 100644
--- a/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin
+++ b/src/main/resources/META-INF/services/net.mamoe.mirai.console.plugin.jvm.JvmPlugin
@@ -1 +1 @@
-org.example.mirai.plugin.PluginMain
\ No newline at end of file
+ltd.guimc.mirai.groupverify.PluginMain
\ No newline at end of file
diff --git a/src/test/kotlin/RunMirai.kt b/src/test/kotlin/RunMirai.kt
deleted file mode 100644
index b4837db..0000000
--- a/src/test/kotlin/RunMirai.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.example.mirai.plugin
-
-import net.mamoe.mirai.alsoLogin
-import net.mamoe.mirai.console.MiraiConsole
-import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.enable
-import net.mamoe.mirai.console.plugin.PluginManager.INSTANCE.load
-import net.mamoe.mirai.console.terminal.MiraiConsoleTerminalLoader
-
-suspend fun main() {
- if (true) {
-
- error("""
- DEPRECATED:
- 此启动方法已经被弃用, 请使用 ./gradlew runConsole 启动测试环境
-
- 详见:
- https://docs.mirai.mamoe.net/console/plugin/JVMPlugin.html#%E8%B0%83%E8%AF%95
- https://github.com/mamoe/mirai/blob/dev/mirai-console/docs/plugin/JVMPlugin.md#%E8%B0%83%E8%AF%95
- """.trimIndent())
- }
-
- MiraiConsoleTerminalLoader.startAsDaemon()
-
- //如果是Kotlin
- PluginMain.load()
- PluginMain.enable()
- //如果是Java
-// JavaPluginMain.INSTANCE.load()
-// JavaPluginMain.INSTANCE.enable()
-
- val bot = MiraiConsole.addBot(123456, "") {
- fileBasedDeviceInfo()
- }.alsoLogin()
-
- MiraiConsole.job.join()
-}
\ No newline at end of file