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

com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database.PostsLikesTable.kt Maven / Gradle / Ivy

Go to download

It is base library for creating smart bot for simple management of channels posts

There is a newer version: 1.7.0
Show newest version
package com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database

import com.github.insanusmokrassar.AutoPostTelegramBot.base.database.tables.PostsTable
import com.github.insanusmokrassar.AutoPostTelegramBot.base.models.PostId
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.abstractions.RatingPair
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.commonLogger
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.NewDefaultCoroutineScope
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.extensions.subscribe
import com.github.insanusmokrassar.TelegramBotAPI.types.ChatId
import kotlinx.coroutines.channels.BroadcastChannel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import org.h2.jdbc.JdbcSQLException
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.SqlExpressionBuilder.eq
import org.jetbrains.exposed.sql.transactions.transaction

typealias PostIdUserId = Pair

private const val resultColumnName = "result"

private val PostsLikesTableScope = NewDefaultCoroutineScope()

class PostsLikesTable : Table() {
    val likesChannel = BroadcastChannel(Channel.CONFLATED)
    val dislikesChannel = BroadcastChannel(Channel.CONFLATED)
    val ratingsChannel = BroadcastChannel(Channel.CONFLATED)

    private val userId = long("userId").primaryKey()
    private val postId = integer("postId").primaryKey()
    private val like = bool("like").default(false)

    internal lateinit var postsLikesMessagesTable: PostsLikesMessagesTable

    init {
        PostsTable.postRemovedChannel.subscribe(
            {
                commonLogger.throwing(
                    "PostsLikesTable",
                    "Clear likes",
                    it
                )
                true
            }
        ) {
            clearPostMarks(it)
        }
    }

    fun userLikePost(userId: ChatId, postId: Int) {
        userLike(userId, postId, true)

        PostsLikesTableScope.launch {
            likesChannel.send(
                PostIdUserId(
                    postId,
                    userId
                )
            )
        }
    }

    fun userDislikePost(userId: ChatId, postId: Int) {
        userLike(userId, postId, false)

        PostsLikesTableScope.launch {
            dislikesChannel.send(
                PostIdUserId(
                    postId,
                    userId
                )
            )
        }
    }

    fun postLikes(postId: PostId): Int = postLikeCount(postId, true)

    fun postDislikes(postId: PostId): Int = postLikeCount(postId, false)

    fun getPostRating(postId: PostId): Int {
        return transaction {
            try {
                exec("SELECT (likes-dislikes) as $resultColumnName FROM " +
                    "(SELECT count(*) as likes FROM ${nameInDatabaseCase()} WHERE ${[email protected]}=$postId AND \"${like.name}\"=${like.columnType.valueToString(true)}), " +
                    "(SELECT count(*) as dislikes FROM ${nameInDatabaseCase()} WHERE ${[email protected]}=$postId AND \"${like.name}\"=${like.columnType.valueToString(false)});") {
                    if (it.first()) {
                        it.getInt(it.findColumn(resultColumnName))
                    } else {
                        0
                    }
                } ?: 0
            } catch (e: JdbcSQLException) {
                select {
                    createChooser(postId, like = true)
                }.count() - select {
                    createChooser(postId, like = false)
                }.count()
            }
        }
    }

    fun getMostRated(): List {
        return transaction {
            postsLikesMessagesTable.getEnabledPostsIdAndRatings().let {
                var maxRating = Float.MIN_VALUE
                ArrayList().apply {
                    it.forEach {
                        val currentRating = it.second
                        if (currentRating > maxRating) {
                            maxRating = currentRating
                            clear()
                        }
                        if (currentRating == maxRating) {
                            add(it.first.toInt())
                        }
                    }
                }
            }
        }
    }

    /**
     * @param min Included. If null - always true
     * @param max Included. If null - always true
     *
     * @return Pairs with postId to Rate
     */
    fun getRateRange(min: Int?, max: Int?): List {
        return postsLikesMessagesTable.getEnabledPostsIdAndRatings().sortedByDescending { (_, rating) ->
            rating
        }.filter { (_, rating) ->
            min ?.let { it <= rating } != false && max ?.let { rating <= it } != false
        }
    }

    private fun postLikeCount(postId: Int, like: Boolean): Int = transaction {
        select {
            [email protected](postId).and([email protected](like))
        }.count()
    }

    private fun createChooser(postId: Int, userId: ChatId? = null, like: Boolean? = null): Op {
        return [email protected](postId).let {
            userId ?.chatId ?.let {
                userId ->
                it.and([email protected](userId))
            } ?: it
        }.let {
            like ?. let {
                like ->
                it.and([email protected](like))
            } ?: it
        }
    }

    private fun userLike(userId: ChatId, postId: Int, like: Boolean) {
        val chooser = createChooser(postId, userId)
        transaction {
            select {
                chooser
            }.firstOrNull() ?.also {
                if (it[[email protected]] == like) {
                    deleteWhere { chooser }
                } else {
                    update(
                        {
                            chooser
                        }
                    ) { updateStatement ->
                        updateStatement[[email protected]] = like
                    }
                }
            } ?: addUser(userId, postId, like)
        }
        PostsLikesTableScope.launch {
            ratingsChannel.send(
                RatingPair(postId.toLong(), getPostRating(postId).toFloat())
            )
        }
    }

    private fun addUser(userId: ChatId, postId: Int, like: Boolean) {
        transaction {
            insert {
                it[[email protected]] = postId
                it[[email protected]] = userId.chatId
                it[[email protected]] = like
            }
        }
    }

    internal fun clearPostMarks(postId: Int) {
        transaction {
            deleteWhere { [email protected](postId) }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy