Add logger for the server, it can be used to log edits, deletions and nickname alters

Add admin role thing, this can be used to set admin roles
Add @random to @ a random user
This commit is contained in:
Julius de Jeu 2018-12-01 00:46:46 +01:00
parent 6b93a5dcc1
commit 948b32f387
10 changed files with 292 additions and 33 deletions

View file

@ -1,10 +1,13 @@
package nl.voidcorp.dbot package nl.voidcorp.dbot
import net.dv8tion.jda.core.OnlineStatus
import net.dv8tion.jda.core.Permission
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceLeaveEvent import net.dv8tion.jda.core.events.guild.voice.GuildVoiceLeaveEvent
import net.dv8tion.jda.core.events.message.MessageReceivedEvent import net.dv8tion.jda.core.events.message.MessageReceivedEvent
import net.dv8tion.jda.core.events.message.react.MessageReactionAddEvent import net.dv8tion.jda.core.events.message.react.MessageReactionAddEvent
import net.dv8tion.jda.core.events.message.react.MessageReactionRemoveEvent import net.dv8tion.jda.core.events.message.react.MessageReactionRemoveEvent
import net.dv8tion.jda.core.hooks.ListenerAdapter import net.dv8tion.jda.core.hooks.ListenerAdapter
import nl.voidcorp.dbot.commands.GSM
import nl.voidcorp.dbot.music.guildMusicMap import nl.voidcorp.dbot.music.guildMusicMap
object Events : ListenerAdapter() { object Events : ListenerAdapter() {
@ -32,6 +35,16 @@ object Events : ListenerAdapter() {
} }
} }
}*/ }*/
if (event.message.contentRaw.contains("@random") and
(event.author != event.jda.selfUser) and
(GSM.getSettings(event.guild).adminRoles.any { it in event.member.roles.map { role -> role.idLong } }
or event.member.hasPermission(Permission.ADMINISTRATOR))
) {
val mem = event.textChannel.members.filter { !it.user.isBot }
.filter { it.onlineStatus in mutableListOf(OnlineStatus.ONLINE, OnlineStatus.IDLE) }
.filter { it.user.idLong != event.author.idLong }.random()
event.channel.sendMessage("Hey, ${mem.asMention} look here ^").queue()
}
if (event.author.idLong == 168743656738521088 && event.message.mentionedMembers.contains( if (event.author.idLong == 168743656738521088 && event.message.mentionedMembers.contains(
event.guild.getMember( event.guild.getMember(
event.jda.selfUser event.jda.selfUser

View file

@ -0,0 +1,120 @@
package nl.voidcorp.dbot
import net.dv8tion.jda.core.EmbedBuilder
import net.dv8tion.jda.core.entities.*
import net.dv8tion.jda.core.events.guild.member.GuildMemberNickChangeEvent
import net.dv8tion.jda.core.events.message.guild.GuildMessageDeleteEvent
import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent
import net.dv8tion.jda.core.events.message.guild.GuildMessageUpdateEvent
import net.dv8tion.jda.core.hooks.ListenerAdapter
import nl.voidcorp.dbot.commands.GSM
import java.awt.Color
import java.time.LocalDateTime
import java.time.temporal.TemporalAccessor
import java.util.*
object Logging : ListenerAdapter() {
val map = mutableMapOf<Long, Queue<Message>>()
enum class Colors(val colorString: String) {
NAMECHANGE("#00cc66"),
MESSAGEDELETE("#ff3300"),
MESSAGEEDIT("#ffcc66"),
UNKNOWN("#000000")
}
override fun onGuildMessageReceived(event: GuildMessageReceivedEvent) {
if (map[event.guild.idLong] == null) map[event.guild.idLong] = ArrayDeque()
map[event.guild.idLong]?.apply {
while (size > 100) {
poll()
}
offer(event.message)
}
}
fun logChannel(guild: Guild): TextChannel? {
val log = GSM.getSettings(guild).logChannel
return if (log != null) {
guild.getTextChannelById(log)
} else {
null
}
}
fun buildEmbed(
affectedMember: Member?,
message: String = "",
color: Colors = Colors.UNKNOWN,
title: String = color.name.toLowerCase().capitalize(),
fields: Map<String, String> = mutableMapOf(),
timestamp: TemporalAccessor = LocalDateTime.now()
): MessageEmbed {
val em = EmbedBuilder()
.setTimestamp(timestamp)
.setColor(Color.decode(color.colorString))
if (title.isNotBlank()) em.setTitle(title)
if (message.isNotBlank()) em.setDescription(message)
fields.forEach { name, content -> em.addField(name, content, true) }
if (affectedMember != null) em.setFooter(affectedMember.user.name, affectedMember.user.effectiveAvatarUrl)
return em.build()
}
override fun onGuildMemberNickChange(event: GuildMemberNickChangeEvent) {
val lc = logChannel(event.guild)
if ((event.user.idLong == 144116077129891840) or (event.user.idLong == 131399667442384896)) return
if (lc != null) {
val em = buildEmbed(
event.member,
fields = mutableMapOf(
"Old name" to (event.prevNick ?: event.user.name),
"New Name" to (event.newNick ?: event.user.name)
),
color = Colors.NAMECHANGE
)
lc.sendMessage(em).queue()
}
}
override fun onGuildMessageDelete(event: GuildMessageDeleteEvent) {
val lc = logChannel(event.guild)
if (lc != null) {
map[event.guild.idLong]?.firstOrNull { it.idLong == event.messageIdLong }.let {
val em = buildEmbed(
it?.member,
color = Colors.MESSAGEDELETE,
fields = mutableMapOf(
"Message" to (it?.contentDisplay ?: "Something else (message too old...)"),
"Channel" to event.channel.asMention
),
timestamp = (it?.editedTime ?: it?.creationTime) ?: LocalDateTime.now()
)
lc.sendMessage(em).queue()
}
}
}
override fun onGuildMessageUpdate(event: GuildMessageUpdateEvent) {
val lc = logChannel(event.guild)
if (lc != null) {
(map[event.guild.idLong]?.firstOrNull { it.idLong == event.messageIdLong }).let {
val em = buildEmbed(
event.member, color = Colors.MESSAGEEDIT,
fields = mutableMapOf(
"Old message" to (it?.contentStripped ?: "Something else (message too old...)"),
"New message" to event.message.contentStripped,
"Channel" to event.channel.asMention
)
)
map[event.guild.idLong]?.removeAll { old -> old.idLong == event.messageIdLong }
map[event.guild.idLong]?.offer(event.message)
lc.sendMessage(em).queue()
}
}
}
}

View file

@ -39,7 +39,7 @@ fun main(args: Array<String>) {
custom.version = "1.5" custom.version = "1.5"
bot = JDABuilder(args[0]).addEventListener(custom).addEventListener(nl.voidcorp.dbot.Events) bot = JDABuilder(args[0]).addEventListener(custom).addEventListener(Events).addEventListener(Logging)
.setAudioSendFactory(NativeAudioSendFactory()).build() .setAudioSendFactory(NativeAudioSendFactory()).build()
/*Runtime.getRuntime().addShutdownHook(thread(start = false) { /*Runtime.getRuntime().addShutdownHook(thread(start = false) {

View file

@ -12,6 +12,8 @@ fun initAdmin() {
commands += ListPrefixes commands += ListPrefixes
commands += RemovePrefix commands += RemovePrefix
commands += AddRoleCommand commands += AddRoleCommand
commands += addAdminRole
commands += logSetCommand
/*commands += MuteCommand /*commands += MuteCommand
commands += LuaExec*/ commands += LuaExec*/
} }
@ -100,6 +102,24 @@ object AddRoleCommand : UnityCommand(
} }
}) })
val addAdminRole = UnityCommand("addadminrole", "Add a role that is considered and admin role", AdminCategory) { ce ->
val g = GSM.getSettings(ce.guild)
g.adminRoles.addAll(ce.message.mentionedRoles.map { it.idLong })
ce.guild.getRolesByName("admin", true).firstOrNull()?.idLong?.let { g.adminRoles.add(it) }
ce.reply("The admin roles are ${g.adminRoles.joinToString { "`${ce.guild.getRoleById(it).name}`" }}")
}
val logSetCommand = UnityCommand("loghere", "Set the logging channel", AdminCategory) { ce ->
val g = GSM.getSettings(ce.guild)
if (ce.textChannel != null) {
ce.reply("Set logging channel to ${ce.textChannel!!.asMention}!")
g.logChannel = ce.textChannel!!.idLong
}
}
object LuaExec : UnityCommand("luaexec", "Executes a bit of lua code", AdminCategory, exec = { ce -> object LuaExec : UnityCommand("luaexec", "Executes a bit of lua code", AdminCategory, exec = { ce ->
ce.message.delete().queue() ce.message.delete().queue()

View file

@ -79,20 +79,32 @@ object GeneralCategory : UnityCategory("general")
}*/ }*/
object MusicCategory : UnityCategory("Music Commands", listOf("bot", "music", "music-bot"), errorMessage = { "Music commands can only be used in ${it.asMention}!" }) object MusicCategory : UnityCategory(
object MusicCategoryPrivate : UnityCategory("Music Commands", listOf("bot", "music", "music-bot"), listOf("admin"), { "Music commands can only be used in ${it.asMention}!" }) "Music Commands",
listOf("bot", "music", "music-bot"),
errorMessage = { "Music commands can only be used in ${it.asMention}!" })
object AdminCategory : UnityCategory("Admin Stuff") { object MusicCategoryPrivate : UnityCategory(
"Music Commands",
listOf("bot", "music", "music-bot"), errorMessage =
{ "Music commands can only be used in ${it.asMention}!" }) {
override fun test(ce: UnityCommandEvent): Boolean { override fun test(ce: UnityCommandEvent): Boolean {
val res = when {
return super.test(ce) and AdminCategory.test(ce)
}
}
object AdminCategory : UnityCategory("Admin Stuff", errorMessage = { "You need to be and admin to use this!" }) {
override fun test(ce: UnityCommandEvent): Boolean {
return when {
ce.member.hasPermission(Permission.ADMINISTRATOR) -> true ce.member.hasPermission(Permission.ADMINISTRATOR) -> true
ce.member.roles.firstOrNull { it.name.equals("admin", true) } != null -> true ce.member.roles.firstOrNull { it.name.equals("admin", true) } != null -> true
ce.member.roles.map { it.idLong }.any {
GSM.getSettings(ce.guild).adminRoles.contains(it)
} -> true
else -> false else -> false
} }
if (!res) {
ce.reply("You are not an admin!")
}
return res
} }
} }

View file

@ -14,13 +14,19 @@ import java.time.temporal.ChronoUnit
import kotlin.random.Random import kotlin.random.Random
val helloCommand = UnityCommand("hello", "Say hello to Andy!") { val helloCommand = UnityCommand("hello", "Say hello to Andy!") { uce ->
val i = Random.nextInt(10) val nick: String? = uce.selfMember.nickname
if (i > 8) { uce.guild.controller.setNickname(uce.selfMember, "Andy Zaidman").queue {
it.reply("Can you speak up or I'll throw you a microphone") val i = Random.nextInt(10)
} else { if (i > 8) {
it.reply(MessageBuilder("Hello, ").append(it.author).append("!").build()) uce.reply("Can you speak up or I'll throw you a microphone")
} else {
uce.reply(MessageBuilder("Hello, ").append(uce.author).append("!").build())
}
uce.guild.controller.setNickname(uce.selfMember, nick).queue()
} }
} }

View file

@ -269,6 +269,47 @@ fun initMusic() {
event.reply("Stopped all playback!") event.reply("Stopped all playback!")
} }
val pauseCommand = UnityMusicCommand(
"pause",
"Pauses the song, or resumes it!",
MusicCategory,
aliases = mutableListOf("resume")
) { event, scheduler ->
if (scheduler.isPaused()) {
scheduler.resume()
event.reply("Resuming playback!")
} else {
scheduler.pause()
event.reply("Paused playback!")
}
}
val seekCommand = UnityMusicCommand(
"seek",
"Seeks to a specified point in the song!",
aliases = mutableListOf(),
arguments = "dd:hh:mm:ss"
) { event, scheduler ->
var a = event.args
if ((a.toLongOrNull() == null) and (a.indexOf(":") == -1) and !a.split(':').map { it.toLongOrNull() }.any { it == null }) {
event.reply("Please supply your time in the format `dd:hh:mm:ss` (values that are `00:` can be omitted)")
return@UnityMusicCommand
} else {
while (a.count { it == ':' } < 3) {
a = "00:$a"
}
val millis = scheduler.timeToMillis(a)
scheduler.seek(millis)
event.reply(scheduler.getCurrentTrackInfo())
}
}
commands.addAll( commands.addAll(
playCommand, playCommand,
skipCommand, skipCommand,
@ -279,7 +320,9 @@ fun initMusic() {
attachmentPlay, attachmentPlay,
voteSkipCommand, voteSkipCommand,
loopCommand, loopCommand,
stopCommand stopCommand,
pauseCommand,
seekCommand
) )
} }
@ -312,7 +355,7 @@ fun getLinkFromSearch(
} }
override fun noMatches() { override fun noMatches() {
event.reply("Sorry, I ")
} }
override fun playlistLoaded(playlist: AudioPlaylist) { override fun playlistLoaded(playlist: AudioPlaylist) {

View file

@ -5,6 +5,7 @@ import net.dv8tion.jda.core.EmbedBuilder
import nl.voidcorp.dbot.commands import nl.voidcorp.dbot.commands
import nl.voidcorp.dbot.commands.MusicCategory import nl.voidcorp.dbot.commands.MusicCategory
import nl.voidcorp.dbot.commands.UnityCommand import nl.voidcorp.dbot.commands.UnityCommand
import java.net.URLEncoder
import java.time.LocalDateTime import java.time.LocalDateTime
val lyricsCommand = UnityCommand("lyrics", "Search for lyrics!", MusicCategory) { ce -> val lyricsCommand = UnityCommand("lyrics", "Search for lyrics!", MusicCategory) { ce ->
@ -53,7 +54,7 @@ data class Song(val artist: String, val title: String)
fun findInfo(search: String): Song? { fun findInfo(search: String): Song? {
val res = khttp.get( val res = khttp.get(
"https://api.genius.com/search?q=${search.replace(" ", "%20")}", "https://api.genius.com/search?q=${URLEncoder.encode(search, "UTF8")}",
headers = mapOf("Authorization" to "Bearer eqn-1xrvrAKtoIFC-pIgNiW7cRzSvaF49wjFzasEu7coSLpufVVnv_IGVnxUIT43") headers = mapOf("Authorization" to "Bearer eqn-1xrvrAKtoIFC-pIgNiW7cRzSvaF49wjFzasEu7coSLpufVVnv_IGVnxUIT43")
) )
val hits = res.jsonObject.getJSONObject("response").getJSONArray("hits")!! val hits = res.jsonObject.getJSONObject("response").getJSONArray("hits")!!
@ -69,9 +70,9 @@ fun findInfo(search: String): Song? {
fun findText(song: Song): String { fun findText(song: Song): String {
val res = val res =
get( get(
"https://orion.apiseeds.com/api/music/lyric/${song.artist}/${song.title.replace( "https://orion.apiseeds.com/api/music/lyric/${URLEncoder.encode(song.artist, "UTF8")}/${URLEncoder.encode(
" ", song.title,
"%20" "UTF8"
)}?apikey=8pLAxkCnWJNGWRaoPcbCFpUAKdKD77zmlcjs2FKYjdH00MDyNr6lXLHb3PQZsKJI" )}?apikey=8pLAxkCnWJNGWRaoPcbCFpUAKdKD77zmlcjs2FKYjdH00MDyNr6lXLHb3PQZsKJI"
) )
return if (res.jsonObject.isNull("error")) { return if (res.jsonObject.isNull("error")) {

View file

@ -51,7 +51,7 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel
pressedSkip = 0 pressedSkip = 0
if (q.isNotEmpty()) { if (q.isNotEmpty()) {
val t = q.poll()!! val t = q.poll()!!
if (loop) q.addLast(t.makeClone().apply { userData = t.userData }) if (loop) q.addLast(track.makeClone().apply { userData = t.userData })
player.playTrack(t) player.playTrack(t)
} else { } else {
if (loop) { if (loop) {
@ -180,7 +180,7 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel
} }
eb.setDescription("${millisToTime(track.position)}/${millisToTime(track.duration)}") eb.setDescription(genTrackTime(track))
return eb.build() return eb.build()
} }
@ -195,20 +195,43 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel
val minutes = TimeUnit.MILLISECONDS.toMinutes(ms) val minutes = TimeUnit.MILLISECONDS.toMinutes(ms)
ms -= TimeUnit.MINUTES.toMillis(minutes) ms -= TimeUnit.MINUTES.toMillis(minutes)
val seconds = TimeUnit.MILLISECONDS.toSeconds(ms) val seconds = TimeUnit.MILLISECONDS.toSeconds(ms)
val current = String.format( return String.format(
"%02d:%02d:%02d:%02d", "%02d:%02d:%02d:%02d",
days, hours, minutes, seconds days, hours, minutes, seconds
).removePrefix("00:").removePrefix("00:") )
return current }
fun timeToMillis(time: String): Long {
val times = time.split(':').map { it.toLong() }
return TimeUnit.DAYS.toMillis(times[0]) + TimeUnit.HOURS.toMillis(times[1]) +
TimeUnit.MINUTES.toMillis(times[2]) + TimeUnit.SECONDS.toMillis(times[3])
}
fun genTrackTime(audioTrack: AudioTrack): String {
val total = millisToTime(audioTrack.duration)
val current = millisToTime(audioTrack.position)
return if (current == "00:00:00:00") {
""
} else {
val t = total.substring(total.lastIndexOf("00:") + 3)
val c = current.substring(current.length - t.length)
"$c/$t"
}
} }
fun getTrackList(member: Member): MessageEmbed { fun getTrackList(member: Member): MessageEmbed {
return EmbedBuilder().setTitle("Hey ${member.effectiveName}, here is the playlist${if (loop) ", it loops!" else ""}") val eb =
.setTimestamp(LocalDateTime.now()).setColor(musicChannel.guild.selfMember.color) EmbedBuilder().setTitle("Hey ${member.effectiveName}, here is the playlist${if (loop) ", it loops!" else ""}")
.setFooter("Requested by ${member.effectiveName}", member.user.effectiveAvatarUrl).setDescription( .setTimestamp(LocalDateTime.now()).setColor(musicChannel.guild.selfMember.color)
"**${player.playingTrack.info.title}**, requested by ${if (player.playingTrack.userData is Member) (player.playingTrack.userData as Member).effectiveName else "someone unknown..."} (*now playing*)\n" + .setFooter("Requested by ${member.effectiveName}", member.user.effectiveAvatarUrl)
q.joinToString(separator = "\n") { "**${it.info.title}**, requested by ${if (it.userData is Member) (it.userData as Member).effectiveName else "someone unknown..."}" } .setDescription(
).build() "**${player.playingTrack.info.title}**, requested by ${if (player.playingTrack.userData is Member) (player.playingTrack.userData as Member).effectiveName else "someone unknown..."} (*now playing*)\n" +
q.joinToString(separator = "\n") { "**${it.info.title}**, requested by ${if (it.userData is Member) (it.userData as Member).effectiveName else "someone unknown..."}" }
)
return eb.build()
} }
var pressedSkip = 0 var pressedSkip = 0
@ -225,5 +248,24 @@ class TrackScheduler(val player: AudioPlayer, val guild: Guild, val voiceChannel
player.stopTrack() player.stopTrack()
} }
fun pause() {
player.isPaused = true
}
fun resume() {
player.isPaused = false
}
fun isPaused() = player.isPaused
fun seek(ms: Long): Boolean {
return if (!player.playingTrack.isSeekable) false
else {
player.playingTrack.position = ms
true
}
}
} }

View file

@ -8,7 +8,9 @@ import java.time.LocalDateTime
data class GuildSettings( data class GuildSettings(
val prefixes: MutableList<String> = mutableListOf(), val prefixes: MutableList<String> = mutableListOf(),
val muted: MutableMap<LocalDateTime, MutableList<MuteInfo>> = mutableMapOf(), val muted: MutableMap<LocalDateTime, MutableList<MuteInfo>> = mutableMapOf(),
val roleMap: MutableMap<String, Long> = mutableMapOf() val roleMap: MutableMap<String, Long> = mutableMapOf(),
val adminRoles: MutableSet<Long> = mutableSetOf(),
var logChannel: Long? = null
) { ) {
fun getPrefixes(): MutableCollection<String> { fun getPrefixes(): MutableCollection<String> {
return prefixes return prefixes