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 }