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