Skip to content

Commit a316ea7

Browse files
Merge pull request #3 from 4o4E/main
完善功能
2 parents 834390d + cb6656c commit a316ea7

File tree

5 files changed

+276
-39
lines changed

5 files changed

+276
-39
lines changed

build.gradle.kts

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ plugins {
77
}
88

99
group = "org.fenglin.GuessGroupFriends"
10-
version = "1.0-SNAPSHOT"
10+
version = "1.0.0"
1111

1212
repositories {
1313
maven("https://maven.aliyun.com/repository/public")
1414
mavenCentral()
1515
}
1616

17+
dependencies {
18+
compileOnly("xyz.cssxsh.mirai:mirai-skia-plugin:1.1.0")
19+
}

src/main/kotlin/Command.kt

+138-36
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,29 @@
11
package org.fenglin
22

3-
import kotlinx.coroutines.runBlocking
3+
import kotlinx.coroutines.*
44
import net.mamoe.mirai.console.command.CommandSender
5+
import net.mamoe.mirai.console.command.CommandSenderOnMessage
56
import net.mamoe.mirai.console.command.CompositeCommand
67
import net.mamoe.mirai.console.command.MemberCommandSender
8+
import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission
79
import net.mamoe.mirai.contact.*
810
import net.mamoe.mirai.data.UserProfile
9-
import net.mamoe.mirai.message.data.At
10-
import net.mamoe.mirai.message.data.MessageChain
11+
import net.mamoe.mirai.message.data.*
1112
import net.mamoe.mirai.message.data.MessageSource.Key.quote
12-
import net.mamoe.mirai.message.data.PlainText
13-
import net.mamoe.mirai.message.data.buildMessageChain
14-
import java.text.SimpleDateFormat
13+
import net.mamoe.mirai.utils.ExternalResource.Companion.toExternalResource
14+
import net.mamoe.mirai.utils.ExternalResource.Companion.uploadAsImage
15+
import org.jetbrains.skia.EncodedImageFormat
16+
import java.net.URL
1517
import java.util.*
1618
import java.util.concurrent.ConcurrentHashMap
19+
import kotlin.concurrent.timerTask
1720
import kotlin.random.Random
1821

