
com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database.PostsLikesTable.kt Maven / Gradle / Ivy
package com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database
import com.github.insanusmokrassar.AutoPostTelegramBot.base.database.tables.PostsTable
import com.github.insanusmokrassar.AutoPostTelegramBot.base.plugins.commonLogger
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.NewDefaultCoroutineScope
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.UnlimitedBroadcastChannel
import com.github.insanusmokrassar.AutoPostTelegramBot.utils.extensions.subscribe
import com.github.insanusmokrassar.TelegramBotAPI.types.ChatId
import kotlinx.coroutines.channels.BroadcastChannel
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 PostIdRatingPair = Pair
typealias PostIdUserId = Pair
private const val resultColumnName = "result"
private val PostsLikesTableScope = NewDefaultCoroutineScope()
class PostsLikesTable : Table() {
val likesChannel: BroadcastChannel = UnlimitedBroadcastChannel()
val dislikesChannel: BroadcastChannel = UnlimitedBroadcastChannel()
val ratingsChannel: BroadcastChannel = UnlimitedBroadcastChannel()
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: Int): Int = postLikeCount(postId, true)
fun postDislikes(postId: Int): Int = postLikeCount(postId, false)
fun getPostRating(postId: Int): 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 = Int.MIN_VALUE
ArrayList().apply {
it.forEach {
val currentRating = it.second
if (currentRating > maxRating) {
maxRating = currentRating
clear()
}
if (currentRating == maxRating) {
add(it.first)
}
}
}
}
}
}
/**
* @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(
PostIdRatingPair(postId, getPostRating(postId))
)
}
}
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 - 2025 Weber Informatics LLC | Privacy Policy