1
1
package org.fenglin
2
2
3
- import kotlinx.coroutines.runBlocking
3
+ import kotlinx.coroutines.*
4
4
import net.mamoe.mirai.console.command.CommandSender
5
+ import net.mamoe.mirai.console.command.CommandSenderOnMessage
5
6
import net.mamoe.mirai.console.command.CompositeCommand
6
7
import net.mamoe.mirai.console.command.MemberCommandSender
8
+ import net.mamoe.mirai.console.permission.PermissionService.Companion.hasPermission
7
9
import net.mamoe.mirai.contact.*
8
10
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.*
11
12
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
15
17
import java.util.*
16
18
import java.util.concurrent.ConcurrentHashMap
19
+ import kotlin.concurrent.timerTask
17
20
import kotlin.random.Random
18
21
19
22
object Command : CompositeCommand(
20
23
GuessGroupFriends ,
21
24
" 猜群友" ,
22
25
" 猜群友游戏" ,
23
- description = " 开始猜群友! "
26
+ description = " 开始猜群友"
24
27
) {
25
28
/* *
26
29
* 正在进行的游戏 <群号, 游戏>
@@ -29,9 +32,9 @@ object Command : CompositeCommand(
29
32
30
33
// 最近24小时发言过的
31
34
private const val lastSpeakLimit = 24 * 60 * 60 * 1000L
35
+
32
36
// 缓存有效期10分钟
33
37
private const val profileCacheTimeout = 600_000
34
- private val sdf = SimpleDateFormat (" yyyy/MM/dd HH:mm:ss" )
35
38
36
39
/* *
37
40
* 用户UserProfile缓存 <id, <profile, 缓存时间戳>>
@@ -62,94 +65,193 @@ object Command : CompositeCommand(
62
65
* 代表一个群里正在进行的游戏
63
66
*
64
67
* @param group 群
65
- * @param member 正在猜的群友
68
+ * @param member 正在猜的群员
69
+ * @param owner 创建游戏的群员id
66
70
*/
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
+
68
82
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()) }) },
77
96
)
78
97
79
98
fun getHint () =
80
99
if (hint.isEmpty()) null
81
100
else hint.removeAt(Random .nextInt(hint.size)).invoke()
82
101
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
+
83
116
suspend fun start () {
117
+ games[group.id] = this
118
+ GuessGroupFriends .logger.info(" 在群${group.name} (${group.id} )开始猜群友游戏, 选择的群员: ${member.nameCardOrNick} (${member.id} )" )
84
119
group.sendMessage(
85
- """ 游戏开始, 下面将发送第一条线索
86
- |如果回答错误, 将随机发送一条线索, 线索用完时游戏失败
120
+ """ 游戏开始, 下面将发送第一条线索
121
+ |如果回答错误, 将随机发送一条线索, 线索用完时游戏失败
87
122
|发送 `/猜群友 猜 @群友` 进行游戏
88
123
""" .trimMargin()
89
124
)
125
+ updateTask()
90
126
group.sendMessage(getHint()!! )
91
127
}
92
128
93
129
fun end () {
130
+ task?.cancel()
94
131
games.remove(group.id)
95
132
}
96
133
}
97
134
98
- // 开始游戏
99
135
@SubCommand(" 开始" )
100
136
@Description(" 开始猜群友游戏" )
101
137
suspend fun CommandSender.play () {
102
138
if (this !is MemberCommandSender ) {
103
139
sendMessage(" 仅可在群聊中使用" )
104
140
return
105
141
}
142
+ this as CommandSenderOnMessage <* >
106
143
if (games[group.id] != null ) {
107
- sendMessage(" 游戏进行中, 发送 `/猜群友 猜 @猜的群友` 来猜群友" )
144
+ sendMessage(
145
+ fromEvent.source.quote().plus(
146
+ """ 游戏进行中
147
+ |发送 `/猜群友 猜 @猜的群友` 猜群友
148
+ |发送 `/猜群友 结束` 停止当前游戏
149
+ """ .trimMargin()
150
+ )
151
+ )
108
152
return
109
153
}
110
154
// 获取群友
111
- val member = group.members.filter { member ->
155
+ val filter = group.members.filter { member ->
112
156
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()
116
164
}
117
165
118
- // 猜群友
119
166
@SubCommand(" 猜" )
120
167
@Description(" 在猜群友游戏猜一次群友" )
121
- suspend fun CommandSender.guess (target : User , chain : MessageChain ) {
168
+ suspend fun CommandSender.guess (target : User ) {
122
169
if (this !is MemberCommandSender ) {
123
170
sendMessage(" 仅可在群聊中使用" )
124
171
return
125
172
}
173
+ this as CommandSenderOnMessage <* >
126
174
val game = games[group.id]
127
175
if (game == null ) {
128
- sendMessage(" 游戏未开始! " )
176
+ sendMessage(fromEvent.source.quote().plus( " 游戏未开始\n 发送 `/猜群友 开始` 开始游戏 " ) )
129
177
return
130
178
}
131
179
val success = target.id == game.member.id
132
180
if (success) {
181
+ game.end()
133
182
group.sendMessage(buildMessageChain {
134
183
append(PlainText (" 恭喜 " ))
135
184
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 发送 `/猜群友 开始` 开始新游戏" ))
137
193
})
138
194
return
139
195
}
140
196
val hint = game.getHint()
141
197
// 提示用完
142
198
if (hint == null ) {
143
199
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 发送 `/猜群友 开始` 开始游戏" ))
145
218
return
146
219
}
220
+ if (! hasPermission(GuessGroupFriends .stopPerm)
221
+ || game.owner.id != user.id
222
+ ) {
223
+ sendMessage(fromEvent.source.quote().plus(" 仅游戏发起者和管理可以停止游戏" ))
224
+ return
225
+ }
226
+ game.end()
147
227
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 发送 `/猜群友 开始` 开始新游戏" )
153
232
)
154
233
}
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
+ }
155
257
}
0 commit comments