2022-11-13 14:13:32 -08:00
/ *
* Vencord , a modification for Discord ' s desktop app
* Copyright ( c ) 2022 Vendicated and contributors
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < https : //www.gnu.org/licenses/>.
* /
2022-12-25 20:47:35 +01:00
import "./messageLogger.css" ;
2024-06-07 23:28:17 +02:00
import { findGroupChildrenByChildId , NavContextMenuPatchCallback } from "@api/ContextMenu" ;
import { updateMessage } from "@api/MessageUpdater" ;
2026-05-04 11:40:05 +02:00
import { definePluginSettings } from "@api/Settings" ;
2023-02-09 19:36:30 +01:00
import { disableStyle , enableStyle } from "@api/Styles" ;
2022-11-28 13:37:55 +01:00
import ErrorBoundary from "@components/ErrorBoundary" ;
2026-08-18 01:37:09 +02:00
import { DeleteIcon , EyeIcon } from "@components/Icons" ;
2025-04-16 11:57:49 -07:00
import { Devs , SUPPORT_CATEGORY_ID , VENBOT_USER_ID } from "@utils/constants" ;
2024-11-05 16:49:27 -03:00
import { getIntlMessage } from "@utils/discord" ;
2023-05-06 01:36:00 +02:00
import { Logger } from "@utils/Logger" ;
2024-07-15 04:36:42 +02:00
import { classes } from "@utils/misc" ;
2022-11-28 13:37:55 +01:00
import definePlugin , { OptionType } from "@utils/types" ;
2026-07-14 17:44:46 -04:00
import { Message , MessageAttachment } from "@vencord/discord-types" ;
2026-01-23 19:59:53 -05:00
import { findCssClassesLazy } from "@webpack" ;
2026-07-15 09:37:22 +02:00
import { AuthenticationStore , ChannelStore , FluxDispatcher , Menu , MessageStore , Parser , SelectedChannelStore , Timestamp , UserStore , useStateFromStores } from "@webpack/common" ;
2022-11-13 14:13:32 -08:00
2023-02-09 19:36:30 +01:00
import overlayStyle from "./deleteStyleOverlay.css?managed" ;
import textStyle from "./deleteStyleText.css?managed" ;
2024-07-15 04:36:42 +02:00
import { openHistoryModal } from "./HistoryModal" ;
2023-02-09 19:36:30 +01:00
2024-06-07 23:28:17 +02:00
interface MLMessage extends Message {
deleted? : boolean ;
editHistory ? : { timestamp : Date ; content : string ; } [ ] ;
2024-07-15 04:36:42 +02:00
firstEditTimestamp? : Date ;
2024-06-07 23:28:17 +02:00
}
2026-07-14 17:44:46 -04:00
interface MLAttachment extends MessageAttachment {
/ * *
* if the attachment was deleted
*
* a non - deleted { @link MLMessage | Message } can have deleted attachments
* /
deleted? : boolean ;
}
2026-01-23 19:59:53 -05:00
const MessageClasses = findCssClassesLazy ( "edited" , "communicationDisabled" , "isSystemMessage" ) ;
2023-03-18 04:37:55 +01:00
2026-05-04 11:40:05 +02:00
const settings = definePluginSettings ( {
deleteStyle : {
type : OptionType . SELECT ,
description : "The style of deleted messages" ,
default : "text" ,
options : [
{ label : "Red text" , value : "text" , default : true } ,
{ label : "Red overlay" , value : "overlay" }
] ,
onChange : ( ) = > addDeleteStyle ( )
} ,
logDeletes : {
type : OptionType . BOOLEAN ,
description : "Whether to log deleted messages" ,
default : true ,
} ,
collapseDeleted : {
type : OptionType . BOOLEAN ,
description : "Whether to collapse deleted messages, similar to blocked messages" ,
default : false ,
restartNeeded : true ,
} ,
logEdits : {
type : OptionType . BOOLEAN ,
description : "Whether to log edited messages" ,
default : true ,
} ,
inlineEdits : {
type : OptionType . BOOLEAN ,
description : "Whether to display edit history as part of message content" ,
default : true
} ,
ignoreBots : {
type : OptionType . BOOLEAN ,
description : "Whether to ignore messages by bots" ,
default : false
} ,
ignoreSelf : {
type : OptionType . BOOLEAN ,
description : "Whether to ignore messages by yourself" ,
default : false
} ,
ignoreUsers : {
type : OptionType . STRING ,
description : "Comma-separated list of user IDs to ignore" ,
default : "" ,
multiline : true
} ,
ignoreChannels : {
type : OptionType . STRING ,
description : "Comma-separated list of channel IDs to ignore" ,
default : "" ,
multiline : true
} ,
ignoreGuilds : {
type : OptionType . STRING ,
description : "Comma-separated list of guild IDs to ignore" ,
default : "" ,
multiline : true
} ,
} ) ;
2023-02-09 19:36:30 +01:00
function addDeleteStyle() {
2026-05-04 11:40:05 +02:00
if ( settings . store . deleteStyle === "text" ) {
2023-02-09 19:36:30 +01:00
enableStyle ( textStyle ) ;
disableStyle ( overlayStyle ) ;
2022-11-13 14:13:32 -08:00
} else {
2023-02-09 19:36:30 +01:00
disableStyle ( textStyle ) ;
enableStyle ( overlayStyle ) ;
2022-11-13 14:13:32 -08:00
}
}
2023-04-17 01:27:25 +02:00
const REMOVE_HISTORY_ID = "ml-remove-history" ;
const TOGGLE_DELETE_STYLE_ID = "ml-toggle-style" ;
2026-08-06 08:51:51 -04:00
/ * *
* Clears a message ' s history ( edit + attachments )
*
* if the message was deleted , it will be completely removed from the UI and MessageStore
* /
function clearMessageHistory ( msg : MLMessage ) {
if ( msg . deleted ) {
FluxDispatcher . dispatch ( {
type : "MESSAGE_DELETE" ,
channelId : msg.channel_id ,
2026-08-09 02:59:18 +02:00
id : msg.id ,
2026-08-06 08:51:51 -04:00
mlDeleted : true
} ) ;
} else {
const attachments = msg . attachments ? . filter ( ( a : MLAttachment ) = > ! a . deleted ) ;
updateMessage ( msg . channel_id , msg . id , { editHistory : [ ] , attachments } ) ;
}
}
/ * *
* checks if a message has any history ( deleted or edited )
* @param message the message to check
*
* @returns true if the message has any history , false otherwise
* /
function doesMessageHaveHistory ( message : MLMessage ) : boolean {
return message . deleted || ! ! message . editHistory ? . length || message . attachments ? . some ( ( a : MLAttachment ) = > a . deleted ) ;
}
2024-03-07 11:06:24 +01:00
const patchMessageContextMenu : NavContextMenuPatchCallback = ( children , props ) = > {
2023-03-25 01:55:40 +01:00
const { message } = props ;
2026-08-06 08:51:51 -04:00
const { deleted , id , channel_id } = message ;
2023-03-25 01:55:40 +01:00
2026-08-06 08:51:51 -04:00
if ( ! doesMessageHaveHistory ( message ) ) return ;
2023-03-25 01:55:40 +01:00
2023-04-28 04:23:42 +02:00
toggle : {
if ( ! deleted ) break toggle ;
const domElement = document . getElementById ( ` chat-messages- ${ channel_id } - ${ id } ` ) ;
if ( ! domElement ) break toggle ;
children . push ( (
< Menu.MenuItem
id = { TOGGLE_DELETE_STYLE_ID }
key = { TOGGLE_DELETE_STYLE_ID }
label = "Toggle Deleted Highlight"
2026-08-18 01:37:09 +02:00
leadingAccessory = { { type : "icon" , icon : EyeIcon } }
2023-04-28 04:23:42 +02:00
action = { ( ) = > domElement . classList . toggle ( "messagelogger-deleted" ) }
/ >
) ) ;
}
2023-03-25 01:55:40 +01:00
children . push ( (
< Menu.MenuItem
2023-04-17 01:27:25 +02:00
id = { REMOVE_HISTORY_ID }
key = { REMOVE_HISTORY_ID }
2023-03-25 01:55:40 +01:00
label = "Remove Message History"
2026-08-18 01:37:09 +02:00
leadingAccessory = { { type : "icon" , icon : DeleteIcon } }
2023-04-28 04:23:42 +02:00
color = "danger"
2023-03-25 01:55:40 +01:00
action = { ( ) = > {
2026-08-06 08:51:51 -04:00
clearMessageHistory ( message ) ;
2023-03-25 01:55:40 +01:00
} }
/ >
) ) ;
} ;
2024-06-07 23:28:17 +02:00
const patchChannelContextMenu : NavContextMenuPatchCallback = ( children , { channel } ) = > {
2026-08-09 02:59:18 +02:00
const messages = MessageStore . getMessages ( channel ? . id ) ;
2026-08-06 08:51:51 -04:00
if ( ! messages ? . some ( msg = > doesMessageHaveHistory ( msg ) ) ) return ;
2024-06-07 23:28:17 +02:00
const group = findGroupChildrenByChildId ( "mark-channel-read" , children ) ? ? children ;
group . push (
< Menu.MenuItem
id = "vc-ml-clear-channel"
label = "Clear Message Log"
color = "danger"
action = { ( ) = > {
messages . forEach ( msg = > {
2026-08-06 08:51:51 -04:00
clearMessageHistory ( msg ) ;
2024-06-07 23:28:17 +02:00
} ) ;
} }
/ >
) ;
} ;
2024-07-15 04:36:42 +02:00
export function parseEditContent ( content : string , message : Message ) {
return Parser . parse ( content , true , {
channelId : message.channel_id ,
messageId : message.id ,
allowLinks : true ,
allowHeading : true ,
allowList : true ,
allowEmojiLinks : true ,
viewingChannelId : SelectedChannelStore.getChannelId ( ) ,
} ) ;
}
2022-11-13 14:13:32 -08:00
export default definePlugin ( {
name : "MessageLogger" ,
description : "Temporarily logs deleted and edited messages." ,
2026-04-16 03:42:59 +02:00
tags : [ "Chat" , "Utility" ] ,
2026-07-14 17:44:46 -04:00
authors : [ Devs . rushii , Devs . Ven , Devs . AutumnVN , Devs . Nickyux , Devs . Kyuuhachi , Devs . sadan ] ,
2024-06-07 23:28:17 +02:00
dependencies : [ "MessageUpdaterAPI" ] ,
2026-05-04 11:40:05 +02:00
settings ,
2024-03-07 11:06:24 +01:00
contextMenus : {
2024-06-07 23:28:17 +02:00
"message" : patchMessageContextMenu ,
"channel-context" : patchChannelContextMenu ,
2024-08-02 00:12:44 +12:00
"thread-context" : patchChannelContextMenu ,
2024-06-07 23:28:17 +02:00
"user-context" : patchChannelContextMenu ,
"gdm-context" : patchChannelContextMenu
2022-11-13 14:13:32 -08:00
} ,
2024-03-07 11:06:24 +01:00
start() {
addDeleteStyle ( ) ;
2022-11-13 14:13:32 -08:00
} ,
2024-06-07 23:28:17 +02:00
renderEdits : ErrorBoundary.wrap ( ( { message : { id : messageId , channel_id : channelId } } : { message : Message ; } ) = > {
const message = useStateFromStores (
[ MessageStore ] ,
( ) = > MessageStore . getMessage ( channelId , messageId ) as MLMessage ,
null ,
2024-06-08 04:15:29 +02:00
( oldMsg , newMsg ) = > oldMsg ? . editHistory === newMsg ? . editHistory
2024-06-07 23:28:17 +02:00
) ;
2026-05-04 11:40:05 +02:00
return settings . store . inlineEdits && (
2024-06-07 23:28:17 +02:00
< >
2025-01-04 07:24:50 +01:00
{ message . editHistory ? . map ( ( edit , idx ) = > (
< div key = { idx } className = "messagelogger-edited" >
2024-07-15 04:36:42 +02:00
{ parseEditContent ( edit . content , message ) }
2024-06-07 23:28:17 +02:00
< Timestamp
timestamp = { edit . timestamp }
isEdited = { true }
isInline = { false }
>
2026-01-23 19:59:53 -05:00
< span className = { MessageClasses . edited } > { " " } ( { getIntlMessage ( "MESSAGE_EDITED" ) } ) < / span >
2024-06-07 23:28:17 +02:00
< / Timestamp >
< / div >
) ) }
< / >
2022-11-13 14:13:32 -08:00
) ;
2024-06-07 23:28:17 +02:00
} , { noop : true } ) ,
2022-11-13 14:13:32 -08:00
makeEdit ( newMessage : any , oldMessage : any ) : any {
return {
2024-02-14 15:07:51 -03:00
timestamp : new Date ( newMessage . edited_timestamp ) ,
2022-11-13 14:13:32 -08:00
content : oldMessage.content
} ;
} ,
2026-07-14 17:44:46 -04:00
handleUpdateAttachments ( newMessage : MLMessage ) : MLAttachment [ ] {
const oldMessage = MessageStore . getMessage ( newMessage . channel_id , newMessage . id ) as MLMessage | undefined ;
// if oldMessage is undefined, this is a new message and we shouldn't touch the attachments
if ( ! oldMessage || this . shouldIgnore ( newMessage , true ) ) {
return newMessage . attachments ;
}
// not sure if it's ever actually null after an edit but discord does a null check here
if ( ! newMessage . attachments ? . length ) {
return oldMessage . attachments . map ( ( a ) : MLAttachment = > ( { . . . a , deleted : true } ) ) ;
}
2026-07-21 08:49:19 +08:00
return oldMessage . attachments
. map ( ( oldAttachment ) : MLAttachment = >
newMessage . attachments . find ( a = > a . id === oldAttachment . id )
? ? { . . . oldAttachment , deleted : true }
)
. concat ( newMessage . attachments . filter ( a = > ! oldMessage . attachments . some ( o = > o . id === a . id ) ) ) ;
2026-07-14 17:44:46 -04:00
} ,
2023-03-25 01:55:40 +01:00
handleDelete ( cache : any , data : { ids : string [ ] , id : string ; mlDeleted? : boolean ; } , isBulk : boolean ) {
2022-11-14 16:22:50 +01:00
try {
if ( cache == null || ( ! isBulk && ! cache . has ( data . id ) ) ) return cache ;
2023-09-06 00:39:22 +07:00
const mutate = ( id : string ) = > {
2022-11-14 16:22:50 +01:00
const msg = cache . get ( id ) ;
if ( ! msg ) return ;
const EPHEMERAL = 64 ;
2023-03-25 01:55:40 +01:00
const shouldIgnore = data . mlDeleted ||
( msg . flags & EPHEMERAL ) === EPHEMERAL ||
2023-09-06 00:39:22 +07:00
this . shouldIgnore ( msg ) ;
2022-11-14 16:22:50 +01:00
if ( shouldIgnore ) {
cache = cache . remove ( id ) ;
} else {
cache = cache . update ( id , m = > m
. set ( "deleted" , true )
. set ( "attachments" , m . attachments . map ( a = > ( a . deleted = true , a ) ) ) ) ;
}
2023-09-06 00:39:22 +07:00
} ;
2022-11-14 16:22:50 +01:00
if ( isBulk ) {
data . ids . forEach ( mutate ) ;
} else {
mutate ( data . id ) ;
}
} catch ( e ) {
new Logger ( "MessageLogger" ) . error ( "Error during handleDelete" , e ) ;
2022-11-13 14:13:32 -08:00
}
2022-11-14 16:22:50 +01:00
return cache ;
2022-11-13 14:13:32 -08:00
} ,
2024-03-16 03:27:59 +01:00
shouldIgnore ( message : any , isEdit = false ) {
2025-10-11 01:42:14 +02:00
try {
2026-05-04 11:40:05 +02:00
const { ignoreBots , ignoreSelf , ignoreUsers , ignoreChannels , ignoreGuilds , logEdits , logDeletes } = settings . store ;
2025-10-11 01:42:14 +02:00
const myId = UserStore . getCurrentUser ( ) . id ;
2023-09-06 00:39:22 +07:00
2025-10-11 01:42:14 +02:00
return ignoreBots && message . author ? . bot ||
ignoreSelf && message . author ? . id === myId ||
ignoreUsers . includes ( message . author ? . id ) ||
ignoreChannels . includes ( message . channel_id ) ||
ignoreChannels . includes ( ChannelStore . getChannel ( message . channel_id ) ? . parent_id ) ||
( isEdit ? ! logEdits : ! logDeletes ) ||
ignoreGuilds . includes ( ChannelStore . getChannel ( message . channel_id ) ? . guild_id ) ||
// Ignore Venbot in the support channels
( message . author ? . id === VENBOT_USER_ID && ChannelStore . getChannel ( message . channel_id ) ? . parent_id === SUPPORT_CATEGORY_ID ) ;
} catch ( e ) {
return false ;
}
2023-09-06 00:39:22 +07:00
} ,
2026-07-15 09:37:22 +02:00
// It is possible to replace a message in place by creating a new message with the same nonce as an existing one.
// This is not considered an edit since it's a new message. Thus it bypasses our edit logging and can be used to "delete" a message by replacing it with an empty one.
// This fixes that bypass
normalizeNonce ( msg : Message ) {
try {
if ( ! msg . nonce || msg . author . id === AuthenticationStore . getId ( ) ) return ;
const prevMsg = MessageStore . getMessage ( msg . channel_id , msg . nonce ) ;
if ( ! prevMsg || prevMsg . state !== "SENT" ) return ;
if ( prevMsg . id !== msg . id ) {
delete msg . nonce ;
}
} catch ( e ) {
console . error ( "[MessageLogger] Error normalizing nonce" ) ;
}
} ,
2024-07-15 04:36:42 +02:00
EditMarker ( { message , className , children , . . . props } : any ) {
return (
< span
{ . . . props }
className = { classes ( "messagelogger-edit-marker" , className ) }
onClick = { ( ) = > openHistoryModal ( message ) }
2025-01-04 07:24:50 +01:00
role = "button"
2024-07-15 04:36:42 +02:00
>
{ children }
< / span >
) ;
} ,
2024-11-07 15:07:00 -03:00
// DELETED_MESSAGE_COUNT: getMessage("{count, plural, =0 {No deleted messages} one {{count} deleted message} other {{count} deleted messages}}")
// TODO: Find a better way to generate intl messages
DELETED_MESSAGE_COUNT : ( ) = > ( {
ast : [ [
6 ,
"count" ,
{
"=0" : [ "No deleted messages" ] ,
one : [
[
1 ,
"count"
2024-11-05 17:01:52 -05:00
] ,
2024-11-07 15:07:00 -03:00
" deleted message"
] ,
other : [
[
1 ,
"count"
] ,
" deleted messages"
]
} ,
0 ,
"cardinal"
] ]
} ) ,
2024-07-15 04:36:42 +02:00
2022-11-13 14:13:32 -08:00
patches : [
{
2024-03-28 03:47:00 +01:00
find : '"MessageStore"' ,
2022-11-13 14:13:32 -08:00
replacement : [
{
// Add deleted=true to all target messages in the MESSAGE_DELETE event
2026-04-23 04:27:58 +02:00
match : /(?<=MESSAGE_DELETE:function\((\i)\)\{)(?=let.{0,100}(\i\.\i)\.getOrCreate)/ ,
replace : `
let cache = $2 . getOrCreate ( $1 . channelId ) ;
cache = $self . handleDelete ( cache , $1 , false ) ;
$2 . commit ( cache ) ;
return ;
`
} ,
2022-11-13 14:13:32 -08:00
{
// Add deleted=true to all target messages in the MESSAGE_DELETE_BULK event
2026-04-23 04:27:58 +02:00
match : /(?<=MESSAGE_DELETE_BULK:function\((\i)\){)(?=let.{0,100}(\i\.\i)\.getOrCreate)/ ,
replace : `
let cache = $2 . getOrCreate ( $1 . channelId ) ;
cache = $self . handleDelete ( cache , $1 , true ) ;
$2 . commit ( cache ) ;
return ;
`
} ,
2022-11-13 14:13:32 -08:00
{
// Add current cached content + new edit time to cached message's editHistory
2026-04-23 04:27:58 +02:00
match : /(MESSAGE_UPDATE:function\((\i)\).+?)\.update\((\i)/ ,
replace : `
$1
. update ( $3 , m = >
( ( $2 . message . flags & 64 ) === 64 || $self . shouldIgnore ( $2 . message , true ) ) ? m :
$2 . message . edited_timestamp && $2 . message . content !== m . content ?
m . set ( 'editHistory' , [ . . . ( m . editHistory || [ ] ) , $self . makeEdit ( $2 . message , m ) ] ) :
m
)
. update ( $3
`
} ,
2023-04-02 22:14:58 +02:00
{
// fix up key (edit last message) attempting to edit a deleted message
2023-10-25 17:46:51 -04:00
match : /(?<=getLastEditableMessage\(\i\)\{.{0,200}\.find\((\i)=>)/ ,
replace : "!$1.deleted &&"
2022-11-13 14:13:32 -08:00
}
]
} ,
{
// Message domain model
2023-10-25 17:46:51 -04:00
find : "}addReaction(" ,
2022-11-13 14:13:32 -08:00
replacement : [
{
2023-10-25 17:46:51 -04:00
match : /this\.customRenderedContent=(\i)\.customRenderedContent,/ ,
replace : "this.customRenderedContent = $1.customRenderedContent," +
"this.deleted = $1.deleted || false," +
2024-07-15 04:36:42 +02:00
"this.editHistory = $1.editHistory || []," +
"this.firstEditTimestamp = $1.firstEditTimestamp || this.editedTimestamp || this.timestamp,"
2022-11-13 14:13:32 -08:00
}
]
} ,
{
2026-07-14 17:44:46 -04:00
// Updated message transformer
2026-02-05 16:50:16 -05:00
find : ".PREMIUM_REFERRAL&&(" ,
2022-11-13 14:13:32 -08:00
replacement : [
{
2026-07-14 17:44:46 -04:00
// Pass through editHistory & deleted to the "edited message" transformer
2024-05-23 03:25:02 +02:00
match : /(?<=null!=\i\.edited_timestamp\)return )\i\(\i,\{reactions:(\i)\.reactions.{0,50}\}\)/ ,
2022-11-13 14:13:32 -08:00
replace :
2024-07-15 04:36:42 +02:00
"Object.assign($&,{ deleted:$1.deleted, editHistory:$1.editHistory, firstEditTimestamp:$1.firstEditTimestamp })"
2022-11-13 14:13:32 -08:00
} ,
2026-07-14 17:44:46 -04:00
// just mark deleted attachments as deleted on MESSAGE_UPDATE
2022-11-13 14:13:32 -08:00
{
2026-07-14 17:44:46 -04:00
match : /attachments:(\i)\.attachments\?\?\[\],/ ,
replace : "attachments: $self.handleUpdateAttachments($1),"
2022-11-13 14:13:32 -08:00
}
]
} ,
{
// Attachment renderer
2026-01-23 19:59:53 -05:00
find : "#{intl::REMOVE_ATTACHMENT_TOOLTIP_TEXT}" ,
2022-11-13 14:13:32 -08:00
replacement : [
2026-07-14 17:44:46 -04:00
// add deleted class to deleted attachments
{
// we can't use arguments[0] because we patch a nested **non-arrow** function
match : /\.SPOILER,(?=\[\i\.\i\]:)(?<=item:(\i),.{0,200}?)/ ,
replace : '$&"messagelogger-deleted-attachment": $1?.originalItem?.deleted,'
} ,
// dont allow deleting attachments from deleted messages
2022-11-13 14:13:32 -08:00
{
2026-07-14 17:44:46 -04:00
match : /(?<=\{let\{[^}]*?item:(\i),autoPlayGif:\i,)canRemoveItem:(\i)(?=,onRemoveItem:)/ ,
replace : "_canRemoveItem:$2 = arguments[0].canRemoveItem && !$1?.originalItem?.deleted" ,
2022-11-13 14:13:32 -08:00
}
]
} ,
{
// Base message component renderer
find : "Message must not be a thread starter message" ,
replacement : [
{
2023-01-30 05:02:17 +01:00
// Append messagelogger-deleted to classNames if deleted
2022-11-13 14:13:32 -08:00
match : /\)\("li",\{(.+?),className:/ ,
2023-03-25 01:55:40 +01:00
replace : ")(\"li\",{$1,className:(arguments[0].message.deleted ? \"messagelogger-deleted \" : \"\")+"
2022-11-13 14:13:32 -08:00
}
]
} ,
2025-05-13 20:59:13 -03:00
{
2025-05-15 19:54:09 -03:00
// Message content renderer
2025-05-13 20:59:13 -03:00
find : ".SEND_FAILED," ,
replacement : {
2025-05-15 19:54:09 -03:00
// Render editHistory behind the message content
2026-01-23 19:59:53 -05:00
match : /\]:\i.isUnsupported.{0,20}?,children:\[/ ,
2025-05-15 19:54:09 -03:00
replace : "$&arguments[0]?.message?.editHistory?.length>0&&$self.renderEdits(arguments[0]),"
2025-05-13 20:59:13 -03:00
}
} ,
2022-11-13 14:13:32 -08:00
{
2024-11-05 16:49:27 -03:00
find : "#{intl::MESSAGE_EDITED}" ,
2025-05-13 20:59:13 -03:00
replacement : {
// Make edit marker clickable
2026-01-23 19:59:53 -05:00
match : /(isInline:!1,children:.{0,50}?)"span",\{(?=className:)/ ,
replace : "$1$self.EditMarker,{message:arguments[0].message,"
2025-05-13 20:59:13 -03:00
}
2022-11-13 14:13:32 -08:00
} ,
{
// ReferencedMessageStore
2024-03-28 03:47:00 +01:00
find : '"ReferencedMessageStore"' ,
2022-11-13 14:13:32 -08:00
replacement : [
2026-04-23 04:27:58 +02:00
{
match : /(?<=MESSAGE_DELETE:function\(\i\)\{)/ ,
replace : "return;"
} ,
{
match : /(?<=MESSAGE_DELETE_BULK:function\(\i\)\{)/ ,
replace : "return;"
2022-11-13 14:13:32 -08:00
}
]
} ,
{
// Message context base menu
2025-01-29 01:04:36 -03:00
find : ".MESSAGE,commandTargetId:" ,
2022-11-13 14:13:32 -08:00
replacement : [
{
// Remove the first section if message is deleted
match : /children:(\[""===.+?\])/ ,
replace : "children:arguments[0].message.deleted?[]:$1"
}
]
2024-07-15 04:36:42 +02:00
} ,
{
// Message grouping
find : "NON_COLLAPSIBLE.has(" ,
replacement : {
match : /if\((\i)\.blocked\)return \i\.\i\.MESSAGE_GROUP_BLOCKED;/ ,
replace : '$&else if($1.deleted) return"MESSAGE_GROUP_DELETED";' ,
} ,
2026-05-04 11:40:05 +02:00
predicate : ( ) = > settings . store . collapseDeleted
2024-07-15 04:36:42 +02:00
} ,
{
// Message group rendering
2024-11-05 16:49:27 -03:00
find : "#{intl::NEW_MESSAGES_ESTIMATED_WITH_DATE}" ,
2024-07-15 04:36:42 +02:00
replacement : [
{
match : /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\|\|/ ,
replace : '$&$1.type==="MESSAGE_GROUP_DELETED"||' ,
} ,
{
2026-04-17 18:26:23 -04:00
match : /(\i).type===\i\.\i\.MESSAGE_GROUP_BLOCKED\?(\i)=.*?:/ ,
replace : '$&$1.type==="MESSAGE_GROUP_DELETED"?$2=$self.DELETED_MESSAGE_COUNT:' ,
2024-07-15 04:36:42 +02:00
} ,
] ,
2026-05-04 11:40:05 +02:00
predicate : ( ) = > settings . store . collapseDeleted
2026-07-15 09:37:22 +02:00
} ,
{
find : "this.truncateTop" ,
replacement : {
match : /receiveMessage\((\i)\)\{/ ,
replace : "$& $self.normalizeNonce($1);"
}
2022-11-13 14:13:32 -08:00
}
]
} ) ;