Skip to content

Commit 63b715a

Browse files
committed
Clean usernames when dehoisting
1 parent c42c369 commit 63b715a

File tree

1 file changed

+85
-30
lines changed

1 file changed

+85
-30
lines changed

bot/src/main/kotlin/me/duncte123/skybot/commands/mod/DeHoistUtils.kt

Lines changed: 85 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,107 @@ import me.duncte123.botcommons.messaging.MessageUtils.sendSuccess
2323
import me.duncte123.skybot.Variables
2424
import me.duncte123.skybot.commands.guild.mod.ModBaseCommand
2525
import me.duncte123.skybot.objects.command.CommandContext
26+
import me.duncte123.skybot.utils.FinderUtils
2627
import me.duncte123.skybot.utils.GuildSettingsUtils
2728
import net.dv8tion.jda.api.Permission
2829
import net.dv8tion.jda.api.entities.Member
2930
import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent
3031
import net.dv8tion.jda.api.events.guild.member.update.GuildMemberUpdateNicknameEvent
3132
import net.dv8tion.jda.api.hooks.ListenerAdapter
33+
import java.lang.Character.UnicodeBlock
34+
import java.text.Normalizer
35+
import kotlin.random.Random
36+
37+
// private val regex = "[!\"#\$%&'()*+,-./](?:.*)".toRegex()
38+
// private const val dehoistChar = "▪"
39+
40+
// Allow special kinds of unicode.
41+
private val allowedUnicodeBlocks = setOf(
42+
UnicodeBlock.ARABIC,
43+
UnicodeBlock.GREEK,
44+
UnicodeBlock.THAI,
45+
)
46+
47+
private fun cleanUsername(username: String): String {
48+
return Normalizer.normalize(username, Normalizer.Form.NFKC)
49+
// TODO: is turning this into a char array more efficient?
50+
.toCharArray()
51+
.filter { it.isWhitespace() || it.isLetterOrDigit() || UnicodeBlock.of(it) in allowedUnicodeBlocks }
52+
// .map { if (it.isTitleCase()) it.lowercase() else it }
53+
.joinToString("") {
54+
if (it.isTitleCase()) it.lowercase() else it.toString()
55+
}
56+
.trim()
57+
}
58+
59+
private val Member.cleanedDisplayName: String
60+
get() {
61+
val cleanedName = cleanUsername(effectiveName)
62+
63+
if (cleanedName.isBlank()) {
64+
return "Member_${Random.nextInt(guild.memberCount)}"
65+
}
66+
67+
if (cleanedName == effectiveName) {
68+
return effectiveName
69+
}
70+
71+
return cleanedName
72+
}
73+
74+
private fun shouldDehoist(member: Member): Boolean {
75+
return member.cleanedDisplayName != member.effectiveName &&
76+
member.guild.selfMember.hasPermission(Permission.NICKNAME_MANAGE)
77+
}
78+
79+
private fun canAutoDehoist(member: Member, variables: Variables): Boolean {
80+
return shouldDehoist(member) && GuildSettingsUtils.getGuild(member.guild.idLong, variables).isAutoDeHoist
81+
}
3282

3383
class DeHoistCommand : ModBaseCommand() {
3484
init {
3585
this.requiresArgs = true
3686
this.name = "dehoist"
37-
this.help = "De-hoists a user"
38-
this.usage = "<@user>"
87+
this.help = "De-hoists a user and cleans their username"
88+
this.usage = "<@user>/all"
3989
this.userPermissions = arrayOf(Permission.NICKNAME_MANAGE)
4090
this.botPermissions = arrayOf(Permission.NICKNAME_MANAGE)
4191
}
4292

4393
override fun execute(ctx: CommandContext) {
44-
val mentionedMembers = ctx.message.mentions.members
94+
val args = ctx.args
4595

46-
if (mentionedMembers.size == 0) {
96+
if (args.isEmpty()) {
4797
this.sendUsageInstructions(ctx)
4898
return
4999
}
50100

51-
val toDehoist = mentionedMembers[0]
52101
val selfMember = ctx.guild.selfMember
102+
val firstArg = args.first()
103+
104+
if (firstArg == "all") {
105+
sendMsg(ctx, "Cleaning all members with a hoisted or unicode username, please wait...")
106+
107+
ctx.guild.loadMembers {
108+
if (selfMember.canInteract(it) && shouldDehoist(it)) {
109+
it.modifyNickname(it.cleanedDisplayName)
110+
.reason("de-hoist/nickname cleaning by ${ctx.author.asTag}")
111+
.queue()
112+
}
113+
}.onSuccess {
114+
sendMsg(ctx, "Cleaning complete!")
115+
}
116+
return
117+
}
118+
119+
val foundMembers = FinderUtils.searchMembers(firstArg, ctx)
120+
121+
if (foundMembers.isEmpty()) {
122+
this.sendUsageInstructions(ctx)
123+
return
124+
}
125+
126+
val toDehoist = foundMembers[0]
53127
val member = ctx.member
54128

55129
if (!selfMember.canInteract(toDehoist)) {
@@ -61,43 +135,24 @@ class DeHoistCommand : ModBaseCommand() {
61135
return
62136
}
63137

64-
ctx.guild.modifyNickname(toDehoist, "\u25AA" + toDehoist.effectiveName)
65-
.reason("de-hoist ctx ${ctx.author.asTag}").queue()
138+
ctx.guild.modifyNickname(toDehoist, toDehoist.cleanedDisplayName)
139+
.reason("de-hoist/nickname cleaning ${ctx.author.asTag}").queue()
66140
sendSuccess(ctx.message)
67141
}
68142
}
69143

70144
class DeHoistListener(private val variables: Variables) : ListenerAdapter() {
71-
private val regex = "[!\"#\$%&'()*+,-./](?:.*)".toRegex()
72-
private val dehoistChar = ""
73-
74145
override fun onGuildMemberJoin(event: GuildMemberJoinEvent) {
75-
if (shouldChangeName(event.member)) {
76-
// the char \uD82F\uDCA2 or \u1BCA2 is a null char that puts a member to the bottom
77-
event.guild.modifyNickname(event.member, dehoistChar + event.member.effectiveName)
146+
if (canAutoDehoist(event.member, variables)) {
147+
event.guild.modifyNickname(event.member, event.member.cleanedDisplayName)
78148
.reason("auto de-hoist").queue()
79149
}
80150
}
81151

82152
override fun onGuildMemberUpdateNickname(event: GuildMemberUpdateNicknameEvent) {
83-
if (shouldChangeName(event.member)) {
84-
// the char \uD82F\uDCA2 or \u1BCA2 is a null char that puts a member to the bottom
85-
event.guild.modifyNickname(event.member, dehoistChar + event.member.effectiveName)
153+
if (canAutoDehoist(event.member, variables)) {
154+
event.guild.modifyNickname(event.member, event.member.cleanedDisplayName)
86155
.reason("auto de-hoist").queue()
87156
}
88157
}
89-
90-
/*
91-
* This checks if we should change the nickname of a member to de-hoist it
92-
* @return [Boolean] true if we should change the nickname
93-
*/
94-
private fun shouldChangeName(member: Member): Boolean {
95-
val memberName = member.effectiveName
96-
val matcher = regex.matches(memberName)
97-
return (
98-
!memberName.startsWith(dehoistChar) && matcher &&
99-
member.guild.selfMember.hasPermission(Permission.NICKNAME_MANAGE) &&
100-
GuildSettingsUtils.getGuild(member.guild.idLong, this.variables).isAutoDeHoist
101-
)
102-
}
103158
}

0 commit comments

Comments
 (0)