1922
object Command : CompositeCommand(
2023
GuessGroupFriends,
2124
"猜群友",
2225
"猜群友游戏",
23-
description = "开始猜群友"
26+
description = "开始猜群友"
2427
) {
2528
/**
2629
* 正在进行的游戏 <群号, 游戏>
@@ -29,9 +32,9 @@ object Command : CompositeCommand(
2932

3033
// 最近24小时发言过的
3134
private const val lastSpeakLimit = 24 * 60 * 60 * 1000L
35+
3236
// 缓存有效期10分钟
3337
private const val profileCacheTimeout = 600_000
34-
private val sdf = SimpleDateFormat("yyyy/MM/dd HH:mm:ss")
3538

3639
/**
3740
* 用户UserProfile缓存 <id, <profile, 缓存时间戳>>
@@ -62,94 +65,193 @@ object Command : CompositeCommand(
6265
* 代表一个群里正在进行的游戏
6366
*
6467
* @param group 群
65-
* @param member 正在猜的群友
68+
* @param member 正在猜的群员
69+
* @param owner 创建游戏的群员id
6670
*/
67-
class Game(val group: Group, val member: NormalMember) {
71+
class Game(val group: Group, val member: NormalMember, val owner: User) {
72+
companion object {
73+
private val timer = Timer("GuessGroupFriends")
74+
75+
// 5分钟无响应即为超时
76+
private const val timeout = 5 * 60 * 1000L
77+
}
78+
79+
private var task: TimerTask? = null
80+
val face by lazy { URL(member.avatarUrl).readBytes().toImage() }
81+
6882
private val profile by lazy { runBlocking { member.getCacheProfile() } }
69-
private val hint = arrayListOf(
70-
{ PlainText("ta的网名中含有${member.nameCardOrNick.random()}") },
71-
{ PlainText("ta的入群时间为:${sdf.format(Date(member.joinTimestamp * 1000L))}") },
72-
{ PlainText("ta的最后发言时间:${sdf.format(Date(member.lastSpeakTimestamp * 1000L))}") },
73-
{ PlainText("ta的性别:${profile.sex.alias()}") },
74-
{ PlainText("ta的QQ等级:${profile.qLevel}") },
75-
{ PlainText("ta的个性签名:${profile.sign}") },
76-
{ PlainText("ta的年龄:${profile.age}") },
83+
val hint = arrayListOf<() -> Message>(
84+
{ PlainText("ta的昵称中包含字符: ${member.nameCardOrNick.random()}") },
85+
{ PlainText("ta的群头衔: ${member.specialTitle}") },
86+
{ PlainText("ta${if (member.permission == MemberPermission.ADMINISTRATOR) "" else "不是"}群管理") },
87+
{ PlainText("ta的入群时间为: ${member.joinTimestamp.formatAsDate()}(${member.joinTimestamp.fromNowSecondly()}前)") },
88+
{ PlainText("ta的最后发言时间: ${member.lastSpeakTimestamp.formatAsDate()}(${member.lastSpeakTimestamp.fromNowSecondly()}前)") },
89+
{ PlainText("ta的性别: ${profile.sex.alias()}") },
90+
{ PlainText("ta的QQ等级: ${profile.qLevel}") },
91+
{ PlainText(if (profile.sign.isEmpty()) "ta的个性签名是空的" else "ta的个性签名: ${profile.sign}") },
92+
{ PlainText("ta的年龄: ${profile.age}") },
93+
{ PlainText("ta的头像包含以下部分\n").plus(runBlocking(Dispatchers.IO) { group.uploadImage(face.split()) }) },
94+
{ PlainText("ta的头像模糊之后是这样的\n").plus(runBlocking(Dispatchers.IO) { group.uploadImage(face.blur()) }) },
95+
{ PlainText("ta的头像缩放之后是这样的\n").plus(runBlocking(Dispatchers.IO) { group.uploadImage(face.scale()) }) },
7796
)
7897

7998
fun getHint() =
8099
if (hint.isEmpty()) null
81100
else hint.removeAt(Random.nextInt(hint.size)).invoke()
82101

102+
fun updateTask() {
103+
task?.cancel()
104+
task = timerTask {
105+
runBlocking {
106+
PlainText("由于长时间无人触发, ")
107+
.plus(At(owner))
108+
.plus(" 创建的游戏已自定关闭\n发送 `/猜群友 开始` 开始新游戏")
109+
.sendTo(group)
110+
}
111+
games.remove(group.id)
112+
}
113+
timer.schedule(task, timeout)
114+
}
115+
83116
suspend fun start() {
117+
games[group.id] = this
118+
GuessGroupFriends.logger.info("在群${group.name}(${group.id})开始猜群友游戏, 选择的群员: ${member.nameCardOrNick}(${member.id})")
84119
group.sendMessage(
85-
"""游戏开始下面将发送第一条线索
86-
|如果回答错误将随机发送一条线索线索用完时游戏失败
120+
"""游戏开始, 下面将发送第一条线索
121+
|如果回答错误, 将随机发送一条线索, 线索用完时游戏失败
87122
|发送 `/猜群友 猜 @群友` 进行游戏
88123
""".trimMargin()
89124
)
125+
updateTask()
90126
group.sendMessage(getHint()!!)
91127
}
92128

93129
fun end() {
130+
task?.cancel()
94131
games.remove(group.id)
95132
}
96133
}
97134

98-
// 开始游戏
99135
@SubCommand("开始")
100136
@Description("开始猜群友游戏")
101137
suspend fun CommandSender.play() {
102138
if (this !is MemberCommandSender) {
103139
sendMessage("仅可在群聊中使用")
104140
return
105141
}
142+
this as CommandSenderOnMessage<*>
106143
if (games[group.id] != null) {
107-
sendMessage("游戏进行中, 发送 `/猜群友 猜 @猜的群友` 来猜群友")
144+
sendMessage(
145+
fromEvent.source.quote().plus(
146+
"""游戏进行中
147+
|发送 `/猜群友 猜 @猜的群友` 猜群友
148+
|发送 `/猜群友 结束` 停止当前游戏
149+
""".trimMargin()
150+
)
151+
)
108152
return
109153
}
110154
// 获取群友
111-
val member = group.members.filter { member ->
155+
val filter = group.members.filter { member ->
112156
conditions.all { it.invoke(member) }
113-
}[Random.nextInt(group.members.size)]
114-
val game = Game(group, member)
115-
game.start()
157+
}
158+
if (filter.isEmpty()) {
159+
sendMessage(fromEvent.source.quote().plus("没有满足条件的群员"))
160+
return
161+
}
162+
val member = filter[Random.nextInt(filter.size)]
163+
Game(group, member, user).start()
116164
}
117165

118-
// 猜群友
119166
@SubCommand("")
120167
@Description("在猜群友游戏猜一次群友")
121-
suspend fun CommandSender.guess(target: User, chain: MessageChain) {
168+
suspend fun CommandSender.guess(target: User) {
122169
if (this !is MemberCommandSender) {
123170
sendMessage("仅可在群聊中使用")
124171
return
125172
}
173+
this as CommandSenderOnMessage<*>
126174
val game = games[group.id]
127175
if (game == null) {
128-
sendMessage("游戏未开始")
176+
sendMessage(fromEvent.source.quote().plus("游戏未开始\n发送 `/猜群友 开始` 开始游戏"))
129177
return
130178
}
131179
val success = target.id == game.member.id
132180
if (success) {
181+
game.end()
133182
group.sendMessage(buildMessageChain {
134183
append(PlainText("恭喜 "))
135184
append(At(user))
136-
append(PlainText(" 猜出了群友, 游戏结束\n发送 `/猜群友 开始` 开始新游戏"))
185+
append(PlainText(" 猜出了群友, 游戏结束\n"))
186+
val face = game.face
187+
.encodeToData(EncodedImageFormat.PNG)!!
188+
.bytes
189+
.toExternalResource()
190+
.use { it.uploadAsImage(group) }
191+
append(face)
192+
append(PlainText("\n发送 `/猜群友 开始` 开始新游戏"))
137193
})
138194
return
139195
}
140196
val hint = game.getHint()
141197
// 提示用完
142198
if (hint == null) {
143199
game.end()
144-
sendMessage("看来没人能猜到我,我要公布答案了:这位群友是: ${game.member.nameCardOrNick}")
200+
sendMessage("没有人猜出来, 游戏结束: 这位群友是: ${game.member.nameCardOrNick}")
201+
return
202+
}
203+
game.updateTask()
204+
sendMessage(fromEvent.source.quote().plus("回答错误\n新提示: ").plus(hint))
205+
}
206+
207+
@SubCommand("结束")
208+
@Description("结束猜群友游戏")
209+
suspend fun CommandSender.stop() {
210+
if (this !is MemberCommandSender) {
211+
sendMessage("仅可在群聊中使用")
212+
return
213+
}
214+
this as CommandSenderOnMessage<*>
215+
val game = games[group.id]
216+
if (game == null) {
217+
sendMessage(fromEvent.source.quote().plus("游戏未开始\n发送 `/猜群友 开始` 开始游戏"))
145218
return
146219
}
220+
if (!hasPermission(GuessGroupFriends.stopPerm)
221+
|| game.owner.id != user.id
222+
) {
223+
sendMessage(fromEvent.source.quote().plus("仅游戏发起者和管理可以停止游戏"))
224+
return
225+
}
226+
game.end()
147227
sendMessage(
148-
buildMessageChain {
149-
append(chain.quote())
150-
append(At(user))
151-
append("回答错误!\n新提示:$hint")
152-
}
228+
fromEvent.source.quote()
229+
.plus("")
230+
.plus(At(game.owner))
231+
.plus(" 创建的游戏已被手动停止\n发送 `/猜群友 开始` 开始新游戏")
153232
)
154233
}
234+
235+
@SubCommand("测试")
236+
@Description("测试线索")
237+
suspend fun CommandSender.test(target: User) {
238+
if (this !is MemberCommandSender) {
239+
sendMessage("仅可在群聊中使用")
240+
return
241+
}
242+
this as CommandSenderOnMessage<*>
243+
if (!hasPermission(GuessGroupFriends.testPerm)) {
244+
sendMessage(fromEvent.source.quote().plus("无权限"))
245+
return
246+
}
247+
val game = Game(group, target as NormalMember, user)
248+
val f = buildForwardMessage(group) {
249+
runBlocking(Dispatchers.IO) {
250+
game.hint.forEach { func ->
251+
launch { add(bot, func.invoke()) }
252+
}
253+
}
254+
}
255+
group.sendMessage(f)
256+
}
155257
}

src/main/kotlin/GuessGroupFriends.kt

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.fenglin
22

33
import net.mamoe.mirai.console.command.CommandManager
4+
import net.mamoe.mirai.console.permission.Permission
5+
import net.mamoe.mirai.console.permission.PermissionService
6+
import net.mamoe.mirai.console.plugin.description.PluginDependency
47
import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
58
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
69
import net.mamoe.mirai.utils.info
@@ -11,12 +14,32 @@ object GuessGroupFriends : KotlinPlugin(
1114
name = "GuessGroupFriends",
1215
version = "1.0-SNAPSHOT",
1316
) {
14-
author("枫叶秋林")
17+
author("枫叶秋林(大佬)")
18+
dependsOn(
19+
PluginDependency(
20+
id = "xyz.cssxsh.mirai.plugin.mirai-skia-plugin",
21+
versionRequirement = ">=1.1.0",
22+
isOptional = false
23+
)
24+
)
1525
}
1626
) {
27+
lateinit var stopPerm: Permission
28+
lateinit var testPerm: Permission
29+
30+
1731
override fun onEnable() {
1832
CommandManager.registerCommand(Command)
33+
stopPerm = PermissionService.INSTANCE.register(
34+
id = permissionId("command.停止猜群友"),
35+
description = "允许使用指令手动停止猜群友游戏",
36+
parent = parentPermission
37+
)
38+
testPerm = PermissionService.INSTANCE.register(
39+
id = permissionId("command.测试猜群友效果"),
40+
description = "允许使用指令手动测试猜群友效果",
41+
parent = parentPermission
42+
)
1943
logger.info { "猜群友插件加载成功!" }
20-
2144
}
2245
}

0 commit comments

Comments
 (0)