diff --git a/ChatTwo/Http/MessageProtocol/DataStructure.cs b/ChatTwo/Http/MessageProtocol/DataStructure.cs
index 4c03ac2..9b24fda 100644
--- a/ChatTwo/Http/MessageProtocol/DataStructure.cs
+++ b/ChatTwo/Http/MessageProtocol/DataStructure.cs
@@ -3,33 +3,69 @@
namespace ChatTwo.Http.MessageProtocol;
#region Outgoing SSE
+///
+/// Contains the current channel name
+///
public struct SwitchChannel(MessageTemplate[] channelName)
{
[JsonProperty("channelName")] public MessageTemplate[] ChannelName = channelName;
}
+///
+/// Contains one or multiple channels that are valid for the user to pick from
+///
public struct ChannelList(Dictionary channels)
{
[JsonProperty("channels")] public Dictionary Channels = channels;
}
+///
+/// Contains one or multiple messages
+///
public struct Messages(MessageResponse[] set)
{
[JsonProperty("messages")] public MessageResponse[] Set = set;
}
+///
+/// Contains a single message with all its templates and a timestamp
+///
public struct MessageResponse()
{
[JsonProperty("timestamp")] public string Timestamp = "";
[JsonProperty("templates")] public MessageTemplate[] Templates;
}
+///
+/// Template that is used for the channel name or any message posted to the chatlog
+///
public struct MessageTemplate()
{
+ ///
+ /// Template type
+ ///
+ /// icon = a game icon
+ /// emote = BetterTTV emote
+ /// url = Simple url that should be clickable
+ /// text = Simple text content of the message
+ ///
+ /// empty = Ignore
+ ///
[JsonProperty("payload")] public required string Payload;
+ ///
+ /// Used for text and emote.
+ ///
[JsonProperty("content")] public string Content = "";
+
+ ///
+ /// Used for icon.
+ ///
[JsonProperty("id")] public uint Id;
+
+ ///
+ /// Used for text and url
+ ///
[JsonProperty("color")] public uint Color;
public static MessageTemplate Empty => new() {Payload = "empty"};
@@ -49,11 +85,19 @@ public struct ErrorResponse(string reason)
#endregion
#region Incoming POST
+///
+/// Message must fulfill the posting requirement
+/// Greater than or equal 2 characters
+/// Less than or equal 500 characters
+///
public struct IncomingMessage()
{
[JsonProperty("message")] public string Message = string.Empty;
}
+///
+/// Channel must be a valid uint number
+///
public struct IncomingChannel()
{
[JsonProperty("channel")] public uint Channel = uint.MaxValue;
diff --git a/ChatTwo/Http/static/start.js b/ChatTwo/Http/static/start.js
index a9fe997..43dbfa3 100644
--- a/ChatTwo/Http/static/start.js
+++ b/ChatTwo/Http/static/start.js
@@ -74,10 +74,7 @@
updateChannelHint(templates) {
this.elements.channelHint.innerHTML = '';
-
- for(const template of templates) {
- this.elements.channelHint.appendChild(this.processTemplate(template));
- }
+ this.elements.channelHint.appendChild(this.processTemplate(templates));
}
updateChannels(channels) {
@@ -131,14 +128,11 @@
const liMessage = document.createElement('li');
const spanTimestamp = document.createElement('span');
spanTimestamp.classList.add('timestamp');
- const spanMessage = document.createElement('span');
- spanMessage.classList.add('message');
-
spanTimestamp.innerText = messageData.timestamp;
- for(const template of messageData.templates) {
- spanMessage.appendChild(this.processTemplate(template));
- }
+ const spanMessage = document.createElement('span');
+ spanMessage.classList.add('message');
+ spanMessage.appendChild(this.processTemplate(messageData.templates))
liMessage.appendChild(spanTimestamp);
liMessage.appendChild(spanMessage);
@@ -149,27 +143,32 @@
}
}
- processTemplate(template) {
- const spanElement = document.createElement('span');
- switch (template.payload) {
- case 'text':
- this.processTextTemplate(template, spanElement);
- break;
- case 'url':
- this.processUrlTemplate(template, spanElement);
- break;
- case 'emote':
- this.processEmote(template, spanElement);
- break;
- case 'icon':
- this.processIcon(template, spanElement);
- break;
- case 'empty':
- // Do nothing
- break;
+ processTemplate(templates) {
+ const frag = document.createDocumentFragment();
+
+ for( const template of templates ) {
+ const spanElement = frag.createElement('span');
+ switch (template.payload) {
+ case 'text':
+ this.processTextTemplate(template, spanElement);
+ break;
+ case 'url':
+ this.processUrlTemplate(template, spanElement);
+ break;
+ case 'emote':
+ this.processEmote(template, spanElement);
+ break;
+ case 'icon':
+ this.processIcon(template, spanElement);
+ break;
+ case 'empty':
+ continue;
+ }
+
+ frag.appendChild(this.processTemplate(template));
}
- return spanElement;
+ return frag;
}
processTextTemplate(template, spanContent) {
@@ -178,27 +177,26 @@
}
processUrlTemplate(template, spanContent) {
- // TODO Sanitize href?
- let urlElement = document.createElement('a');
+ const urlElement = document.createElement('a');
urlElement.innerText = template.content;
- urlElement.href = template.content;
+ urlElement.href = encodeURI(template.content);
urlElement.target = '_blank'
this.processColor(template, spanContent);
}
+ // converts a RGBA uint number to components
processColor(template, spanContent) {
- let r = (template.color & 0xFF000000) >>> 24;
- let g = (template.color & 0xFF0000) >>> 16;
- let b = (template.color & 0xFF00) >>> 8;
- let a = (template.color & 0xFF) / 255.0;
+ const r = (template.color & 0xFF000000) >>> 24;
+ const g = (template.color & 0xFF0000) >>> 16;
+ const b = (template.color & 0xFF00) >>> 8;
+ const a = (template.color & 0xFF) / 255.0;
spanContent.style.color = `rgba(${r}, ${g}, ${b}, ${a})`;
}
processEmote(template, spanContent) {
- // TODO Sanitize url?
- let imgElement = document.createElement('img');
+ const imgElement = document.createElement('img');
imgElement.src = `/emote/${template.content}`;
spanContent.classList.add('emote-icon');