All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.stormbit.vksdk.vkapi.MessageRoutes.kt Maven / Gradle / Ivy

The newest version!
package com.github.stormbit.vksdk.vkapi

import com.github.stormbit.vksdk.events.AttachmentEvent
import com.github.stormbit.vksdk.events.MessageEvent
import com.github.stormbit.vksdk.events.ServiceActionEvent
import com.github.stormbit.vksdk.events.chat.*
import com.github.stormbit.vksdk.events.chat.ChatScreenshotEvent
import com.github.stormbit.vksdk.events.message.*
import com.github.stormbit.vksdk.objects.Message
import com.github.stormbit.vksdk.objects.attachments.AttachmentType
import com.github.stormbit.vksdk.objects.models.ServiceAction

internal data class RoutePath(val restOfMessage: CharSequence)

@ContextDsl
open class MessageRoute(
    open val context: ApiContext,
    open val parent: MessageRoute<*>?,
    open val senderType: Message.SenderType?,
    open val attachmentType: AttachmentType?,
    open val serviceActionType: ServiceAction.Type?,
    open val withForwards: Boolean,
    open val withReply: Boolean,
    open val phrases: List) {

    private var handler: (suspend ApiContext.(E) -> Unit)? = null
    private var interceptor: (suspend ApiContext.(MessageEvent) -> Unit)? = null

    @PublishedApi
    internal val children: MutableSet> = mutableSetOf()

    @Suppress("unchecked_cast")
    internal suspend fun pass(event: MessageEvent, routePath: RoutePath) {
        handler?.let { context.it(event as E) }

        val iterator = children.iterator()

        while (iterator.hasNext()) {
            val messageRoute = iterator.next()
            val overlapPhrase = messageRoute.phrases.find {
                routePath.restOfMessage.startsWith(it, true)
            } ?: String()

            if (messageRoute.phrases.isNotEmpty() && overlapPhrase.isBlank()) continue
            if (messageRoute.senderType != null && messageRoute.senderType != event.message.senderType) continue
            if (messageRoute.attachmentType != null && messageRoute.attachmentType !in event.message.attachmentTypes) continue
            if (messageRoute.serviceActionType != null && messageRoute.serviceActionType != event.message.serviceActionType) continue
            if (messageRoute.withForwards && !event.message.hasForwards) continue
            if (messageRoute.withReply && !event.message.hasReply) continue

            var restOfMessage = routePath.restOfMessage.substring(overlapPhrase.length)
            if (overlapPhrase.isNotBlank()) {
                restOfMessage = restOfMessage.trim()
            }

            val newRoute = RoutePath(routePath.restOfMessage)

            when {
                messageRoute.senderType != null -> {
                    when (event.message.senderType) {
                        Message.SenderType.USER -> messageRoute.pass(event as UserMessageEvent, newRoute)
                        Message.SenderType.COMMUNITY -> messageRoute.pass(event as CommunityMessageEvent, newRoute)
                        Message.SenderType.CHAT -> messageRoute.pass(event as ChatMessageEvent, newRoute)
                    }
                }

                messageRoute.attachmentType != null -> {
                    when {
                        event.message.isAudioMessage -> messageRoute.pass(event as Audio, newRoute)
                        event.message.isVideoMessage -> messageRoute.pass(event as Video, newRoute)
                        event.message.isVoiceMessage -> messageRoute.pass(event as Voice, newRoute)
                        event.message.isPhotoMessage -> messageRoute.pass(event as Photo, newRoute)
                        event.message.isDocMessage -> messageRoute.pass(event as Document, newRoute)
                        event.message.isStickerMessage -> messageRoute.pass(event as Sticker, newRoute)
                    }
                }

                messageRoute.serviceActionType != null -> {
                    when (event.message.serviceActionType) {
                        ServiceAction.Type.CHAT_CREATE -> messageRoute.pass(event as ChatCreate, newRoute)
                        ServiceAction.Type.CHAT_TITLE_UPDATE -> messageRoute.pass(event as ChatTitleUpdate, newRoute)
                        ServiceAction.Type.CHAT_INVITE_USER -> messageRoute.pass(event as ChatJoin, newRoute)
                        ServiceAction.Type.CHAT_KICK_USER -> messageRoute.pass(event as ChatLeave, newRoute)
                        ServiceAction.Type.CHAT_PHOTO_UPDATE -> messageRoute.pass(event as ChatPhotoUpdate, newRoute)
                        ServiceAction.Type.CHAT_PHOTO_REMOVE -> messageRoute.pass(event as ChatPhotoRemove, newRoute)
                        ServiceAction.Type.CHAT_PIN_MESSAGE -> messageRoute.pass(event as ChatPinMessage, newRoute)
                        ServiceAction.Type.CHAT_UNPIN_MESSAGE -> messageRoute.pass(event as ChatUnpinMessage, newRoute)
                        ServiceAction.Type.CHAT_INVITE_USER_BY_LINK -> messageRoute.pass(event as ChatInviteUserByLink, newRoute)
                        ServiceAction.Type.CHAT_SCREENSHOT -> messageRoute.pass(event as ChatScreenshot, newRoute)
                        ServiceAction.Type.CHAT_GROUP_CALL_IN_PROGRESS -> messageRoute.pass(event as ChatGroupCallInProgress, routePath)
                        ServiceAction.Type.CHAT_INVITE_USER_BY_CALL -> messageRoute.pass(event as ChatInviteUserByCall, routePath)
                    }
                }

                messageRoute.phrases.isNotEmpty() -> {
                    if (overlapPhrase in messageRoute.phrases) {
                        messageRoute.pass(CommandMessageEvent(overlapPhrase, restOfMessage.split(" "), event.message), newRoute)
                    }
                }

                else -> messageRoute.pass(event, newRoute)
            }

            return
        }

        interceptor?.let { context.it(event as E) }
    }

    fun handle(block: suspend ApiContext.(E) -> Unit) {
        if (handler != null)
            throw Exception("You can set only one handler")
        else handler = block
    }

    fun intercept(block: suspend ApiContext.(MessageEvent) -> Unit) {
        if (interceptor != null)
            throw Exception("You can set only one interceptor")
        else interceptor = block
    }
}

