com.github.insanusmokrassar.AutoPostTelegramBot.plugins.rating.database.PostsLikesTable.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of AutoPostTelegramBot Show documentation
Show all versions of AutoPostTelegramBot Show documentation
It is base library for creating smart bot for simple management of channels posts
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) }
}
}
}