diff --git a/src/mami.js/main.js b/src/mami.js/main.js index f98045f..b978a81 100644 --- a/src/mami.js/main.js +++ b/src/mami.js/main.js @@ -88,11 +88,7 @@ window.Umi = { UI: {} }; settings.define('autoScroll').default(true).create(); settings.define('closeTabConfirm').default(false).create(); settings.define('showChannelList').default(false).create(); - settings.define('fancyInfo').default(true).create(); settings.define('autoCloseUserContext').default(true).create(); - settings.define('enableParser').default(true).create(); - settings.define('enableEmoticons').default(true).create(); - settings.define('autoParseUrls').default(true).create(); settings.define('preventOverflow').default(false).create(); settings.define('expandTextBox').default(false).create(); settings.define('eepromAutoInsert').default(true).create(); @@ -113,7 +109,6 @@ window.Umi = { UI: {} }; settings.define('windowsLiveMessenger').default(false).create(); settings.define('seinfeld').default(false).create(); settings.define('flashTitle').default(true).create(); - settings.define('showServerMsgInTitle').default(true).create(); settings.define('onlyConnectWhenVisible').default(true).create(); settings.define('playJokeSounds').default(true).create(); settings.define('weeaboo').default(false).create(); diff --git a/src/mami.js/messages.js b/src/mami.js/messages.js index 392c924..d03cee8 100644 --- a/src/mami.js/messages.js +++ b/src/mami.js/messages.js @@ -1,40 +1,74 @@ -#include channels.js #include users.js -#include ui/messages.jsx -// messages should probably also be an "event" like how it is on the server +const MamiMessageAuthorInfo = function(self = false, user = null, id = null, name = null, colour = null, rank = null, avatar = null) { + if(typeof self !== 'boolean') + throw 'self must be a boolean'; -Umi.Message = (() => { - const chatBot = new MamiUserInfo('-1', 'Server'); + if(user === null) { + id ??= ''; + name ??= ''; + colour ??= 'inherit'; + rank ??= 0; + avatar ??= new MamiUserAvatarInfo(id); + } else { + if(typeof user !== 'object') + throw 'user must be an object or null'; - return function(msgId, time, user, text, channel, highlight, botInfo, isAction, isLog) { - msgId = (msgId || '').toString(); - time = time === null ? new Date() : (typeof time === 'object' ? time : new Date(parseInt(time || 0) * 1000)); - user = user !== null && typeof user === 'object' ? user : chatBot; - text = (text || '').toString(); - channel = (channel || '').toString(); - highlight = !!highlight; - isAction = !!isAction; - isLog = !!isLog; - hasSeen = isLog; + id ??= user.id; + name ??= user.name; + colour ??= user.colour; + rank ??= user.perms.rank; + avatar ??= user.avatar; + } - return { - getId: () => msgId, - getIdInt: () => { - const num = parseInt(msgId); - return isNaN(num) ? (Math.round(Number.MIN_SAFE_INTEGER * Math.random())) : num; - }, - getTime: () => time, - getUser: () => MamiConvertUserInfoToUmi(user), - getUserV2: () => user, - getText: () => text, - getChannel: () => channel, - shouldHighlight: () => highlight, - getBotInfo: () => botInfo, - isAction: () => isAction, - isLog: () => isLog, - hasSeen: () => hasSeen, - markSeen: () => hasSeen = true, - }; + if(typeof id !== 'string') + throw 'id must be a string'; + if(typeof name !== 'string') + throw 'name must be a string'; + if(typeof colour !== 'string') + throw 'colour must be a string'; + if(typeof rank !== 'number') + throw 'rank must be a number'; + if(typeof avatar !== 'object') + throw 'avatar must be an object'; + + return { + get self() { return self; }, + + get user() { return user; }, + get hasUser() { return user !== null; }, + + get id() { return id; }, + get name() { return name; }, + get colour() { return colour; }, + get rank() { return rank; }, + get avatar() { return avatar; }, }; -})(); +}; + +const MamiMessageInfo = function(type, created = null, detail = null, id = '', author = null, channel = '', silent = false) { + if(typeof type !== 'string') + throw 'type must be a string'; + if(created === null) + created = new Date; + else if(!(created instanceof Date)) + throw 'created must be an instance of window.Date or null'; + if(typeof id !== 'string') + throw 'id must be a string'; + if(typeof author !== 'object') + throw 'author must be an object'; + if(typeof channel !== 'string') + throw 'channel must be a string'; + if(typeof silent !== 'boolean') + throw 'silent must be a boolean'; + + return { + get type() { return type; }, + get created() { return created; }, + get detail() { return detail; }, + get id() { return id; }, + get author() { return author; }, + get channel() { return channel; }, + get silent() { return silent; }, + }; +}; diff --git a/src/mami.js/parsing.js b/src/mami.js/parsing.js index 2736c1d..18b694a 100644 --- a/src/mami.js/parsing.js +++ b/src/mami.js/parsing.js @@ -378,8 +378,6 @@ Umi.Parsing = (function() { } } } - - return element; }, }; })(); diff --git a/src/mami.js/rng.js b/src/mami.js/rng.js index c67c700..972d4c4 100644 --- a/src/mami.js/rng.js +++ b/src/mami.js/rng.js @@ -3,7 +3,7 @@ const MamiRNG = function(seed) { const MBIG = 0x7FFFFFFF; const MSEED = 161803398; - if((typeof seed).toLowerCase() !== 'number') + if(isNaN(seed)) seed = Math.round(Date.now() / 1000); const seedArray = new Int32Array(56); diff --git a/src/mami.js/sockchat/handlers.js b/src/mami.js/sockchat/handlers.js index 8e8ed3a..2c15b69 100644 --- a/src/mami.js/sockchat/handlers.js +++ b/src/mami.js/sockchat/handlers.js @@ -142,14 +142,13 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe Umi.Users.Add(userInfo); if(ev.detail.msg !== undefined) - Umi.UI.Messages.Add(new Umi.Message( - ev.detail.msg.id, ev.detail.msg.time, undefined, '', ev.detail.msg.channel, false, - { - isError: false, - type: ev.detail.msg.botInfo.type, - args: ev.detail.msg.botInfo.args, - target: userInfo, - } + Umi.UI.Messages.Add(new MamiMessageInfo( + 'user:join', + ev.detail.msg.time, + null, + ev.detail.msg.id, + new MamiMessageAuthorInfo(ev.detail.user.self, userInfo), + ev.detail.msg.channel )); }; handlers['user:remove'] = ev => { @@ -160,19 +159,13 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe return; if(ev.detail.msg !== undefined) - Umi.UI.Messages.Add(new Umi.Message( - ev.detail.msg.id, + Umi.UI.Messages.Add(new MamiMessageInfo( + 'user:leave', ev.detail.msg.time, - undefined, - '', - ev.detail.msg.channel, - false, - { - isError: false, - type: ev.detail.msg.botInfo.type, - args: ev.detail.msg.botInfo.args, - target: userInfo, - }, + { reason: ev.detail.leave.type }, + ev.detail.msg.id, + new MamiMessageAuthorInfo(ev.detail.user.self, userInfo), + ev.detail.msg.channel )); Umi.Users.Remove(userInfo); @@ -183,7 +176,7 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe const userInfo = Umi.Users.Get(ev.detail.user.id); userInfo.name = ev.detail.user.name; userInfo.colour = ev.detail.user.colour; - userInfo.avatarChangeTime = Date.now(); + userInfo.avatar = new MamiUserAvatarInfo(ev.detail.user.id); userInfo.status = new MamiUserStatusInfo(ev.detail.user.status.isAway, ev.detail.user.status.message); userInfo.perms = new MamiUserPermsInfo( ev.detail.user.perms.rank, ev.detail.user.perms.kick, @@ -250,14 +243,12 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe Umi.Users.Add(userInfo); if(ev.detail.msg !== undefined) - Umi.UI.Messages.Add(new Umi.Message( - ev.detail.msg.id, null, undefined, '', ev.detail.msg.channel, false, - { - isError: false, - type: ev.detail.msg.botInfo.type, - args: [ userInfo.name ], - target: userInfo, - }, + Umi.UI.Messages.Add(new MamiMessageInfo( + 'channel:join', + null, null, + ev.detail.msg.id, + new MamiMessageAuthorInfo(ev.detail.user.self, userInfo), + ev.detail.msg.channel )); }; handlers['chan:leave'] = ev => { @@ -271,14 +262,12 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe return; if(ev.detail.msg !== undefined) - Umi.UI.Messages.Add(new Umi.Message( - ev.detail.msg.id, null, undefined, '', ev.detail.msg.channel, false, - { - isError: false, - type: ev.detail.msg.botInfo.type, - args: [ userInfo.name ], - target: userInfo, - }, + Umi.UI.Messages.Add(new MamiMessageInfo( + 'channel:leave', + null, null, + ev.detail.msg.id, + new MamiMessageAuthorInfo(ev.detail.user.self, userInfo), + ev.detail.msg.channel )); Umi.Users.Remove(userInfo); @@ -289,8 +278,9 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe if(dumpEvents) console.log('msg:add', ev.detail); const senderInfo = ev.detail.msg.sender; + const rawUserInfo = Umi.Users.Get(senderInfo.id); const userInfo = senderInfo.name === undefined - ? Umi.Users.Get(senderInfo.id) + ? rawUserInfo : new MamiUserInfo( senderInfo.id, senderInfo.name, @@ -319,15 +309,71 @@ const MamiSockChatHandlers = function(ctx, client, setLoadingOverlay, sockChatRe Umi.UI.Menus.Attention('channels'); } - Umi.UI.Messages.Add(new Umi.Message( - ev.detail.msg.id, + let type, detail, author; + if(ev.detail.msg.isBot) { + const botInfo = ev.detail.msg.botInfo; + let authorMethod; + + if(botInfo.type === 'join') { + type = 'user:join'; + authorMethod = 'nameArg'; + } else if(['leave', 'kick', 'flood', 'timeout'].includes(botInfo.type)) { + type = 'user:leave'; + authorMethod = 'nameArg'; + detail = { reason: botInfo.type }; + } else if(botInfo.type === 'jchan') { + type = 'channel:join'; + authorMethod = 'nameArg'; + } else if(botInfo.type === 'lchan') { + type = 'channel:leave'; + authorMethod = 'nameArg'; + } + + if(authorMethod === 'nameArg') { + author = botInfo.args[0]; + authorMethod = 'name'; + } + + if(authorMethod === 'name') { + // the concat below should be done by the server, remove when fixed + const botUserInfo = Umi.Users.FindExact(author) ?? Umi.Users.FindExact(`~${author}`); + author = new MamiMessageAuthorInfo( + Umi.User.isCurrentUser(botUserInfo), + botUserInfo, + null, + author + ); + } + + if(typeof type !== 'string') { + type = `legacy:${botInfo.type}`; + detail = { + error: botInfo.isError, + args: botInfo.args, + }; + } + } else { + author = new MamiMessageAuthorInfo( + senderInfo.self, + rawUserInfo, + senderInfo.id ?? rawUserInfo.id, + senderInfo.name ?? rawUserInfo.name, + senderInfo.colour ?? rawUserInfo.colour, + senderInfo.perms?.rank ?? rawUserInfo.perms?.rank ?? 0, + new MamiUserAvatarInfo(senderInfo.id ?? rawUserInfo.id ?? '0'), + ); + + type = `message:${ev.detail.msg.flags.isAction ? 'action' : 'text'}`; + detail = { body: ev.detail.msg.text }; + } + + Umi.UI.Messages.Add(new MamiMessageInfo( + type, ev.detail.msg.time, - userInfo, - ev.detail.msg.text, - channelName, - false, - ev.detail.msg.botInfo, - ev.detail.msg.flags.isAction, + detail, + ev.detail.msg.id, + author, + ev.detail.msg.channel, ev.detail.msg.silent, )); }; diff --git a/src/mami.js/ui/emotes.js b/src/mami.js/ui/emotes.js index d095df1..1621fde 100644 --- a/src/mami.js/ui/emotes.js +++ b/src/mami.js/ui/emotes.js @@ -33,12 +33,9 @@ Umi.UI.Emoticons = (function() { }); }, Parse: function(element, message) { - if(!mami.settings.get('enableEmoticons')) - return element; - let inner = element.innerHTML; - MamiEmotes.forEach(message.getUserV2().perms.rank, function(emote) { + MamiEmotes.forEach(message?.author?.perms?.rank ?? 0, function(emote) { const image = $e({ tag: 'img', attrs: { @@ -56,8 +53,6 @@ Umi.UI.Emoticons = (function() { }); element.innerHTML = inner; - - return element; }, Insert: function(sender) { const emoticon = sender.getAttribute('data-umi-emoticon'); diff --git a/src/mami.js/ui/messages.jsx b/src/mami.js/ui/messages.jsx index fe3e35e..f292b81 100644 --- a/src/mami.js/ui/messages.jsx +++ b/src/mami.js/ui/messages.jsx @@ -122,103 +122,44 @@ Umi.UI.Messages = (function() { return { Add: function(msg) { - mami.globalEvents.dispatch('umi:message_add', msg); + const elementId = `message-${msg.id}`; - const msgId = msg.getId(); - const elementId = `message-${msgId}`; - - if(msgId !== '' && $i(elementId)) + if(msg.id !== '' && $i(elementId)) return; - 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 = focusChannelName === '' || channelName === '' || channelName === focusChannelName; - const notifyPM = !displayMessage && !isOutgoing && !hasSeen && channelName.startsWith('@'); - let isTiny = false; let skipTextParsing = false; - let msgText = msg.getText(); - let msgTextLong = msgText; - - let eBase; - let eAvatar; - let eText; - let eMeta; - let eUser; - - let avatarUser = sender; - let avatarSize = '80'; + let msgText = ''; + let msgTextLong = ''; + let msgAuthor = msg.author; let soundIsLegacy = true; - let soundName = isOutgoing ? 'outgoing' : 'incoming'; + let soundName; let soundVolume; let soundRate; - const userClass = `message--user-${sender.id}`; + const eText =
; + const eAvatar = ; + const eUser = ; + const eMeta = ; + const eTime = ; + const eContainer = ; + const eBase = ; - const classes = ['message', userClass]; - const styles = {}; + if(msg.type.startsWith('message:')) { + msgText = msgTextLong = msg.detail.body; + soundName = msg.author?.self === true ? 'outgoing' : 'incoming'; - const avatarClasses = ['message__avatar']; + if(msg.type === 'message:action') + isTiny = true; - if(msg.isAction()) { - isTiny = true; - classes.push('message-action'); - } - - if(!displayMessage) - classes.push('hidden'); - - 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 msgCreated = msg.getTime(); - const msgDateTime = msgCreated.getHours().toString().padStart(2, '0') - + ':' + msgCreated.getMinutes().toString().padStart(2, '0') - + ':' + msgCreated.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); @@ -229,103 +170,135 @@ Umi.UI.Messages = (function() { soundRate = trigger.getRate(); } } catch(ex) {} + } else { + let bIsError = false; + let bType; + let bArgs; + + if(msg.type === 'user:join') { + bType = 'join'; + bArgs = [msgAuthor.name]; + } else if(msg.type === 'user:leave') { + bType = msg.detail.reason; + bArgs = [msgAuthor.name]; + } else if(msg.type === 'channel:join') { + bType = 'jchan'; + bArgs = [msgAuthor.name]; + } else if(msg.type === 'channel:leave') { + bType = 'lchan'; + bArgs = [msgAuthor.name]; + } else if(msg.type.startsWith('legacy:')) { + bType = msg.type.substring(7); + bIsError = msg.detail.error; + bArgs = msg.detail.args; + msgAuthor = Umi.Users.FindExact(bArgs[0]) ?? Umi.Users.FindExact('~' + bArgs[0]); + } + + soundName = bIsError ? 'error' : 'server'; + + if(botMsgs.hasOwnProperty(bType)) { + const bmInfo = botMsgs[bType]; + + 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') + if(msgAuthor) { + actionSuccess = true; + isTiny = true; + skipTextParsing = true; + + msgText = formatTemplate(bmInfo.action, bArgs); + if(typeof bmInfo.avatar === 'string') + eAvatar.classList.add(`avatar-filter-${bmInfo.avatar}`); + } + + msgTextLong = formatTemplate(bmInfo.text, bArgs); + + if(!actionSuccess) + msgText = msgTextLong; + } else + msgText = msgTextLong = `!!! Received unsupported message type: ${msg.type} !!!`; } - 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); - - eAvatar =