Skip to content

Commit 1425226

Browse files
committed
feat: add link / alt functionality
Added: - New commands - `link`, `unlink`, `viewalts` to allow accounts to be linked together for a user's alt accounts - Updated embed that is sent while using the history reaction.
1 parent a2c80dd commit 1425226

File tree

6 files changed

+210
-33
lines changed

6 files changed

+210
-33
lines changed

commands.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,18 @@
5252
| rules | (Message) | List the rules of this guild. Pass a message ID to edit existing rules embed. |
5353

5454
## User
55-
| Commands | Arguments | Description |
56-
| ------------- | ----------------------------------------- | ---------------------------------------------------------- |
57-
| ban | LowerUserArg, (Delete message days), Text | Ban a member from this guild. |
58-
| getBanReason | User | Get a ban reason for a banned user |
59-
| history, h, H | User | Use this to view a user's record. |
60-
| selfHistory | | View your infraction history (contents will be DM'd) |
61-
| setBanReason | User, Reason | Set a ban reason for a banned user |
62-
| unban | User | Unban a banned member from this guild. |
63-
| whatpfp | User | Perform a reverse image search of a User's profile picture |
55+
| Commands | Arguments | Description |
56+
| -------------- | ----------------------------------------- | ---------------------------------------------------------- |
57+
| ban | LowerUserArg, (Delete message days), Text | Ban a member from this guild. |
58+
| getBanReason | User | Get a ban reason for a banned user |
59+
| history, h, H | User | Use this to view a user's record. |
60+
| link | Main Account, Alt Account | Link a user's alt account with their main |
61+
| listAlts, alts | User | Use this to view a user's alt accounts. |
62+
| selfHistory | | View your infraction history (contents will be DM'd) |
63+
| setBanReason | User, Reason | Set a ban reason for a banned user |
64+
| unban | User | Unban a banned member from this guild. |
65+
| unlink | Main Account, Alt Account | Link a user's alt account with their main |
66+
| whatpfp | User | Perform a reverse image search of a User's profile picture |
6467

6568
## Utility
6669
| Commands | Arguments | Description |

src/main/kotlin/me/ddivad/judgebot/commands/UserCommands.kt

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
package me.ddivad.judgebot.commands
22

3-
import me.ddivad.judgebot.arguments.LowerMemberArg
43
import me.ddivad.judgebot.arguments.LowerUserArg
54
import me.ddivad.judgebot.dataclasses.*
65
import me.ddivad.judgebot.embeds.createHistoryEmbed
6+
import me.ddivad.judgebot.embeds.createCondensedHistoryEmbed
7+
import me.ddivad.judgebot.embeds.createLinkedAccountMenu
78
import me.ddivad.judgebot.embeds.createSelfHistoryEmbed
89
import me.ddivad.judgebot.services.DatabaseService
910
import me.ddivad.judgebot.services.LoggingService
1011
import me.ddivad.judgebot.services.PermissionLevel
1112
import me.ddivad.judgebot.services.infractions.BanService
1213
import me.ddivad.judgebot.services.requiredPermissionLevel
13-
import me.jakejmattson.discordkt.api.arguments.EitherArg
14-
import me.jakejmattson.discordkt.api.arguments.EveryArg
15-
import me.jakejmattson.discordkt.api.arguments.IntegerArg
16-
import me.jakejmattson.discordkt.api.arguments.UserArg
14+
import me.jakejmattson.discordkt.api.arguments.*
1715
import me.jakejmattson.discordkt.api.dsl.commands
1816
import me.jakejmattson.discordkt.api.extensions.sendPrivateMessage
1917
import java.awt.Color
@@ -29,12 +27,33 @@ fun createUserCommands(databaseService: DatabaseService,
2927
execute(UserArg) {
3028
val user = databaseService.users.getOrCreateUser(args.first, guild)
3129
databaseService.users.incrementUserHistory(user, guild)
30+
val linkedAccounts = user.getLinkedAccounts(guild)
3231
respondMenu {
3332
createHistoryEmbed(args.first, user, guild, config, databaseService)
3433
}
3534
}
3635
}
3736

37+
guildCommand("listAlts", "alts") {
38+
description = "Use this to view a user's alt accounts."
39+
requiredPermissionLevel = PermissionLevel.Moderator
40+
execute(UserArg) {
41+
val target = args.first
42+
val user = databaseService.users.getOrCreateUser(args.first, guild)
43+
databaseService.users.incrementUserHistory(user, guild)
44+
val linkedAccounts = user.getLinkedAccounts(guild)
45+
46+
if(linkedAccounts.isEmpty()) {
47+
respond("User ${target.mention} has no alt accounts recorded.")
48+
return@execute
49+
}
50+
51+
respondMenu {
52+
createLinkedAccountMenu(linkedAccounts, guild, config, databaseService)
53+
}
54+
}
55+
}
56+
3857
guildCommand("whatpfp") {
3958
description = "Perform a reverse image search of a User's profile picture"
4059
requiredPermissionLevel = PermissionLevel.Moderator
@@ -55,7 +74,7 @@ fun createUserCommands(databaseService: DatabaseService,
5574
requiredPermissionLevel = PermissionLevel.Staff
5675
execute(LowerUserArg, IntegerArg("Delete message days").makeOptional(0), EveryArg) {
5776
val (target, deleteDays, reason) = args
58-
val ban = Punishment(target.id.value, InfractionType.Ban , reason, author.id.value)
77+
val ban = Punishment(target.id.value, InfractionType.Ban, reason, author.id.value)
5978
banService.banUser(target, guild, ban, deleteDays).also {
6079
loggingService.userBanned(guild, target, ban)
6180
respond("User ${target.mention} banned")
@@ -121,4 +140,30 @@ fun createUserCommands(databaseService: DatabaseService,
121140
}
122141
}
123142
}
143+
144+
guildCommand("link") {
145+
description = "Link a user's alt account with their main"
146+
requiredPermissionLevel = PermissionLevel.Staff
147+
execute(UserArg("Main Account"), UserArg("Alt Account")) {
148+
val (main, alt) = args
149+
val mainRecord = databaseService.users.getOrCreateUser(main, guild)
150+
val altRecord = databaseService.users.getOrCreateUser(alt, guild)
151+
databaseService.users.addLinkedAccount(guild, mainRecord, alt.id.value)
152+
databaseService.users.addLinkedAccount(guild, altRecord, main.id.value)
153+
respond("Linked accounts ${main.mention} and ${alt.mention}")
154+
}
155+
}
156+
157+
guildCommand("unlink") {
158+
description = "Link a user's alt account with their main"
159+
requiredPermissionLevel = PermissionLevel.Staff
160+
execute(UserArg("Main Account"), UserArg("Alt Account")) {
161+
val (main, alt) = args
162+
val mainRecord = databaseService.users.getOrCreateUser(main, guild)
163+
val altRecord = databaseService.users.getOrCreateUser(alt, guild)
164+
databaseService.users.removeLinkedAccount(guild, mainRecord, alt.id.value)
165+
databaseService.users.removeLinkedAccount(guild, altRecord, main.id.value)
166+
respond("Unlinked accounts ${main.mention} and ${alt.mention}")
167+
}
168+
}
124169
}

src/main/kotlin/me/ddivad/judgebot/dataclasses/GuildMember.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ data class GuildMemberDetails(
1010
val infractions: MutableList<Infraction> = mutableListOf(),
1111
val info: MutableList<Info> = mutableListOf(),
1212
val leaveHistory: MutableList<GuildLeave> = mutableListOf(),
13+
val linkedAccounts: MutableList<String> = mutableListOf(),
1314
var historyCount: Int = 0,
1415
var points: Int = 0,
1516
var pointDecayTimer: Long = DateTime().millis,
@@ -57,6 +58,23 @@ data class GuildMember(
5758
this.info.removeIf { it.id == id }
5859
}
5960

61+
fun addLinkedAccount(guild: Guild, userId: String) = with(this.getGuildInfo(guild.id.value)) {
62+
this.linkedAccounts.find { it == userId }.let {
63+
if (it == null) {
64+
this.linkedAccounts.add(userId)
65+
}
66+
return@let
67+
}
68+
}
69+
70+
fun getLinkedAccounts(guild: Guild) = with(this.getGuildInfo(guild.id.value)) {
71+
this.linkedAccounts
72+
}
73+
74+
fun removeLinkedAccount(guild: Guild, userId: String) = with(this.getGuildInfo(guild.id.value)) {
75+
this.linkedAccounts.removeIf { it == userId }
76+
}
77+
6078
fun cleanseNotes(guild: Guild) = with(this.getGuildInfo(guild.id.value)) {
6179
this.notes.clear()
6280
}

src/main/kotlin/me/ddivad/judgebot/embeds/UserEmbeds.kt

Lines changed: 113 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import me.ddivad.judgebot.util.*
1111
import me.jakejmattson.discordkt.api.dsl.MenuBuilder
1212
import me.jakejmattson.discordkt.api.extensions.addField
1313
import me.jakejmattson.discordkt.api.extensions.addInlineField
14+
import me.jakejmattson.discordkt.api.extensions.toSnowflake
1415
import org.joda.time.DateTime
1516
import java.awt.Color
1617
import java.text.SimpleDateFormat
@@ -55,25 +56,30 @@ private suspend fun MenuBuilder.buildOverviewPage(
5556

5657
addInlineField("Record",
5758
"""
58-
${userRecord.infractions.size} Infaction(s)
59-
${userRecord.notes.size} Note(s)
60-
${userRecord.info.size} Information(s)
59+
**${userRecord.infractions.size}** Infraction(s)
60+
**${userRecord.notes.size}** Note(s)
61+
**${userRecord.info.size}** Information(s)
62+
**${userRecord.deletedMessageCount.deleteReaction}** Deletes (${config.reactions.deleteMessageReaction})
6163
""".trimIndent())
62-
6364
addInlineField("Points", "**${userRecord.points} / ${config.infractionConfiguration.pointCeiling}**")
6465
addInlineField("History Invokes", "${userRecord.historyCount}")
65-
addInlineField("Creation date", formatOffsetTime(target.id.timeStamp))
66+
67+
addInlineField("Created", formatOffsetTime(target.id.timeStamp))
6668
if (memberInGuild != null) {
67-
addInlineField("Join date", formatOffsetTime(memberInGuild.joinedAt))
69+
addInlineField("Joined", formatOffsetTime(memberInGuild.joinedAt))
6870
} else addInlineField("", "")
6971

72+
if(userRecord.linkedAccounts.isNotEmpty()) {
73+
addInlineField("Alts", userRecord.linkedAccounts.map { guild.kord.getUser(Snowflake(it))?.mention }.joinToString("\n"))
74+
}
75+
7076
getStatus(guild, target, databaseService)?.let { addField("Status", it) }
7177

7278
if (userRecord.infractions.size > 0) {
7379
val lastInfraction = userRecord.infractions[userRecord.infractions.size - 1]
7480
addField(
7581
"**__Most Recent Infraction__**",
76-
"Type: **${lastInfraction.type}** :: Weight: **${lastInfraction.points}**\n " +
82+
"Type: **${lastInfraction.type} (${lastInfraction.points})**\n " +
7783
"Issued by **${guild.kord.getUser(Snowflake(lastInfraction.moderator))?.username}** " +
7884
"on **${SimpleDateFormat("dd/MM/yyyy").format(Date(lastInfraction.dateTime))}**\n" +
7985
"Punishment: **${lastInfraction.punishment?.punishment}** ${getDurationText(lastInfraction.punishment)}\n" +
@@ -301,15 +307,29 @@ private suspend fun getStatus(guild: Guild, target: User, databaseService: Datab
301307
return null
302308
}
303309

304-
suspend fun EmbedBuilder.createSelfHistoryEmbed(target: User,
305-
member: GuildMember,
310+
suspend fun MenuBuilder.createLinkedAccountMenu(linkedAccountIds: List<String>,
306311
guild: Guild,
307-
config: Configuration) {
312+
config: Configuration,
313+
databaseService: DatabaseService) {
314+
linkedAccountIds.forEach {
315+
val linkedUser = guild.kord.getUser(it.toSnowflake()) ?: return@forEach
316+
val linkedUserRecord = databaseService.users.getOrCreateUser(linkedUser, guild)
317+
page {
318+
createCondensedHistoryEmbed(linkedUser, linkedUserRecord, guild, config)
319+
}
320+
}
321+
}
322+
323+
suspend fun EmbedBuilder.createCondensedHistoryEmbed(target: User,
324+
member: GuildMember,
325+
guild: Guild,
326+
config: Configuration) {
308327

309328
val userGuildDetails = member.getGuildInfo(guild.id.value)
310329
val infractions = userGuildDetails.infractions
311330
val warnings = userGuildDetails.infractions.filter { it.type == InfractionType.Warn }
312331
val strikes = userGuildDetails.infractions.filter { it.type == InfractionType.Strike }
332+
val notes = userGuildDetails.notes
313333
val maxPoints = config[guild.id.longValue]?.infractionConfiguration?.pointCeiling
314334

315335
color = Color.MAGENTA
@@ -318,37 +338,114 @@ suspend fun EmbedBuilder.createSelfHistoryEmbed(target: User,
318338
url = target.asUser().avatar.url
319339
}
320340
addInlineField("Infractions", "${infractions.size}")
341+
addInlineField("Notes", "${notes.size}")
321342
addInlineField("Points", "**${member.getPoints(guild)} / $maxPoints**")
322343

344+
if(notes.isEmpty()) {
345+
addField("", "**__Notes__**")
346+
addField("No notes recorded.", "")
347+
} else {
348+
addField("", "**__Notes__**")
349+
notes.forEach { note ->
350+
val moderator = guild.kord.getUser(Snowflake(note.moderator))?.username
351+
352+
addField(
353+
"ID :: ${note.id} :: Staff :: __${moderator}__",
354+
"Noted by **${moderator}** on **${SimpleDateFormat("dd/MM/yyyy").format(Date(note.dateTime))}**\n" +
355+
note.note
356+
)
357+
}
358+
}
359+
323360
if (infractions.isEmpty()) {
324-
addField("", "")
361+
addField("", "**__Infractions__**")
325362
addField("No infractions issued.", "")
326363
} else {
327364
if (warnings.isNotEmpty()) addField("", "**__Warnings__**")
328365
warnings.forEachIndexed { index, infraction ->
329-
addField(
330-
"ID :: $index :: Weight :: ${infraction.points}",
366+
val moderator = guild.kord.getUser(Snowflake(infraction.moderator))?.username
367+
addField(
368+
"ID :: $index :: Staff :: $moderator",
331369
"Type: **${infraction.type} (${infraction.points})** :: " +
332370
"Date: **${SimpleDateFormat("dd/MM/yyyy").format(Date(infraction.dateTime))}**\n " +
333371
"Punishment: **${infraction.punishment?.punishment}** ${
334372
if (infraction.punishment?.duration != null && infraction.punishment?.punishment !== PunishmentType.NONE)
335373
"for **" + timeToString(infraction.punishment?.duration!!) + "**" else "indefinitely"
336374
}\n" +
337375
infraction.reason
338-
)
376+
)
339377
}
340378

341379
if (strikes.isNotEmpty()) addField("", "**__Strikes__**")
342380
strikes.forEachIndexed { index, infraction ->
343-
addField(
344-
"ID :: $index :: Weight :: ${infraction.points}",
381+
val moderator = guild.kord.getUser(Snowflake(infraction.moderator))?.username
382+
addField(
383+
"ID :: $index :: Staff :: $moderator",
345384
"Type: **${infraction.type} (${infraction.points})** :: " +
346385
"Date: **${SimpleDateFormat("dd/MM/yyyy").format(Date(infraction.dateTime))}**\n " +
347386
"Punishment: **${infraction.punishment?.punishment}** ${
348387
if (infraction.punishment?.duration != null && infraction.punishment?.punishment !== PunishmentType.NONE)
349388
"for **" + timeToString(infraction.punishment?.duration!!) + "**" else "indefinitely"
350389
}\n" +
351390
infraction.reason
391+
)
392+
}
393+
}
394+
395+
footer {
396+
icon = guild.getIconUrl(Image.Format.PNG) ?: ""
397+
text = guild.name
398+
}
399+
}
400+
401+
suspend fun EmbedBuilder.createSelfHistoryEmbed(target: User,
402+
member: GuildMember,
403+
guild: Guild,
404+
config: Configuration) {
405+
406+
val userGuildDetails = member.getGuildInfo(guild.id.value)
407+
val infractions = userGuildDetails.infractions
408+
val warnings = userGuildDetails.infractions.filter { it.type == InfractionType.Warn }
409+
val strikes = userGuildDetails.infractions.filter { it.type == InfractionType.Strike }
410+
val maxPoints = config[guild.id.longValue]?.infractionConfiguration?.pointCeiling
411+
412+
color = Color.MAGENTA
413+
title = "${target.asUser().tag}'s Record"
414+
thumbnail {
415+
url = target.asUser().avatar.url
416+
}
417+
addInlineField("Infractions", "${infractions.size}")
418+
addInlineField("Points", "**${member.getPoints(guild)} / $maxPoints**")
419+
420+
if (infractions.isEmpty()) {
421+
addField("", "")
422+
addField("No infractions issued.", "")
423+
} else {
424+
if (warnings.isNotEmpty()) addField("", "**__Warnings__**")
425+
warnings.forEachIndexed { index, infraction ->
426+
addField(
427+
"ID :: $index :: Weight :: ${infraction.points}",
428+
"Type: **${infraction.type} (${infraction.points})** :: " +
429+
"Date: **${SimpleDateFormat("dd/MM/yyyy").format(Date(infraction.dateTime))}**\n " +
430+
"Punishment: **${infraction.punishment?.punishment}** ${
431+
if (infraction.punishment?.duration != null && infraction.punishment?.punishment !== PunishmentType.NONE)
432+
"for **" + timeToString(infraction.punishment?.duration!!) + "**" else "indefinitely"
433+
}\n" +
434+
infraction.reason
435+
)
436+
}
437+
438+
if (strikes.isNotEmpty()) addField("", "**__Strikes__**")
439+
strikes.forEachIndexed { index, infraction ->
440+
addField(
441+
"ID :: $index :: Weight :: ${infraction.points}",
442+
"Type: **${infraction.type} (${infraction.points})** :: " +
443+
"Date: **${SimpleDateFormat("dd/MM/yyyy").format(Date(infraction.dateTime))}**\n " +
444+
"Punishment: **${infraction.punishment?.punishment}** ${
445+
if (infraction.punishment?.duration != null && infraction.punishment?.punishment !== PunishmentType.NONE)
446+
"for **" + timeToString(infraction.punishment?.duration!!) + "**" else "indefinitely"
447+
}\n" +
448+
infraction.reason
352449
)
353450
}
354451
}

0 commit comments

Comments
 (0)