1
1
package org.fenglin
2
2
3
+ import kotlinx.coroutines.runBlocking
3
4
import net.mamoe.mirai.console.command.CommandSender
4
5
import net.mamoe.mirai.console.command.CompositeCommand
5
- import net.mamoe.mirai.contact.NormalMember
6
- import net.mamoe.mirai.contact.User
6
+ import net.mamoe.mirai.console.command.MemberCommandSender
7
+ import net.mamoe.mirai.contact.*
7
8
import net.mamoe.mirai.data.UserProfile
8
9
import net.mamoe.mirai.message.data.At
9
- import org.fenglin.Command.data
10
- import org.fenglin.Command.normalMember
11
- import org.fenglin.Command.playing
10
+ import net.mamoe.mirai.message.data.MessageChain
11
+ 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
12
14
import java.text.SimpleDateFormat
13
15
import java.util.*
14
-
16
+ import java.util.concurrent.ConcurrentHashMap
17
+ import kotlin.random.Random
15
18
16
19
object Command : CompositeCommand(
17
20
GuessGroupFriends ,
18
21
" 猜群友" ,
19
22
" 猜群友游戏" ,
20
23
description = " 开始猜群友!"
21
24
) {
22
- var normalMember: NormalMember ? = null
23
- var playing: Boolean = false ;
24
- var data: ArrayList <String >? = null
25
- @SubCommand // 标记这是指令处理器 // 函数名随意
26
- suspend fun CommandSender.paly () {
27
- if (playing){
28
- sendMessage(" 游戏进行中" )
29
- return ;
25
+ /* *
26
+ * 正在进行的游戏 <群号, 游戏>
27
+ */
28
+ private val games = ConcurrentHashMap <Long , Game >()
29
+
30
+ // 最近24小时发言过的
31
+ private const val lastSpeakLimit = 24 * 60 * 60 * 1000L
32
+ // 缓存有效期10分钟
33
+ private const val profileCacheTimeout = 600_000
34
+ private val sdf = SimpleDateFormat (" yyyy/MM/dd HH:mm:ss" )
35
+
36
+ /* *
37
+ * 用户UserProfile缓存 <id, <profile, 缓存时间戳>>
38
+ */
39
+ private val profileCache = ConcurrentHashMap <Long , Pair <UserProfile , Long >>()
40
+ suspend fun Member.getCacheProfile (): UserProfile {
41
+ val pair = profileCache[id]
42
+ if (pair != null
43
+ && pair.second > System .currentTimeMillis() - profileCacheTimeout
44
+ ) return pair.first
45
+ // 缓存过期
46
+ return queryProfile().also {
47
+ profileCache[id] = Pair (it, System .currentTimeMillis())
30
48
}
31
- this .subject?.id?.let { it ->
32
- // 获取群友
33
- val members = this .bot?.getGroup(it)?.members?.toList()
34
- normalMember = members?.get((0 until members.size).random())
35
- if (normalMember== null ){
36
- sendMessage(" 获取群友失败" )
37
- return ;
38
- }
39
- // 获取信息
40
- val sdf = SimpleDateFormat (" yyyy/MM/dd HH:mm:ss" )
41
- data= arrayListOf (
42
- " ta的网名中含有${normalMember!! .nick.random()} " ,
43
- " ta的入群时间为:${sdf.format(Date (normalMember!! .joinTimestamp.toLong()* 1000L ))} " ,
44
- " ta的最后发言时间:${sdf.format(Date (normalMember!! .lastSpeakTimestamp.toLong()* 1000L ))} " ,
45
- " ta的性别:${if (normalMember?.queryProfile()?.sex== UserProfile .Sex .MALE && normalMember?.queryProfile()?.sex != UserProfile .Sex .UNKNOWN ) " 男" else " 女" } " ,
46
- " ta的QQ等级:${normalMember?.queryProfile()?.qLevel? : " 没有获取到" } " ,
47
- " ta的个性签名:${normalMember?.queryProfile()?.sign? : " 没有获取到" } " ,
48
- " ta的年龄:${normalMember?.queryProfile()?.age? : " 没有获取到" } "
49
+ }
50
+
51
+ private fun UserProfile.Sex.alias () = when (this ) {
52
+ UserProfile .Sex .MALE -> " 男"
53
+ UserProfile .Sex .FEMALE -> " 女"
54
+ UserProfile .Sex .UNKNOWN -> " 未知"
55
+ }
56
+
57
+ private val conditions = mutableListOf<NormalMember .() - > Boolean > (
58
+ { lastSpeakTimestamp * 1000L + lastSpeakLimit >= System .currentTimeMillis() }
59
+ )
60
+
61
+ /* *
62
+ * 代表一个群里正在进行的游戏
63
+ *
64
+ * @param group 群
65
+ * @param member 正在猜的群友
66
+ */
67
+ class Game (val group : Group , val member : NormalMember ) {
68
+ 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} " ) },
77
+ )
78
+
79
+ fun getHint () =
80
+ if (hint.isEmpty()) null
81
+ else hint.removeAt(Random .nextInt(hint.size)).invoke()
82
+
83
+ suspend fun start () {
84
+ group.sendMessage(
85
+ """ 游戏开始,下面将发送第一条线索
86
+ |如果回答错误,将随机发送一条线索,线索用完时游戏失败
87
+ |发送 `/猜群友 猜 @群友` 进行游戏
88
+ """ .trimMargin()
49
89
)
50
- sendMessage(" 游戏开始!下面将发送第一条线索。如果回答错误,将随机发送一条线索!" )
51
- sendMessage(random())
52
- playing = true
90
+ group.sendMessage(getHint()!! )
91
+ }
92
+
93
+ fun end () {
94
+ games.remove(group.id)
53
95
}
54
96
}
55
- // 猜群友
56
- @SubCommand
57
- suspend fun CommandSender.guess (target : User ) {
58
- if (! playing) {this .sendMessage(" 游戏未开始!" );} else {
59
- if (target.id == normalMember?.id) {
60
- this .sendMessage(At (this .user!! ).plus(" 恭喜你回答正确!" ))
61
- } else {
62
- this .sendMessage(At (this .user!! ).plus(" 回答错误!出现新提示:" ).plus(random()))
63
- }
97
+
98
+ // 开始游戏
99
+ @SubCommand(" 开始" )
100
+ @Description(" 开始猜群友游戏" )
101
+ suspend fun CommandSender.play () {
102
+ if (this !is MemberCommandSender ) {
103
+ sendMessage(" 仅可在群聊中使用" )
104
+ return
64
105
}
106
+ if (games[group.id] != null ) {
107
+ sendMessage(" 游戏进行中, 发送 `/猜群友 猜 @猜的群友` 来猜群友" )
108
+ return
109
+ }
110
+ // 获取群友
111
+ val member = group.members.filter { member ->
112
+ conditions.all { it.invoke(member) }
113
+ }[Random .nextInt(group.members.size)]
114
+ val game = Game (group, member)
115
+ game.start()
65
116
}
66
- }
67
- private fun random ():String {
68
- if (data.isNullOrEmpty()) {
69
- playing = false ;
70
- return " 看来没人能猜到我,我要公布答案了:这位群友是:${normalMember?.nick} "
71
- } else {
72
- val r = (0 until data?.size!! ).random()
73
- if (data!! .elementAtOrNull(r)== null ){
74
- playing = false ;
75
- return " 看来没人能猜到我,我要公布答案了:这位群友是:${normalMember?.nick} "
76
- }
77
- val res = data!! [r]
78
- data?.removeAt(r)
79
- return res
117
+
118
+ // 猜群友
119
+ @SubCommand(" 猜" )
120
+ @Description(" 在猜群友游戏猜一次群友" )
121
+ suspend fun CommandSender.guess (target : User , chain : MessageChain ) {
122
+ if (this !is MemberCommandSender ) {
123
+ sendMessage(" 仅可在群聊中使用" )
124
+ return
80
125
}
81
- }
126
+ val game = games[group.id]
127
+ if (game == null ) {
128
+ sendMessage(" 游戏未开始!" )
129
+ return
130
+ }
131
+ val success = target.id == game.member.id
132
+ if (success) {
133
+ group.sendMessage(buildMessageChain {
134
+ append(PlainText (" 恭喜 " ))
135
+ append(At (user))
136
+ append(PlainText (" 猜出了群友, 游戏结束\n 发送 `/猜群友 开始` 开始新游戏" ))
137
+ })
138
+ return
139
+ }
140
+ val hint = game.getHint()
141
+ // 提示用完
142
+ if (hint == null ) {
143
+ game.end()
144
+ sendMessage(" 看来没人能猜到我,我要公布答案了:这位群友是: ${game.member.nameCardOrNick} " )
145
+ return
146
+ }
147
+ sendMessage(
148
+ buildMessageChain {
149
+ append(chain.quote())
150
+ append(At (user))
151
+ append(" 回答错误!\n 新提示:$hint " )
152
+ }
153
+ )
154
+ }
155
+ }
0 commit comments