2024-04-17 14:29:47 -04:00
|
|
|
/*
|
|
|
|
|
* Vencord, a modification for Discord's desktop app
|
|
|
|
|
* Copyright (c) 2023 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/>.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-11-18 22:15:50 -05:00
|
|
|
import { createStore } from "@api/DataStore";
|
|
|
|
|
import { DataStore } from "@api/index";
|
|
|
|
|
import { Settings } from "@api/Settings";
|
|
|
|
|
|
|
|
|
|
import { Flogger, Native, settings } from ".";
|
|
|
|
|
import { LoggedMessage, LoggedMessageIds, LoggedMessageJSON, LoggedMessages, MessageRecord } from "./types";
|
|
|
|
|
import { cleanupMessage, getNative, sortMessagesByDate } from "./utils";
|
|
|
|
|
import { cacheMessageImages, deleteMessageImages } from "./utils/saveImage";
|
|
|
|
|
|
|
|
|
|
export const defaultLoggedMessages = { deletedMessages: {}, editedMessages: {}, };
|
|
|
|
|
|
|
|
|
|
export const LOGGED_MESSAGES_KEY = "logged-messages-hi";
|
|
|
|
|
export const MessageLoggerStore = createStore("MessageLoggerData", "MessageLoggerStore");
|
|
|
|
|
|
|
|
|
|
// this gets used by the logs modal. logs modal should only use saved messages not messages that are being processed
|
|
|
|
|
// also hasMessageInLogs should only check saved messages not the ones that are being processed
|
|
|
|
|
export let savedLoggedMessages: LoggedMessages = { ...defaultLoggedMessages };
|
|
|
|
|
|
|
|
|
|
export let loggedMessages: LoggedMessages = { ...defaultLoggedMessages };
|
|
|
|
|
|
|
|
|
|
(async () => {
|
|
|
|
|
try {
|
|
|
|
|
const Native = getNative();
|
|
|
|
|
const res = await Native.getLogsFromFs();
|
|
|
|
|
if (res != null) {
|
|
|
|
|
Flogger.log("Got logged messages from native wont be checking DataStore");
|
|
|
|
|
const cleaned = await cleanMessages(res, Native);
|
|
|
|
|
loggedMessages = cleaned;
|
|
|
|
|
savedLoggedMessages = cleaned;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (IS_WEB) {
|
|
|
|
|
Flogger.log("hii. no point in checking DataStore if. we already did up there ^^");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const data = await DataStore.get(LOGGED_MESSAGES_KEY, MessageLoggerStore);
|
|
|
|
|
|
|
|
|
|
if (data == null) {
|
|
|
|
|
Flogger.log("No logged messages in DataStore");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Flogger.log("Loading logged messages from DataStore and writing to native");
|
|
|
|
|
Native.writeLogs(JSON.stringify(data));
|
|
|
|
|
|
|
|
|
|
loggedMessages = data;
|
|
|
|
|
savedLoggedMessages = res;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Error loading logged messages from the store:", error);
|
|
|
|
|
}
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
// api
|
|
|
|
|
|
|
|
|
|
export const saveLoggedMessages = async () => {
|
|
|
|
|
if (settings.store.saveMessages) {
|
|
|
|
|
await Native.writeLogs(JSON.stringify(loggedMessages));
|
|
|
|
|
}
|
|
|
|
|
savedLoggedMessages = loggedMessages;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export const addMessage = async (message: LoggedMessage | LoggedMessageJSON, key: keyof LoggedMessageIds, isBulk = false) => {
|
|
|
|
|
if (settings.store.saveImages && key === "deletedMessages")
|
2024-04-17 14:29:47 -04:00
|
|
|
await cacheMessageImages(message);
|
|
|
|
|
const finalMessage = cleanupMessage(message);
|
2024-11-18 22:15:50 -05:00
|
|
|
loggedMessages[message.id] = { message: finalMessage };
|
2024-04-17 14:29:47 -04:00
|
|
|
|
2024-11-18 22:15:50 -05:00
|
|
|
if (!loggedMessages[key][message.channel_id])
|
|
|
|
|
loggedMessages[key][message.channel_id] = [];
|
2024-04-17 14:29:47 -04:00
|
|
|
|
2024-11-18 22:15:50 -05:00
|
|
|
if (!loggedMessages[key][message.channel_id].includes(message.id))
|
|
|
|
|
loggedMessages[key][message.channel_id].push(message.id);
|
2024-04-17 14:29:47 -04:00
|
|
|
|
2024-11-18 22:15:50 -05:00
|
|
|
// if limit is negative or 0 there is no limit
|
|
|
|
|
if (settings.store.messageLimit > 0 && (Object.keys(loggedMessages).length - 2) > settings.store.messageLimit)
|
|
|
|
|
await deleteOldestMessageWithoutSaving(loggedMessages);
|
2024-04-17 14:29:47 -04:00
|
|
|
|
2024-11-18 22:15:50 -05:00
|
|
|
if (!isBulk)
|
|
|
|
|
await saveLoggedMessages();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const removeFromKey = (
|
|
|
|
|
message_id: string,
|
|
|
|
|
channel_id: string,
|
|
|
|
|
loggedMessages: LoggedMessages,
|
|
|
|
|
key: keyof LoggedMessageIds,
|
|
|
|
|
) => {
|
|
|
|
|
if (loggedMessages[key][channel_id]) {
|
|
|
|
|
loggedMessages[key][channel_id] = loggedMessages[key][channel_id].filter(msgid => msgid !== message_id);
|
|
|
|
|
|
|
|
|
|
if (loggedMessages[key][channel_id].length === 0) {
|
|
|
|
|
delete loggedMessages[key][channel_id];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function removeLogWithoutSaving(messageId: string, loggedMessages: LoggedMessages) {
|
|
|
|
|
const record = loggedMessages[messageId];
|
|
|
|
|
if (record) {
|
|
|
|
|
const channel_id = record.message?.channel_id;
|
|
|
|
|
|
|
|
|
|
if (channel_id != null) {
|
|
|
|
|
removeFromKey(messageId, channel_id, loggedMessages, "editedMessages");
|
|
|
|
|
removeFromKey(messageId, channel_id, loggedMessages, "deletedMessages");
|
2024-04-17 14:29:47 -04:00
|
|
|
}
|
2024-11-18 22:15:50 -05:00
|
|
|
|
|
|
|
|
delete loggedMessages[messageId];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return loggedMessages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export async function removeLogs(ids: string[]) {
|
|
|
|
|
for (const msgId of ids) {
|
|
|
|
|
removeLogWithoutSaving(msgId, loggedMessages);
|
2024-04-17 14:29:47 -04:00
|
|
|
}
|
2024-11-18 22:15:50 -05:00
|
|
|
await saveLoggedMessages();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function removeLog(id: string) {
|
|
|
|
|
const record = loggedMessages[id];
|
|
|
|
|
|
|
|
|
|
if (record?.message)
|
|
|
|
|
deleteMessageImages(record.message);
|
|
|
|
|
|
|
|
|
|
removeLogWithoutSaving(id, loggedMessages);
|
|
|
|
|
|
|
|
|
|
await saveLoggedMessages();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function clearLogs() {
|
|
|
|
|
await Native.writeLogs(JSON.stringify(defaultLoggedMessages));
|
|
|
|
|
loggedMessages = defaultLoggedMessages;
|
|
|
|
|
savedLoggedMessages = defaultLoggedMessages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// utils
|
|
|
|
|
|
|
|
|
|
export const hasMessageInLogs = (messageId: string) => {
|
|
|
|
|
const bruh = Object.values(savedLoggedMessages)
|
|
|
|
|
.filter(m => !Array.isArray(m)) as MessageRecord[];
|
|
|
|
|
|
|
|
|
|
return bruh.find(m => m.message?.id === messageId);
|
2024-04-17 14:29:47 -04:00
|
|
|
};
|
2024-11-18 22:15:50 -05:00
|
|
|
|
|
|
|
|
export const hasLogs = async () => {
|
|
|
|
|
const hasDeletedMessages = Object.keys(loggedMessages.deletedMessages).length > 0;
|
|
|
|
|
const hasEditedMessages = Object.keys(loggedMessages.editedMessages).length > 0;
|
|
|
|
|
|
|
|
|
|
const hasMessages = Object.keys(loggedMessages).filter(m => m !== "editedMessages" && m !== "deletedMessages").length > 0;
|
|
|
|
|
|
|
|
|
|
if (hasDeletedMessages && hasEditedMessages && hasMessages) return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export function findLoggedChannelByMessageIdSync(messageId: string, loggedMessages: LoggedMessages, key: keyof LoggedMessageIds) {
|
|
|
|
|
for (const channelId in loggedMessages[key]) {
|
|
|
|
|
if (loggedMessages[key][channelId].includes(messageId)) return channelId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function findLoggedChannelByMessage(messageId: string, key?: keyof LoggedMessageIds): Promise<[string | null, keyof LoggedMessageIds]> {
|
|
|
|
|
|
|
|
|
|
if (!key) {
|
|
|
|
|
const id1 = findLoggedChannelByMessageIdSync(messageId, loggedMessages, "deletedMessages");
|
|
|
|
|
if (id1) return [id1, "deletedMessages"];
|
|
|
|
|
const id2 = findLoggedChannelByMessageIdSync(messageId, loggedMessages, "editedMessages");
|
|
|
|
|
return [id2, "editedMessages"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [findLoggedChannelByMessageIdSync(messageId, loggedMessages, key), key];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export function getOldestMessage(loggedMessageIds: LoggedMessages) {
|
|
|
|
|
const messags = Object.values(loggedMessageIds)
|
|
|
|
|
.filter(m => !Array.isArray(m) && m.message != null) as MessageRecord[];
|
|
|
|
|
|
|
|
|
|
const sortedMessages = messags.sort((a, b) => sortMessagesByDate(a.message.timestamp, b.message.timestamp));
|
|
|
|
|
|
|
|
|
|
const oldestMessage = sortedMessages[sortedMessages.length - 1];
|
|
|
|
|
|
|
|
|
|
return oldestMessage ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getMessage(channelId: string, messageId: string) {
|
|
|
|
|
const messags = Object.values(savedLoggedMessages)
|
|
|
|
|
.filter(m => !Array.isArray(m) && m.message != null) as MessageRecord[];
|
|
|
|
|
|
|
|
|
|
return messags.find(m => m.message.channel_id === channelId && m.message.id === messageId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function deleteOldestMessageWithoutSaving(loggedMessages: LoggedMessages) {
|
|
|
|
|
const oldestMessage = getOldestMessage(loggedMessages);
|
|
|
|
|
if (!oldestMessage || !oldestMessage.message) {
|
|
|
|
|
console.warn("couldnt find oldest message. oldestMessage == null || oldestMessage.message == null");
|
|
|
|
|
return loggedMessages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const { message } = oldestMessage;
|
|
|
|
|
|
|
|
|
|
const [channelId, key] = await findLoggedChannelByMessage(message.id);
|
|
|
|
|
|
|
|
|
|
if (!channelId || !key) {
|
|
|
|
|
console.warn("couldnt find oldest message. channelId =", channelId, " key =", key);
|
|
|
|
|
return loggedMessages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
removeLogWithoutSaving(message.id, loggedMessages);
|
|
|
|
|
// console.log("removing", message);
|
|
|
|
|
|
|
|
|
|
return loggedMessages;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function cleanMessages(loggedMessages: LoggedMessages, _Native: any = Native) {
|
|
|
|
|
try {
|
|
|
|
|
const cleaned = { ...loggedMessages };
|
|
|
|
|
|
|
|
|
|
if (IS_WEB) return cleaned;
|
|
|
|
|
|
|
|
|
|
const messageRecords = Object.values(cleaned)
|
|
|
|
|
.filter(m => !Array.isArray(m)) as MessageRecord[];
|
|
|
|
|
|
|
|
|
|
let hasChanged = false;
|
|
|
|
|
for (const messageRecord of messageRecords) {
|
|
|
|
|
const { message } = messageRecord;
|
|
|
|
|
|
|
|
|
|
if (message?.attachments) {
|
|
|
|
|
for (const attachment of message.attachments) {
|
|
|
|
|
if (attachment.blobUrl) {
|
|
|
|
|
hasChanged = true;
|
|
|
|
|
delete attachment.blobUrl;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasChanged)
|
|
|
|
|
await _Native.writeLogs(Settings.plugins.MessageLoggerEnhanced.logsDir, JSON.stringify(cleaned));
|
|
|
|
|
|
|
|
|
|
return cleaned;
|
|
|
|
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
Flogger.error("Error cleaning messages:", err);
|
|
|
|
|
return loggedMessages;
|
|
|
|
|
}
|
|
|
|
|
}
|