Twitch SDK (Internal)
chatjsonobjectschemas.h
Go to the documentation of this file.
1 /********************************************************************************************
2  * Twitch Broadcasting SDK
3  *
4  * This software is supplied under the terms of a license agreement with Twitch Interactive, Inc. and
5  * may not be copied or used except in accordance with the terms of that agreement
6  * Copyright (c) 2012-2016 Twitch Interactive, Inc.
7  *********************************************************************************************/
8 
9 #pragma once
10 
17 
18 
19 namespace ttv
20 {
21  namespace chat
22  {
23  namespace json
24  {
25  struct PubSubChatRoomMessageContentSchema;
26  struct PubSubChatRoomMessageSenderSchema;
27  struct GraphQLChatRoomMessageContentSchema;
28  struct GraphQLChatRoomMessageSenderSchema;
29  struct RoomMessageDeletedSchema;
30  struct EmoticonTokenSchema;
31  }
32  }
33 }
34 
36 {
37  static bool Parse(const ttv::json::Value& value, MessageInfo& info)
38  {
39  if (value.isNull())
40  {
41  return false;
42  }
43 
44  const auto& jFragments = value["fragments"];
45  if (jFragments.isNull() || !jFragments.isArray())
46  {
47  // This is valid since a deleted message has no fragments
48  return true;
49  }
50 
51  bool firstFragment = true;
52  for (const auto& jFragment : jFragments)
53  {
54  const auto& jText = jFragment["text"];
55  if (!jText.isNull() && jText.isString())
56  {
57  bool parsed = false;
58  std::string text = jText.asString();
59  const auto& jEmoticon = jFragment["emoticon"];
60  const auto& jMention = jFragment["mention"];
61 
62  if (!jEmoticon.isNull() && jEmoticon.isObject())
63  {
64  uint32_t emoticonId;
65  const auto& jEmoticonId = jEmoticon["id"];
66  if (ParseUInt32(jEmoticonId, emoticonId))
67  {
68  info.tokens.emplace_back(std::make_unique<ttv::chat::EmoticonToken>(text, emoticonId));
69  parsed = true;
70  }
71  }
72  else if (!jMention.isNull() && jMention.isObject())
73  {
74  std::string userName;
75  if (ParseString(jMention, "display_name", userName))
76  {
77  info.tokens.emplace_back(std::make_unique<ttv::chat::MentionToken>(userName, text, false));
78  parsed = true;
79  }
80  }
81  else if (ttv::chat::IsTwitchChatUrl(text))
82  {
83  info.tokens.emplace_back(std::make_unique<ttv::chat::UrlToken>(text, false));
84  parsed = true;
85  }
86 
87  // If parsing as the other tokens failed, just parse it as normal text.
88  if (!parsed)
89  {
90  std::string fragmentText = jText.asString();
91 
92  // If the message starts with "/me ", handle the command.
93  if (firstFragment)
94  {
95  std::string mePrefix = "/me ";
96 
97  if (StartsWith(fragmentText, mePrefix))
98  {
99  fragmentText = fragmentText.substr(mePrefix.size());
100  info.flags.action = true;
101  }
102  }
103  info.tokens.emplace_back(std::make_unique<ttv::chat::TextToken>(fragmentText));
104  }
105 
106  firstFragment = false;
107  }
108  else
109  {
110  return false;
111  }
112  }
113 
114  return true;
115  }
116 
117  static bool Emit(std::vector<std::unique_ptr<ttv::chat::MessageToken>>& tokens, const ttv::json::Value& value)
118  {
119  return false;
120  }
121 };
122 
123 
125 {
126  static bool Parse(const ttv::json::Value& value, ttv::chat::MessageInfo& message)
127  {
128  if (value.isNull())
129  {
130  return false;
131  }
132 
133  // parse badges
134  const auto& jBadges = value["badges"];
135  if (jBadges.isNull() || !jBadges.isArray())
136  {
137  return false;
138  }
139  for (auto& jBadge : jBadges)
140  {
141  MessageBadge badge;
142  if (ParseString(jBadge, "id", badge.name) && ParseString(jBadge, "version", badge.version))
143  {
144  // Badges are currently the only way to determine UserModes through pubsub
145  if (badge.name == "moderator")
146  {
147  message.userMode.moderator = true;
148  }
149  else if (badge.name == "broadcaster")
150  {
151  message.userMode.broadcaster = true;
152  }
153  else if (badge.name == "staff")
154  {
155  message.userMode.staff = true;
156  }
157  else if (badge.name == "admin")
158  {
159  message.userMode.administrator = true;
160  }
161  else if (badge.name == "global_mod")
162  {
163  message.userMode.globalModerator = true;
164  }
165 
166  message.badges.emplace_back(std::move(badge));
167  }
168  }
169 
170  if (!ParseString(value, "display_name", message.displayName))
171  {
172  return false;
173  }
174 
175  if (!ParseString(value, "login", message.userName))
176  {
177  return false;
178  }
179 
180  if (!ParseColor(value, "chat_color", message.nameColorARGB))
181  {
182  message.nameColorARGB = GetRandomUserColor(message.userName);
183  }
184 
185  const auto& jId = value["user_id"];
186  if (!ParseUserId(jId, message.userId))
187  {
188  return false;
189  }
190 
191  return true;
192  }
193 
194  static bool Emit(ttv::chat::MessageInfo& message, const ttv::json::Value& value)
195  {
196  return false;
197  }
198 };
199 
200 
202 {
203  static bool Parse(const ttv::json::Value& value, MessageInfo& info)
204  {
205  if (value.isNull())
206  {
207  return false;
208  }
209 
210  const auto& jFragments = value["fragments"];
211  if (jFragments.isNull() || !jFragments.isArray())
212  {
213  // This is valid since a deleted message has no fragments
214  return true;
215  }
216 
217  bool firstFragment = true;
218  for (const auto& jFragment : jFragments)
219  {
220  const auto& jText = jFragment["text"];
221  if (!jText.isNull() && jText.isString())
222  {
223  bool parsed = false;
224  std::string text = jText.asString();
225  const auto& jContent = jFragment["content"];
226  if (!jContent.isNull() && jContent.isObject())
227  {
228  std::string type;
229  ParseString(jContent, "__typename", type);
230  if (type == "Emote")
231  {
232  uint32_t emoticonId;
233  const auto& jEmoticonId = jContent["id"];
234  if (ParseUInt32(jEmoticonId, emoticonId))
235  {
236  info.tokens.emplace_back(std::make_unique<ttv::chat::EmoticonToken>(text, emoticonId));
237  parsed = true;
238  }
239  }
240  else if (type == "User")
241  {
242  std::string userName;
243  if (ParseString(jContent, "displayName", userName))
244  {
245  info.tokens.emplace_back(std::make_unique<ttv::chat::MentionToken>(userName, text, false));
246  parsed = true;
247  }
248  }
249  }
250  else if (ttv::chat::IsTwitchChatUrl(text))
251  {
252  info.tokens.emplace_back(std::make_unique<ttv::chat::UrlToken>(text, false));
253  parsed = true;
254  }
255 
256  // If parsing as the other tokens failed, just parse it as normal text.
257  if (!parsed)
258  {
259  // If the message starts with "/me ", handle the command.
260  if (firstFragment)
261  {
262  std::string mePrefix = "/me ";
263 
264  if (StartsWith(text, mePrefix))
265  {
266  text = text.substr(mePrefix.size());
267  info.flags.action = true;
268  }
269  }
270  info.tokens.emplace_back(std::make_unique<ttv::chat::TextToken>(text));
271  }
272 
273  firstFragment = false;
274  }
275  else
276  {
277  return false;
278  }
279  }
280 
281  return true;
282  }
283 
284  static bool Emit(std::vector<std::unique_ptr<ttv::chat::MessageToken>>& tokens, const ttv::json::Value& value)
285  {
286  return false;
287  }
288 };
289 
290 
292 {
293  static bool Parse(const ttv::json::Value& value, ttv::chat::MessageInfo& message)
294  {
295  if (value.isNull())
296  {
297  return false;
298  }
299 
300  // Parse badges
301  const auto& jBadges = value["displayBadges"];
302  if (jBadges.isNull() || !jBadges.isArray())
303  {
304  return false;
305  }
306  for (auto& jBadge : jBadges)
307  {
308  MessageBadge badge;
309  if (ParseString(jBadge, "setID", badge.name) && ParseString(jBadge, "version", badge.version))
310  {
311  // Badges are currently the only way to check if the sender is a moderator or broadcaster of the channel
312  if (badge.name == "moderator")
313  {
314  message.userMode.moderator = true;
315  }
316  else if (badge.name == "broadcaster")
317  {
318  message.userMode.broadcaster = true;
319  }
320 
321  message.badges.emplace_back(std::move(badge));
322  }
323  }
324 
325  if (!ParseString(value, "displayName", message.displayName))
326  {
327  return false;
328  }
329 
330  if (!ParseString(value, "login", message.userName))
331  {
332  return false;
333  }
334 
335  if (!ParseColor(value, "chatColor", message.nameColorARGB))
336  {
337  message.nameColorARGB = GetRandomUserColor(message.userName);
338  }
339 
340  const auto& jId = value["id"];
341  if (!ParseUserId(jId, message.userId))
342  {
343  return false;
344  }
345 
346  // Parse UserMode
347  const auto& jRoles = value["roles"];
348  bool isMode;
349  ParseBool(jRoles, "isGlobalMod", isMode, false);
350  message.userMode.globalModerator = isMode;
351  ParseBool(jRoles, "isSiteAdmin", isMode, false);
352  message.userMode.administrator = isMode;
353  ParseBool(jRoles, "isStaff", isMode, false);
354  message.userMode.staff = isMode;
355 
356  return true;
357  }
358 
359  static bool Emit(ttv::chat::MessageInfo& message, const ttv::json::Value& value)
360  {
361  return false;
362  }
363 };
364 
365 
367 {
368  static bool Parse(const ttv::json::Value& value, MessageInfo::Flags& flags)
369  {
370  if (value.isNull())
371  {
372  return false;
373  }
374 
375  Timestamp time;
376  if (ParseTimestamp(value, time))
377  {
378  flags.deleted = true;
379  return true;
380  }
381  else
382  {
383  return false;
384  }
385  }
386 
387  static bool Emit(ttv::chat::MessageInfo& message, const ttv::json::Value& value)
388  {
389  return false;
390  }
391 };
392 
393 
395 {
396  static bool Parse(const ttv::json::Value& value, Emoticon& emoticon)
397  {
398  if (value.isNull() || !value.isString())
399  {
400  return false;
401  }
402 
403  std::string token = value.asString();
404  if (token == "")
405  {
406  return false;;
407  }
408 
409  UnescapeEmoticonToken(token);
410 
411  // Determine if this is a regex or a simple string match
412  // See web/tmi/tmi/clue/emoticons.py
413  static std::regex isRegexRegex = std::regex("[\\|\\\\\\^\\$\\*\\+\\?\\:\\#]");
414 
415  if (std::regex_search(token, isRegexRegex))
416  {
417  emoticon.regex = std::regex(std::string("^") + token + std::string("$"));
418  emoticon.match = token;
419  emoticon.isRegex = true;
420  }
421  else
422  {
423  emoticon.match = token;
424  emoticon.isRegex = false;
425  }
426 
427  return true;
428  }
429 
430  static bool Emit(Emoticon& message, const ttv::json::Value& value)
431  {
432  return false;
433  }
434 };
bool ParseTimestamp(const json::Value &jTimestamp, Timestamp &result)
bool administrator
An admin.
Definition: chattypes.h:103
bool moderator
A moderator.
Definition: chattypes.h:101
bool StartsWith(const std::string &str, const std::string &prefix)
Definition: chatjsonobjectschemas.h:201
bool ParseBool(const ttv::json::Value &root, const char *key, bool &result)
bool ParseString(const ttv::json::Value &root, const char *key, std::string &result)
static bool Emit(ttv::chat::MessageInfo &message, const ttv::json::Value &value)
Definition: chatjsonobjectschemas.h:387
std::string name
Corresponds to Badge::name.
Definition: chattypes.h:284
Definition: chatjsonobjectschemas.h:291
bool staff
A member of Twitch.
Definition: chattypes.h:104
Definition: chattypes.h:299
std::vector< std::unique_ptr< MessageToken > > tokens
The array of message tokens.
Definition: chattypes.h:315
std::string asString() const
uint32_t Timestamp
Definition: coretypes.h:27
Definition: chatjsonobjectschemas.h:124
static bool Emit(std::vector< std::unique_ptr< ttv::chat::MessageToken >> &tokens, const ttv::json::Value &value)
Definition: chatjsonobjectschemas.h:117
Definition: chattypes.h:293
std::vector< MessageBadge > badges
The array of badges.
Definition: chattypes.h:316
JSON (JavaScript Object Notation).
Definition: adsapi.h:16
static bool Parse(const ttv::json::Value &value, MessageInfo &info)
Definition: chatjsonobjectschemas.h:203
Color GetRandomUserColor(const std::string &username)
static bool Parse(const ttv::json::Value &value, ttv::chat::MessageInfo &message)
Definition: chatjsonobjectschemas.h:293
bool isString() const
Flags flags
Any additional details about the message.
Definition: chattypes.h:318
Definition: chattypes.h:652
Represents a JSON value.
Definition: value.h:114
std::string match
Definition: chattypes.h:658
Color nameColorARGB
The current ARGB color of the user&#39;s name text.
Definition: chattypes.h:319
bool broadcaster
The broadcaster.
Definition: chattypes.h:102
bool isRegex
Definition: chattypes.h:660
bool ParseUInt32(const json::Value &jValue, uint32_t &result)
bool IsTwitchChatUrl(const std::string &url)
static bool Emit(std::vector< std::unique_ptr< ttv::chat::MessageToken >> &tokens, const ttv::json::Value &value)
Definition: chatjsonobjectschemas.h:284
static bool Emit(Emoticon &message, const ttv::json::Value &value)
Definition: chatjsonobjectschemas.h:430
UserMode userMode
The modes of the user who sent the message.
Definition: chattypes.h:317
std::string displayName
The UTF-8 encoded displayed name.
Definition: chattypes.h:314
Definition: chatjsonobjectschemas.h:35
bool action
Whether or not the message is an action. If true, it should be displayed entirely in the name text co...
Definition: chattypes.h:306
static bool Parse(const ttv::json::Value &value, Emoticon &emoticon)
Definition: chatjsonobjectschemas.h:396
Definition: chattypes.h:282
static bool Emit(ttv::chat::MessageInfo &message, const ttv::json::Value &value)
Definition: chatjsonobjectschemas.h:359
bool globalModerator
A global moderator.
Definition: chattypes.h:106
std::regex regex
Definition: chattypes.h:657
UserId userId
The unique user id.
Definition: chattypes.h:321
bool isNull() const
void UnescapeEmoticonToken(std::string &token)
static bool Emit(ttv::chat::MessageInfo &message, const ttv::json::Value &value)
Definition: chatjsonobjectschemas.h:194
Definition: chatjsonobjectschemas.h:366
bool deleted
Whether or not the message has been deleted.
Definition: chattypes.h:309
bool ParseUserId(const json::Value &jId, UserId &result)
Definition: chatjsonobjectschemas.h:394
static bool Parse(const ttv::json::Value &value, MessageInfo::Flags &flags)
Definition: chatjsonobjectschemas.h:368
bool ParseColor(const std::string &str, Color &result)
static bool Parse(const ttv::json::Value &value, MessageInfo &info)
Definition: chatjsonobjectschemas.h:37
std::string userName
The UTF-8 encoded user name.
Definition: chattypes.h:313
static bool Parse(const ttv::json::Value &value, ttv::chat::MessageInfo &message)
Definition: chatjsonobjectschemas.h:126
std::string version
Corresponds to BadgeVersion::name.
Definition: chattypes.h:285