@ContextDsl
@Suppress("unused")
open class TypedMessageRoute(
    context: ApiContext,
    parent: MessageRoute<*>?,
    senderType: Message.SenderType?,
    attachmentType: AttachmentType?,
    phrases: List
) : MessageRoute(context, parent, senderType, attachmentType, null, false, false, phrases) {

    suspend fun onCommand(vararg words: String, block: suspend MessageRoute.() -> Unit) {
        onCommand(words.toList(), block)
    }

    suspend fun onCommand(words: List, block: suspend MessageRoute.() -> Unit) {
        MessageRoute(
            context, this, null, null, null, withForwards = false, withReply = false, words
        ).also { children.add(it) }.block()
    }

    suspend inline fun  onMessageWith(noinline block: suspend TypedMessageRoute.() -> Unit) {
        val attachmentType = when (T::class) {
            Audio::class -> AttachmentType.AUDIO
            Video::class -> AttachmentType.VIDEO
            Voice::class -> AttachmentType.VOICE
            Photo::class -> AttachmentType.PHOTO
            Document::class -> AttachmentType.DOC
            Sticker::class -> AttachmentType.STICKER
            else -> throw IllegalArgumentException("Unknown attachment type class")
        }

        TypedMessageRoute(context, this, senderType, attachmentType, emptyList())
            .also { children.add(it) }.block()
    }

    suspend fun onMessageWithForwards(block: suspend MessageRoute.() -> Unit) {
        MessageRoute(context, this, null, null, null,
            withForwards = true,
            withReply = false,
            emptyList()
        ).also { children.add(it) }.block()
    }

    suspend fun onMessageWithReply(block: suspend MessageRoute.() -> Unit) {
        MessageRoute(context, this, null, null, null,
            withForwards = false,
            withReply = true,
            emptyList()
        ).also { children.add(it) }.block()
    }
}

@ContextDsl
@Suppress("unused")
class DefaultMessageRoute(
    context: ApiContext,
    parent: MessageRoute<*>?,
    phrases: List
) : MessageRoute(context, parent, null, null, null, false, false, phrases) {

    @Suppress("unused")
    suspend inline fun > onMessageFrom(noinline block: suspend T.() -> Unit) {
        when (T::class) {
            User::class -> (UserMessageRoute(context, parent, phrases).also { children.add(it) } as T).block()
            Chat::class -> (ChatMessageRoute(context, parent, phrases).also { children.add(it) } as T).block()
            Community::class -> (ChatMessageRoute(context, parent, phrases).also { children.add(it) } as T).block()
            else -> throw IllegalArgumentException("Unknown sender type class")
        }
    }

    suspend fun onCommand(vararg words: String, block: suspend MessageRoute.() -> Unit) {
        onCommand(words.toList(), block)
    }

    suspend fun onCommand(words: List, block: suspend MessageRoute.() -> Unit) {
        MessageRoute(
            context, this, null, null, null, withForwards = false, withReply = false, words
        ).also { children.add(it) }.block()
    }

    suspend inline fun  onMessageWith(noinline block: suspend TypedMessageRoute.() -> Unit) {
        val attachmentType = when (T::class) {
            Audio::class -> AttachmentType.AUDIO
            Video::class -> AttachmentType.VIDEO
            Voice::class -> AttachmentType.VOICE
            Photo::class -> AttachmentType.PHOTO
            Document::class -> AttachmentType.DOC
            Sticker::class -> AttachmentType.STICKER
            else -> throw IllegalArgumentException("Unknown attachment type class")
        }

        TypedMessageRoute(context, this, null, attachmentType, emptyList())
            .also { children.add(it) }.block()
    }

    suspend fun onMessageWithForwards(block: suspend MessageRoute.() -> Unit) {
        MessageRoute(context, this, null, null, null,
            withForwards = true,
            withReply = false,
            emptyList()
        ).also { children.add(it) }.block()
    }

    suspend fun onMessageWithReply(block: suspend MessageRoute.() -> Unit) {
        MessageRoute(context, this, null, null, null,
            withForwards = false,
            withReply = true,
            emptyList()
        ).also { children.add(it) }.block()
    }
}

