1#include "mtx/responses/sync.hpp"
2#include "mtx/events/collections.hpp"
3#include "mtx/log.hpp"
4#include "mtx/responses/common.hpp"
5
6#include <nlohmann/json.hpp>
7
8#include <algorithm>
9#include <variant>
10
11using json = nlohmann::json;
12
13namespace mtx {
14namespace responses {
15
16void
17from_json(const json &obj, AccountData &account_data)
18{
19 if (auto it = obj.find(key: "events"); it != obj.end() && it->is_array())
20 utils::parse_room_account_data_events(events: *it, container&: account_data.events);
21}
22
23void
24from_json(const json &obj, State &state)
25{
26 if (auto it = obj.find(key: "events"); it != obj.end() && it->is_array())
27 utils::parse_state_events(events: *it, container&: state.events);
28}
29
30void
31from_json(const json &obj, Timeline &timeline)
32{
33 timeline.prev_batch = obj.value(key: "prev_batch", default_value: std::string{});
34 timeline.limited = obj.value(key: "limited", default_value: false);
35
36 utils::parse_timeline_events(events: obj.at(key: "events"), container&: timeline.events);
37}
38
39void
40from_json(const json &obj, UnreadNotifications &notifications)
41{
42 if (auto it = obj.find(key: "highlight_count"); it != obj.end())
43 notifications.highlight_count = it->get<uint64_t>();
44
45 if (auto it = obj.find(key: "notification_count"); it != obj.end())
46 notifications.notification_count = it->get<uint64_t>();
47}
48
49void
50from_json(const json &obj, Ephemeral &ephemeral)
51{
52 if (auto it = obj.find(key: "events"); it != obj.end() && it->is_array())
53 utils::parse_ephemeral_events(events: *it, container&: ephemeral.events);
54}
55
56void
57from_json(const json &obj, JoinedRoom &room)
58{
59 if (auto it = obj.find(key: "state"); it != obj.end())
60 room.state = it->get<State>();
61
62 if (auto it = obj.find(key: "timeline"); it != obj.end())
63 room.timeline = it->get<Timeline>();
64
65 if (auto it = obj.find(key: "unread_notifications"); it != obj.end())
66 room.unread_notifications = it->get<UnreadNotifications>();
67
68 if (auto it = obj.find(key: "ephemeral"); it != obj.end())
69 room.ephemeral = it->get<Ephemeral>();
70
71 if (auto it = obj.find(key: "account_data"); it != obj.end())
72 room.account_data = it->get<AccountData>();
73}
74
75void
76from_json(const json &obj, LeftRoom &room)
77{
78 if (auto it = obj.find(key: "state"); it != obj.end())
79 room.state = it->get<State>();
80
81 if (auto it = obj.find(key: "timeline"); it != obj.end())
82 room.timeline = it->get<Timeline>();
83}
84
85std::string
86InvitedRoom::name() const
87{
88 using Name = mtx::events::StrippedEvent<mtx::events::state::Name>;
89 using Member = mtx::events::StrippedEvent<mtx::events::state::Member>;
90
91 std::string room_name;
92 std::string member_name;
93
94 for (const auto &event : invite_state) {
95 if (auto name = std::get_if<Name>(ptr: &event); name != nullptr) {
96 room_name = name->content.name;
97 } else if (auto avatar = std::get_if<Member>(ptr: &event); avatar != nullptr) {
98 if (member_name.empty())
99 member_name = avatar->content.display_name;
100 }
101 }
102
103 if (room_name.empty())
104 return member_name;
105
106 return room_name;
107}
108
109std::string
110InvitedRoom::avatar() const
111{
112 using Avatar = mtx::events::StrippedEvent<mtx::events::state::Avatar>;
113 using Member = mtx::events::StrippedEvent<mtx::events::state::Member>;
114
115 std::string room_avatar;
116 std::string member_avatar;
117
118 for (const auto &event : invite_state) {
119 if (auto avatar = std::get_if<Avatar>(ptr: &event); avatar != nullptr) {
120 room_avatar = avatar->content.url;
121 } else if (auto member = std::get_if<Member>(ptr: &event); member != nullptr) {
122 // Pick the first avatar.
123 if (member_avatar.empty())
124 member_avatar = member->content.avatar_url;
125 }
126 }
127
128 if (room_avatar.empty())
129 return member_avatar;
130
131 return room_avatar;
132}
133
134void
135from_json(const json &obj, InvitedRoom &room)
136{
137 if (auto state = obj.find(key: "invite_state"); state != obj.end())
138 if (auto events = state->find(key: "events"); events != state->end())
139 utils::parse_stripped_events(events: *events, container&: room.invite_state);
140}
141
142void
143from_json(const json &obj, KnockedRoom &room)
144{
145 if (auto state = obj.find(key: "knock_state"); state != obj.end())
146 if (auto events = state->find(key: "events"); events != state->end())
147 utils::parse_stripped_events(events: *events, container&: room.knock_state);
148}
149
150void
151from_json(const json &obj, Rooms &rooms)
152{
153 if (auto entries = obj.find(key: "join"); entries != obj.end()) {
154 for (const auto &r : entries->items()) {
155 if (r.key().size() < 256) {
156 rooms.join.emplace_hint(pos: rooms.join.end(), args: r.key(), args: r.value().get<JoinedRoom>());
157 } else {
158 mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes.");
159 }
160 }
161 }
162
163 if (auto entries = obj.find(key: "leave"); entries != obj.end()) {
164 for (const auto &r : entries->items()) {
165 if (r.key().size() < 256) {
166 rooms.leave.emplace_hint(pos: rooms.leave.end(), args: r.key(), args: r.value().get<LeftRoom>());
167 } else {
168 mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes.");
169 }
170 }
171 }
172
173 if (auto entries = obj.find(key: "invite"); entries != obj.end()) {
174 for (const auto &r : entries->items()) {
175 if (r.key().size() < 256) {
176 rooms.invite.emplace_hint(
177 pos: rooms.invite.end(), args: r.key(), args: r.value().get<InvitedRoom>());
178 } else {
179 mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes.");
180 }
181 }
182 }
183
184 if (auto entries = obj.find(key: "knock"); entries != obj.end()) {
185 for (const auto &r : entries->items()) {
186 if (r.key().size() < 256) {
187 rooms.knock.emplace_hint(pos: rooms.knock.end(), args: r.key(), args: r.value().get<KnockedRoom>());
188 } else {
189 mtx::utils::log::log()->warn(msg: "Skipping roomid which exceeds 255 bytes.");
190 }
191 }
192 }
193}
194
195void
196from_json(const json &obj, DeviceLists &device_lists)
197{
198 if (obj.count(key: "changed") != 0) {
199 device_lists.changed = obj.at(key: "changed").get<std::vector<std::string>>();
200
201 std::erase_if(cont&: device_lists.changed, pred: [](const std::string &user) {
202 if (user.size() > 255) {
203 mtx::utils::log::log()->warn(msg: "Invalid userid in device list changed.");
204 return true;
205 } else
206 return false;
207 });
208 }
209
210 if (obj.count(key: "left") != 0) {
211 device_lists.left = obj.at(key: "left").get<std::vector<std::string>>();
212
213 std::erase_if(cont&: device_lists.left, pred: [](const std::string &user) {
214 if (user.size() > 255) {
215 mtx::utils::log::log()->warn(msg: "Invalid userid in device list left.");
216 return true;
217 } else
218 return false;
219 });
220 }
221}
222
223void
224from_json(const json &obj, ToDevice &to_device)
225{
226 if (obj.count(key: "events") != 0)
227 utils::parse_device_events(events: obj.at(key: "events"), container&: to_device.events);
228}
229
230void
231from_json(const json &obj, Sync &response)
232{
233 if (auto it = obj.find(key: "rooms"); it != obj.end())
234 response.rooms = it->get<Rooms>();
235
236 if (auto it = obj.find(key: "device_lists"); it != obj.end())
237 response.device_lists = it->get<DeviceLists>();
238
239 if (auto it = obj.find(key: "to_device"); it != obj.end())
240 response.to_device = it->get<ToDevice>();
241
242 if (auto it = obj.find(key: "device_one_time_keys_count"); it != obj.end())
243 response.device_one_time_keys_count = it->get<std::map<std::string, uint16_t>>();
244
245 if (auto fallback_keys = obj.find(key: "device_unused_fallback_key_types");
246 fallback_keys != obj.end() && fallback_keys->is_array())
247 response.device_unused_fallback_key_types = fallback_keys->get<std::vector<std::string>>();
248
249 if (obj.count(key: "presence") != 0 && obj.at(key: "presence").contains(key: "events")) {
250 const auto &events = obj.at(key: "presence").at(key: "events");
251 response.presence.reserve(n: events.size());
252 for (const auto &e : events) {
253 try {
254 response.presence.push_back(
255 x: e.get<mtx::events::Event<mtx::events::presence::Presence>>());
256 } catch (std::exception &ex) {
257 mtx::utils::log::log()->warn(
258 fmt: "Error parsing presence event: {}, {}", args: ex.what(), args: e.dump(indent: 2));
259 }
260 }
261 }
262
263 if (auto it = obj.find(key: "account_data"); it != obj.end())
264 response.account_data = it->get<AccountData>();
265
266 response.next_batch = obj.at(key: "next_batch").get<std::string>();
267}
268}
269}
270