UNPKG

@juzi/wechaty-puppet-whatsapp

Version:
304 lines 11.5 kB
import * as path from 'path'; import * as fs from 'fs-extra'; import * as os from 'os'; import { FlashStore } from 'flash-store'; import { log } from '../config.js'; import { WA_ERROR_TYPE } from '../exception/error-type.js'; import WAError from '../exception/whatsapp-error.js'; import '@juzi/wechaty-puppet/payloads'; const PRE = 'CacheManager'; export class CacheManager { /** * ************************************************************************ * Static Methods * ************************************************************************ */ static _instance; static get Instance() { if (!this._instance) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'no instance'); } return this._instance; } static async init(userId) { if (this._instance) { return; } this._instance = new CacheManager(); await this._instance.initCache(userId); } static async release() { if (!this._instance) { return; } await this._instance.releaseCache(); this._instance = undefined; } /** * ************************************************************************ * Instance Methods * ************************************************************************ */ // Static cache, won't change over time cacheMessageRawPayload; cacheContactOrRoomRawPayload; cacheRoomMemberIdList; cacheRoomInvitationRawPayload; cacheLatestMessageTimestampForChat; cacheFriendshipRawPayload; /** * ------------------------------- * Message Cache Section * -------------------------------- */ async getMessageRawPayload(id) { const cache = this.getMessageCache(); return cache.get(id); } async setMessageRawPayload(id, payload) { const cache = this.getMessageCache(); // @ts-ignore client is in implementation but not in interface const { client, ...rest } = payload; await cache.set(id, rest); } deleteMessage(id) { const cache = this.getMessageCache(); return cache.delete(id); } getMessageCache() { if (!this.cacheMessageRawPayload) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'getMessageCache() has no cache'); } return this.cacheMessageRawPayload; } /** * ------------------------------- * Contact And Room Cache Section * -------------------------------- */ async getContactOrRoomRawPayload(id) { const cache = this.getContactOrRoomCache(); return cache.get(id); } async setContactOrRoomRawPayload(id, payload) { const cache = this.getContactOrRoomCache(); // @ts-ignore client is in implementation but not in interface const { client, ...rest } = payload; await cache.set(id, rest); } deleteContactOrRoom(id) { const cache = this.getContactOrRoomCache(); return cache.delete(id); } getContactOrRoomCache() { if (!this.cacheContactOrRoomRawPayload) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'getContactOrRoomCache() has no cache'); } return this.cacheContactOrRoomRawPayload; } async getContactIdList() { const cache = this.getContactOrRoomCache(); const list = []; for await (const key of cache.keys()) { const value = await cache.get(key); if (!value) { continue; } if (!value.isGroup && value.id._serialized) { list.push(value.id._serialized); } } return list; } async getRoomIdList() { const cache = this.getContactOrRoomCache(); const list = []; for await (const key of cache.keys()) { const value = await cache.get(key); if (!value) { continue; } if (value.isGroup && value.id._serialized) { list.push(value.id._serialized); } } return list; } /** * ------------------------------- * Room Member Cache Section * -------------------------------- */ async getRoomMemberIdList(roomId) { const cache = this.getRoomMemberCache(); const memberIdList = await cache.get(roomId); return memberIdList || []; } async setRoomMemberIdList(roomId, list) { const cache = this.getRoomMemberCache(); await cache.set(roomId, list); } async addRoomMemberToList(roomId, memberIds) { const memberIdListInCache = await this.getRoomMemberIdList(roomId); if (Array.isArray(memberIds)) { memberIds.forEach(memberId => !memberIdListInCache.includes(memberId) && memberIdListInCache.push(memberId)); await this.setRoomMemberIdList(roomId, memberIdListInCache); } else { !memberIdListInCache.includes(memberIds) && memberIdListInCache.push(memberIds); await this.setRoomMemberIdList(roomId, memberIdListInCache); } } async removeRoomMemberFromList(roomId, memberIds) { const memberIdListInCache = await this.getRoomMemberIdList(roomId); if (Array.isArray(memberIds)) { const memberIdList = memberIdListInCache.filter(id => !memberIds.includes(id)); await this.setRoomMemberIdList(roomId, memberIdList); } else { if (memberIdListInCache.includes(memberIds)) { const memberIdList = memberIdListInCache.filter(id => id !== memberIds); await this.setRoomMemberIdList(roomId, memberIdList); } } } async deleteRoomMemberIdList(roomId) { const cache = this.getRoomMemberCache(); await cache.delete(roomId); } getRoomMemberCache() { if (!this.cacheRoomMemberIdList) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'getRoomMemberCache() has no cache'); } return this.cacheRoomMemberIdList; } /** * ------------------------------- * Room Invitation Cache Section * -------------------------------- */ async getRoomInvitationRawPayload(id) { const cache = this.getRoomInvitationCache(); return cache.get(id); } async setRoomInvitationRawPayload(id, payload) { const cache = this.getRoomInvitationCache(); await cache.set(id, payload); } deleteRoomInvitation(id) { const cache = this.getRoomInvitationCache(); return cache.delete(id); } getRoomInvitationCache() { if (!this.cacheRoomInvitationRawPayload) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'getRoomInvitationCache() has no cache'); } return this.cacheRoomInvitationRawPayload; } /** * ------------------------------- * Message Cache Section * -------------------------------- */ /** * get timestamp of the latest message for contact or room, if timestamp is not in cache, return Number.MAX_SAFE_INTEGER * @param {string} id message id * @returns {number} timestamp or Number.MAX_SAFE_INTEGER */ async getLatestMessageTimestampForChat(id) { const cache = this.getLatestMessageTimestampForChatCache(); const timestamp = await cache.get(id); if (!timestamp) { return Number.MAX_SAFE_INTEGER; } return timestamp; } async setLatestMessageTimestampForChat(id, num) { const cache = this.getLatestMessageTimestampForChatCache(); await cache.set(id, num); } getLatestMessageTimestampForChatCache() { if (!this.cacheLatestMessageTimestampForChat) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'getLatestMessageTimestampForChatCache() has no cache'); } return this.cacheLatestMessageTimestampForChat; } /** * ------------------------------- * Friendship Cache Section * -------------------------------- */ async getFriendshipRawPayload(id) { const cache = this.getFriendshipCache(); return cache.get(id); } async setFriendshipRawPayload(id, payload) { const cache = this.getFriendshipCache(); // @ts-ignore client is in implementation but not in interface const { client, ...rest } = payload; await cache.set(id, rest); } deleteFriendship(id) { const cache = this.getFriendshipCache(); return cache.delete(id); } getFriendshipCache() { if (!this.cacheFriendshipRawPayload) { throw WAError(WA_ERROR_TYPE.ERR_NO_CACHE, 'getFriendshipCache() has no cache'); } return this.cacheFriendshipRawPayload; } /** * ------------------------------- * Private Method Section * -------------------------------- */ async initCache(userId) { if (this.cacheMessageRawPayload) { throw WAError(WA_ERROR_TYPE.ERR_INIT, 'cacheMessageRawPayload does not exist.'); } const baseDir = path.join(os.homedir(), '.wechaty', 'puppet-whatsapp', 'flash-store-v0.12', userId); const baseDirExist = await fs.pathExists(baseDir); if (!baseDirExist) { await fs.mkdirp(baseDir); } this.cacheMessageRawPayload = new FlashStore(path.join(baseDir, 'message')); this.cacheContactOrRoomRawPayload = new FlashStore(path.join(baseDir, 'contact-or-room')); this.cacheRoomInvitationRawPayload = new FlashStore(path.join(baseDir, 'room-invitation')); this.cacheRoomMemberIdList = new FlashStore(path.join(baseDir, 'room-member')); this.cacheLatestMessageTimestampForChat = new FlashStore(path.join(baseDir, 'latest-message-timestamp-for-chat')); this.cacheFriendshipRawPayload = new FlashStore(path.join(baseDir, 'friendship')); const messageTotal = await this.cacheMessageRawPayload.size; log.verbose(PRE, `initCache() inited Messages: ${messageTotal} cacheDir="${baseDir}"`); } async releaseCache() { log.verbose(PRE, 'releaseCache()'); if (this.cacheMessageRawPayload && this.cacheContactOrRoomRawPayload && this.cacheRoomInvitationRawPayload && this.cacheRoomMemberIdList && this.cacheLatestMessageTimestampForChat && this.cacheFriendshipRawPayload) { log.silly(PRE, 'releaseCache() closing caches ...'); await Promise.all([ this.cacheMessageRawPayload.close(), this.cacheContactOrRoomRawPayload.close(), this.cacheRoomInvitationRawPayload.close(), this.cacheRoomMemberIdList.close(), this.cacheLatestMessageTimestampForChat.close(), this.cacheFriendshipRawPayload.close(), ]); this.cacheMessageRawPayload = undefined; this.cacheContactOrRoomRawPayload = undefined; this.cacheRoomInvitationRawPayload = undefined; this.cacheRoomMemberIdList = undefined; this.cacheLatestMessageTimestampForChat = undefined; this.cacheFriendshipRawPayload = undefined; log.silly(PRE, 'releaseCache() cache closed.'); } else { log.verbose(PRE, 'releaseCache() cache not exist.'); } } } //# sourceMappingURL=cache-manager.js.map