nyxx/nyxx_extensions/lib/src/message_resolver/message_resolver.dart

296 lines
9.3 KiB
Dart
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import "dart:async";
import "package:nyxx/nyxx.dart" show TextGuildChannel, Message, Nyxx, Role, Snowflake;
import "Regexes.dart" show Regexes;
/// Possible types of tag handling for [MessageResolver]
enum TagHandling {
/// Ignores tag handling completely - leaves content as is.
ignore,
/// Removes tag completely.
remove,
/// Returns name of tag, eg. `<@932489234> -> @l7ssha`
name,
/// Returns name of tag without mention prefix, eg. `<@932489234> -> l7ssha`
nameNoPrefix,
/// Returns name of the tag with full possible data, eg. `<@932489234> -> @l7ssha#6712`
fullName,
/// Returns name of the tag with full possible data without mention prefix, eg. `<@932489234> -> l7ssha#6712`
fullNameNoPrefix,
/// Sanitizes tag to form that client wont treat it as valid tag
sanitize
}
/// Extends [Message] class with [MessageResolver]
extension MessageResolverExtension on Message {
/// Resolves raw message content to human readable string.
/// Allows to set what to do with particular parts of message.
/// Each mention, channel reference and emoji can be resolved by [TagHandling]
FutureOr<String> resolveContent( {
TagHandling userTagHandling = TagHandling.sanitize,
TagHandling roleTagHandling = TagHandling.sanitize,
TagHandling everyoneTagHandling = TagHandling.sanitize,
TagHandling channelTagHandling = TagHandling.sanitize,
TagHandling emojiTagHandling = TagHandling.sanitize
}) {
if(this.content.isEmpty) {
return "";
}
return MessageResolver(this.client as Nyxx,
userTagHandling: userTagHandling,
roleTagHandling: roleTagHandling,
everyoneTagHandling: everyoneTagHandling,
channelTagHandling: channelTagHandling,
emojiTagHandling: everyoneTagHandling).resolve(this.content);
}
}
/// Allows to return custom messages in case of missing entities when resolving message content.
/// [entityType] could be either `role`, `channel` or `user`.
typedef MissingEntityHandler = FutureOr<String> Function(String entityType);
/// Resolves raw message content to human readable string.
/// Allows to set what to do with particular parts of message.
/// Each mention, channel reference and emoji can be resolved by [TagHandling]
class MessageResolver {
final Nyxx _client;
static const String _whiteSpaceCharacter = "";
/// Handles resolving of user mentions
final TagHandling userTagHandling;
/// Handles resolving of role mentions
final TagHandling roleTagHandling;
/// Handles resolving of everyone/here mentions
final TagHandling everyoneTagHandling;
/// Handles resolving of channels mentions
final TagHandling channelTagHandling;
/// Handles resolving of guild emojis
final TagHandling emojiTagHandling;
/// Handles what will be returned in case if entity cannot be resolved
late final MissingEntityHandler missingEntityHandler;
/// Create message resolver with given options
MessageResolver(this._client, {
this.userTagHandling = TagHandling.sanitize,
this.roleTagHandling = TagHandling.sanitize,
this.everyoneTagHandling = TagHandling.sanitize,
this.channelTagHandling = TagHandling.sanitize,
this.emojiTagHandling = TagHandling.sanitize,
MissingEntityHandler? missingEntityHandler
}) {
if (missingEntityHandler == null) {
this.missingEntityHandler = _defaultMissingEntityHandler;
} else {
this.missingEntityHandler = missingEntityHandler;
}
}
/// Create message resolver with tag handlers set to [tagHandling].
factory MessageResolver.uniform(Nyxx client, TagHandling tagHandling) =>
MessageResolver(client,
userTagHandling: tagHandling,
roleTagHandling: tagHandling,
everyoneTagHandling: tagHandling,
channelTagHandling: tagHandling,
emojiTagHandling: tagHandling
);
/// Resolves raw [messageContent] into human readable form.
Future<String> resolve(String messageContent) async {
if (messageContent.trim().isEmpty) {
return "";
}
final messageParts = messageContent.split(" ");
final outputBuffer = StringBuffer();
for(final part in messageParts) {
outputBuffer.write(" ");
final userMatch = Regexes.userMentionRegex.firstMatch(part);
if (userMatch != null) {
outputBuffer.write(await this._resoleUserMention(userMatch, part));
continue;
}
final roleMatch = Regexes.roleMentionRegex.firstMatch(part);
if (roleMatch != null) {
outputBuffer.write(await this._resolveRoleMention(roleMatch, part));
continue;
}
final everyoneMatch = Regexes.everyoneMentionRegex.firstMatch(part);
if (everyoneMatch != null) {
outputBuffer.write(await this._resolveEveryone(everyoneMatch, part));
continue;
}
final channelMatch = Regexes.channelMentionRegex.firstMatch(part);
if (channelMatch != null) {
outputBuffer.write(await this._resolveChannel(channelMatch, part));
continue;
}
final emojiMatch = Regexes.emojiMentionRegex.firstMatch(part);
if (emojiMatch != null) {
outputBuffer.write(await this._resolveEmoji(emojiMatch, part));
continue;
}
outputBuffer.write(part);
}
return outputBuffer.toString().trim();
}
FutureOr<String> _resolveEmoji(RegExpMatch match, String content) async {
if (emojiTagHandling == TagHandling.ignore) {
return content;
}
if (emojiTagHandling == TagHandling.remove) {
return "";
}
if (emojiTagHandling == TagHandling.sanitize) {
// TODO: check how to escape emoji properly
return "<${match.group(1)}:${match.group(2)}$_whiteSpaceCharacter:$_whiteSpaceCharacter${match.group(3)}>";
}
if (emojiTagHandling == TagHandling.name || emojiTagHandling == TagHandling.fullName) {
return ":${match.group(2)}:";
}
if (emojiTagHandling == TagHandling.nameNoPrefix || emojiTagHandling == TagHandling.fullNameNoPrefix) {
return match.group(2).toString();
}
return content;
}
FutureOr<String> _resolveChannel(RegExpMatch match, String content) async {
if (channelTagHandling == TagHandling.ignore) {
return content;
}
if (channelTagHandling == TagHandling.remove) {
return "";
}
if (channelTagHandling == TagHandling.sanitize) {
return "<#$_whiteSpaceCharacter${match.group(1)}>";
}
final channel = _client.channels.values.firstWhere((ch) => ch is TextGuildChannel && ch.id == match.group(1)) as TextGuildChannel?;
if (channelTagHandling == TagHandling.name || channelTagHandling == TagHandling.fullName) {
return channel != null ? "#${channel.name}" : this.missingEntityHandler("channel");
}
if (channelTagHandling == TagHandling.nameNoPrefix || channelTagHandling == TagHandling.fullNameNoPrefix) {
return channel != null ? channel.name : this.missingEntityHandler("channel");
}
return content;
}
FutureOr<String> _resolveEveryone(RegExpMatch match, String content) async {
if (roleTagHandling == TagHandling.remove) {
return "";
}
if (roleTagHandling == TagHandling.sanitize) {
return "@$_whiteSpaceCharacter${match.group(1)}";
}
return content;
}
FutureOr<String> _resolveRoleMention(RegExpMatch match, String content) async {
if (roleTagHandling == TagHandling.ignore) {
return content;
}
if (roleTagHandling == TagHandling.remove) {
return "";
}
if (roleTagHandling == TagHandling.sanitize) {
return "<@&$_whiteSpaceCharacter${match.group(1)}>";
}
try {
final role = _client.guilds.values
.map((e) => e.roles.values)
.expand((element) => element)
.firstWhere((element) => element.id == Snowflake(match.group(1)));
if (roleTagHandling == TagHandling.name || roleTagHandling == TagHandling.fullName) {
return "@${role.name}";
}
if (roleTagHandling == TagHandling.nameNoPrefix || roleTagHandling == TagHandling.fullNameNoPrefix) {
return role.name;
}
} on Exception {
return this.missingEntityHandler("role");
}
return content;
}
FutureOr<String> _resoleUserMention(RegExpMatch match, String content) async {
if (userTagHandling == TagHandling.ignore) {
return content;
}
if (userTagHandling == TagHandling.remove) {
return "";
}
if (userTagHandling == TagHandling.sanitize) {
return "<@$_whiteSpaceCharacter${match.group(1)}>";
}
final user = _client.users[Snowflake(match.group(1))];
if (userTagHandling == TagHandling.name) {
return user != null ? "@${user.username}" : this.missingEntityHandler("user");
}
if (userTagHandling == TagHandling.nameNoPrefix) {
return user != null ? user.username : this.missingEntityHandler("user");
}
if (userTagHandling == TagHandling.fullName) {
return user != null ? "@${user.tag}" : this.missingEntityHandler("user");
}
if (userTagHandling == TagHandling.fullNameNoPrefix) {
return user != null ? "${user.tag}" : this.missingEntityHandler("user");
}
return "";
}
}
FutureOr<String> _defaultMissingEntityHandler(String entityType) {
switch (entityType) {
case "user":
return "[Could not get user]";
case "role":
return "[Could not get role]";
case "channel":
return "[Could not get channel]";
default:
return "";
}
}