1 module dcord.types.user; 2 3 import std.stdio, 4 std.format, 5 std.algorithm.searching; 6 7 import std.conv : to; 8 9 import dcord.types, 10 dcord.client; 11 12 alias UserMap = ModelMap!(Snowflake, User); 13 14 /// Enumeration of available game types 15 enum GameType: short { 16 DEFAULT = 0, 17 STREAMING = 1, 18 LISTENING = 2, 19 WATCHING = 3, 20 } 21 22 /// Enumeration of available statues (online/idle/dnd/invisible/offline) 23 enum UserStatus: string { 24 ONLINE = "online", 25 IDLE = "idle", 26 DND = "dnd", 27 INVISIBLE = "invisible", 28 OFFLINE = "offline", 29 } 30 31 /// Enumeration of default avatar colors, assigned as integers to names 32 enum DefaultAvatarColor { 33 BLURPLE = 0, 34 GREY = 1, 35 GREEN = 2, 36 ORANGE = 3, 37 RED = 4, 38 } 39 40 class Game { 41 string name; 42 string url; 43 GameType type; 44 45 this(string name="", string url="", GameType type=GameType.DEFAULT) { 46 this.name = name; 47 this.url = url; 48 this.type = type; 49 } 50 51 // TODO: remove 52 VibeJSON dump() { 53 VibeJSON obj = VibeJSON.emptyObject; 54 55 obj["name"] = VibeJSON(this.name); 56 57 if (this.url != "") { 58 obj["url"] = VibeJSON(this.url); 59 obj["type"] = VibeJSON(cast(ushort)this.type); 60 } 61 62 return obj; 63 } 64 } 65 66 class Presence: IModel { 67 mixin Model; 68 69 User user; /// the user that the presence is coming from 70 Game game; /// a Game object for the presence 71 UserStatus status; /// user status 72 } 73 74 class User: IModel { 75 mixin Model; 76 /// The user's ID 77 Snowflake id; 78 /// The user's username 79 string username; 80 /// The user's unique four digit discriminator 81 string discriminator; 82 /// A URL pointing to the user's avatar 83 string avatar; 84 /// A boolean representing whether the user is verified 85 bool verified; 86 /// The user's email, if the account is linked with one 87 string email; 88 89 override string toString() { // stfu 90 return format("<User %s#%s (%s)>", this.username, this.discriminator, this.id); 91 } 92 93 string getAvatarURL(string fmt = null, size_t size = 1024) { 94 // TODO: when Discord moves it's CDN to cdn.discord.com, move accordingly to avoid overhead of redirects 95 if (!this.avatar) { // if no avatar is detected 96 return format("https://cdn.discordapp.com/embed/avatars/%s.png", cast(int)this.defaultAvatarColor); // return the default avatar 97 } 98 99 if (fmt is null) { 100 fmt = this.avatar.startsWith("a_") ? "gif" : "webp"; 101 } 102 103 return format("https://cdn.discordapp.com/avatars/%s/%s.%s?size=%s", this.id, this.avatar, fmt, size); 104 } 105 106 @property DefaultAvatarColor defaultAvatarColor() { 107 auto discrimNumber = this.discriminator.to!int; 108 109 return cast(DefaultAvatarColor)(discrimNumber % DefaultAvatarColor.sizeof); 110 } 111 }