Skip to content

Commit

Permalink
feat: new username system (#497)
Browse files Browse the repository at this point in the history
* feat: new username system

* Bad idea to put true animated here

* new release

* fix test

* new release

fix test

Release 5.1.0
  • Loading branch information
Lexedia authored Jun 16, 2023
1 parent 37c432a commit 9e7b2e8
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 21 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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__

Expand Down
2 changes: 1 addition & 1 deletion lib/src/core/application/app_team_user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/core/guild/webhook.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
21 changes: 16 additions & 5 deletions lib/src/core/user/user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ abstract class IUser implements SnowflakeEntity, ISend, Mentionable, IMessageAut
FutureOr<IDMChannel> 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});
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -127,13 +134,17 @@ 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;
discriminator = int.parse(raw["discriminator"] as String);
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);
Expand Down Expand Up @@ -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);
Expand Down
28 changes: 17 additions & 11 deletions lib/src/internal/cdn_http_endpoints.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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].
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion lib/src/internal/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/integration/integration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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'));
});
Expand Down

0 comments on commit 9e7b2e8

Please sign in to comment.