@juzi/wechaty-puppet-whatsapp
Version:
Wechaty Puppet for WhatsApp
304 lines • 11.5 kB
JavaScript
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