mirror of
https://github.com/hack-chat/main.git
synced 2024-03-22 13:20:33 +08:00
Impl updateMessage command
This commit is contained in:
parent
9982207983
commit
7c612871f4
104
client/client.js
104
client/client.js
|
@ -25,12 +25,12 @@ var markdownOptions = {
|
||||||
if (lang && hljs.getLanguage(lang)) {
|
if (lang && hljs.getLanguage(lang)) {
|
||||||
try {
|
try {
|
||||||
return hljs.highlight(lang, str).value;
|
return hljs.highlight(lang, str).value;
|
||||||
} catch (__) {}
|
} catch (__) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return hljs.highlightAuto(str).value;
|
return hljs.highlightAuto(str).value;
|
||||||
} catch (__) {}
|
} catch (__) { }
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
@ -76,11 +76,11 @@ md.renderer.rules.link_open = function (tokens, idx, options) {
|
||||||
return '<a rel="noreferrer" onclick="return verifyLink(this)" href="' + Remarkable.utils.escapeHtml(tokens[idx].href) + '"' + title + target + '>';
|
return '<a rel="noreferrer" onclick="return verifyLink(this)" href="' + Remarkable.utils.escapeHtml(tokens[idx].href) + '"' + title + target + '>';
|
||||||
};
|
};
|
||||||
|
|
||||||
md.renderer.rules.text = function(tokens, idx) {
|
md.renderer.rules.text = function (tokens, idx) {
|
||||||
tokens[idx].content = Remarkable.utils.escapeHtml(tokens[idx].content);
|
tokens[idx].content = Remarkable.utils.escapeHtml(tokens[idx].content);
|
||||||
|
|
||||||
if (tokens[idx].content.indexOf('?') !== -1) {
|
if (tokens[idx].content.indexOf('?') !== -1) {
|
||||||
tokens[idx].content = tokens[idx].content.replace(/(^|\s)(\?)\S+?(?=[,.!?:)]?\s|$)/gm, function(match) {
|
tokens[idx].content = tokens[idx].content.replace(/(^|\s)(\?)\S+?(?=[,.!?:)]?\s|$)/gm, function (match) {
|
||||||
var channelLink = Remarkable.utils.escapeHtml(Remarkable.utils.replaceEntities(match.trim()));
|
var channelLink = Remarkable.utils.escapeHtml(Remarkable.utils.replaceEntities(match.trim()));
|
||||||
var whiteSpace = '';
|
var whiteSpace = '';
|
||||||
if (match[0] !== '?') {
|
if (match[0] !== '?') {
|
||||||
|
@ -164,6 +164,34 @@ var myChannel = window.location.search.replace(/^\?/, '');
|
||||||
var lastSent = [""];
|
var lastSent = [""];
|
||||||
var lastSentPos = 0;
|
var lastSentPos = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores active messages
|
||||||
|
* These are messages that can be edited.
|
||||||
|
* @type {{ customId: string, userid: number, sent: number, text: string, elem: HTMLElement }[]}
|
||||||
|
*/
|
||||||
|
var activeMessages = [];
|
||||||
|
|
||||||
|
setInterval(function () {
|
||||||
|
var editTimeout = 6 * 60 * 1000;
|
||||||
|
var now = Date.now();
|
||||||
|
for (var i = 0; i < activeMessages.length; i++) {
|
||||||
|
if (now - activeMessages[i].sent > editTimeout) {
|
||||||
|
activeMessages.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 30 * 1000);
|
||||||
|
|
||||||
|
function addActiveMessage(customId, userid, text, elem) {
|
||||||
|
activeMessages.push({
|
||||||
|
customId,
|
||||||
|
userid,
|
||||||
|
sent: Date.now(),
|
||||||
|
text,
|
||||||
|
elem,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** Notification switch and local storage behavior **/
|
/** Notification switch and local storage behavior **/
|
||||||
var notifySwitch = document.getElementById("notify-switch")
|
var notifySwitch = document.getElementById("notify-switch")
|
||||||
var notifySetting = localStorageGet("notify-api")
|
var notifySetting = localStorageGet("notify-api")
|
||||||
|
@ -364,7 +392,59 @@ var COMMANDS = {
|
||||||
if (ignoredUsers.indexOf(args.nick) >= 0) {
|
if (ignoredUsers.indexOf(args.nick) >= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pushMessage(args);
|
|
||||||
|
var elem = pushMessage(args);
|
||||||
|
|
||||||
|
if (typeof (args.customId) === 'string') {
|
||||||
|
addActiveMessage(args.customId, args.userid, args.text, elem);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateMessage: function (args) {
|
||||||
|
var customId = args.customId;
|
||||||
|
var mode = args.mode;
|
||||||
|
|
||||||
|
if (!mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var message;
|
||||||
|
for (var i = 0; i < activeMessages.length; i++) {
|
||||||
|
var msg = activeMessages[i];
|
||||||
|
if (msg.userid === args.userid && msg.customId === customId) {
|
||||||
|
message = msg;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var textElem = message.elem.querySelector('.text');
|
||||||
|
if (!textElem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newText = message.text;
|
||||||
|
if (mode === 'overwrite') {
|
||||||
|
newText = args.text;
|
||||||
|
} else if (mode === 'append') {
|
||||||
|
newText += args.text;
|
||||||
|
} else if (mode === 'prepend') {
|
||||||
|
newText = args.text + newText;
|
||||||
|
}
|
||||||
|
|
||||||
|
message.text = newText;
|
||||||
|
|
||||||
|
// Scroll to bottom if necessary
|
||||||
|
var atBottom = isAtBottom();
|
||||||
|
|
||||||
|
textElem.innerHTML = md.render(newText);
|
||||||
|
|
||||||
|
if (atBottom) {
|
||||||
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
info: function (args) {
|
info: function (args) {
|
||||||
|
@ -518,6 +598,8 @@ function pushMessage(args) {
|
||||||
|
|
||||||
unread += 1;
|
unread += 1;
|
||||||
updateTitle();
|
updateTitle();
|
||||||
|
|
||||||
|
return messageEl;
|
||||||
}
|
}
|
||||||
|
|
||||||
function insertAtCursor(text) {
|
function insertAtCursor(text) {
|
||||||
|
@ -734,8 +816,8 @@ if (localStorageGet('joined-left') == 'false') {
|
||||||
|
|
||||||
if (localStorageGet('parse-latex') == 'false') {
|
if (localStorageGet('parse-latex') == 'false') {
|
||||||
$('#parse-latex').checked = false;
|
$('#parse-latex').checked = false;
|
||||||
md.inline.ruler.disable([ 'katex' ]);
|
md.inline.ruler.disable(['katex']);
|
||||||
md.block.ruler.disable([ 'katex' ]);
|
md.block.ruler.disable(['katex']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$('#pin-sidebar').onchange = function (e) {
|
$('#pin-sidebar').onchange = function (e) {
|
||||||
|
@ -750,11 +832,11 @@ $('#parse-latex').onchange = function (e) {
|
||||||
var enabled = !!e.target.checked;
|
var enabled = !!e.target.checked;
|
||||||
localStorageSet('parse-latex', enabled);
|
localStorageSet('parse-latex', enabled);
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
md.inline.ruler.enable([ 'katex' ]);
|
md.inline.ruler.enable(['katex']);
|
||||||
md.block.ruler.enable([ 'katex' ]);
|
md.block.ruler.enable(['katex']);
|
||||||
} else {
|
} else {
|
||||||
md.inline.ruler.disable([ 'katex' ]);
|
md.inline.ruler.disable(['katex']);
|
||||||
md.block.ruler.disable([ 'katex' ]);
|
md.block.ruler.disable(['katex']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,33 +6,64 @@
|
||||||
* @module chat
|
* @module chat
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { parseText } from '../utility/_Text.js';
|
||||||
import {
|
import {
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isModerator,
|
isModerator,
|
||||||
} from '../utility/_UAC.js';
|
} from '../utility/_UAC.js';
|
||||||
|
|
||||||
|
export const MAX_MESSAGE_ID_LENGTH = 6;
|
||||||
/**
|
/**
|
||||||
* Check and trim string provided by remote client
|
* The time in milliseconds before a message is considered stale, and thus no longer allowed
|
||||||
* @param {string} text - Subject string
|
* to be edited.
|
||||||
* @private
|
* @type {number}
|
||||||
* @todo Move into utility module
|
|
||||||
* @return {string|boolean}
|
|
||||||
*/
|
*/
|
||||||
const parseText = (text) => {
|
const ACTIVE_TIMEOUT = 5 * 60 * 1000;
|
||||||
// verifies user input is text
|
/**
|
||||||
if (typeof text !== 'string') {
|
* The time in milliseconds that a check for stale messages should be performed.
|
||||||
return false;
|
* @type {number}
|
||||||
|
*/
|
||||||
|
const TIMEOUT_CHECK_INTERVAL = 30 * 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores active messages that can be edited.
|
||||||
|
* @type {{ customId: string, userid: number, sent: number }[]}
|
||||||
|
*/
|
||||||
|
export const ACTIVE_MESSAGES = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up stale messages.
|
||||||
|
* @public
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
export function cleanActiveMessages() {
|
||||||
|
const now = Date.now();
|
||||||
|
for (let i = 0; i < ACTIVE_MESSAGES.length; i++) {
|
||||||
|
const message = ACTIVE_MESSAGES[i];
|
||||||
|
if (now - message.sent > ACTIVE_TIMEOUT) {
|
||||||
|
ACTIVE_MESSAGES.splice(i, 1);
|
||||||
|
i--;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let sanitizedText = text;
|
// TODO: This won't get cleared on module reload.
|
||||||
|
setInterval(cleanActiveMessages, TIMEOUT_CHECK_INTERVAL);
|
||||||
|
|
||||||
// strip newlines from beginning and end
|
/**
|
||||||
sanitizedText = sanitizedText.replace(/^\s*\n|^\s+$|\n\s*$/g, '');
|
* Adds a message to the active messages map.
|
||||||
// replace 3+ newlines with just 2 newlines
|
* @public
|
||||||
sanitizedText = sanitizedText.replace(/\n{3,}/g, '\n\n');
|
* @param {string} id
|
||||||
|
* @param {number} userid
|
||||||
return sanitizedText;
|
* @return {void}
|
||||||
};
|
*/
|
||||||
|
export function addActiveMessage(customId, userid) {
|
||||||
|
ACTIVE_MESSAGES.push({
|
||||||
|
customId,
|
||||||
|
userid,
|
||||||
|
sent: Date.now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes when invoked by a remote client
|
* Executes when invoked by a remote client
|
||||||
|
@ -61,6 +92,13 @@ export async function run({
|
||||||
}, socket);
|
}, socket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customId = payload.customId;
|
||||||
|
|
||||||
|
if (typeof (customId) === 'string' && customId.length > MAX_MESSAGE_ID_LENGTH) {
|
||||||
|
// There's a limit on the custom id length.
|
||||||
|
return server.police.frisk(socket.address, 13);
|
||||||
|
}
|
||||||
|
|
||||||
// build chat payload
|
// build chat payload
|
||||||
const outgoingPayload = {
|
const outgoingPayload = {
|
||||||
cmd: 'chat',
|
cmd: 'chat',
|
||||||
|
@ -70,6 +108,7 @@ export async function run({
|
||||||
channel: socket.channel,
|
channel: socket.channel,
|
||||||
text,
|
text,
|
||||||
level: socket.level,
|
level: socket.level,
|
||||||
|
customId,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isAdmin(socket.level)) {
|
if (isAdmin(socket.level)) {
|
||||||
|
@ -86,6 +125,7 @@ export async function run({
|
||||||
outgoingPayload.color = socket.color;
|
outgoingPayload.color = socket.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
addActiveMessage(outgoingPayload.customId, socket.userid);
|
||||||
// broadcast to channel peers
|
// broadcast to channel peers
|
||||||
server.broadcast(outgoingPayload, { channel: socket.channel });
|
server.broadcast(outgoingPayload, { channel: socket.channel });
|
||||||
|
|
||||||
|
|
97
commands/core/updateMessage.js
Normal file
97
commands/core/updateMessage.js
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
import { parseText } from "../utility/_Text.js";
|
||||||
|
import { isAdmin, isModerator } from "../utility/_UAC.js";
|
||||||
|
import { ACTIVE_MESSAGES, MAX_MESSAGE_ID_LENGTH } from "./chat.js";
|
||||||
|
|
||||||
|
export async function run({ core, server, socket, payload }) {
|
||||||
|
// undefined | "overwrite" | "append" | "prepend"
|
||||||
|
let mode = payload.mode;
|
||||||
|
|
||||||
|
if (!mode) {
|
||||||
|
mode = 'overwrite';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode !== 'overwrite' && mode !== 'append' && mode !== 'prepend') {
|
||||||
|
return server.police.frisk(socket.address, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
const customId = payload.customId;
|
||||||
|
|
||||||
|
if (!customId || typeof customId !== "string" || customId.length > MAX_MESSAGE_ID_LENGTH) {
|
||||||
|
return server.police.frisk(socket.address, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = payload.text;
|
||||||
|
|
||||||
|
if (typeof (text) !== 'string') {
|
||||||
|
return server.police.frisk(socket.address, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode === 'overwrite') {
|
||||||
|
text = parseText(text);
|
||||||
|
|
||||||
|
if (text === '') {
|
||||||
|
text = '\u0000';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!text) {
|
||||||
|
return server.police.frisk(socket.address, 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: What score should we use for this? It isn't as space filling as chat messages.
|
||||||
|
// But we also don't want a massive growing message.
|
||||||
|
// Or flashing between huge and small. Etc.
|
||||||
|
|
||||||
|
let message;
|
||||||
|
for (let i = 0; i < ACTIVE_MESSAGES.length; i++) {
|
||||||
|
const msg = ACTIVE_MESSAGES[i];
|
||||||
|
|
||||||
|
if (msg.userid === socket.userid && msg.customId === customId) {
|
||||||
|
message = ACTIVE_MESSAGES[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!message) {
|
||||||
|
return server.police.frisk(socket.address, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
const outgoingPayload = {
|
||||||
|
cmd: 'updateMessage',
|
||||||
|
userid: socket.userid,
|
||||||
|
channel: socket.channel,
|
||||||
|
level: socket.level,
|
||||||
|
mode,
|
||||||
|
text,
|
||||||
|
customId: message.customId,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isAdmin(socket.level)) {
|
||||||
|
outgoingPayload.admin = true;
|
||||||
|
} else if (isModerator(socket.level)) {
|
||||||
|
outgoingPayload.mod = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
server.broadcast(outgoingPayload, { channel: socket.channel });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const requiredData = ['text', 'customId'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module meta information
|
||||||
|
* @public
|
||||||
|
* @typedef {Object} updateMessage/info
|
||||||
|
* @property {string} name - Module command name
|
||||||
|
* @property {string} category - Module category name
|
||||||
|
* @property {string} description - Information about module
|
||||||
|
* @property {string} usage - Information about module usage
|
||||||
|
*/
|
||||||
|
export const info = {
|
||||||
|
name: 'updateMessage',
|
||||||
|
category: 'core',
|
||||||
|
description: 'Update a message you have sent.',
|
||||||
|
usage: `
|
||||||
|
API: { cmd: 'updateMessage', mode: 'overwrite'|'append'|'prepand', text: '<text to apply>',customId: '<customId sent with the chat message>' }`,
|
||||||
|
};
|
21
commands/utility/_Text.js
Normal file
21
commands/utility/_Text.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
/**
|
||||||
|
* Check and trim string provided by remote client
|
||||||
|
* @public
|
||||||
|
* @param {string} text - Subject string
|
||||||
|
* @return {string|null}
|
||||||
|
*/
|
||||||
|
export const parseText = (text) => {
|
||||||
|
// verifies user input is text
|
||||||
|
if (typeof text !== 'string') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sanitizedText = text;
|
||||||
|
|
||||||
|
// strip newlines from beginning and end
|
||||||
|
sanitizedText = sanitizedText.replace(/^\s*\n|^\s+$|\n\s*$/g, '');
|
||||||
|
// replace 3+ newlines with just 2 newlines
|
||||||
|
sanitizedText = sanitizedText.replace(/\n{3,}/g, '\n\n');
|
||||||
|
|
||||||
|
return sanitizedText;
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user