flash
cf71bab92d
This has been in the works for over a month and might break things because it's a very radical change. If it causes you to be unable to join chat, report it on the forum or try joining using the legacy chat on https://sockchat.flashii.net.
416 lines
17 KiB
JavaScript
416 lines
17 KiB
JavaScript
#include channels.js
|
|
#include common.js
|
|
#include parsing.js
|
|
#include title.js
|
|
#include txtrigs.js
|
|
#include url.js
|
|
#include users.js
|
|
#include utility.js
|
|
#include weeb.js
|
|
#include sound/umisound.js
|
|
#include ui/emotes.js
|
|
|
|
Umi.UI.Messages = (function() {
|
|
let forceUserInfo = false;
|
|
let lastMsgUser = null;
|
|
let lastMsgChannel = null;
|
|
let lastWasTiny = null;
|
|
|
|
const title = new MamiWindowTitle({
|
|
getName: () => futami.get('title'),
|
|
});
|
|
|
|
window.addEventListener('focus', () => title.clear());
|
|
|
|
const botMsgs = {
|
|
'say': { text: '%0' },
|
|
'generr': { text: 'Something unexpected happened.' },
|
|
'flwarn': { text: 'You are about to hit the flood limit! If you continue you will be kicked.' },
|
|
'unban': { text: '%0 is no longer banned.', sound: 'unban' },
|
|
'delerr': { text: 'You are not allowed to delete this message.' },
|
|
'notban': { text: '%0 is not banned.' },
|
|
'whoerr': { text: '%0 does not exist.' },
|
|
'join': { text: '%0 has joined.', action: 'has joined', sound: 'join' },
|
|
'leave': { text: '%0 has disconnected.', action: 'has disconnected', avatar: 'greyscale', sound: 'leave' },
|
|
'jchan': { text: '%0 has joined the channel.', action: 'has joined the channel', sound: 'join' },
|
|
'lchan': { text: '%0 has left the channel.', action: 'has left the channel', avatar: 'greyscale', sound: 'leave' },
|
|
'kick': { text: '%0 got bludgeoned to death.', action: 'got bludgeoned to death', avatar: 'invert', sound: 'kick' },
|
|
'flood': { text: '%0 got kicked for flood protection.', action: 'got kicked for flood protection', avatar: 'invert', sound: 'flood' },
|
|
'timeout': { text: '%0 exploded.', action: 'exploded', avatar: 'greyscale', sound: 'timeout' },
|
|
'nick': { text: '%0 changed their name to %1.', action: 'changed their name to %1' },
|
|
'crchan': { text: 'Channel %0 has been created.' },
|
|
'delchan': { text: 'Channel %0 has been deleted.' },
|
|
'cpwdchan': { text: 'Channel password has been changed.' },
|
|
'cprivchan': { text: 'Channel access level has been changed.' },
|
|
'ipaddr': { text: 'IP address of %0 is %1.' },
|
|
'cmdna': { text: 'You are not allowed to use %0.' },
|
|
'nocmd': { text: 'Command %0 does not exist.' },
|
|
'cmderr': { text: 'You did not use that command correctly.' },
|
|
'usernf': { text: '%0 is not logged in right now!' },
|
|
'kickna': { text: 'You are not allowed to kick %0.' },
|
|
'samechan': { text: 'You are already in channel %0.' },
|
|
'ipchan': { text: 'You are not allowed to join channel %0.' },
|
|
'nochan': { text: 'Channel %0 does not exist.' },
|
|
'nopwchan': { text: 'Channel %0 requires a password. Use /join %0 <password>' },
|
|
'ipwchan': { text: 'Wrong password for channel %0.' },
|
|
'inchan': { text: 'Channel name contains invalid characters.' },
|
|
'nischan': { text: 'A channel with the name %0 already exists.' },
|
|
'ndchan': { text: 'You are not allowed to deleted channel %0.' },
|
|
'namchan': { text: 'You are not allowed to edit channel %0.' },
|
|
'nameinuse': { text: 'Someone else is already using the name %0.' },
|
|
'rankerr': { text: 'You cannot set the access level of a channel higher than that of your own.' },
|
|
'banlist': {
|
|
text: 'Banned: %0',
|
|
filter: args => {
|
|
const bans = args[0].split(', ');
|
|
for(const i in bans)
|
|
bans[i] = bans[i].slice(92, -4);
|
|
|
|
args[0] = bans.join(', ');
|
|
return args;
|
|
},
|
|
},
|
|
'who': {
|
|
text: 'Online: %0',
|
|
filter: args => {
|
|
const users = args[0].split(', ');
|
|
for(const i in users) {
|
|
const isSelf = users[i].includes(' style="font-weight: bold;"');
|
|
users[i] = users[i].slice(isSelf ? 102 : 75, -4);
|
|
if(isSelf) users[i] += ' (You)';
|
|
}
|
|
|
|
args[0] = users.join(', ');
|
|
return args;
|
|
},
|
|
},
|
|
'whochan': {
|
|
text: 'Online in %0: %1',
|
|
filter: args => {
|
|
const users = args[1].split(', ');
|
|
for(const i in users) {
|
|
const isSelf = users[i].includes(' style="font-weight: bold;"');
|
|
users[i] = users[i].slice(isSelf ? 102 : 75, -4);
|
|
if(isSelf) users[i] += ' (You)';
|
|
}
|
|
|
|
args[1] = users.join(', ');
|
|
return args;
|
|
},
|
|
},
|
|
};
|
|
|
|
const formatTemplate = (template, args) => {
|
|
if(typeof template !== 'string')
|
|
template = '';
|
|
|
|
if(Array.isArray(args))
|
|
for(let i = 0; i < args.length; ++i) {
|
|
const arg = args[i] === undefined || args[i] === null ? '' : args[i].toString();
|
|
template = template.replace(new RegExp(`%${i}`, 'g'), arg);
|
|
}
|
|
|
|
return template;
|
|
};
|
|
|
|
return {
|
|
Add: function(msg) {
|
|
const currentChannel = Umi.Channels.Current();
|
|
const channelName = msg.getChannel();
|
|
const sender = msg.getUserV2();
|
|
const isBot = sender.id === '-1';
|
|
const isOutgoing = Umi.User.isCurrentUser(sender);
|
|
const hasSeen = msg.hasSeen();
|
|
const displayMessage = currentChannel === null || channelName === null || channelName === currentChannel.name;
|
|
const notifyPM = !displayMessage && !isOutgoing && !hasSeen && channelName.startsWith('@');
|
|
|
|
let isTiny = false,
|
|
skipTextParsing = false,
|
|
msgText = msg.getText(),
|
|
msgTextLong = msgText;
|
|
|
|
let eBase, eAvatar, eText, eMeta, eUser;
|
|
|
|
let avatarUser = sender,
|
|
avatarSize = '80';
|
|
|
|
let soundName = isOutgoing ? 'outgoing' : 'incoming',
|
|
soundVolume, soundRate, soundIsLegacy = true;
|
|
|
|
const userClass = `message--user-${sender.id}`;
|
|
|
|
const classes = ['message', userClass];
|
|
const styles = {};
|
|
|
|
const avatarClasses = ['message__avatar'];
|
|
|
|
const msgIsFirst = forceUserInfo || lastMsgUser !== sender.id || lastMsgChannel !== msg.getChannel();
|
|
if(msgIsFirst) {
|
|
forceUserInfo = false;
|
|
classes.push('message--first');
|
|
}
|
|
|
|
if(msg.isAction()) {
|
|
isTiny = true;
|
|
classes.push('message-action');
|
|
}
|
|
|
|
if(sender.id === "136")
|
|
styles.transform = 'scaleY(' + (0.76 + (0.01 * Math.max(0, Math.ceil(Date.now() / (7 * 24 * 60 * 60000)) - 2813))).toString() + ')';
|
|
|
|
const msgDateTimeObj = msg.getTime();
|
|
const msgDateTime = msgDateTimeObj.getHours().toString().padStart(2, '0')
|
|
+ ':' + msgDateTimeObj.getMinutes().toString().padStart(2, '0')
|
|
+ ':' + msgDateTimeObj.getSeconds().toString().padStart(2, '0');
|
|
|
|
if(isBot) {
|
|
const botInfo = msg.getBotInfo();
|
|
soundName = botInfo.isError ? 'error' : 'server';
|
|
|
|
if(botMsgs.hasOwnProperty(botInfo.type)) {
|
|
const bmInfo = botMsgs[botInfo.type];
|
|
|
|
let bArgs = botInfo.args;
|
|
if(typeof bmInfo.filter === 'function')
|
|
bArgs = bmInfo.filter(bArgs);
|
|
|
|
if(typeof bmInfo.sound === 'string')
|
|
soundName = bmInfo.sound;
|
|
|
|
let actionSuccess = false;
|
|
if(typeof bmInfo.action === 'string' && mami.settings.get('fancyInfo')) {
|
|
const target = botInfo.target ?? Umi.Users.FindExact(bArgs[0]) ?? Umi.Users.FindExact('~' + bArgs[0]); // shitty fix for server sending invalid data
|
|
|
|
if(target) {
|
|
actionSuccess = true;
|
|
isTiny = true;
|
|
skipTextParsing = true;
|
|
avatarUser = target;
|
|
|
|
$ari(classes, userClass);
|
|
|
|
msgText = formatTemplate(bmInfo.action, bArgs);
|
|
if(typeof bmInfo.avatar === 'string')
|
|
avatarClasses.push(`avatar-filter-${bmInfo.avatar}`);
|
|
}
|
|
}
|
|
|
|
msgTextLong = formatTemplate(bmInfo.text, bArgs);
|
|
|
|
if(!actionSuccess)
|
|
msgText = msgTextLong;
|
|
} else
|
|
msgText = msgTextLong = `!!! Received unsupported message type: ${botInfo.type} !!!`;
|
|
} else {
|
|
if(mami.settings.get('playJokeSounds'))
|
|
try {
|
|
const trigger = mami.textTriggers.getTrigger(msgText);
|
|
if(trigger.isSoundType()) {
|
|
soundIsLegacy = false;
|
|
soundName = trigger.getRandomSoundName();
|
|
soundVolume = trigger.getVolume();
|
|
soundRate = trigger.getRate();
|
|
}
|
|
} catch(ex) {}
|
|
}
|
|
|
|
let avatarUrl = futami.get('avatar');
|
|
if(typeof avatarUrl !== 'string' || avatarUrl.length < 1)
|
|
avatarUrl = undefined;
|
|
else
|
|
avatarUrl = avatarUrl.replace('{user:id}', avatarUser.id)
|
|
.replace('{resolution}', avatarSize)
|
|
.replace('{user:avatar_change}', avatarUser.avatarChangeTime);
|
|
|
|
if(displayMessage) {
|
|
if(isTiny) {
|
|
if(!msgIsFirst) // small messages must always be "first"
|
|
classes.push('message--first');
|
|
classes.push('message-tiny');
|
|
|
|
avatarSize = '40';
|
|
|
|
if(msgText.indexOf("'") !== 0 || (msgText.match(/\'/g).length % 2) === 0)
|
|
msgText = "\xA0" + msgText;
|
|
|
|
eBase = <div id={`message-${msg.getId()}`} class={classes} style={styles}>
|
|
{eAvatar = <div class={avatarClasses}/>}
|
|
<div class="message__container">
|
|
{eMeta = <div class="message__meta">
|
|
{eUser = <div class="message__user" style={{ color: avatarUser.colour }}>{avatarUser.name}</div>}
|
|
{eText = <div class="message-tiny-text"/>}
|
|
<div class="message__time">{msgDateTime}</div>
|
|
</div>}
|
|
</div>
|
|
</div>;
|
|
} else {
|
|
eBase = <div id={`message-${msg.getId()}`} class={classes} style={styles}>
|
|
{eAvatar = <div class={avatarClasses}/>}
|
|
<div class="message__container">
|
|
{eMeta = <div class="message__meta">
|
|
{eUser = <div class="message__user" style={{ color: avatarUser.colour }}>{avatarUser.name}</div>}
|
|
<div class="message__time">{msgDateTime}</div>
|
|
</div>}
|
|
{eText = <div class="message__text"/>}
|
|
</div>
|
|
</div>;
|
|
}
|
|
|
|
eText.innerText = msgText;
|
|
|
|
if(!skipTextParsing) {
|
|
eText = Umi.UI.Emoticons.Parse(eText, msg);
|
|
eText = Umi.Parsing.Parse(eText, msg);
|
|
|
|
const urls = [];
|
|
|
|
if(mami.settings.get('autoParseUrls')) {
|
|
const textSplit = eText.innerText.split(' ');
|
|
for(const textPart of textSplit) {
|
|
const uri = Umi.URI.Parse(textPart);
|
|
|
|
if(uri !== null && uri.Slashes !== null) {
|
|
urls.push(textPart);
|
|
|
|
const anchorElem = <a class="markup__link" href={textPart} target="_blank" rel="nofollow noreferrer noopener">{textPart}</a>;
|
|
eText.innerHTML = eText.innerHTML.replace(textPart.replace(/&/g, '&'), anchorElem.outerHTML);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(mami.settings.get('weeaboo')) {
|
|
eText.appendChild($t(Weeaboo.getTextSuffix(sender)));
|
|
|
|
const kaomoji = Weeaboo.getRandomKaomoji(true, msg);
|
|
if(kaomoji) {
|
|
eText.appendChild($t(' '));
|
|
eText.appendChild($t(kaomoji));
|
|
}
|
|
}
|
|
|
|
if(mami.settings.get('weeaboo'))
|
|
eUser.appendChild($t(Weeaboo.getNameSuffix(sender)));
|
|
}
|
|
|
|
if(isTiny !== lastWasTiny) {
|
|
if(!msgIsFirst)
|
|
eBase.classList.add('message--first');
|
|
eBase.classList.add(isTiny ? 'message-tiny-fix' : 'message-big-fix');
|
|
}
|
|
lastWasTiny = isTiny;
|
|
|
|
if(avatarUrl === undefined)
|
|
eAvatar.classList.add('message__avatar--disabled');
|
|
else
|
|
eAvatar.style.backgroundImage = `url(${avatarUrl})`;
|
|
|
|
const msgsList = $i('umi-messages');
|
|
|
|
msgsList.appendChild(eBase);
|
|
lastMsgUser = sender.id;
|
|
lastMsgChannel = msg.getChannel();
|
|
|
|
if(mami.settings.get('autoEmbedV1')) {
|
|
const callEmbedOn = eBase.querySelectorAll('a[onclick^="Umi.Parser.SockChatBBcode.Embed"]');
|
|
for(const embedElem of callEmbedOn)
|
|
if(embedElem.dataset.embed !== '1')
|
|
embedElem.click();
|
|
}
|
|
|
|
if(mami.settings.get('autoScroll'))
|
|
msgsList.scrollTop = msgsList.scrollHeight;
|
|
}
|
|
|
|
let isMentioned = false;
|
|
const mentionTriggers = (mami.settings.get('notificationTriggers') || '').toLowerCase().split(' ');
|
|
const currentUser = Umi.User.getCurrentUser();
|
|
if(typeof currentUser === 'object' && typeof currentUser.name === 'string')
|
|
mentionTriggers.push(currentUser.name.toLowerCase());
|
|
|
|
const mentionText = ` ${msgTextLong} `.toLowerCase();
|
|
for(const trigger of mentionTriggers) {
|
|
if(trigger.trim() === '')
|
|
continue;
|
|
|
|
if(mentionText.includes(` ${trigger} `)) {
|
|
isMentioned = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!isMentioned && mami.settings.get('onlySoundOnMention'))
|
|
soundName = undefined;
|
|
|
|
if(document.hidden) {
|
|
if(mami.settings.get('flashTitle')) {
|
|
let titleText = isBot && mami.settings.get('showServerMsgInTitle')
|
|
? ` ${msgTextLong}`
|
|
: ` ${sender.name}`;
|
|
|
|
// oops this won't work lol, we're filtering at the top
|
|
if(currentChannel !== null && currentChannel.name !== channelName)
|
|
titleText += ` @ ${channelName}`;
|
|
|
|
title.strobe([
|
|
`[ @] ${titleText}`,
|
|
`[@ ] ${titleText}`,
|
|
]);
|
|
}
|
|
|
|
if(!hasSeen) {
|
|
Umi.UI.Channels.Unread(channelName);
|
|
|
|
if(mami.settings.get('enableNotifications') && isMentioned) {
|
|
const options = {};
|
|
|
|
options.body = 'Click here to see what they said.';
|
|
if(mami.settings.get('notificationShowMessage'))
|
|
options.body += "\n" + msgTextLong;
|
|
|
|
if(avatarUrl !== undefined)
|
|
options.icon = avatarUrl;
|
|
|
|
const notif = new Notification(`${sender.name} mentioned you!`, options);
|
|
notif.addEventListener('click', () => {
|
|
window.focus();
|
|
});
|
|
document.addEventListener('visibilitychange', () => {
|
|
if(document.visibilityState === 'visible')
|
|
notif.close();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if(soundName !== undefined && !hasSeen) {
|
|
if(soundIsLegacy)
|
|
soundName = Umi.Sound.Convert(soundName);
|
|
|
|
mami.sound.library.play(soundName, soundVolume, soundRate);
|
|
}
|
|
|
|
if(eBase instanceof HTMLElement)
|
|
mami.globalEvents.dispatch('umi:ui:message_add', {
|
|
element: eBase,
|
|
message: msg,
|
|
});
|
|
|
|
msg.markSeen();
|
|
},
|
|
Remove: function(msg) {
|
|
forceUserInfo = true;
|
|
lastMsgUser = null;
|
|
lastMsgChannel = null;
|
|
lastWasTiny = null;
|
|
$ri(`message-${msg.getId()}`);
|
|
},
|
|
RemoveAll: function() {
|
|
forceUserInfo = true;
|
|
lastMsgUser = null;
|
|
lastMsgChannel = null;
|
|
lastWasTiny = null;
|
|
$i('umi-messages').innerHTML = '';
|
|
},
|
|
};
|
|
})();
|