diff --git a/app/src/main/java/chat/revolt/api/internals/MessageProcessor.kt b/app/src/main/java/chat/revolt/api/internals/MessageProcessor.kt index 36cb909d..1e345b36 100644 --- a/app/src/main/java/chat/revolt/api/internals/MessageProcessor.kt +++ b/app/src/main/java/chat/revolt/api/internals/MessageProcessor.kt @@ -1,14 +1,26 @@ package chat.revolt.api.internals import chat.revolt.api.RevoltAPI +import chat.revolt.api.schemas.ChannelType +import chat.revolt.internals.EmojiImpl object MessageProcessor { private val MentionRegex = Regex("@((?:\\p{L}|[\\d_.-])+)#([0-9]{4})", RegexOption.IGNORE_CASE) + private val ChannelRegex = Regex("(?:\\s|^)#(.+?)(?:\\s|\$)", RegexOption.IGNORE_CASE) + private val EmojiRegex = Regex(":(.+?):", RegexOption.IGNORE_CASE) - fun processOutgoing(content: String): String { + private val emojiMetadata = EmojiImpl() + + /** + * Processes an outgoing message for sending. + * 1. Replaces @mentions#0000 with <@userId> + * 2. Replaces #channel with <#channelId> if the current server has a channel with that name + * 2. Replaces :emoji-shortcode: with the emoji's unicode character, if it exists + */ + fun processOutgoing(content: String, serverId: String?): String { val mentions = MentionRegex.findAll(content).map { it.value }.toList() - return mentions.fold(content) { acc, mention -> + var returnable = mentions.fold(content) { acc, mention -> val (username, discriminator) = MentionRegex.matchEntire(mention)?.destructured ?: return@fold acc @@ -18,5 +30,34 @@ object MessageProcessor { val userId = user?.id ?: return@fold acc acc.replace(mention, "<@$userId>") } + + val channels = ChannelRegex.findAll(returnable).map { it.value }.toList() + + returnable = channels.fold(returnable) { acc, channel -> + val channelName = ChannelRegex.matchEntire(channel)?.destructured?.component1() + ?: return@fold acc + + val fetchedChannel = + RevoltAPI.channelCache.values.find { + it.name == channelName && it.server == serverId && it.channelType == ChannelType.TextChannel + } + ?: return@fold acc + + fetchedChannel.name?.let { acc.replace("#${it}", "<#${fetchedChannel.id}>") } ?: acc + } + + val emojis = EmojiRegex.findAll(returnable).map { it.value }.toList() + + returnable = emojis.fold(returnable) { acc, emoji -> + val emojiName = EmojiRegex.matchEntire(emoji)?.destructured?.component1() + ?: return@fold acc + + val byShortcode = emojiMetadata.unicodeByShortcode(emojiName) + ?: return@fold acc + + acc.replace(":$emojiName:", byShortcode) + } + + return returnable } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/internals/EmojiImpl.kt b/app/src/main/java/chat/revolt/internals/EmojiImpl.kt index 9d20fc03..1903b009 100644 --- a/app/src/main/java/chat/revolt/internals/EmojiImpl.kt +++ b/app/src/main/java/chat/revolt/internals/EmojiImpl.kt @@ -247,6 +247,18 @@ class EmojiImpl { return list } + fun unicodeByShortcode(shortcode: String): String? { + return metadata.asSequence().mapNotNull { group -> + group.emoji.find { emoji -> + emoji.shortcodes.any { code -> + code == ":${shortcode}:" + } + } + }.firstOrNull().let { emoji -> + emoji?.base?.joinToString("") { String(Character.toChars(it.toInt())) } + } + } + init { metadata = initMetadata(RevoltApplication.instance.applicationContext) } diff --git a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt index 706bf78b..839849fe 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/channel/ChannelScreenViewModel.kt @@ -212,7 +212,10 @@ class ChannelScreenViewModel : ViewModel() { sendMessage( activeChannel!!.id!!, - MessageProcessor.processOutgoing(pendingMessageContent.trimIndent()), + MessageProcessor.processOutgoing( + pendingMessageContent.trimIndent(), + activeChannel?.server + ), attachments = if (attachmentIds.isEmpty()) null else attachmentIds, replies = pendingReplies )