diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/Prizebot.kt b/bot/src/main/kotlin/me/y9san9/prizebot/Prizebot.kt index 7bdf385..137eb19 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/Prizebot.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/Prizebot.kt @@ -6,7 +6,9 @@ import dev.inmo.tgbotapi.bot.TelegramBot import dev.inmo.tgbotapi.extensions.api.send.sendMessage import dev.inmo.tgbotapi.extensions.utils.updates.retrieving.longPolling import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.message.abstracts.ChannelContentMessage import dev.inmo.tgbotapi.types.message.abstracts.PrivateContentMessage +import dev.inmo.tgbotapi.types.message.abstracts.PublicContentMessage import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch @@ -16,7 +18,7 @@ import me.y9san9.prizebot.actors.giveaway.AutoRaffleActor import me.y9san9.prizebot.database.giveaways_active_messages_storage.GiveawaysActiveMessagesStorage import me.y9san9.prizebot.database.giveaways_storage.GiveawaysStorage import me.y9san9.prizebot.database.language_codes_storage.LanguageCodesStorage -import me.y9san9.prizebot.database.participants_storage.ParticipantsStorage +import me.y9san9.prizebot.database.linked_channels_storage.LinkedChannelsStorage import me.y9san9.prizebot.database.states_storage.PrizebotFSMStorage import me.y9san9.prizebot.handlers.callback_queries.CallbackQueryHandler import me.y9san9.prizebot.handlers.choosen_inline_result.ChosenInlineResultHandler @@ -26,9 +28,13 @@ import me.y9san9.prizebot.handlers.private_messages.fsm.states.MainState import me.y9san9.prizebot.handlers.private_messages.fsm.states.statesSerializers import me.y9san9.prizebot.di.PrizebotDI import me.y9san9.prizebot.extensions.telegram.PrizebotPrivateMessageUpdate +import me.y9san9.prizebot.handlers.channel_posts.ChannelPostsHandler import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.* +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions.InvitationsCountInputState +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions.SubscriptionChannelInputState import me.y9san9.telegram.updates.CallbackQueryUpdate import me.y9san9.telegram.updates.ChosenInlineResultUpdate +import me.y9san9.telegram.updates.ChannelPostUpdate import me.y9san9.telegram.updates.InlineQueryUpdate import org.jetbrains.exposed.sql.Database @@ -53,15 +59,21 @@ class Prizebot ( val di = PrizebotDI ( giveawaysStorage = GiveawaysStorage(database), giveawaysActiveMessagesStorage = GiveawaysActiveMessagesStorage(database), - languageCodesStorage = LanguageCodesStorage(database) + languageCodesStorage = LanguageCodesStorage(database), + linkedChannelsStorage = LinkedChannelsStorage(database) ) scheduleRaffles(bot, di) - val messages = messageFlow + val privateMessages = messageFlow .mapNotNull { it.data as? PrivateContentMessage<*> } .map { PrizebotPrivateMessageUpdate(bot, di, message = it) } - createFSM(events = messages) + createFSM(events = privateMessages) + + channelPostFlow + .mapNotNull { it.data as? ChannelContentMessage<*> } + .map { ChannelPostUpdate(bot, di, message = it) } + .subscribeSafely(scope, ::logException, ChannelPostsHandler::handle) inlineQueryFlow .map { InlineQueryUpdate(bot, di, query = it) } @@ -86,7 +98,9 @@ class Prizebot ( initial = MainState, TitleInputState, ParticipateTextInputState, RaffleDateInputState, TimezoneInputState, - CustomTimezoneInputState, WinnersCountInputState + CustomTimezoneInputState, WinnersCountInputState, + ConditionInputState, InvitationsCountInputState, + SubscriptionChannelInputState ), storage = PrizebotFSMStorage(database, statesSerializers), scope = scope, diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/AutoRaffleActor.kt b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/AutoRaffleActor.kt index 813b72b..8a47c99 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/AutoRaffleActor.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/AutoRaffleActor.kt @@ -8,10 +8,8 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import me.y9san9.prizebot.database.giveaways_active_messages_storage.GiveawaysActiveMessagesStorage import me.y9san9.prizebot.database.giveaways_storage.ActiveGiveaway -import me.y9san9.prizebot.database.giveaways_storage.Giveaway import me.y9san9.prizebot.database.giveaways_storage.GiveawaysStorage import me.y9san9.prizebot.database.language_codes_storage.LanguageCodesStorage -import me.y9san9.prizebot.database.participants_storage.ParticipantsStorage import me.y9san9.prizebot.actors.telegram.updater.GiveawayActiveMessagesUpdater import me.y9san9.prizebot.resources.locales.Locale import me.y9san9.telegram.updates.hierarchies.DIBotUpdate @@ -52,7 +50,7 @@ object AutoRaffleActor : CoroutineScope { scheduledMutex.withLock { if (giveaway.id in scheduled && di.getGiveawayById(giveaway.id) != null) { scheduled.remove(giveaway.id) - handleRaffleResult(bot, di, giveaway, RaffleActor.raffle(giveaway)) + handleRaffleResult(bot, di, giveaway, RaffleActor.raffle(bot, giveaway)) } } } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/ConditionsChecker.kt b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/ConditionsChecker.kt new file mode 100644 index 0000000..d10abac --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/ConditionsChecker.kt @@ -0,0 +1,45 @@ +package me.y9san9.prizebot.actors.giveaway + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.bot.exceptions.RequestException +import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.extensions.api.chat.members.getChatMember +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.ChatMember.MemberChatMember +import dev.inmo.tgbotapi.types.UserId +import dev.inmo.tgbotapi.types.chat.abstracts.UsernameChat +import me.y9san9.prizebot.database.giveaways_storage.ActiveGiveaway +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.Condition +import me.y9san9.prizebot.extensions.list.on + + +sealed class CheckConditionsResult { + object GiveawayInvalid : CheckConditionsResult() + object NotSubscribedToConditions : CheckConditionsResult() + class FriendsAreNotInvited(val invitedCount: Int, val requiredCount: Int) : CheckConditionsResult() + object Success : CheckConditionsResult() +} + +object ConditionsChecker { + suspend fun check(bot: TelegramBot, participantId: Long, giveaway: ActiveGiveaway): CheckConditionsResult { + giveaway.conditions.list + .on { condition: Condition.Subscription -> + val channel = try { + bot.getChat(ChatId(condition.channelId)) as? UsernameChat + } catch (_: RequestException) { null } + ?: return CheckConditionsResult.GiveawayInvalid + + if(channel.username?.username != condition.channelUsername) + return CheckConditionsResult.GiveawayInvalid + + try { + bot.getChatMember(channel.id, UserId(participantId)) + .takeIf { it is MemberChatMember } + } catch (_: RequestException) { null } + ?: return CheckConditionsResult.NotSubscribedToConditions + + }.on { _: Condition.Invitations -> TODO() } + + return CheckConditionsResult.Success + } +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/CreateGiveawayActor.kt b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/CreateGiveawayActor.kt index 361549e..5466977 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/CreateGiveawayActor.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/CreateGiveawayActor.kt @@ -4,6 +4,7 @@ import me.y9san9.fsm.FSMStateResult import me.y9san9.fsm.stateResult import me.y9san9.prizebot.database.giveaways_storage.WinnersCount import me.y9san9.prizebot.actors.telegram.sender.GiveawayCreatedSender +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.GiveawayConditions import me.y9san9.prizebot.extensions.telegram.PrizebotPrivateMessageUpdate import me.y9san9.prizebot.handlers.private_messages.fsm.states.MainState import java.time.OffsetDateTime @@ -15,13 +16,14 @@ object CreateGiveawayActor { title: String, participateText: String, raffleDate: OffsetDateTime?, - winnersCount: WinnersCount + winnersCount: WinnersCount, + conditions: GiveawayConditions ): FSMStateResult<*> { val giveaway = update.di.saveGiveaway ( update.chatId, title, participateText, languageCode = update.di.getLanguageCode(update.chatId) ?: update.languageCode, - raffleDate, winnersCount + raffleDate, winnersCount, conditions ) GiveawayCreatedSender.send(update, giveaway) diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/RaffleActor.kt b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/RaffleActor.kt index 31bba12..03d127d 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/RaffleActor.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/actors/giveaway/RaffleActor.kt @@ -1,23 +1,32 @@ package me.y9san9.prizebot.actors.giveaway +import dev.inmo.tgbotapi.bot.TelegramBot +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.take +import kotlinx.coroutines.flow.toList import me.y9san9.prizebot.database.giveaways_storage.ActiveGiveaway -import me.y9san9.prizebot.database.giveaways_storage.GiveawaysStorage import me.y9san9.random.extensions.shuffledRandomOrg object RaffleActor { suspend fun raffle ( + bot: TelegramBot, giveaway: ActiveGiveaway ): Boolean { - val winnerIds = chooseWinners(giveaway) ?: return false + val winnerIds = chooseWinners(bot, giveaway) ?: return false giveaway.finish(winnerIds) return true } private suspend fun chooseWinners ( + bot: TelegramBot, giveaway: ActiveGiveaway, ) = giveaway.participants .shuffledRandomOrg() + .asFlow() + .filter { userId -> ConditionsChecker.check(bot, userId, giveaway) is CheckConditionsResult.Success } .take(giveaway.winnersCount.value) + .toList() .takeIf { it.size == giveaway.winnersCount.value } } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/Giveaway.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/Giveaway.kt index 758a22b..37cd2af 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/Giveaway.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/Giveaway.kt @@ -1,12 +1,15 @@ package me.y9san9.prizebot.database.giveaways_storage +import kotlinx.serialization.Serializable +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.ConditionsStorage import me.y9san9.prizebot.database.giveaways_storage.giveaways_patch_storage.GiveawaysPatchStorage -import me.y9san9.prizebot.database.participants_storage.ParticipantsStorage -import me.y9san9.prizebot.database.winners_storage.WinnersStorage +import me.y9san9.prizebot.database.giveaways_storage.participants_storage.ParticipantsStorage +import me.y9san9.prizebot.database.giveaways_storage.winners_storage.WinnersStorage import me.y9san9.prizebot.resources.locales.Locale import java.time.OffsetDateTime +@Serializable inline class WinnersCount(val value: Int) { init { require(value in 1..50_000) { "Winners count is out of range" } @@ -36,6 +39,12 @@ sealed class Giveaway { internal abstract val giveawaysPatchStorage: GiveawaysPatchStorage fun delete() = giveawaysPatchStorage.deleteGiveaway(id) + + /* Conditions composition */ + + internal abstract val conditionsStorage: ConditionsStorage + + val conditions by lazy { conditionsStorage.loadConditions(id) } } data class ActiveGiveaway internal constructor ( @@ -47,6 +56,7 @@ data class ActiveGiveaway internal constructor ( override val raffleDate: OffsetDateTime?, override val participantsStorage: ParticipantsStorage, override val giveawaysPatchStorage: GiveawaysPatchStorage, + override val conditionsStorage: ConditionsStorage, val winnersCount: WinnersCount ) : Giveaway() { fun removeRaffleDate() = giveawaysPatchStorage.removeRaffleDate(id) @@ -62,6 +72,7 @@ data class FinishedGiveaway internal constructor ( override val raffleDate: OffsetDateTime?, override val participantsStorage: ParticipantsStorage, override val giveawaysPatchStorage: GiveawaysPatchStorage, + override val conditionsStorage: ConditionsStorage, private val winnersStorage: WinnersStorage ) : Giveaway() { val winnerIds by lazy { winnersStorage.getWinners(id) } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/GiveawaysStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/GiveawaysStorage.kt index fa12342..8dd1b67 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/GiveawaysStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/GiveawaysStorage.kt @@ -1,5 +1,6 @@ package me.y9san9.prizebot.database.giveaways_storage +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.GiveawayConditions import org.jetbrains.exposed.sql.Database import java.time.OffsetDateTime @@ -14,7 +15,8 @@ interface GiveawaysStorage { participateButton: String, languageCode: String?, raffleDate: OffsetDateTime?, - winnersCount: WinnersCount + winnersCount: WinnersCount, + conditions: GiveawayConditions ): ActiveGiveaway fun getUserGiveaways(ownerId: Long, count: Int = 20, offset: Long = 0): List fun getAllGiveaways(): List diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/TableGiveawaysStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/TableGiveawaysStorage.kt index 2eb8f0e..ce3fd1f 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/TableGiveawaysStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/TableGiveawaysStorage.kt @@ -7,10 +7,11 @@ import me.y9san9.prizebot.database.giveaways_storage.Giveaways.GIVEAWAY_PARTICIP import me.y9san9.prizebot.database.giveaways_storage.Giveaways.GIVEAWAY_RAFFLE_DATE import me.y9san9.prizebot.database.giveaways_storage.Giveaways.GIVEAWAY_TITLE import me.y9san9.prizebot.database.giveaways_storage.Giveaways.GIVEAWAY_WINNERS_COUNT +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.ConditionsStorage +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.GiveawayConditions import me.y9san9.prizebot.database.giveaways_storage.giveaways_patch_storage.GiveawaysPatchStorage -import me.y9san9.prizebot.database.participants_storage.ParticipantsStorage -import me.y9san9.prizebot.database.winners_storage.WinnersStorage -import me.y9san9.prizebot.extensions.any.unit +import me.y9san9.prizebot.database.giveaways_storage.participants_storage.ParticipantsStorage +import me.y9san9.prizebot.database.giveaways_storage.winners_storage.WinnersStorage import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction import java.time.OffsetDateTime @@ -22,6 +23,7 @@ internal class TableGiveawaysStorage ( private val participantsStorage = ParticipantsStorage(database) private val winnersStorage = WinnersStorage(database) + private val conditionsStorage = ConditionsStorage(database) private val giveawaysPatchStorage = GiveawaysPatchStorage(database, winnersStorage) init { @@ -40,7 +42,8 @@ internal class TableGiveawaysStorage ( participateButton: String, languageCode: String?, raffleDate: OffsetDateTime?, - winnersCount: WinnersCount + winnersCount: WinnersCount, + conditions: GiveawayConditions ) = transaction(database) { Giveaways.insert { it[GIVEAWAY_OWNER_ID] = ownerId @@ -51,11 +54,12 @@ internal class TableGiveawaysStorage ( it[GIVEAWAY_WINNERS_COUNT] = winnersCount.value } }.let { data -> + conditionsStorage.addConditions(data[GIVEAWAY_ID], conditions) ActiveGiveaway ( id = data[GIVEAWAY_ID], ownerId, title, participateButton, languageCode, raffleDate, participantsStorage, - giveawaysPatchStorage, winnersCount + giveawaysPatchStorage, conditionsStorage, winnersCount ) } @@ -79,7 +83,7 @@ internal class TableGiveawaysStorage ( this[GIVEAWAY_PARTICIPATE_BUTTON], this[GIVEAWAY_LANGUAGE_CODE], this[GIVEAWAY_RAFFLE_DATE]?.let(OffsetDateTime::parse), - participantsStorage, giveawaysPatchStorage, + participantsStorage, giveawaysPatchStorage, conditionsStorage, WinnersCount(this[GIVEAWAY_WINNERS_COUNT]) ) else @@ -90,7 +94,8 @@ internal class TableGiveawaysStorage ( this[GIVEAWAY_PARTICIPATE_BUTTON], this[GIVEAWAY_LANGUAGE_CODE], this[GIVEAWAY_RAFFLE_DATE]?.let(OffsetDateTime::parse), - participantsStorage, giveawaysPatchStorage, winnersStorage + participantsStorage, giveawaysPatchStorage, + conditionsStorage, winnersStorage ) } } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/Condition.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/Condition.kt new file mode 100644 index 0000000..8138cde --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/Condition.kt @@ -0,0 +1,52 @@ +@file:Suppress("MemberVisibilityCanBePrivate", "CanBeParameter") + +package me.y9san9.prizebot.database.giveaways_storage.conditions_storage + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + + +@Serializable +inline class PositiveInt internal constructor(val int: Int) { + init { + require(int > 0) { "The value must be positive" } + } +} + +fun Int.wrapPositiveInt() = PositiveInt(this) + + +@Serializable +sealed class Condition { + @Serializable + @SerialName("subscription") + data class Subscription( + val channelId: Long, + val channelUsername: String + ) : Condition() + + @Serializable + @SerialName("invitations") + data class Invitations ( + val count: PositiveInt + ) : Condition() +} + + +@Serializable +// It is not inline because of serialization bug :( +/*inline*/ class GiveawayConditions internal constructor ( + val list: List +) { + init { + require(list.count { it is Condition.Invitations } <= 1) { + "Only one invitations condition allowed" + } + require(list.filterIsInstance().isEmpty() + || list.filterIsInstance().isNotEmpty()) { + "You should add at least one channel in case you want to invite friends" + } + } +} + +fun List.wrapGiveawayConditions() = GiveawayConditions(list = this) diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/ConditionsStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/ConditionsStorage.kt new file mode 100644 index 0000000..f13c2f6 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/ConditionsStorage.kt @@ -0,0 +1,11 @@ +package me.y9san9.prizebot.database.giveaways_storage.conditions_storage + +import org.jetbrains.exposed.sql.Database + + +internal fun ConditionsStorage(database: Database): ConditionsStorage = TableConditionsStorage(database) + +internal interface ConditionsStorage { + fun addConditions(giveawayId: Long, conditions: GiveawayConditions) + fun loadConditions(giveawayId: Long): GiveawayConditions +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/TableConditionsStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/TableConditionsStorage.kt new file mode 100644 index 0000000..0421b20 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/conditions_storage/TableConditionsStorage.kt @@ -0,0 +1,58 @@ +package me.y9san9.prizebot.database.giveaways_storage.conditions_storage + +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.TableConditionsStorage.Conditions.GIVEAWAY_ID +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.TableConditionsStorage.Conditions.INVITATIONS_COUNT +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.TableConditionsStorage.Conditions.SUBSCRIPTION_CHANNEL_ID +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.TableConditionsStorage.Conditions.SUBSCRIPTION_CHANNEL_USERNAME +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + + +internal class TableConditionsStorage ( + private val database: Database +) : ConditionsStorage { + private object Conditions : Table(name = "conditions") { + val GIVEAWAY_ID = long("giveawayId") + val INVITATIONS_COUNT = integer("invitationsCount").nullable() + val SUBSCRIPTION_CHANNEL_ID = long("subscriptionChannelId").nullable() + val SUBSCRIPTION_CHANNEL_USERNAME = text("subscriptionChannelUsername").nullable() + } + + init { + transaction(database) { + SchemaUtils.create(Conditions) + } + } + + override fun addConditions(giveawayId: Long, conditions: GiveawayConditions) = transaction(database) { + conditions.list.forEach { condition -> + Conditions.insert { + it[GIVEAWAY_ID] = giveawayId + when(condition) { + is Condition.Subscription -> { + it[SUBSCRIPTION_CHANNEL_ID] = condition.channelId + it[SUBSCRIPTION_CHANNEL_USERNAME] = condition.channelUsername + } + is Condition.Invitations -> it[INVITATIONS_COUNT] = condition.count.int + } + } + } + } + + override fun loadConditions(giveawayId: Long): GiveawayConditions = transaction(database) { + Conditions.select { GIVEAWAY_ID eq giveawayId } + .map { it.toCondition() } + .wrapGiveawayConditions() + } + + private fun ResultRow.toCondition(): Condition { + this[SUBSCRIPTION_CHANNEL_ID]?.let { channelId -> + return Condition.Subscription ( + channelId, + channelUsername = this[SUBSCRIPTION_CHANNEL_USERNAME] ?: error("Username must be present")) + } + this[INVITATIONS_COUNT]?.let { return Condition.Invitations(it.wrapPositiveInt()) } + + error("This condition is not valid") + } +} \ No newline at end of file diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/GiveawaysPatchStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/GiveawaysPatchStorage.kt index 43b095f..130b42d 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/GiveawaysPatchStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/GiveawaysPatchStorage.kt @@ -1,6 +1,6 @@ package me.y9san9.prizebot.database.giveaways_storage.giveaways_patch_storage -import me.y9san9.prizebot.database.winners_storage.WinnersStorage +import me.y9san9.prizebot.database.giveaways_storage.winners_storage.WinnersStorage import org.jetbrains.exposed.sql.Database diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/TableGiveawaysPatchStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/TableGiveawaysPatchStorage.kt index 012e72b..444d0ac 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/TableGiveawaysPatchStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/giveaways_patch_storage/TableGiveawaysPatchStorage.kt @@ -1,7 +1,7 @@ package me.y9san9.prizebot.database.giveaways_storage.giveaways_patch_storage import me.y9san9.prizebot.database.giveaways_storage.Giveaways -import me.y9san9.prizebot.database.winners_storage.WinnersStorage +import me.y9san9.prizebot.database.giveaways_storage.winners_storage.WinnersStorage import me.y9san9.prizebot.extensions.any.unit import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/participants_storage/ParticipantsStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/participants_storage/ParticipantsStorage.kt similarity index 85% rename from bot/src/main/kotlin/me/y9san9/prizebot/database/participants_storage/ParticipantsStorage.kt rename to bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/participants_storage/ParticipantsStorage.kt index 70e4f46..21d6ac0 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/participants_storage/ParticipantsStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/participants_storage/ParticipantsStorage.kt @@ -1,4 +1,4 @@ -package me.y9san9.prizebot.database.participants_storage +package me.y9san9.prizebot.database.giveaways_storage.participants_storage import org.jetbrains.exposed.sql.Database diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/participants_storage/TableParticipantsStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/participants_storage/TableParticipantsStorage.kt similarity index 80% rename from bot/src/main/kotlin/me/y9san9/prizebot/database/participants_storage/TableParticipantsStorage.kt rename to bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/participants_storage/TableParticipantsStorage.kt index 19ff838..0f56284 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/participants_storage/TableParticipantsStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/participants_storage/TableParticipantsStorage.kt @@ -1,7 +1,7 @@ -package me.y9san9.prizebot.database.participants_storage +package me.y9san9.prizebot.database.giveaways_storage.participants_storage -import me.y9san9.prizebot.database.participants_storage.TableParticipantsStorage.Participants.PARTICIPANTS_GIVEAWAY_ID -import me.y9san9.prizebot.database.participants_storage.TableParticipantsStorage.Participants.PARTICIPANTS_USER_ID +import me.y9san9.prizebot.database.giveaways_storage.participants_storage.TableParticipantsStorage.Participants.PARTICIPANTS_GIVEAWAY_ID +import me.y9san9.prizebot.database.giveaways_storage.participants_storage.TableParticipantsStorage.Participants.PARTICIPANTS_USER_ID import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/winners_storage/TableWinnersStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/winners_storage/TableWinnersStorage.kt similarity index 78% rename from bot/src/main/kotlin/me/y9san9/prizebot/database/winners_storage/TableWinnersStorage.kt rename to bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/winners_storage/TableWinnersStorage.kt index 08e30f3..985d335 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/winners_storage/TableWinnersStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/winners_storage/TableWinnersStorage.kt @@ -1,7 +1,7 @@ -package me.y9san9.prizebot.database.winners_storage +package me.y9san9.prizebot.database.giveaways_storage.winners_storage -import me.y9san9.prizebot.database.winners_storage.TableWinnersStorage.Winners.GIVEAWAY_ID -import me.y9san9.prizebot.database.winners_storage.TableWinnersStorage.Winners.WINNER_ID +import me.y9san9.prizebot.database.giveaways_storage.winners_storage.TableWinnersStorage.Winners.GIVEAWAY_ID +import me.y9san9.prizebot.database.giveaways_storage.winners_storage.TableWinnersStorage.Winners.WINNER_ID import org.jetbrains.exposed.sql.* import org.jetbrains.exposed.sql.transactions.transaction diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/winners_storage/WinnersStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/winners_storage/WinnersStorage.kt similarity index 82% rename from bot/src/main/kotlin/me/y9san9/prizebot/database/winners_storage/WinnersStorage.kt rename to bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/winners_storage/WinnersStorage.kt index db023b3..05b8395 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/database/winners_storage/WinnersStorage.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/giveaways_storage/winners_storage/WinnersStorage.kt @@ -1,4 +1,4 @@ -package me.y9san9.prizebot.database.winners_storage +package me.y9san9.prizebot.database.giveaways_storage.winners_storage import org.jetbrains.exposed.sql.Database diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/linked_channels_storage/LinkedChannelsStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/linked_channels_storage/LinkedChannelsStorage.kt new file mode 100644 index 0000000..8ff07c0 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/linked_channels_storage/LinkedChannelsStorage.kt @@ -0,0 +1,12 @@ +package me.y9san9.prizebot.database.linked_channels_storage + +import org.jetbrains.exposed.sql.Database + + +fun LinkedChannelsStorage(database: Database): LinkedChannelsStorage = TableLinkedChannelsStorage(database) + +interface LinkedChannelsStorage { + fun linkChannel(userId: Long, channelId: Long) + fun unlinkChannel(userId: Long, channelId: Long) + fun getChannels(userId: Long): List +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/database/linked_channels_storage/TableLinkedChannelsStorage.kt b/bot/src/main/kotlin/me/y9san9/prizebot/database/linked_channels_storage/TableLinkedChannelsStorage.kt new file mode 100644 index 0000000..f80593d --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/database/linked_channels_storage/TableLinkedChannelsStorage.kt @@ -0,0 +1,39 @@ +package me.y9san9.prizebot.database.linked_channels_storage + +import me.y9san9.prizebot.database.linked_channels_storage.TableLinkedChannelsStorage.LinkedChannels.CHANNEL_ID +import me.y9san9.prizebot.database.linked_channels_storage.TableLinkedChannelsStorage.LinkedChannels.USER_ID +import me.y9san9.prizebot.extensions.any.unit +import org.jetbrains.exposed.sql.* +import org.jetbrains.exposed.sql.transactions.transaction + + +internal class TableLinkedChannelsStorage ( + private val database: Database +) : LinkedChannelsStorage { + private object LinkedChannels : Table(name = "linked_channels") { + val CHANNEL_ID = long("channelId") + val USER_ID = long("userId") + } + + init { + transaction(database) { + SchemaUtils.create(LinkedChannels) + } + } + + override fun linkChannel(userId: Long, channelId: Long) = transaction(database) { + if(LinkedChannels.select { (CHANNEL_ID eq channelId) and (USER_ID eq userId) }.firstOrNull() == null) + LinkedChannels.insert { + it[USER_ID] = userId + it[CHANNEL_ID] = channelId + } + }.unit + + override fun unlinkChannel(userId: Long, channelId: Long) = transaction(database) { + LinkedChannels.deleteWhere { (USER_ID eq userId) and (CHANNEL_ID eq channelId) } + }.unit + + override fun getChannels(userId: Long): List = transaction(database) { + LinkedChannels.select { USER_ID eq userId }.map { it[CHANNEL_ID] } + } +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/di/PrizebotDI.kt b/bot/src/main/kotlin/me/y9san9/prizebot/di/PrizebotDI.kt index 2d4678f..87baa4f 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/di/PrizebotDI.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/di/PrizebotDI.kt @@ -3,13 +3,15 @@ package me.y9san9.prizebot.di import me.y9san9.prizebot.database.giveaways_active_messages_storage.GiveawaysActiveMessagesStorage import me.y9san9.prizebot.database.giveaways_storage.GiveawaysStorage import me.y9san9.prizebot.database.language_codes_storage.LanguageCodesStorage -import me.y9san9.prizebot.database.participants_storage.ParticipantsStorage +import me.y9san9.prizebot.database.linked_channels_storage.LinkedChannelsStorage class PrizebotDI ( giveawaysStorage: GiveawaysStorage, giveawaysActiveMessagesStorage: GiveawaysActiveMessagesStorage, - languageCodesStorage: LanguageCodesStorage + languageCodesStorage: LanguageCodesStorage, + linkedChannelsStorage: LinkedChannelsStorage ) : GiveawaysStorage by giveawaysStorage, GiveawaysActiveMessagesStorage by giveawaysActiveMessagesStorage, - LanguageCodesStorage by languageCodesStorage + LanguageCodesStorage by languageCodesStorage, + LinkedChannelsStorage by linkedChannelsStorage diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/List.kt b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/List.kt deleted file mode 100644 index ae7731e..0000000 --- a/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/List.kt +++ /dev/null @@ -1,9 +0,0 @@ -package me.y9san9.prizebot.extensions.list - - -fun MutableList.replaceFirst(predicate: (T) -> Boolean, transformer: (T) -> (T)) { - val index = indexOfFirst(predicate).takeIf { it != -1 } ?: return - this[index] = transformer(this[index]) -} - -inline fun List.plusIf(condition: Boolean, item: () -> T) = if(condition) this + item() else this diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/on.kt b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/on.kt new file mode 100644 index 0000000..22c7f86 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/on.kt @@ -0,0 +1,7 @@ +package me.y9san9.prizebot.extensions.list + + +inline fun List.on(handler: (T) -> Unit) = onEach { + if(it is T) + handler(it) +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/plusIf.kt b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/plusIf.kt new file mode 100644 index 0000000..370a0c7 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/list/plusIf.kt @@ -0,0 +1,4 @@ +package me.y9san9.prizebot.extensions.list + + +inline fun List.plusIf(condition: Boolean, item: () -> T) = if(condition) this + item() else this diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/extensions/telegram/Telegram.kt b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/telegram/Telegram.kt index c07aff7..a251b74 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/extensions/telegram/Telegram.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/extensions/telegram/Telegram.kt @@ -4,10 +4,7 @@ import me.y9san9.fsm.FSMState import me.y9san9.fsm.FSMStorage import me.y9san9.prizebot.database.language_codes_storage.LanguageCodesStorage import me.y9san9.prizebot.di.PrizebotDI -import me.y9san9.telegram.updates.CallbackQueryUpdate -import me.y9san9.telegram.updates.ChosenInlineResultUpdate -import me.y9san9.telegram.updates.InlineQueryUpdate -import me.y9san9.telegram.updates.PrivateMessageUpdate +import me.y9san9.telegram.updates.* import me.y9san9.telegram.updates.hierarchies.FromChatLocalizedDIBotUpdate import me.y9san9.telegram.updates.hierarchies.FromChatLocalizedDIUpdate @@ -16,6 +13,7 @@ typealias PrizebotPrivateMessageUpdate = PrivateMessageUpdate typealias PrizebotInlineQueryUpdate = InlineQueryUpdate typealias PrizebotChosenInlineResultUpdate = ChosenInlineResultUpdate typealias PrizebotCallbackQueryUpdate = CallbackQueryUpdate +typealias PrizebotChannelPostUpdate = ChannelPostUpdate typealias PrizebotFSMStorage = FSMStorage typealias PrizebotFSMState = FSMState typealias PrizebotLocalizedUpdate = FromChatLocalizedDIUpdate diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/ParticipateCommand.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/ParticipateCommand.kt index e9c48cf..1d0bde8 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/ParticipateCommand.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/ParticipateCommand.kt @@ -1,13 +1,16 @@ +@file:Suppress("MemberVisibilityCanBePrivate") + package me.y9san9.prizebot.handlers.callback_queries.command -import me.y9san9.prizebot.database.giveaways_storage.FinishedGiveaway -import me.y9san9.prizebot.database.participants_storage.ParticipantsStorage +import me.y9san9.prizebot.actors.giveaway.ConditionsChecker +import me.y9san9.prizebot.actors.giveaway.CheckConditionsResult import me.y9san9.prizebot.actors.telegram.extractor.GiveawayFromCommandExtractor import me.y9san9.prizebot.actors.telegram.updater.GiveawayCallbackQueryMessageUpdater +import me.y9san9.prizebot.database.giveaways_storage.ActiveGiveaway +import me.y9san9.prizebot.database.giveaways_storage.FinishedGiveaway import me.y9san9.prizebot.extensions.telegram.locale import me.y9san9.prizebot.extensions.telegram.PrizebotCallbackQueryUpdate - object ParticipateCommand { suspend fun handle(update: PrizebotCallbackQueryUpdate) { val participantId = update.chatId @@ -18,17 +21,21 @@ object ParticipateCommand { val answer = when { giveaway is FinishedGiveaway -> locale.giveawayFinished - giveaway.ownerId == participantId -> { - locale.cannotParticipateInSelfGiveaway - } - giveaway.isParticipant(participantId) -> { - locale.alreadyParticipating - } - else -> { - giveaway.saveParticipant(participantId) - locale.nowParticipating + giveaway.ownerId == participantId -> locale.cannotParticipateInSelfGiveaway + giveaway.isParticipant(participantId) -> locale.alreadyParticipating + else -> when(val result = ConditionsChecker.check(update.bot, participantId, giveaway as ActiveGiveaway)) { + is CheckConditionsResult.GiveawayInvalid -> locale.giveawayTitleInput + is CheckConditionsResult.NotSubscribedToConditions -> locale.notSubscribedToConditions + is CheckConditionsResult.FriendsAreNotInvited -> locale.friendsAreNotInvited ( + result.invitedCount, result.requiredCount + ) + is CheckConditionsResult.Success -> { + giveaway.saveParticipant(participantId) + locale.nowParticipating + } } } + update.answer(answer) GiveawayCallbackQueryMessageUpdater.update(update, giveaway) diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/RaffleCommand.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/RaffleCommand.kt index 7844e32..6f3c390 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/RaffleCommand.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/callback_queries/command/RaffleCommand.kt @@ -18,7 +18,7 @@ object RaffleCommand { if(giveaway is ActiveGiveaway) { val success = RaffleActor - .raffle(giveaway) + .raffle(update.bot, giveaway) if (success) updateMessage(update, update.di.getGiveawayById(giveaway.id)!!) diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/channel_posts/ChannelPostsHandler.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/channel_posts/ChannelPostsHandler.kt new file mode 100644 index 0000000..a5d619f --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/channel_posts/ChannelPostsHandler.kt @@ -0,0 +1,24 @@ +package me.y9san9.prizebot.handlers.channel_posts + +import dev.inmo.micro_utils.coroutines.safelyWithoutExceptions +import dev.inmo.tgbotapi.extensions.api.chat.get.getChatAdministrators +import dev.inmo.tgbotapi.types.ChatId +import me.y9san9.prizebot.extensions.telegram.PrizebotChannelPostUpdate +import me.y9san9.telegram.updates.extensions.command.command + + +object ChannelPostsHandler { + suspend fun handle(update: PrizebotChannelPostUpdate) = update.command { + case("/connect_prizebot") { + // If permission was not granted it is OK! + safelyWithoutExceptions { + update.delete() + } + + val admins = update.bot.getChatAdministrators(ChatId(update.chatId)) + for(admin in admins) { + update.di.linkChannel(admin.user.id.chatId, update.chatId) + } + } + } +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/Serializers.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/Serializers.kt index b084bd6..dd34375 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/Serializers.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/Serializers.kt @@ -3,10 +3,8 @@ package me.y9san9.prizebot.handlers.private_messages.fsm.states import kotlinx.serialization.builtins.serializer import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic -import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.GiveawayTitle -import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.RaffleDateInputData -import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.TimezoneInputData -import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.WinnersCountData +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.* +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions.SubscriptionChannelInputData val statesSerializers = SerializersModule { @@ -17,6 +15,8 @@ val statesSerializers = SerializersModule { subclass(GiveawayTitle::class, GiveawayTitle.serializer()) subclass(RaffleDateInputData::class, RaffleDateInputData.serializer()) subclass(TimezoneInputData::class, TimezoneInputData.serializer()) - subclass(WinnersCountData::class, WinnersCountData.serializer()) + subclass(WinnersCountInputData::class, WinnersCountInputData.serializer()) + subclass(ConditionInputData::class, ConditionInputData.serializer()) + subclass(SubscriptionChannelInputData::class, SubscriptionChannelInputData.serializer()) } } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/ConditionInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/ConditionInputState.kt new file mode 100644 index 0000000..e646cac --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/ConditionInputState.kt @@ -0,0 +1,76 @@ +package me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway + +import kotlinx.serialization.Serializable +import me.y9san9.fsm.FSMStateResult +import me.y9san9.fsm.stateResult +import me.y9san9.prizebot.actors.giveaway.CreateGiveawayActor +import me.y9san9.prizebot.database.giveaways_storage.WinnersCount +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.Condition +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.wrapGiveawayConditions +import me.y9san9.prizebot.extensions.offset_date_time.OffsetDateTimeSerializer +import me.y9san9.prizebot.extensions.telegram.* +import me.y9san9.prizebot.handlers.private_messages.fsm.states.MainState +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions.InvitationsCountInputState +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions.SubscriptionChannelInputState +import me.y9san9.prizebot.resources.locales.Locale +import me.y9san9.prizebot.resources.markups.conditionsMarkup +import me.y9san9.telegram.updates.extensions.send_message.sendMessage +import java.time.OffsetDateTime + + +@Serializable +data class ConditionInputData ( + val title: String, + val participateText: String, + @Serializable(with = OffsetDateTimeSerializer::class) + val raffleDate: OffsetDateTime?, + val winnersCount: WinnersCount, + val conditions: List = listOf() +) + +object ConditionInputState : PrizebotFSMState { + override suspend fun process ( + data: ConditionInputData, + event: PrizebotPrivateMessageUpdate + ): FSMStateResult<*> { + event.commandOrDefault { + case("/cancel") { + return MainState.cancellation(event) + } + case("/next") { + if(data.conditions.filterIsInstance().isEmpty() || + data.conditions.filterIsInstance().isNotEmpty()) + return CreateGiveawayActor.create ( + event, data.title, + data.participateText, data.raffleDate, + data.winnersCount, data.conditions.wrapGiveawayConditions() + ) + else event.sendMessage(event.locale.atLeastOneChannelSubscriptionRequired) + } + raw(Locale::invitations) { + if(data.conditions.count { it is Condition.Invitations } > 0) + event.sendMessage ( + text = event.locale.youHaveAlreadyAddedInvitations, + replyMarkup = conditionsMarkup(event) + ) + else + return InvitationsCountInputState(event, data) + } + raw(Locale::channelSubscription) { + return SubscriptionChannelInputState(event, data) + } + } + + return stateResult(ConditionInputState, data) + } +} + + +@Suppress("FunctionName") +suspend fun ConditionInputState ( + update: PrizebotPrivateMessageUpdate, + data: ConditionInputData, + text: String = update.locale.chooseConditions +) = stateResult(ConditionInputState, data) { + update.sendMessage(text, replyMarkup = conditionsMarkup(update)) +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/CustomTimezoneInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/CustomTimezoneInputState.kt index b9564a8..3ada213 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/CustomTimezoneInputState.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/CustomTimezoneInputState.kt @@ -30,7 +30,7 @@ object CustomTimezoneInputState : PrizebotFSMState { val date = OffsetDateTime.of(LocalDateTime.parse(data.localDate, dateTimeFormatter), parsedOffset) return WinnersCountInputState ( - event, WinnersCountData(data.title, data.participateText, date) + event, WinnersCountInputData(data.title, data.participateText, date) ) } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/RaffleDateInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/RaffleDateInputState.kt index f0d8895..3b02d59 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/RaffleDateInputState.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/RaffleDateInputState.kt @@ -45,7 +45,7 @@ object RaffleDateInputState : PrizebotFSMState { if(raffleDate == null) return WinnersCountInputState ( update, - WinnersCountData(data.title, data.participateText, raffleDate = null) + WinnersCountInputData(data.title, data.participateText, raffleDate = null) ) else if(!isDateValid(raffleDate)) return stateResult(RaffleDateInputState, data) { diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/TimezoneInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/TimezoneInputState.kt index 4bbfbca..843a8d5 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/TimezoneInputState.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/TimezoneInputState.kt @@ -73,7 +73,7 @@ object TimezoneInputState : PrizebotFSMState { val date = OffsetDateTime.of(LocalDateTime.parse(data.localDate, dateTimeFormatter), offset) return WinnersCountInputState ( - update, WinnersCountData(data.title, data.participateText, date) + update, WinnersCountInputData(data.title, data.participateText, date) ) } } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/WinnersCountInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/WinnersCountInputState.kt index 99d0ee6..3de675a 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/WinnersCountInputState.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/WinnersCountInputState.kt @@ -3,7 +3,6 @@ package me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway import kotlinx.serialization.Serializable import me.y9san9.fsm.FSMStateResult import me.y9san9.fsm.stateResult -import me.y9san9.prizebot.actors.giveaway.CreateGiveawayActor import me.y9san9.prizebot.database.giveaways_storage.WinnersCount import me.y9san9.prizebot.extensions.any.unit import me.y9san9.prizebot.extensions.offset_date_time.OffsetDateTimeSerializer @@ -17,30 +16,32 @@ import java.time.OffsetDateTime @Serializable -data class WinnersCountData ( +data class WinnersCountInputData ( val title: String, val participateText: String, @Serializable(with = OffsetDateTimeSerializer::class) val raffleDate: OffsetDateTime? ) -object WinnersCountInputState : PrizebotFSMState { +object WinnersCountInputState : PrizebotFSMState { override suspend fun process ( - data: WinnersCountData, + data: WinnersCountInputData, event: PrizebotPrivateMessageUpdate ): FSMStateResult<*> { - suspend fun createGiveaway(winnersCount: WinnersCount = WinnersCount(1)) = - CreateGiveawayActor.create ( - event, data.title, - data.participateText, data.raffleDate, - winnersCount + suspend fun next(winnersCount: WinnersCount = WinnersCount(1)) = + ConditionInputState ( + event, + ConditionInputData ( + data.title, data.participateText, + data.raffleDate, winnersCount + ) ) event.textOrDefault { text -> when(text) { "/cancel" -> return MainState.cancellation(event) - "/skip" -> return createGiveaway() + "/skip" -> return next() } val winnersCount = try { @@ -52,7 +53,7 @@ object WinnersCountInputState : PrizebotFSMState { return@textOrDefault event.sendMessage(event.locale.winnersCountIsOutOfRange).unit } - return createGiveaway(winnersCount) + return next(winnersCount) } return stateResult(WinnersCountInputState, data) @@ -62,7 +63,7 @@ object WinnersCountInputState : PrizebotFSMState { @Suppress("FunctionName") suspend fun WinnersCountInputState ( update: PrizebotPrivateMessageUpdate, - data: WinnersCountData + data: WinnersCountInputData ) = stateResult(WinnersCountInputState, data) { update.sendMessage ( update.locale.enterWinnersCount diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/conditions/InvitationsCountInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/conditions/InvitationsCountInputState.kt new file mode 100644 index 0000000..fe76e56 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/conditions/InvitationsCountInputState.kt @@ -0,0 +1,48 @@ +package me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions + +import dev.inmo.tgbotapi.types.buttons.ReplyKeyboardRemove +import me.y9san9.fsm.FSMStateResult +import me.y9san9.fsm.stateResult +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.Condition +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.wrapPositiveInt +import me.y9san9.prizebot.extensions.any.unit +import me.y9san9.prizebot.extensions.telegram.PrizebotFSMState +import me.y9san9.prizebot.extensions.telegram.PrizebotPrivateMessageUpdate +import me.y9san9.prizebot.extensions.telegram.locale +import me.y9san9.prizebot.extensions.telegram.textOrDefault +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.ConditionInputData +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.ConditionInputState +import me.y9san9.telegram.updates.extensions.send_message.sendMessage + + +object InvitationsCountInputState : PrizebotFSMState { + override suspend fun process ( + data: ConditionInputData, + event: PrizebotPrivateMessageUpdate + ): FSMStateResult<*> { + event.textOrDefault { text -> + val number = text.toIntOrNull() + ?: return@textOrDefault event.sendMessage(event.locale.enterNumber).unit + + if(number > 0) { + val conditions = data.conditions + Condition.Invitations(number.wrapPositiveInt()) + return ConditionInputState( + event, + data.copy(conditions = conditions), + event.locale.chooseMoreConditions + ) + } else + event.sendMessage(event.locale.invitationsCountShouldBePositive) + } + + return stateResult(InvitationsCountInputState, data) + } +} + + +@Suppress("FunctionName") +suspend fun InvitationsCountInputState ( + update: PrizebotPrivateMessageUpdate, data: ConditionInputData +) = stateResult(InvitationsCountInputState, data) { + update.sendMessage(update.locale.enterInvitationsCount, replyMarkup = ReplyKeyboardRemove()) +} diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/conditions/SubscriptionChannelInputState.kt b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/conditions/SubscriptionChannelInputState.kt new file mode 100644 index 0000000..b02db67 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/handlers/private_messages/fsm/states/giveaway/conditions/SubscriptionChannelInputState.kt @@ -0,0 +1,106 @@ +package me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.conditions + +import dev.inmo.tgbotapi.extensions.api.chat.get.getChat +import dev.inmo.tgbotapi.types.ChatId +import dev.inmo.tgbotapi.types.chat.abstracts.UsernameChat +import kotlinx.serialization.Serializable +import me.y9san9.fsm.FSMStateResult +import me.y9san9.fsm.stateResult +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.Condition +import me.y9san9.prizebot.extensions.any.unit +import me.y9san9.prizebot.extensions.telegram.PrizebotFSMState +import me.y9san9.prizebot.extensions.telegram.PrizebotPrivateMessageUpdate +import me.y9san9.prizebot.extensions.telegram.locale +import me.y9san9.prizebot.extensions.telegram.textOrDefault +import me.y9san9.prizebot.handlers.private_messages.fsm.states.MainState +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.ConditionInputData +import me.y9san9.prizebot.handlers.private_messages.fsm.states.giveaway.ConditionInputState +import me.y9san9.prizebot.resources.locales.Locale +import me.y9san9.prizebot.resources.markups.selectLinkedChatMarkup +import me.y9san9.telegram.updates.extensions.send_message.sendMessage + + +@Serializable +data class Channel ( + val id: Long, + val username: String +) + +@Serializable +data class SubscriptionChannelInputData ( + val conditionInputData: ConditionInputData, + val channels: List +) + +object SubscriptionChannelInputState : PrizebotFSMState { + override suspend fun process ( + data: SubscriptionChannelInputData, + event: PrizebotPrivateMessageUpdate + ): FSMStateResult<*> { + event.textOrDefault { username -> + when(username) { + "/cancel" -> return MainState.cancellation(event) + "/help" -> event.sendMessage ( + event.locale.channelLinkingHelp + ) + in Locale.all(Locale::updateChannels) -> { + val channels = getUserChannels(event) + return stateResult(SubscriptionChannelInputState, data.copy(channels = channels)) { + event.sendMessage ( + event.locale.channelsUpdated, + replyMarkup = selectLinkedChatMarkup(event, channels.map(Channel::username)) + ) + } + } + else -> { + val conditions = data.conditionInputData.conditions + + val usernames = conditions + .filterIsInstance() + .map { it.channelUsername } + + if(username in usernames) + return@textOrDefault event.sendMessage(event.locale.channelIsAlreadyInConditions).unit + + val channel = data.channels.firstOrNull { it.username == username } + ?: return@textOrDefault event.sendMessage(event.locale.channelIsNotLinked).unit + + val newConditions = conditions + Condition.Subscription(channel.id, channel.username) + val conditionData = data.conditionInputData.copy ( + conditions = newConditions + ) + + return ConditionInputState(event, conditionData, event.locale.chooseMoreConditions) + } + } + } + + return stateResult(SubscriptionChannelInputState, data) + } +} + + +@Suppress("FunctionName") +suspend fun SubscriptionChannelInputState ( + update: PrizebotPrivateMessageUpdate, + data: ConditionInputData +): FSMStateResult<*> { + val channels = getUserChannels(update) + + val usernames = channels.map(Channel::username) + + return stateResult(SubscriptionChannelInputState, SubscriptionChannelInputData(data, channels)) { + update.sendMessage(update.locale.selectLinkedChat, replyMarkup = selectLinkedChatMarkup(update, usernames)) + } +} + +private suspend fun getUserChannels(update: PrizebotPrivateMessageUpdate) = + update.di.getChannels(update.chatId) + .mapNotNull { channelId -> + val username = try { + (update.bot.getChat(ChatId(channelId)) as? UsernameChat)?.username?.username + } catch (_: Exception) { null } + ?: return@mapNotNull update.di.unlinkChannel(update.chatId, channelId).run { null } + + channelId to username + }.map { (id, username) -> Channel(id, username) } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/resources/entities/giveawayEntities.kt b/bot/src/main/kotlin/me/y9san9/prizebot/resources/entities/giveawayEntities.kt index 4c772dc..111a23d 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/resources/entities/giveawayEntities.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/resources/entities/giveawayEntities.kt @@ -6,6 +6,7 @@ import dev.inmo.tgbotapi.types.MessageEntity.textsources.* import me.y9san9.prizebot.database.giveaways_storage.ActiveGiveaway import me.y9san9.prizebot.database.giveaways_storage.FinishedGiveaway import me.y9san9.prizebot.database.giveaways_storage.Giveaway +import me.y9san9.prizebot.database.giveaways_storage.conditions_storage.Condition import me.y9san9.prizebot.database.giveaways_storage.locale import me.y9san9.telegram.updates.primitives.BotUpdate import me.y9san9.telegram.extensions.telegram_bot.getUserLink @@ -33,8 +34,22 @@ suspend fun giveawayEntities ( underline(locale.winnersCount) + ": ${giveaway.winnersCount.value}\n" else listOf() - val winner = if(giveaway is FinishedGiveaway) { + val conditions = giveaway.conditions.list + val conditionsEntities = if(conditions.isNotEmpty()) { + bold(underline(locale.giveawayConditions)) + "\n" + + conditions + .sortedBy { if(it is Condition.Invitations) 0 else 1 } + .flatMap { condition -> + when(condition) { + is Condition.Subscription -> + regular("• ") + locale.subscribeToChannel(condition.channelUsername) + is Condition.Invitations -> + regular("• ") + locale.inviteFriends(condition.count.int) + } + "\n" + } + "\n\n" + } else listOf() + val winner = if(giveaway is FinishedGiveaway) { val links = giveaway.winnerIds .map { id -> update.bot.getUserLink(id, locale.deletedUser) } .flatMap { it + ", " } @@ -47,5 +62,5 @@ suspend fun giveawayEntities ( italic(locale.giveawayParticipateHint) else regular("") - return title + winnersCount + untilTime + "\n" + winner + participateHint + return title + winnersCount + untilTime + conditionsEntities + winner + participateHint } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/Locale.kt b/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/Locale.kt index 22bc683..eb10f5e 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/Locale.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/Locale.kt @@ -143,6 +143,53 @@ open class Locale { open val winnersCount = "Winners count" + open val chooseConditions = "Now you can choose conditions for giveaway members (use /next to create giveaway without conditions or /cancel to cancel)" + + open val chooseMoreConditions = "Choose next condition (use /next to create giveaway or /cancel to cancel)" + + open val invitations = "Invite friends" + + open val channelSubscription = "Join channel" + + open val youHaveAlreadyAddedInvitations = "You have already added invitations condition!" + + open val enterInvitationsCount = "Enter invitations count for participation" + + open val selectLinkedChat = "Select linked chat with buttons below (use /help for channel linking help, or use /cancel to cancel giveaway creation)" + + open val updateChannels = "Update linked channels" + + open val channelsUpdated = "Linked channels updated!" + + open val channelLinkingHelp = underline("To link a channel you should follow these 4 steps:\n\n") + + "• Add @secure_prize_bot to channel you want link (" + bold("it must have username") + " so anyone can join with it), " + + "later it will be used to check if member joined\n" + + "• Grant 'Delete messages' permission, it will be used to immediately delete message from next step (Optional)\n" + + "• Type /connect_prizebot in your channel\n" + + "• Click the update button and then select your channel" + + open val channelIsNotLinked = "This channel is not linked!" + + open val channelIsAlreadyInConditions = "This channel is already in conditions" + + open val giveawayConditions = "Giveaway conditions:" + + open fun subscribeToChannel(username: String) = regular("Subscribe to channel ") + bold(username) + + open fun inviteFriends(count: Int) = regular("Invite ") + bold("$count") + " friend" + + (if(count > 1) "s" else "") + " in giveaway" + + open val atLeastOneChannelSubscriptionRequired = "At least one channel subscription required if you want to add " + + "friends invitations" + + open val invitationsCountShouldBePositive = "Invitation count should be positive number" + + open val giveawayInvalid = "Contact organizer because giveaway seems to be invalid" + + open val notSubscribedToConditions = "You have not subscribed to all channels" + + open fun friendsAreNotInvited(invitedCount: Int, requiredCount: Int) = "You have invited $invitedCount / $requiredCount friends" + companion object { fun with(language: String?) = locales .firstOrNull { it.code == language } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/RuLocale.kt b/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/RuLocale.kt index a9c8965..e24f756 100644 --- a/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/RuLocale.kt +++ b/bot/src/main/kotlin/me/y9san9/prizebot/resources/locales/RuLocale.kt @@ -1,5 +1,6 @@ package me.y9san9.prizebot.resources.locales +import dev.inmo.tgbotapi.CommonAbstracts.TextSource import dev.inmo.tgbotapi.CommonAbstracts.plus import dev.inmo.tgbotapi.types.MessageEntity.textsources.* import me.y9san9.prizebot.extensions.string.awesomeCut @@ -133,4 +134,53 @@ object RuLocale : Locale() { override val enterNumber = "Пожалуйста, ведите число" override val enterWinnersCount = "Введите количество победителей" + + override val chooseConditions = "Теперь выберите условия участия (используйте /next, чтобы создать розыгрыш без условий, или /cancel, чтобы отменить)" + + override val chooseMoreConditions = "Выберите следующее условие (используйте /next, чтобы создать розыгрыш, или /cancel, чтобы отменить)" + + override val invitations = "Пригласить друзей" + + override val channelSubscription = "Присоединиться к каналу" + + override val youHaveAlreadyAddedInvitations = "Вы уже добавили условие с приглашением друзей!" + + override val enterInvitationsCount = "Введите количество приглашений для участия" + + override val selectLinkedChat = "Выберите подключённый чат (используйте /help, чтобы узнать как привязать канал, или /cancel, чтобы отменить создание розыгрыша)" + + override val updateChannels = "Обновить привязанные каналы" + + override val channelsUpdated = "Обновлено!" + + override val channelLinkingHelp = underline("Чтобы привязать канал вам нужно выполнить следующие 4 шага:\n\n") + + "• Добавить @secure_prize_bot в ваш канал " + bold("с username") + ", который вы хотите привязать, (чтобы любой мог присоединиться к нему), позже это будет использовано для проверки участников\n" + + "• Разрешить боту удалять сообщения, чтобы бот моментально удалил сообщение из следующего шага (Не обязательно)\n" + + "• Напишите /connect_prizebot в вашем канале\n" + + "• Нажмите кнопку обновления и выберите привязанный канал" + + override val channelIsNotLinked = "Этот канал не привязан" + + override val channelIsAlreadyInConditions = "Этот канал уже есть в условиях участия" + + override val giveawayConditions = "Условия участия:" + + override fun subscribeToChannel(username: String) = regular("Подписаться на канал ") + bold(username) + + override fun inviteFriends(count: Int) = regular("Пригласить ") + bold("$count") + " ${getValidFriendsForm(count)} в розыгрыш" + + private fun getValidFriendsForm(count: Int) = when { + count % 10 == 1 -> "друга" + else -> "друзей" + } + + override val atLeastOneChannelSubscriptionRequired = "Добавьте хотя бы одну подписку на канал, чтобы использовать приглашения" + + override val invitationsCountShouldBePositive = "Количество приглашений должно быть больше нуля" + + override val giveawayInvalid = "Свяжитесь с организаторами, розыгрыш некорретный" + + override val notSubscribedToConditions = "Вы не подписались на все каналы" + + override fun friendsAreNotInvited(invitedCount: Int, requiredCount: Int) = "Вы пригласили $invitedCount / $requiredCount друзей" } diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/resources/markups/conditionsMarkup.kt b/bot/src/main/kotlin/me/y9san9/prizebot/resources/markups/conditionsMarkup.kt new file mode 100644 index 0000000..7a2e19e --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/resources/markups/conditionsMarkup.kt @@ -0,0 +1,19 @@ +package me.y9san9.prizebot.resources.markups + +import dev.inmo.tgbotapi.types.buttons.ReplyKeyboardMarkup +import dev.inmo.tgbotapi.types.buttons.SimpleKeyboardButton +import me.y9san9.prizebot.extensions.telegram.PrizebotLocalizedBotUpdate +import me.y9san9.prizebot.extensions.telegram.locale + + +fun conditionsMarkup(update: PrizebotLocalizedBotUpdate) = ReplyKeyboardMarkup ( + keyboard = listOf ( + listOf ( + SimpleKeyboardButton(update.locale.channelSubscription) + ), +// listOf ( +// SimpleKeyboardButton(update.locale.invitations) +// ) + ), + resizeKeyboard = true +) diff --git a/bot/src/main/kotlin/me/y9san9/prizebot/resources/markups/selectLinkedChatMarkup.kt b/bot/src/main/kotlin/me/y9san9/prizebot/resources/markups/selectLinkedChatMarkup.kt new file mode 100644 index 0000000..b9b7661 --- /dev/null +++ b/bot/src/main/kotlin/me/y9san9/prizebot/resources/markups/selectLinkedChatMarkup.kt @@ -0,0 +1,20 @@ +package me.y9san9.prizebot.resources.markups + +import dev.inmo.tgbotapi.types.buttons.ReplyKeyboardMarkup +import dev.inmo.tgbotapi.types.buttons.SimpleKeyboardButton +import me.y9san9.prizebot.extensions.telegram.PrizebotLocalizedBotUpdate +import me.y9san9.prizebot.extensions.telegram.locale + + +fun selectLinkedChatMarkup(update: PrizebotLocalizedBotUpdate, usernames: List): ReplyKeyboardMarkup { + val linkedChats = usernames + .map(::SimpleKeyboardButton) + .chunked(size = 4) + + return ReplyKeyboardMarkup ( + keyboard = listOf ( + listOf(SimpleKeyboardButton(update.locale.updateChannels)) + ) + linkedChats, + resizeKeyboard = true + ) +} diff --git a/build.gradle.kts b/build.gradle.kts index f4526a5..9ecc44b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -22,6 +22,7 @@ allprojects { tasks.withType() { kotlinOptions { + useIR = true freeCompilerArgs = listOf("-Xinline-classes") } } diff --git a/buildSrc/src/main/kotlin/Version.kt b/buildSrc/src/main/kotlin/Version.kt index 2468d68..5aac199 100644 --- a/buildSrc/src/main/kotlin/Version.kt +++ b/buildSrc/src/main/kotlin/Version.kt @@ -2,7 +2,7 @@ object Version { const val JVM = "1.8" const val KOTLIN = "1.4.30" - const val SERIALIZATION = "1.0.1" + const val SERIALIZATION = "1.1.0" const val SERIALIZATION_PLUGIN = "1.4.21" const val COROUTINES = "1.4.2" @@ -10,6 +10,6 @@ object Version { const val KDS = "1.2.11" const val EXPOSED = "0.24.1" const val POSTGRESQL = "42.2.18" - const val TG_BOT_API = "0.32.5" + const val TG_BOT_API = "0.33.4" const val KTOR = "1.5.1" } diff --git a/telegram/src/main/kotlin/me/y9san9/telegram/updates/ChannelPostUpdate.kt b/telegram/src/main/kotlin/me/y9san9/telegram/updates/ChannelPostUpdate.kt new file mode 100644 index 0000000..72b01e8 --- /dev/null +++ b/telegram/src/main/kotlin/me/y9san9/telegram/updates/ChannelPostUpdate.kt @@ -0,0 +1,27 @@ +package me.y9san9.telegram.updates + +import dev.inmo.tgbotapi.bot.TelegramBot +import dev.inmo.tgbotapi.extensions.api.delete +import dev.inmo.tgbotapi.types.CommonUser +import dev.inmo.tgbotapi.types.message.abstracts.* +import me.y9san9.telegram.extensions.asTextContentMessage +import me.y9san9.telegram.updates.hierarchies.FromChatLocalizedDIBotUpdate +import me.y9san9.telegram.updates.primitives.DeletableUpdate +import me.y9san9.telegram.updates.primitives.FromGroupUserUpdate +import me.y9san9.telegram.updates.primitives.HasTextUpdate + + +class ChannelPostUpdate ( + override val bot: TelegramBot, + override val di: DI, + private val message: ChannelContentMessage<*>, +) : FromChatLocalizedDIBotUpdate, HasTextUpdate, FromGroupUserUpdate, DeletableUpdate { + + override val chatId: Long = message.chat.id.chatId + override val languageCode: String? = ((message as? FromUserMessage)?.user as? CommonUser)?.languageCode + override val text: String? = message.asTextContentMessage()?.content?.text + override val userId: Long? = (message as? FromUserMessage)?.user?.id?.chatId + + override suspend fun delete() = message.delete(bot).let { } + +} diff --git a/telegram/src/main/kotlin/me/y9san9/telegram/updates/primitives/DeletableUpdate.kt b/telegram/src/main/kotlin/me/y9san9/telegram/updates/primitives/DeletableUpdate.kt new file mode 100644 index 0000000..e2e43d3 --- /dev/null +++ b/telegram/src/main/kotlin/me/y9san9/telegram/updates/primitives/DeletableUpdate.kt @@ -0,0 +1,6 @@ +package me.y9san9.telegram.updates.primitives + + +interface DeletableUpdate { + suspend fun delete() +} diff --git a/telegram/src/main/kotlin/me/y9san9/telegram/updates/primitives/FromGroupUserUpdate.kt b/telegram/src/main/kotlin/me/y9san9/telegram/updates/primitives/FromGroupUserUpdate.kt new file mode 100644 index 0000000..ff87a2a --- /dev/null +++ b/telegram/src/main/kotlin/me/y9san9/telegram/updates/primitives/FromGroupUserUpdate.kt @@ -0,0 +1,6 @@ +package me.y9san9.telegram.updates.primitives + + +interface FromGroupUserUpdate { + val userId: Long? +}