Skip to content

Commit be1632d

Browse files
committed
Handle options for quizoji
1 parent fb0867f commit be1632d

File tree

4 files changed

+387
-0
lines changed

4 files changed

+387
-0
lines changed

dialogs/src/main/kotlin/by/jprof/telegram/bot/dialogs/model/quizoji/WaitingForOptions.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,5 @@ data class WaitingForOptions(
1111
override val chatId: Long,
1212
override val userId: Long,
1313
val question: MessageContent,
14+
val options: List<String> = emptyList(),
1415
) : DialogState
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package by.jprof.telegram.bot.quizoji
2+
3+
import by.jprof.telegram.bot.core.UpdateProcessor
4+
import by.jprof.telegram.bot.dialogs.dao.DialogStateDAO
5+
import by.jprof.telegram.bot.dialogs.model.quizoji.WaitingForOptions
6+
import dev.inmo.tgbotapi.bot.RequestsExecutor
7+
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
8+
import dev.inmo.tgbotapi.extensions.utils.asMessageUpdate
9+
import dev.inmo.tgbotapi.extensions.utils.asPrivateChat
10+
import dev.inmo.tgbotapi.extensions.utils.asPrivateContentMessage
11+
import dev.inmo.tgbotapi.types.MessageEntity.textsources.BotCommandTextSource
12+
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
13+
import dev.inmo.tgbotapi.types.message.content.TextContent
14+
import dev.inmo.tgbotapi.types.update.abstracts.Update
15+
import org.apache.logging.log4j.LogManager
16+
17+
class QuizojiOptionUpdateProcessor(
18+
private val dialogStateDAO: DialogStateDAO,
19+
private val bot: RequestsExecutor,
20+
) : UpdateProcessor {
21+
companion object {
22+
private val logger = LogManager.getLogger(QuizojiOptionUpdateProcessor::class.java)!!
23+
}
24+
25+
override suspend fun process(update: Update) {
26+
val message = update.asMessageUpdate()?.data?.asPrivateContentMessage() ?: return
27+
val chat = message.chat.asPrivateChat() ?: return
28+
val state = dialogStateDAO.get(chat.id.chatId, message.user.id.chatId)
29+
val content = message.content
30+
31+
if (state !is WaitingForOptions) {
32+
return
33+
}
34+
35+
if (content !is TextContent) {
36+
logger.warn("Unsupported option content: {}", content)
37+
38+
bot.sendMessage(
39+
chat = chat,
40+
text = "Unsupported option type: ${content::class.simpleName?.replace("Content", "")}"
41+
)
42+
43+
return
44+
}
45+
46+
if (content.textSources.any { it is BotCommandTextSource }) {
47+
logger.warn("Command sent as option: {}", content)
48+
49+
return
50+
}
51+
52+
logger.debug("{} provided an option ({}) for his Quizoji", chat.id.chatId, content.text)
53+
54+
dialogStateDAO.save(
55+
WaitingForOptions(
56+
chatId = chat.id.chatId,
57+
userId = message.user.id.chatId,
58+
question = state.question,
59+
options = state.options + content.text
60+
)
61+
)
62+
63+
bot.sendMessage(
64+
chat = chat,
65+
text = if (state.options.size >= 7) {
66+
"Send more options or /done when ready\\."
67+
} else {
68+
"Send more options or /done when ready\\.\n\n_Up to ${7 - state.options.size} more options are recommended\\._"
69+
},
70+
parseMode = MarkdownV2
71+
)
72+
}
73+
}
Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
package by.jprof.telegram.bot.quizoji
2+
3+
import by.jprof.telegram.bot.dialogs.dao.DialogStateDAO
4+
import by.jprof.telegram.bot.dialogs.model.DialogState
5+
import by.jprof.telegram.bot.dialogs.model.quizoji.WaitingForOptions
6+
import com.soywiz.klock.DateTime
7+
import dev.inmo.tgbotapi.bot.RequestsExecutor
8+
import dev.inmo.tgbotapi.extensions.api.send.sendMessage
9+
import dev.inmo.tgbotapi.types.ChatId
10+
import dev.inmo.tgbotapi.types.CommonUser
11+
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
12+
import dev.inmo.tgbotapi.types.chat.PrivateChatImpl
13+
import dev.inmo.tgbotapi.types.chat.abstracts.ChannelChat
14+
import dev.inmo.tgbotapi.types.dice.Dice
15+
import dev.inmo.tgbotapi.types.dice.SlotMachineDiceAnimationType
16+
import dev.inmo.tgbotapi.types.message.PrivateContentMessageImpl
17+
import dev.inmo.tgbotapi.types.message.abstracts.ChannelContentMessage
18+
import dev.inmo.tgbotapi.types.message.content.DiceContent
19+
import dev.inmo.tgbotapi.types.message.content.TextContent
20+
import dev.inmo.tgbotapi.types.update.MessageUpdate
21+
import dev.inmo.tgbotapi.types.update.PollUpdate
22+
import io.mockk.*
23+
import io.mockk.impl.annotations.MockK
24+
import io.mockk.junit5.MockKExtension
25+
import kotlinx.coroutines.runBlocking
26+
import org.junit.jupiter.api.BeforeEach
27+
import org.junit.jupiter.api.Test
28+
import org.junit.jupiter.api.extension.ExtendWith
29+
30+
@ExtendWith(MockKExtension::class)
31+
internal class QuizojiOptionUpdateProcessorTest {
32+
@MockK(relaxed = true)
33+
private lateinit var bot: RequestsExecutor
34+
35+
@MockK(relaxed = true)
36+
private lateinit var dialogStateDAO: DialogStateDAO
37+
38+
lateinit var sut: QuizojiOptionUpdateProcessor
39+
40+
@BeforeEach
41+
fun setUp() {
42+
sut = QuizojiOptionUpdateProcessor(
43+
dialogStateDAO = dialogStateDAO,
44+
bot = bot,
45+
)
46+
}
47+
48+
@Test
49+
fun processNonMessageUpdate() = runBlocking {
50+
sut.process(
51+
PollUpdate(
52+
updateId = 1,
53+
data = mockk()
54+
)
55+
)
56+
57+
verify { listOf(dialogStateDAO, bot) wasNot called }
58+
59+
clearAllMocks()
60+
}
61+
62+
@Test
63+
fun processNonPrivateContentMessage() = runBlocking {
64+
sut.process(
65+
MessageUpdate(
66+
updateId = 1,
67+
data = mockk<ChannelContentMessage<*>>()
68+
)
69+
)
70+
71+
verify { listOf(dialogStateDAO, bot) wasNot called }
72+
73+
clearAllMocks()
74+
}
75+
76+
@Test
77+
fun processNonPrivateChat() = runBlocking {
78+
sut.process(
79+
MessageUpdate(
80+
updateId = 1,
81+
data = PrivateContentMessageImpl(
82+
messageId = 1,
83+
user = mockk(),
84+
chat = mockk<ChannelChat>(),
85+
content = mockk(),
86+
date = DateTime.now(),
87+
editDate = null,
88+
forwardInfo = null,
89+
replyTo = null,
90+
replyMarkup = null,
91+
senderBot = null,
92+
paymentInfo = null,
93+
)
94+
)
95+
)
96+
97+
verify { listOf(dialogStateDAO, bot) wasNot called }
98+
99+
clearAllMocks()
100+
}
101+
102+
@Test
103+
fun processNonWaitingForOptions() = runBlocking {
104+
val chat = PrivateChatImpl(
105+
id = ChatId(1),
106+
)
107+
108+
coEvery { dialogStateDAO.get(1, 2) }.returns(object : DialogState {
109+
override val chatId = 1L
110+
override val userId = 2L
111+
})
112+
113+
sut.process(
114+
MessageUpdate(
115+
updateId = 1,
116+
data = PrivateContentMessageImpl(
117+
messageId = 1,
118+
user = CommonUser(id = ChatId(2), "Test"),
119+
chat = chat,
120+
content = TextContent(
121+
text = "Test"
122+
),
123+
date = DateTime.now(),
124+
editDate = null,
125+
forwardInfo = null,
126+
replyTo = null,
127+
replyMarkup = null,
128+
senderBot = null,
129+
paymentInfo = null,
130+
)
131+
)
132+
)
133+
134+
coVerify(exactly = 1) { dialogStateDAO.get(1, 2) }
135+
verify { listOf(bot) wasNot called }
136+
137+
clearAllMocks()
138+
}
139+
140+
@Test
141+
fun processUnsupportedContent() = runBlocking {
142+
val chat = PrivateChatImpl(
143+
id = ChatId(1),
144+
)
145+
146+
coEvery { dialogStateDAO.get(1, 2) }.returns(WaitingForOptions(1, 2, TextContent("Test")))
147+
148+
sut.process(
149+
MessageUpdate(
150+
updateId = 1,
151+
data = PrivateContentMessageImpl(
152+
messageId = 1,
153+
user = CommonUser(id = ChatId(2), "Test"),
154+
chat = chat,
155+
content = DiceContent(dice = Dice(value = 3, animationType = SlotMachineDiceAnimationType)),
156+
date = DateTime.now(),
157+
editDate = null,
158+
forwardInfo = null,
159+
replyTo = null,
160+
replyMarkup = null,
161+
senderBot = null,
162+
paymentInfo = null,
163+
)
164+
)
165+
)
166+
167+
coVerify(exactly = 1) { dialogStateDAO.get(1, 2) }
168+
coVerify(exactly = 1) {
169+
bot.sendMessage(
170+
chat = chat,
171+
text = "Unsupported option type: Dice"
172+
)
173+
}
174+
175+
clearAllMocks()
176+
}
177+
178+
@Test
179+
fun process() = runBlocking {
180+
val chat = PrivateChatImpl(
181+
id = ChatId(1),
182+
)
183+
val content = TextContent(
184+
text = "Option"
185+
)
186+
187+
coEvery { dialogStateDAO.get(1, 2) }.returns(WaitingForOptions(1, 2, TextContent("Test")))
188+
189+
sut.process(
190+
MessageUpdate(
191+
updateId = 1,
192+
data = PrivateContentMessageImpl(
193+
messageId = 1,
194+
user = CommonUser(id = ChatId(2), "Test"),
195+
chat = chat,
196+
content = content,
197+
date = DateTime.now(),
198+
editDate = null,
199+
forwardInfo = null,
200+
replyTo = null,
201+
replyMarkup = null,
202+
senderBot = null,
203+
paymentInfo = null,
204+
)
205+
)
206+
)
207+
208+
coVerify(exactly = 1) { dialogStateDAO.get(1, 2) }
209+
coVerify(exactly = 1) {
210+
dialogStateDAO.save(
211+
WaitingForOptions(
212+
chatId = 1,
213+
userId = 2,
214+
question = TextContent("Test"),
215+
options = listOf("Option")
216+
)
217+
)
218+
}
219+
coVerify(exactly = 1) {
220+
bot.sendMessage(
221+
chat = chat,
222+
text = "Send more options or /done when ready\\.\n\n_Up to 7 more options are recommended\\._",
223+
parseMode = MarkdownV2
224+
)
225+
}
226+
227+
clearAllMocks()
228+
}
229+
230+
@Test
231+
fun processManyOptions() = runBlocking {
232+
val chat = PrivateChatImpl(
233+
id = ChatId(1),
234+
)
235+
val content = TextContent(
236+
text = "Option 8"
237+
)
238+
239+
coEvery { dialogStateDAO.get(1, 2) }.returns(
240+
WaitingForOptions(
241+
1,
242+
2,
243+
TextContent("Test"),
244+
listOf(
245+
"Option 1",
246+
"Option 2",
247+
"Option 3",
248+
"Option 4",
249+
"Option 5",
250+
"Option 6",
251+
"Option 7",
252+
)
253+
)
254+
)
255+
256+
sut.process(
257+
MessageUpdate(
258+
updateId = 1,
259+
data = PrivateContentMessageImpl(
260+
messageId = 1,
261+
user = CommonUser(id = ChatId(2), "Test"),
262+
chat = chat,
263+
content = content,
264+
date = DateTime.now(),
265+
editDate = null,
266+
forwardInfo = null,
267+
replyTo = null,
268+
replyMarkup = null,
269+
senderBot = null,
270+
paymentInfo = null,
271+
)
272+
)
273+
)
274+
275+
coVerify(exactly = 1) { dialogStateDAO.get(1, 2) }
276+
coVerify(exactly = 1) {
277+
dialogStateDAO.save(
278+
WaitingForOptions(
279+
chatId = 1,
280+
userId = 2,
281+
question = TextContent("Test"),
282+
options = listOf(
283+
"Option 1",
284+
"Option 2",
285+
"Option 3",
286+
"Option 4",
287+
"Option 5",
288+
"Option 6",
289+
"Option 7",
290+
"Option 8",
291+
)
292+
)
293+
)
294+
}
295+
coVerify(exactly = 1) {
296+
bot.sendMessage(
297+
chat = chat,
298+
text = "Send more options or /done when ready\\.",
299+
parseMode = MarkdownV2
300+
)
301+
}
302+
303+
clearAllMocks()
304+
}
305+
}

0 commit comments

Comments
 (0)