@ContextDsl
class ServiceActionMessageRoute(
    context: ApiContext,
    parent: MessageRoute<*>?,
    serviceActionType: ServiceAction.Type
) : MessageRoute(context, parent, null, null, serviceActionType, false, false, emptyList())


@ContextDsl
class ChatMessageRoute(
    context: ApiContext,
    parent: MessageRoute<*>?,
    phrases: List,
    override val senderType: Message.SenderType = Message.SenderType.CHAT
) : TypedMessageRoute(context, parent, senderType, null, phrases) {

    @Suppress("unchecked_cast")
    suspend inline fun  onServiceAction(noinline block: suspend ServiceActionMessageRoute.() -> Unit) {
        val serviceActionType = when (T::class) {
            ChatCreate::class -> ServiceAction.Type.CHAT_CREATE
            ChatTitleUpdate::class -> ServiceAction.Type.CHAT_TITLE_UPDATE
            ChatJoin::class -> ServiceAction.Type.CHAT_INVITE_USER
            ChatLeave::class -> ServiceAction.Type.CHAT_KICK_USER
            ChatPhotoUpdate::class -> ServiceAction.Type.CHAT_PHOTO_UPDATE
            ChatPhotoRemove::class -> ServiceAction.Type.CHAT_PHOTO_REMOVE
            ChatPinMessage::class -> ServiceAction.Type.CHAT_PIN_MESSAGE
            ChatUnpinMessage::class -> ServiceAction.Type.CHAT_UNPIN_MESSAGE
            else -> throw IllegalArgumentException("Unknown service action type class")
        }

        ServiceActionMessageRoute(context, this, serviceActionType)
            .also { children.add(it) }.block()
    }
}

@ContextDsl
class UserMessageRoute(
    context: ApiContext,
    parent: MessageRoute<*>?,
    phrases: List,
    override val senderType: Message.SenderType = Message.SenderType.USER
) : TypedMessageRoute(context, parent, null, null, phrases)

@ContextDsl
class CommunityMessageRoute(
    context: ApiContext,
    parent: MessageRoute<*>?,
    phrases: List,
    override val senderType: Message.SenderType = Message.SenderType.COMMUNITY
) : TypedMessageRoute(context, parent, null, null, phrases)


typealias User = UserMessageRoute
typealias Chat = ChatMessageRoute
typealias Community = CommunityMessageRoute

typealias Audio = AudioMessageEvent
typealias Video = VideoMessageEvent
typealias Voice = VoiceMessageEvent
typealias Photo = PhotoMessageEvent
typealias Document = DocumentMessageEvent
typealias Sticker = StickerMessageEvent

typealias ChatCreate = ChatCreateEvent
typealias ChatTitleUpdate = ChatTitleUpdateEvent
typealias ChatJoin = ChatJoinEvent
typealias ChatLeave = ChatLeaveEvent
typealias ChatPhotoUpdate = ChatPhotoUpdateEvent
typealias ChatPhotoRemove = ChatPhotoRemoveEvent
typealias ChatPinMessage = ChatPinMessageEvent
typealias ChatUnpinMessage = ChatUnpinMessageEvent
typealias ChatInviteUserByLink = ChatInviteUserByLinkEvent
typealias ChatScreenshot = ChatScreenshotEvent
typealias ChatInviteUserByCall = ChatInviteUserByCallEvent
typealias ChatGroupCallInProgress = ChatGroupCallInProgressEvent




© 2015 - 2024 Weber Informatics LLC | Privacy Policy