1 module dcord.types.guild;
2 
3 import std.stdio,
4        std.algorithm,
5        std.array,
6        std.conv;
7 
8 import dcord.types,
9        dcord.client,
10        dcord.gateway;
11 
12 alias GuildMap = ModelMap!(Snowflake, Guild);
13 alias RoleMap = ModelMap!(Snowflake, Role);
14 alias GuildMemberMap = ModelMap!(Snowflake, GuildMember);
15 alias EmojiMap = ModelMap!(Snowflake, Emoji);
16 
17 /// Enumeration representing the verification level of the guild
18 enum VerificationLevel: ushort {
19   NONE = 0,
20   LOW = 1,
21   MEDIUM = 2,
22   HIGH = 3,
23   EXTREME = 4,
24 }
25 
26 /// A class representing roles
27 class Role: IModel {
28   mixin Model;
29 
30   Snowflake id;
31   Snowflake guildID;
32 
33   string name;
34   bool hoist;
35   bool managed;
36   uint color;
37   Permission permissions;
38   short position;
39   bool mentionable;
40 
41   @property Guild guild() {
42     return this.client.state.guilds.get(this.guildID);
43   }
44 }
45 
46 class Emoji: IModel {
47   mixin Model;
48 
49   Snowflake  id;
50   Snowflake  guildID;
51   string     name;
52   bool       requireColons;
53   bool       managed;
54 
55   Snowflake[]  roles;
56 
57   @property Guild guild() {
58     return this.client.state.guilds.get(this.guildID);
59   }
60 
61   bool matches(string usage) {
62     if (this.name == ":" ~ usage ~ ":") {
63       return true;
64     } else if (this.requireColons) {
65       return false;
66     } else {
67       return this.name == usage;
68     }
69   }
70 }
71 
72 class GuildMember : IModel {
73   mixin Model;
74 
75   User       user;
76   Snowflake  guildID;
77   string     nick;
78   string     joinedAt;
79   bool       mute;
80   bool       deaf;
81 
82   Snowflake[]  roles;
83 
84   @property Snowflake id() {
85     return this.user.id;
86   }
87 
88   @property Guild guild() {
89     return this.client.state.guilds.get(this.guildID);
90   }
91 
92   override string toString() {
93     return format("<GuildMember %s#%s (%s / %s)>",
94       this.user.username,
95       this.user.discriminator,
96       this.id,
97       this.guild.id);
98   }
99 
100   bool hasRole(Role role) {
101     return this.hasRole(role.id);
102   }
103 
104   bool hasRole(Snowflake id) {
105     return this.roles.canFind(id);
106   }
107 }
108 
109 class Guild : IModel, IPermissible {
110   mixin Model;
111   mixin Permissible;
112 
113   Snowflake  id;
114   Snowflake  ownerID;
115   Snowflake  afkChannelID;
116   Snowflake  embedChannelID;
117   string     name;
118   string     icon;
119   string     splash;
120   string     region;
121   uint       afkTimeout;
122   bool       embedEnabled;
123   ushort     verificationLevel;
124   ushort     mfaLevel;
125   string[]   features;
126 
127   bool  unavailable;
128 
129   @JSONListToMap("id")
130   GuildMemberMap  members;
131 
132   @JSONListToMap("sessionID")
133   VoiceStateMap   voiceStates;
134 
135   @JSONListToMap("id")
136   ChannelMap      channels;
137 
138   @JSONListToMap("id")
139   RoleMap         roles;
140 
141   @JSONListToMap("id")
142   EmojiMap        emojis;
143 
144   override void initialize() {
145     // It's possible these are not created
146     if (!this.members) return;
147 
148     this.members.each((m) { m.guildID = this.id; });
149     this.voiceStates.each((vc) { vc.guildID = this.id; });
150     this.channels.each((c) { c.guildID = this.id; });
151     this.roles.each((r) { r.guildID = this.id; });
152     this.emojis.each((e) { e.guildID = this.id; });
153   }
154 
155   override string toString() {
156     return format("<Guild %s (%s)>", this.name, this.id);
157   }
158 
159   /// Returns a URL to the guild icon
160   string getIconURL(string fmt = "webp", size_t size = 1024) {
161     if (this.icon == "") {
162       return "";
163     }
164     return format("https://cdn.discordapp.com/icons/%s/%s.%s?size=%s", this.id, this.icon, fmt, size);
165   }
166 
167   /// Returns a GuildMember for a given user object
168   GuildMember getMember(User obj) {
169     return this.getMember(obj.id);
170   }
171 
172   /// Returns a GuildMember for a given user/member id
173   GuildMember getMember(Snowflake id) {
174     return this.members.get(id);
175   }
176 
177   /// Kick a given GuildMember
178   void kick(GuildMember member) {
179     this.kick(member.user);
180   }
181 
182   /// Kick a given User
183   void kick(User user) {
184     this.client.api.guildsMembersKick(this.id, user.id);
185   }
186 
187   /// Ban a given GuildMember
188   void ban(GuildMember member) {
189     this.ban(member.user);
190   }
191 
192   /// Ban a given User 
193   void ban(User user) {
194     this.client.api.guildMembersBan(this.id, user.id);
195   }
196 
197   /// Unban a given GuildMember
198   void removeBan(GuildMember member) {
199     this.removeBan(member.user);
200   }
201 
202   /// Unban a given User
203   void removeBan(User user) {
204     this.client.api.guildMembersRemoveBan(this.id, user.id);
205   }
206 
207   /// Default role for this Guild
208   @property Role defaultRole() {
209     return this.roles.pick((r) {
210       return r.id == this.id;
211     });
212   }
213 
214   /// Default channel for this Guild
215   @property Channel defaultChannel() {
216     return this.channels.pick((c) {
217       return c.id == this.id;
218     });
219   }
220 
221   /// Request offline members for this guild
222   void requestOfflineMembers() {
223     this.client.gw.send(new RequestGuildMembers(this.id));
224   }
225 
226   override Permission getPermissions(Snowflake user) {
227     // If the user is the owner, they have all permisisons 
228     if(this.ownerID == user)return Permissions.ADMINISTRATOR;
229     
230 
231     // Otherwise grab the member object
232     GuildMember member = this.getMember(user);
233     Permission perm;
234     auto roles = member.roles.map!(rid => this.roles.get(rid));
235 
236     // Iterate over roles and add permissions
237     foreach (role; roles) {
238       perm |= role.permissions;
239     }
240 
241     return perm;
242   }
243 
244   /// Set this servers name
245   void setName(string name) {
246     this.client.api.guildsModify(this.id, VibeJSON(["name" : VibeJSON(name)]));
247   }
248 
249   /// Set this servers region
250   void setRegion(string region) {
251     this.client.api.guildsModify(this.id, VibeJSON(["region" : VibeJSON(region)]));
252   }
253 }