1 /**
2   Implementations of packets sent over the Gateway websocket.
3 */
4 module dscord.gateway.packets;
5 
6 import std.stdio;
7 
8 import dscord.types;
9 
10 enum OPCode : ushort {
11   DISPATCH = 0,
12   HEARTBEAT = 1,
13   IDENTIFY = 2,
14   STATUS_UPDATE = 3,
15   VOICE_STATE_UPDATE = 4,
16   VOICE_SERVER_PING = 5,
17   RESUME = 6,
18   RECONNECT = 7,
19   REQUEST_GUILD_MEMBERS = 8,
20   INVALID_SESSION = 9,
21   HELLO = 10,
22   HEARTBEAT_ACK = 11,
23   GUILD_SYNC = 12,
24 };
25 
26 interface Serializable {
27   VibeJSON serialize();
28 }
29 
30 interface Deserializable {
31 }
32 
33 class BasePacket {
34   OPCode      op;
35   VibeJSON    data;
36   VibeJSON    raw;
37 
38   VibeJSON serialize(ushort op, VibeJSON data) {
39     return VibeJSON([
40       "op": VibeJSON(op),
41       "d": data,
42     ]);
43   }
44 }
45 
46 class HeartbeatPacket : BasePacket, Serializable {
47   uint seq;
48 
49   this(uint seq) {
50     this.seq = seq;
51   }
52 
53   override VibeJSON serialize() {
54     return super.serialize(OPCode.HEARTBEAT, VibeJSON(this.seq));
55   }
56 }
57 
58 class ResumePacket : BasePacket, Serializable {
59   string  token;
60   string  sessionID;
61   uint    seq;
62 
63   this(string token, string sessionID, uint seq) {
64     this.token = token;
65     this.sessionID = sessionID;
66     this.seq = seq;
67   }
68 
69   override VibeJSON serialize() {
70     return super.serialize(OPCode.RESUME, VibeJSON([
71       "token": VibeJSON(this.token),
72       "session_id": VibeJSON(this.sessionID),
73       "seq": VibeJSON(this.seq),
74     ]));
75   }
76 }
77 
78 class VoiceStateUpdatePacket : BasePacket, Serializable {
79   Snowflake  guildID;
80   Snowflake  channelID;
81   bool       self_mute;
82   bool       self_deaf;
83 
84   this(Snowflake guild_id, Snowflake channel_id, bool self_mute, bool self_deaf) {
85     this.guildID = guild_id;
86     this.channelID = channel_id;
87     this.self_mute = self_mute;
88     this.self_deaf = self_deaf;
89   }
90 
91   override VibeJSON serialize() {
92     return super.serialize(OPCode.VOICE_STATE_UPDATE, VibeJSON([
93       "self_mute": VibeJSON(this.self_mute),
94       "self_deaf": VibeJSON(this.self_deaf),
95       "guild_id": this.guildID ? VibeJSON(this.guildID) : VibeJSON(null),
96       "channel_id": this.channelID ? VibeJSON(this.channelID) : VibeJSON(null),
97     ]));
98   }
99 }
100 
101 class IdentifyPacket : BasePacket, Serializable {
102   string token;
103   bool compress = true;
104   ushort largeThreshold = 250;
105   ushort[2] shard;
106 
107   this(string token, ushort shard = 0, ushort numShards = 1) {
108     this.token = token;
109     this.shard = [shard, numShards];
110   }
111 
112   @property VibeJSON properties() {
113     return VibeJSON([
114       "$os": VibeJSON("linux"),
115       "$browser": VibeJSON("dscord"),
116       "$device": VibeJSON("dscord"),
117       "$referrer": VibeJSON(""),
118     ]);
119   }
120 
121   override VibeJSON serialize() {
122     return super.serialize(OPCode.IDENTIFY, VibeJSON([
123       "token": VibeJSON(this.token),
124       "properties": this.properties,
125       "compress": VibeJSON(this.compress),
126       "large_threshold": VibeJSON(this.largeThreshold),
127       "shard": VibeJSON([VibeJSON(this.shard[0]), VibeJSON(this.shard[1])]),
128     ]));
129   }
130 }
131 
132 class RequestGuildMembers : BasePacket, Serializable {
133   Snowflake guildID;
134   string query;
135   uint limit;
136 
137   this(Snowflake guildID, string query="", uint limit=0) {
138     this.guildID = guildID;
139     this.query = query;
140     this.limit = limit;
141   }
142 
143   override VibeJSON serialize() {
144     return super.serialize(OPCode.REQUEST_GUILD_MEMBERS, VibeJSON([
145       "guild_id": VibeJSON(this.guildID.toString),
146       "query": VibeJSON(this.query),
147       "limit": VibeJSON(this.limit),
148     ]));
149   }
150 }
151 
152 class StatusUpdate : BasePacket, Serializable {
153   uint idleSince;
154   Game game;
155 
156   this(uint idleSince=0, Game game=null) {
157     this.idleSince = idleSince;
158     this.game = game;
159 
160   }
161 
162   override VibeJSON serialize() {
163     VibeJSON obj = VibeJSON.emptyObject;
164 
165     if (this.idleSince > 0) {
166       obj["idle_since"] = VibeJSON(this.idleSince);
167     } else {
168       obj["idle_since"] = VibeJSON(null);
169     }
170 
171     if (this.game) {
172       obj["game"] = this.game.dump();
173     } else {
174       obj["game"] = VibeJSON.emptyObject;
175     }
176 
177     return super.serialize(OPCode.STATUS_UPDATE, obj);
178   }
179 }