2019-06-08 21:14:57 +02:00
package moe.kageru.kagebot
2019-07-13 15:39:50 +02:00
import moe.kageru.kagebot.config.Config
import moe.kageru.kagebot.config.Config.server
2019-06-12 23:43:36 +02:00
import org.javacord.api.entity.channel.TextChannel
2019-06-10 09:19:03 +02:00
import org.javacord.api.entity.message.MessageAuthor
2019-06-15 12:27:20 +02:00
import org.javacord.api.entity.message.embed.EmbedBuilder
2019-06-12 23:43:36 +02:00
import org.javacord.api.entity.permission.Role
2019-07-11 22:16:02 +02:00
import org.javacord.api.entity.user.User
import org.javacord.api.event.message.MessageCreateEvent
2019-06-15 12:27:20 +02:00
import java.awt.Color
2019-07-13 15:39:50 +02:00
import java.util.*
2019-06-13 00:19:02 +02:00
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionException
2019-06-08 23:50:05 +02:00
2019-06-08 21:14:57 +02:00
object Util {
2019-07-17 22:47:47 +02:00
inline fun < T > T . applyIf ( condition : Boolean , op : ( T ) -> T ) : T {
return if ( condition ) op ( this ) else this
2019-06-08 21:14:57 +02:00
}
2019-06-08 23:50:05 +02:00
2019-06-12 23:43:36 +02:00
/ * *
* Mimics the behavior of [ Optional . ifPresent ] , but returns null if the optional is empty ,
* allowing easier fallback behavior via Kotlin ’ s ?: operator .
* /
2019-07-23 21:50:55 +02:00
internal inline fun < T , R > Optional < T > . ifNotEmpty ( op : ( T ) -> R ) : R ? {
2019-06-09 18:41:51 +02:00
if ( this . isPresent ) {
return op ( this . get ( ) )
}
return null
}
2019-06-10 09:19:03 +02:00
2019-06-12 23:43:36 +02:00
fun hasOneOf ( messageAuthor : MessageAuthor , roles : Set < Role > ) : Boolean {
2019-06-10 09:19:03 +02:00
return messageAuthor . asUser ( ) . ifNotEmpty { user ->
2019-06-14 23:16:31 +02:00
user . getRoles ( server ) . toSet ( ) . intersect ( roles ) . isNotEmpty ( )
2019-06-10 09:19:03 +02:00
} ?: false
}
2019-06-12 23:43:36 +02:00
private val channelIdRegex = Regex ( " \\ d{18} " )
2019-06-15 11:47:11 +02:00
private fun String . isEntityId ( ) = channelIdRegex . matches ( this )
2019-06-12 23:43:36 +02:00
@Throws ( IllegalArgumentException :: class )
fun findRole ( idOrName : String ) : Role {
return when {
idOrName . isEntityId ( ) -> server . getRoleById ( idOrName ) . ifNotEmpty { it }
?: throw IllegalArgumentException ( " Role $idOrName not found. " )
else -> server . getRolesByNameIgnoreCase ( idOrName ) . let {
when ( it . size ) {
0 -> throw IllegalArgumentException ( " Role $idOrName not found. " )
1 -> it [ 0 ]
else -> throw IllegalArgumentException ( " More than one role found with name $idOrName . Please specify the role ID instead " )
}
}
}
}
2019-07-23 21:50:55 +02:00
private fun < T > Optional < T > . toNullable ( ) : T ? {
return ifNotEmpty { it }
}
fun findUser ( idOrName : String ) : User ? {
return when {
idOrName . isEntityId ( ) -> server . getMemberById ( idOrName ) . toNullable ( )
else -> {
when {
idOrName . contains ( '#' ) -> server . getMemberByDiscriminatedNameIgnoreCase ( idOrName ) . toNullable ( )
else -> server . getMembersByName ( idOrName ) . firstOrNull ( )
}
}
}
}
2019-07-17 23:17:16 +02:00
fun < T > CompletableFuture < T > . failed ( ) : Boolean {
2019-06-13 00:19:02 +02:00
try {
2019-07-17 23:17:16 +02:00
join ( )
2019-06-13 00:19:02 +02:00
} catch ( e : CompletionException ) {
// we don’t care about this error, but I don’t want to spam stdout
}
2019-07-17 23:17:16 +02:00
return isCompletedExceptionally
2019-06-13 00:19:02 +02:00
}
2019-06-12 23:43:36 +02:00
@Throws ( IllegalArgumentException :: class )
fun findChannel ( idOrName : String ) : TextChannel {
return when {
idOrName . isEntityId ( ) -> server . getTextChannelById ( idOrName ) . ifNotEmpty { it }
?: throw IllegalArgumentException ( " Channel ID $idOrName not found. " )
else -> if ( idOrName . startsWith ( '@' ) ) {
2019-07-13 15:39:50 +02:00
Globals . api . getCachedUserByDiscriminatedName ( idOrName . removePrefix ( " @ " ) ) . ifNotEmpty { user ->
2019-07-11 23:30:39 +02:00
val channelFuture = user . openPrivateChannel ( )
val channel = channelFuture . join ( )
if ( channelFuture . isCompletedExceptionally ) {
throw IllegalArgumentException ( " Could not open private channel with user $idOrName for redirection. " )
}
channel
2019-06-12 23:43:36 +02:00
}
?: throw IllegalArgumentException ( " Can’t find user $idOrName for redirection. " )
} else {
server . getTextChannelsByName ( idOrName ) . let {
when ( it . size ) {
0 -> throw IllegalArgumentException ( " Channel $idOrName not found. " )
1 -> it [ 0 ]
else -> throw IllegalArgumentException ( " More than one channel found with name $idOrName . Please specify the channel ID instead " )
}
}
}
}
}
2019-06-15 12:27:20 +02:00
inline fun checked ( op : ( ( ) -> Unit ) ) {
try {
op ( )
} catch ( e : Exception ) {
2019-07-17 21:16:17 +02:00
Log . warn ( " An uncaught exception occurred. \n $e " )
2019-07-23 21:50:55 +02:00
Log . warn ( e . stackTrace . joinToString ( " \n " ) )
2019-07-17 21:16:17 +02:00
MessageUtil . sendEmbed (
Globals . api . owner . get ( ) ,
2019-06-15 12:27:20 +02:00
EmbedBuilder ( )
. setColor ( Color . RED )
. addField ( " Error " , " kagebot has encountered an error " )
. addField (
" $e " , """ ```
$ { e . stackTrace . joinToString ( " \n " ) }
2019-07-23 21:50:55 +02:00
` ` ` """ .trimIndent().run { applyIf(length > 1800) { substring(1..1800) } }
2019-06-15 12:27:20 +02:00
)
)
}
}
2019-07-11 22:16:02 +02:00
fun userFromMessage ( message : MessageCreateEvent ) : User ? {
return message . messageAuthor . id . let { id ->
2019-07-13 15:39:50 +02:00
Config . server . getMemberById ( id ) . orElse ( null )
2019-07-11 22:16:02 +02:00
}
}
2019-07-17 22:40:44 +02:00
/ * *
* Convert a list of elements to pairs , retaining order .
* The last element is dropped if the input size is odd .
* [ 1 , 2 , 3 , 4 , 5 ] -> [ [ 1 , 2 ] , [ 3 , 4 ] ]
* /
fun < T > Collection < T > . toPairs ( ) : List < Pair < T , T > > = this . iterator ( ) . run {
( 0 until size / 2 ) . map {
Pair ( next ( ) , next ( ) )
}
}
2019-07-13 14:52:05 +02:00
}