135 lines
4.5 KiB
TypeScript
135 lines
4.5 KiB
TypeScript
import * as electronRemote from '@electron/remote';
|
|
const electronConsole = electronRemote.getGlobal('console') as Console;
|
|
|
|
import Logger from '../../logger/logger';
|
|
const LOG = new Logger(__filename, electronConsole);
|
|
|
|
import Globals from './globals';
|
|
|
|
import { ShouldNeverHappenError } from './data-types';
|
|
|
|
// TODO: make this non-static
|
|
export default class RecentMessageRAMCache {
|
|
_data = new Map(); // (serverId, channelId) -> { messages, size, hasFirstMessage, lastUsed }
|
|
_size = 0;
|
|
|
|
clear(): void {
|
|
this._data.clear();
|
|
this._size = 0;
|
|
}
|
|
|
|
_cullIfNeeded(): void { // TODO: Test this
|
|
if (this._size > Globals.MAX_RAM_CACHED_MESSAGES_TOTAL_SIZE) {
|
|
let entries = Array.from(this._data.entries())
|
|
.map(([ key, value ]) => { return { id: key, value: value }})
|
|
.sort((a, b) => b.value.lastUsed - a.value.lastUsed);
|
|
while (this._size > Globals.MAX_RAM_CACHED_MESSAGES_TOTAL_SIZE) {
|
|
let entry = entries.pop();
|
|
if (entry === undefined) throw new ShouldNeverHappenError('No entry in the array but the message cache still has a size...');
|
|
this._data.delete(entry.id);
|
|
this._size -= entry.value.size;
|
|
}
|
|
}
|
|
}
|
|
|
|
_cullChannelIfNeeded(serverId: string, channelId: string): void { // TODO: test this
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
let value = this._data.get(id);
|
|
|
|
while (value.size > Globals.MAX_RAM_CACHED_MESSAGES_CHANNEL_SIZE) {
|
|
value.hasFirstMessage = false;
|
|
let message = value.messages.shift();
|
|
value.size -= message.text.length;
|
|
this._size -= message.text.length;
|
|
}
|
|
}
|
|
|
|
// @param messages may be modified in addNewMessage due to pass-by-reference fun
|
|
putRecentMessages(serverId: string, channelId: string, messages: any[]): void {
|
|
let size = 0;
|
|
for (let message of messages) {
|
|
size += message.text.length;
|
|
}
|
|
if (size > Globals.MAX_RAM_CACHED_MESSAGES_CHANNEL_SIZE) return;
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
this._data.set(id, {
|
|
messages: messages,
|
|
size: size,
|
|
hasFirstMessage: true, // will be false if this was ever channel-culled
|
|
lastUsed: new Date()
|
|
});
|
|
this._size += size;
|
|
this._cullIfNeeded();
|
|
}
|
|
|
|
addNewMessage(serverId: string, message: any): void {
|
|
let channelId = message.channel.id;
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
if (!this._data.has(id)) return;
|
|
|
|
let value = this._data.get(id);
|
|
value.messages.push(message);
|
|
value.size += message.text.length;
|
|
this._size += message.text.length;
|
|
|
|
this._cullChannelIfNeeded(serverId, channelId);
|
|
this._cullIfNeeded();
|
|
}
|
|
|
|
updateMessage(serverId: string, oldMessage: any, newMessage: any): void {
|
|
let channelId = oldMessage.channel.id;
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
if (!this._data.has(id)) return;
|
|
|
|
let value = this._data.get(id);
|
|
let taggedIndex = value.messages.findIndex(cachedMessage => cachedMessage.id == oldMessage.id);
|
|
|
|
let [ oldCachedMessage ] = value.messages.splice(taggedIndex, 1, newMessage);
|
|
value.size -= oldCachedMessage.text.length;
|
|
value.size += newMessage.text.length;
|
|
this._size -= oldCachedMessage.text.length;
|
|
this._size += newMessage.text.length;
|
|
|
|
this._cullChannelIfNeeded(serverId, channelId);
|
|
this._cullIfNeeded();
|
|
}
|
|
|
|
deleteMessage(serverId: string, message: any): void {
|
|
let channelId = message.channel.id;
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
if (!this._data.has(id)) return;
|
|
|
|
let value = this._data.get(id);
|
|
let taggedIndex = value.messages.findIndex(cachedMessage => cachedMessage.id == message.id);
|
|
|
|
let [ oldCachedMessage ] = value.messages.splice(taggedIndex, 1);
|
|
value.size -= oldCachedMessage.text.length;
|
|
this._size -= oldCachedMessage.text.length;
|
|
|
|
// No need to cull since we are always shrinking
|
|
}
|
|
|
|
dropChannel(serverId: string, channelId: string): void {
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
if (!this._data.has(id)) return;
|
|
let value = this._data.get(id);
|
|
this._size -= value.size;
|
|
this._data.delete(id);
|
|
}
|
|
|
|
getRecentMessages(serverId: string, channelId: string, number: number): any | null {
|
|
let id = `s#${serverId}/c#${channelId}`;
|
|
if (!this._data.has(id)) {
|
|
//LOG.silly(`recent message cache miss on ${id} requesting ${number}`);
|
|
return null; // not in the cache
|
|
}
|
|
let value = this._data.get(id);
|
|
if (!value.hasFirstMessage && value.messages.length < number) {
|
|
//LOG.silly(`recent message cache incomplete on ${id} requesting ${number}`, { value });
|
|
return null; // requesting older messages than we have
|
|
}
|
|
value.lastUsed = new Date();
|
|
return value.messages.slice(-number);
|
|
}
|
|
}
|