-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmessageCache.ts
More file actions
107 lines (88 loc) · 3.42 KB
/
Copy pathmessageCache.ts
File metadata and controls
107 lines (88 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { EmbedlyErrors, formatLog, getErrorContext } from "@embedly/logging";
import { createClient, type RedisClientType } from "redis";
const MESSAGE_CACHE_TTL_SECONDS = Number(process.env.MESSAGE_CACHE_TTL_SECONDS ?? 60 * 60 * 24);
const CACHE_URL = process.env.CACHE_URL ?? "redis://localhost:6379";
interface SourceMessageCache {
botMessageIds: string[];
}
export class MessageCache {
private constructor(private readonly client: RedisClientType) {}
public static async connect() {
const client = createClient({ url: CACHE_URL });
client.on("error", (error) => {
console.error(
formatLog("error", EmbedlyErrors.MessageCacheFailed, {
...getErrorContext(error),
}),
);
});
await client.connect();
return new MessageCache(client as RedisClientType);
}
public async save(sourceMessageId: string, botMessageId: string, authorId: string) {
const messageKey = this.getSourceMessageKey(sourceMessageId);
const authorKey = this.getBotMessageAuthorKey(botMessageId);
const sourceKey = this.getBotMessageSourceKey(botMessageId);
const existing = await this.getSourceMessage(sourceMessageId);
const botMessageIds = existing?.botMessageIds ?? [];
if (!botMessageIds.includes(botMessageId)) {
botMessageIds.push(botMessageId);
}
await this.client
.multi()
.set(messageKey, JSON.stringify({ botMessageIds }), { EX: MESSAGE_CACHE_TTL_SECONDS })
.set(authorKey, authorId, { EX: MESSAGE_CACHE_TTL_SECONDS })
.set(sourceKey, sourceMessageId, { EX: MESSAGE_CACHE_TTL_SECONDS })
.exec();
}
public async getOriginalAuthorId(botMessageId: string) {
return await this.client.get(this.getBotMessageAuthorKey(botMessageId));
}
public async removeBotMessage(botMessageId: string) {
const sourceMessageId = await this.client.get(this.getBotMessageSourceKey(botMessageId));
const keys = [
this.getBotMessageAuthorKey(botMessageId),
this.getBotMessageSourceKey(botMessageId),
];
if (!sourceMessageId) {
await this.client.del(keys);
return;
}
const sourceMessage = await this.getSourceMessage(sourceMessageId);
const botMessageIds = sourceMessage?.botMessageIds.filter((id) => id !== botMessageId) ?? [];
const transaction = this.client.multi().del(keys);
if (botMessageIds.length === 0) {
transaction.del(this.getSourceMessageKey(sourceMessageId));
} else {
transaction.set(
this.getSourceMessageKey(sourceMessageId),
JSON.stringify({ botMessageIds }),
{
EX: MESSAGE_CACHE_TTL_SECONDS,
},
);
}
await transaction.exec();
}
public async getBotMessageIds(sourceMessageId: string) {
const sourceMessage = await this.getSourceMessage(sourceMessageId);
return sourceMessage?.botMessageIds ?? [];
}
public async close() {
await this.client.close();
}
private async getSourceMessage(sourceMessageId: string): Promise<SourceMessageCache | null> {
const raw = await this.client.get(this.getSourceMessageKey(sourceMessageId));
if (!raw) return null;
return JSON.parse(raw) as SourceMessageCache;
}
private getSourceMessageKey(messageId: string) {
return `embedly:message:${messageId}`;
}
private getBotMessageAuthorKey(messageId: string) {
return `embedly:bot-author:${messageId}`;
}
private getBotMessageSourceKey(messageId: string) {
return `embedly:bot-source:${messageId}`;
}
}