Skip to content

Commit

Permalink
Improve avatar lookup for IRC->Discord messages via webhook.
Browse files Browse the repository at this point in the history
We will now lookup avatars for IRC message senders by first looking for
a matching Discord username, if we don't find one with a matching
username, we will then check the user's nickname.

I expect this is usually preferred, but if not we can investigate an
option or alternate behavior.

We will also now fire off a task to query Discord for matching users
rather than only relying on the bot's local user cache. This means that
if someone speaks in IRC who has not yet spoken in Discord, we should
automatically pull down their avatar by the second or third message
(depending on network conditions, API, guild size, other factors).

This should mean that users no longer need to speak in Discord after
the bot is restarted; we should figure out their name within a few
messages. It is also much cheaper from a memory standpoint than always
aggressively caching the entire guild (especially on very large guilds).
  • Loading branch information
zachbr committed Jun 24, 2024
1 parent cf712bf commit b9fa7d5
Showing 1 changed file with 21 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import net.dv8tion.jda.api.JDABuilder
import net.dv8tion.jda.api.entities.Activity
import net.dv8tion.jda.api.entities.Activity.ActivityType
import net.dv8tion.jda.api.entities.Guild
import net.dv8tion.jda.api.entities.Member
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel
import net.dv8tion.jda.api.requests.GatewayIntent
import net.dv8tion.jda.api.utils.MemberCachePolicy
Expand Down Expand Up @@ -157,12 +158,8 @@ class DiscordPier(private val bridge: Bridge) : Pier {
}

private fun sendMessageWebhook(guild: Guild, webhook: WebhookClient, msg: Message) {
// try and get avatar for matching user
var avatarUrl: String? = null
val matchingUsers = guild.getMembersByEffectiveName(msg.sender.displayName, true)
if (matchingUsers.isNotEmpty()) {
avatarUrl = matchingUsers.first().user.avatarUrl
}
val guildUser = getMemberByUserNameOrDisplayName(msg.sender.displayName, guild)
var avatarUrl = guildUser?.effectiveAvatarUrl

var senderName = enforceSenderName(msg.sender.displayName)
// if sender is command, use bot's actual name and avatar if possible
Expand Down Expand Up @@ -258,6 +255,24 @@ class DiscordPier(private val bridge: Bridge) : Pier {
}
}

private fun getMemberByUserNameOrDisplayName(name: String, guild: Guild, ignoreCase: Boolean = true): Member? {
// check by username first
var matchingUsers = guild.getMembersByName(name, ignoreCase)
// if no results, check by their nickname instead
if (matchingUsers.isEmpty()) {
matchingUsers = guild.getMembersByNickname(name, ignoreCase)
}
// if we still don't have any results, fire off a findMembers call to look it up (and cache it for later)
// this won't help us with this specific call (we don't really want to wait around for this task to come back),
// but it will help us with future calls to this and other functions, so the next time they talk, we'll have it.
if (matchingUsers.isEmpty()) {
guild.findMembers { it.user.name.equals(name, ignoreCase) || it.nickname.equals(name, ignoreCase) }
.onSuccess { logger.debug("Cached ${it.size} results for user lookup: $name") }
}

return matchingUsers.firstOrNull()
}

/**
* Gets an enum from the given string or throw an IllegalArgumentException with the given error message
*/
Expand Down

0 comments on commit b9fa7d5

Please sign in to comment.