diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bd0c3bda..ab92cb91f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## 5.1.0 +__16.06.2023__ + +- feature: Support the new unique username system with global display names. +- bug: remove the `!` in the mention string as it has been deprecated. + ## 5.0.4 __04.06.2023__ diff --git a/lib/src/core/application/app_team_user.dart b/lib/src/core/application/app_team_user.dart index a7a75d172..a02897a95 100644 --- a/lib/src/core/application/app_team_user.dart +++ b/lib/src/core/application/app_team_user.dart @@ -49,7 +49,7 @@ class AppTeamUser extends SnowflakeEntity implements IAppTeamUser { @override String avatarUrl({String format = 'webp', int? size, bool animated = false}) { if (avatar == null) { - return client.cdnHttpEndpoints.defaultAvatar(int.tryParse(discriminator) ?? 0); + return client.cdnHttpEndpoints.defaultAvatar(int.tryParse(discriminator) ?? 0, id.id); } return client.cdnHttpEndpoints.avatar(id, avatar!, format: format, size: size, animated: animated); diff --git a/lib/src/core/guild/webhook.dart b/lib/src/core/guild/webhook.dart index 10883d104..1570c34cc 100644 --- a/lib/src/core/guild/webhook.dart +++ b/lib/src/core/guild/webhook.dart @@ -189,7 +189,7 @@ class Webhook extends SnowflakeEntity implements IWebhook { @override String avatarUrl({String format = 'webp', int? size, bool animated = false}) { if (avatarHash == null) { - return client.cdnHttpEndpoints.defaultAvatar(defaultAvatarId); + return client.cdnHttpEndpoints.defaultAvatar(defaultAvatarId, id.id); } return client.cdnHttpEndpoints.avatar(id, avatarHash!, format: format, size: size, animated: animated); diff --git a/lib/src/core/user/user.dart b/lib/src/core/user/user.dart index 88222adbb..2d2219390 100644 --- a/lib/src/core/user/user.dart +++ b/lib/src/core/user/user.dart @@ -49,7 +49,10 @@ abstract class IUser implements SnowflakeEntity, ISend, Mentionable, IMessageAut FutureOr get dmChannel; /// The hash of the user's avatar decoration. - String? avatarDecorationHash; + String? get avatarDecorationHash; + + /// The user's display name, if it set. + String? get globalName; /// The user's banner url. String? bannerUrl({String format = 'webp', int? size, bool animated = false}); @@ -82,11 +85,15 @@ class User extends SnowflakeEntity implements IUser { /// The string to mention the user. @override - String get mention => "<@!$id>"; + String get mention => "<@$id>"; - /// Returns String with username#discriminator + /// Returns the complete username of user with discriminator for non-migrated users on the new username system (e.g. `Username#0001`). + /// For migrated users it returns the global name if it's set, otherwise it returns the username. @override - String get tag => "$username#$formattedDiscriminator"; + String get tag { + final isPomelo = discriminator == 0; + return isPomelo ? (globalName ?? username) : "$username#$formattedDiscriminator"; + } /// Whether the user belongs to an OAuth2 application @override @@ -127,6 +134,9 @@ class User extends SnowflakeEntity implements IUser { @override late final String? avatarDecorationHash; + @override + late final String? globalName; + /// Creates an instance of [User] User(this.client, RawApiMap raw) : super(Snowflake(raw["id"])) { username = raw["username"] as String; @@ -134,6 +144,7 @@ class User extends SnowflakeEntity implements IUser { avatar = raw["avatar"] as String?; bot = raw["bot"] as bool? ?? false; system = raw["system"] as bool? ?? false; + globalName = raw["global_name"] as String?; if (raw["public_flags"] != null) { userFlags = UserFlags(raw["public_flags"] as int); @@ -172,7 +183,7 @@ class User extends SnowflakeEntity implements IUser { @override String avatarUrl({String format = 'webp', int? size, bool animated = false}) { if (avatar == null) { - return client.cdnHttpEndpoints.defaultAvatar(discriminator); + return client.cdnHttpEndpoints.defaultAvatar(discriminator, id.id); } return client.cdnHttpEndpoints.avatar(id, avatar!, format: format, size: size, animated: animated); diff --git a/lib/src/internal/cdn_http_endpoints.dart b/lib/src/internal/cdn_http_endpoints.dart index 1818943e6..c5d1eab20 100644 --- a/lib/src/internal/cdn_http_endpoints.dart +++ b/lib/src/internal/cdn_http_endpoints.dart @@ -29,15 +29,16 @@ abstract class ICdnHttpEndpoints { /// With given [format] and [size]. String channelIcon(Snowflake channelId, String iconHash, {String format = 'webp', int? size}); - /// Returns URL to ``/embed/avatars/[discriminator]``. + /// Returns URL to ``/embed/avatars/{index}``. /// - /// The [discriminator] is passed as modulo 5 (`% 5`); and will lead to 0,1,2,3,4. (There's 5, but 5 modulo 5 will never give 5). + /// For non-migrated users to the new username system, the [discriminator] is passed as modulo 5 (`% 5`); and will lead to 0,1,2,3,4. (There's 5, but 5 modulo 5 will never give 5). + /// For migrated users, the [id] is passed and is left shifted by 22 bits and then the result is modulo 6 (`% 6`). (For pink avatar). /// /// E.g: /// ```dart - /// client.cdnHttpEndpoints.defaultAvatar(6712); // https://cdn.discordapp.com/embed/avatars/2.png + /// client.cdnHttpEndpoints.defaultAvatar(6712, 123456789123456789); // https://cdn.discordapp.com/embed/avatars/2.png /// ``` - String defaultAvatar(int discriminator); + String defaultAvatar(int discriminator, int id); /// Returns URL to ``/emojis/[emojiId]``. /// With given [format] and [size]. @@ -163,13 +164,18 @@ class CdnHttpEndpoints implements ICdnHttpEndpoints { ); @override - String defaultAvatar(int discriminator) => _makeCdnUrl( - ICdnHttpRoute() - ..embed() - ..avatars() - ..addHash(hash: (discriminator % 5).toString()), - format: 'png', - ); + // TODO: Remove [discriminator] once migration is done? + String defaultAvatar(int discriminator, int id) { + final isPomelo = discriminator == 0; + final index = isPomelo ? (id >> 22) % 6 : discriminator % 5; + + return _makeCdnUrl( + ICdnHttpRoute() + ..embed() + ..avatars(id: index.toString()), + format: 'png', + ); + } @override String discoverySplash(Snowflake guildId, String splashHash, {String format = 'webp', int? size}) => _makeCdnUrl( diff --git a/lib/src/internal/constants.dart b/lib/src/internal/constants.dart index d7b410176..df6cce36c 100644 --- a/lib/src/internal/constants.dart +++ b/lib/src/internal/constants.dart @@ -33,7 +33,7 @@ class Constants { static const int apiVersion = 10; /// Version of Nyxx - static const String version = "5.0.4"; + static const String version = "5.1.0"; /// Url to Nyxx repo static const String repoUrl = "https://github.com/nyxx-discord/nyxx"; diff --git a/pubspec.yaml b/pubspec.yaml index 1c69671ac..08ab7b4cd 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: nyxx -version: 5.0.4 +version: 5.1.0 description: A Discord library for Dart. Simple, robust framework for creating discord bots for Dart language. homepage: https://github.com/nyxx-discord/nyxx repository: https://github.com/nyxx-discord/nyxx diff --git a/test/integration/integration.dart b/test/integration/integration.dart index d4428c3a4..98f028a50 100644 --- a/test/integration/integration.dart +++ b/test/integration/integration.dart @@ -138,7 +138,7 @@ main() async { expect(userBot.discriminator, equals(1759)); expect(userBot.formattedDiscriminator, equals("1759")); expect(userBot.bot, isTrue); - expect(userBot.mention, "<@!${testUserBotSnowflake.toString()}>"); + expect(userBot.mention, "<@${testUserBotSnowflake.toString()}>"); expect(userBot.tag, equals("Running on Dart#1759")); expect(userBot.avatarUrl(), equals('https://cdn.discordapp.com/avatars/476603965396746242/be6107505d7b9d15292da4e54d88836e.webp')); });