@voltro/plugin-chat 0.0.0-stage → 0.88.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24492 -0
- package/LICENSE +61 -0
- package/README.md +516 -2
- package/SECURITY.md +56 -0
- package/THIRD-PARTY-NOTICES.md +16105 -0
- package/dist/admissions.entity.js +10 -0
- package/dist/channels.entity.js +19 -0
- package/dist/chat-app/events/chat.mentioned.event.ts +1 -0
- package/dist/chat-app/events/chat.messageChanged.event.ts +1 -0
- package/dist/chat-app/events/chat.typing.event.ts +1 -0
- package/dist/chat-app/schedules/chat.retention.cron.tsx +16 -0
- package/dist/chat-app/subscribers/chat.messages.subscribe.ts +4 -0
- package/dist/components.js +431 -0
- package/dist/contract.js +91 -0
- package/dist/errors.d.ts +13 -0
- package/dist/errors.js +20 -0
- package/dist/eventProducer.d.ts +18 -0
- package/dist/eventProducer.js +45 -0
- package/dist/events.d.ts +38 -0
- package/dist/events.js +35 -0
- package/dist/evidence.entity.js +24 -0
- package/dist/index.d.ts +623 -0
- package/dist/index.js +180 -0
- package/dist/inspect.js +37 -0
- package/dist/media.entity.js +14 -0
- package/dist/members.entity.js +21 -0
- package/dist/mentionDraft.js +20 -0
- package/dist/mentionNotifications.js +109 -0
- package/dist/messages.entity.js +27 -0
- package/dist/reportEvidence.js +15 -0
- package/dist/retention.entity.js +10 -0
- package/dist/rpc.d.ts +295 -0
- package/dist/rpc.js +266 -0
- package/dist/rpcRoutes.js +96 -0
- package/dist/server.d.ts +200 -0
- package/dist/server.js +6 -0
- package/dist/service.js +1411 -0
- package/dist/strategies.d.ts +148 -0
- package/dist/strategies.js +89 -0
- package/dist/testing.d.ts +139 -0
- package/dist/testing.js +110 -0
- package/dist/typing.js +87 -0
- package/dist/typingInvalidation.js +85 -0
- package/dist/typingLease.js +58 -0
- package/dist/typingPublisher.js +58 -0
- package/dist/useChatTypingPublisher.js +32 -0
- package/dist/useTyping.js +23 -0
- package/dist/web.d.ts +354 -0
- package/dist/web.js +18 -0
- package/package.json +98 -4
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { ChatError as e, chatError as t } from "./errors.js";
|
|
2
|
+
import { chatAdmissionsTable as n } from "./admissions.entity.js";
|
|
3
|
+
import { chatEvidenceTable as r } from "./evidence.entity.js";
|
|
4
|
+
import { CHAT_MAX_MESSAGE_VERSION as i, CHAT_MEDIA_SWEEP_BATCH_SIZE as a, CHAT_RETENTION_BATCH_SIZE as o, CHAT_RETENTION_RUN_BATCHES as s } from "./contract.js";
|
|
5
|
+
import { chatMediaTable as c } from "./media.entity.js";
|
|
6
|
+
import { chatRetentionTable as l } from "./retention.entity.js";
|
|
7
|
+
import { mentioned as u, messageChanged as d, typing as f } from "./events.js";
|
|
8
|
+
import { chatChannelsTable as p } from "./channels.entity.js";
|
|
9
|
+
import { chatMembersTable as m } from "./members.entity.js";
|
|
10
|
+
import { chatMessagesTable as h } from "./messages.entity.js";
|
|
11
|
+
import { dataStoreChat as g } from "./service.js";
|
|
12
|
+
import { BASE_NAME as _, chatRpcClientImports as v } from "./rpc.js";
|
|
13
|
+
import { chatRpcRoutes as y } from "./rpcRoutes.js";
|
|
14
|
+
import { defaultChatStrategies as b, resolveChatOptions as x } from "./strategies.js";
|
|
15
|
+
import { chatStatus as S } from "./inspect.js";
|
|
16
|
+
import { ChatAdministration as C } from "./server.js";
|
|
17
|
+
import { defineLicensedGamePlugin as w } from "@voltro/plugin-licensing/gate";
|
|
18
|
+
import { Effect as T, Layer as E } from "effect";
|
|
19
|
+
import { fileURLToPath as D } from "node:url";
|
|
20
|
+
import { replicaEvidence as O } from "@voltro/protocol/identity";
|
|
21
|
+
import { redisStore as k } from "@voltro/plugin-ratelimit";
|
|
22
|
+
// src/index.ts
|
|
23
|
+
var A = {
|
|
24
|
+
slots: {
|
|
25
|
+
moderate: {
|
|
26
|
+
defaultExport: "keywordChatModeration",
|
|
27
|
+
fixtureExport: "chatModerationFixture"
|
|
28
|
+
},
|
|
29
|
+
rateLimit: {
|
|
30
|
+
defaultExport: "fixedChatRateBudgets",
|
|
31
|
+
fixtureExport: "chatRateLimitFixture"
|
|
32
|
+
},
|
|
33
|
+
mentionResolver: {
|
|
34
|
+
defaultExport: "playerChatMentions",
|
|
35
|
+
fixtureExport: "chatMentionResolverFixture"
|
|
36
|
+
},
|
|
37
|
+
retention: {
|
|
38
|
+
defaultExport: "channelChatRetention",
|
|
39
|
+
fixtureExport: "chatRetentionFixture"
|
|
40
|
+
},
|
|
41
|
+
moderateMedia: {
|
|
42
|
+
defaultExport: "typedChatMedia",
|
|
43
|
+
fixtureExport: "chatMediaModerationFixture"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
ports: {},
|
|
47
|
+
events: [
|
|
48
|
+
d.name,
|
|
49
|
+
u.name,
|
|
50
|
+
f.name
|
|
51
|
+
]
|
|
52
|
+
}, j = (i = {}) => {
|
|
53
|
+
let a = x(i), o = k(a.rateLimit), s, u, d = !1, b = (e) => T.suspend(() => u === void 0 ? T.fail(t("unavailable")) : e(u)), A = (e, n, r) => {
|
|
54
|
+
try {
|
|
55
|
+
let t = structuredClone(e), i = structuredClone(n);
|
|
56
|
+
return b((e) => r(e, t, i));
|
|
57
|
+
} catch {
|
|
58
|
+
return T.fail(t("invalid_input"));
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
return w({
|
|
62
|
+
name: _,
|
|
63
|
+
description: "Membership-filtered durable chat with replaceable rule slots.",
|
|
64
|
+
eventClientPolicies: [{
|
|
65
|
+
id: f.clientPolicy,
|
|
66
|
+
admit: (e) => b((t) => t.typingPolicy.admit(e)),
|
|
67
|
+
deliver: (e) => b((t) => t.typingPolicy.deliver(e)),
|
|
68
|
+
monitor: (e) => b((t) => t.typingPolicy.monitor(e))
|
|
69
|
+
}],
|
|
70
|
+
services: E.succeed(C, {
|
|
71
|
+
readReportEvidence: (e, t) => A(e, t, (e, t, n) => e.readReportEvidence(t, n)),
|
|
72
|
+
collectReportContext: (e, t) => A(e, t, (e, t, n) => e.collectReportContext(t, n)),
|
|
73
|
+
prune: (e) => A(e, null, (e, t) => e.prune(t)),
|
|
74
|
+
sweepMedia: (e) => A(e, null, (e, t) => e.sweepMedia(t)),
|
|
75
|
+
canNotifyMention: (e, t) => A(e, t, (e, t, n) => e.canNotifyMention(t, n)),
|
|
76
|
+
createChannel: (e, t) => A(e, t, (e, t, n) => e.createChannel(t, n)),
|
|
77
|
+
addMember: (e, t) => A(e, t, (e, t, n) => e.addMember(t, n)),
|
|
78
|
+
removeMember: (e, t) => A(e, t, (e, t, n) => e.removeMember(t, n)),
|
|
79
|
+
archiveMatch: (e, t) => A(e, t, (e, t, n) => e.archiveMatch(t, n))
|
|
80
|
+
}),
|
|
81
|
+
permissions: ["store:write", "inspect:read"],
|
|
82
|
+
routes: y({
|
|
83
|
+
mentionCandidates: (e, t) => A(e, t, (e, t, n) => e.service.mentionCandidates(t, n)),
|
|
84
|
+
authors: (e, t) => A(e, t, (e, t, n) => e.service.authors(t, n)),
|
|
85
|
+
prepareTyping: (e, t) => b((n) => n.service.prepareTyping(e, t)),
|
|
86
|
+
dm: (e, t) => b((n) => n.service.dm(e, t)),
|
|
87
|
+
send: (e, t) => b((n) => n.service.send(e, t)),
|
|
88
|
+
edit: (e, t) => b((n) => n.service.edit(e, t)),
|
|
89
|
+
remove: (e, t) => b((n) => n.service.remove(e, t)),
|
|
90
|
+
messages: (e, t) => b((n) => n.service.messages(e, t)),
|
|
91
|
+
channel: (e, t) => b((n) => n.service.channel(e, t)),
|
|
92
|
+
membership: (e, t) => b((n) => n.service.membership(e, t)),
|
|
93
|
+
unread: (e) => b((t) => t.service.unread(e)),
|
|
94
|
+
original: (e, t) => b((n) => n.service.original(e, t)),
|
|
95
|
+
mediaUrl: (e, t) => b((n) => n.service.mediaUrl(e, t)),
|
|
96
|
+
channels: (e) => b((t) => t.service.channels(e)),
|
|
97
|
+
markRead: (e, t, n) => b((r) => r.service.markRead(e, t, n)),
|
|
98
|
+
moderateMember: (e, t) => b((n) => n.service.moderateMember(e, t)),
|
|
99
|
+
configure: (e, t) => b((n) => n.service.configure(e, t)),
|
|
100
|
+
join: (e, t) => b((n) => n.service.join(e, t)),
|
|
101
|
+
leave: (e, t) => b((n) => n.service.leave(e, t)),
|
|
102
|
+
transferOwnership: (e, t, n) => b((r) => r.service.transferOwnership(e, t, n))
|
|
103
|
+
}),
|
|
104
|
+
rpcClientDescriptors: v,
|
|
105
|
+
sources: D(new URL("./chat-app", "" + import.meta.url)),
|
|
106
|
+
errorSchemas: [{
|
|
107
|
+
schema: e,
|
|
108
|
+
import: {
|
|
109
|
+
module: "@voltro/plugin-chat/errors",
|
|
110
|
+
name: "ChatError"
|
|
111
|
+
}
|
|
112
|
+
}],
|
|
113
|
+
extendSchema: { tables: [
|
|
114
|
+
p,
|
|
115
|
+
m,
|
|
116
|
+
h,
|
|
117
|
+
l,
|
|
118
|
+
r,
|
|
119
|
+
n,
|
|
120
|
+
c
|
|
121
|
+
] },
|
|
122
|
+
subjectPrivacy: {
|
|
123
|
+
tables: [
|
|
124
|
+
p.tableName,
|
|
125
|
+
m.tableName,
|
|
126
|
+
h.tableName,
|
|
127
|
+
l.tableName,
|
|
128
|
+
r.tableName,
|
|
129
|
+
n.tableName,
|
|
130
|
+
c.tableName
|
|
131
|
+
],
|
|
132
|
+
kinds: ["user", "guest"]
|
|
133
|
+
},
|
|
134
|
+
inspectEndpoints: [{
|
|
135
|
+
method: "GET",
|
|
136
|
+
path: "/status",
|
|
137
|
+
scope: "process",
|
|
138
|
+
description: "Declared chat budgets and process binding; no channel/message/body enumeration.",
|
|
139
|
+
handler: () => T.try({
|
|
140
|
+
try: () => ({
|
|
141
|
+
kind: "json",
|
|
142
|
+
data: S(a, u !== void 0, d)
|
|
143
|
+
}),
|
|
144
|
+
catch: () => t("unavailable")
|
|
145
|
+
})
|
|
146
|
+
}],
|
|
147
|
+
bindDataStore: (e) => {
|
|
148
|
+
if (s !== void 0 && s !== e) throw Error("Chat plugin instance cannot cross app stores");
|
|
149
|
+
s = e, u ??= g(e, a, o);
|
|
150
|
+
},
|
|
151
|
+
onActivate: (e) => T.tryPromise({
|
|
152
|
+
try: async () => {
|
|
153
|
+
if (a.rateLimit?.hotStore !== void 0 && O(e.env) !== null) throw t("unavailable");
|
|
154
|
+
await o.activate(e.env, e.connectionRegistry), d = !0;
|
|
155
|
+
},
|
|
156
|
+
catch: () => t("unavailable")
|
|
157
|
+
}),
|
|
158
|
+
onExport: (e, t) => b((n) => n.exportSubject({
|
|
159
|
+
subjectId: e,
|
|
160
|
+
tenantId: t.tenantId,
|
|
161
|
+
kind: t.kind
|
|
162
|
+
})),
|
|
163
|
+
onErase: (e, t) => b((n) => n.eraseSubject({
|
|
164
|
+
subjectId: e,
|
|
165
|
+
tenantId: t.tenantId,
|
|
166
|
+
kind: t.kind
|
|
167
|
+
})),
|
|
168
|
+
onDeactivate: () => T.tryPromise({
|
|
169
|
+
try: async () => {
|
|
170
|
+
try {
|
|
171
|
+
u?.disposeTyping();
|
|
172
|
+
} finally {
|
|
173
|
+
u = void 0, s = void 0, d = !1, await o.deactivate();
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
catch: () => t("unavailable")
|
|
177
|
+
})
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
export { i as CHAT_MAX_MESSAGE_VERSION, a as CHAT_MEDIA_SWEEP_BATCH_SIZE, o as CHAT_RETENTION_BATCH_SIZE, s as CHAT_RETENTION_RUN_BATCHES, e as ChatError, n as chatAdmissionsTable, p as chatChannelsTable, r as chatEvidenceTable, c as chatMediaTable, m as chatMembersTable, h as chatMessagesTable, j as chatPlugin, l as chatRetentionTable, A as chatSystemContract, g as dataStoreChat, b as defaultChatStrategies, x as resolveChatOptions };
|
package/dist/inspect.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { chatError as e } from "./errors.js";
|
|
2
|
+
import { defaultChatStrategies as t } from "./strategies.js";
|
|
3
|
+
// src/inspect.ts
|
|
4
|
+
var n = (n, r, i) => {
|
|
5
|
+
let a = n.now();
|
|
6
|
+
if (!Number.isSafeInteger(a) || a < 0 || a > 864e13) throw e("unavailable");
|
|
7
|
+
return {
|
|
8
|
+
observedAt: a,
|
|
9
|
+
bound: r,
|
|
10
|
+
rateActive: i,
|
|
11
|
+
maxBodyLength: n.maxBodyLength,
|
|
12
|
+
pageSize: n.pageSize,
|
|
13
|
+
maxMembers: n.maxMembers,
|
|
14
|
+
maxChannelsPerSubject: n.maxChannelsPerSubject,
|
|
15
|
+
retentionMs: n.retentionMs,
|
|
16
|
+
evidenceRetentionMs: n.evidenceRetentionMs,
|
|
17
|
+
perChannelRate: n.perChannelRate,
|
|
18
|
+
globalRate: n.globalRate,
|
|
19
|
+
maxMentions: n.maxMentions,
|
|
20
|
+
moderationAction: n.moderationAction,
|
|
21
|
+
denyWordCount: n.denyWords.length,
|
|
22
|
+
mediaContentTypes: n.mediaContentTypes,
|
|
23
|
+
maxMediaBytes: n.maxMediaBytes,
|
|
24
|
+
mediaUrlTtlSec: n.mediaUrlTtlSec,
|
|
25
|
+
slots: Object.fromEntries(Object.entries(t).map(([e, t]) => [e, n.strategies[e] === t ? "default" : "custom"])),
|
|
26
|
+
tables: [
|
|
27
|
+
"_voltro_chat_channels",
|
|
28
|
+
"_voltro_chat_members",
|
|
29
|
+
"_voltro_chat_messages",
|
|
30
|
+
"_voltro_chat_retention",
|
|
31
|
+
"_voltro_chat_evidence",
|
|
32
|
+
"_voltro_chat_admissions",
|
|
33
|
+
"_voltro_chat_media"
|
|
34
|
+
]
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export { n as chatStatus };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { MEDIA_TABLE as e } from "./contract.js";
|
|
2
|
+
import { id as t, table as n, text as r, timestamp as i } from "@voltro/database";
|
|
3
|
+
// src/media.entity.ts
|
|
4
|
+
var a = n(e, {
|
|
5
|
+
id: t({ prefix: "cmed" }),
|
|
6
|
+
tenantId: r().maxLength(256).externalId().serverOnly(),
|
|
7
|
+
channelId: r().maxLength(64).serverOnly(),
|
|
8
|
+
messageId: r().maxLength(64).serverOnly(),
|
|
9
|
+
storageId: r().maxLength(256).serverOnly(),
|
|
10
|
+
ownerId: r().maxLength(256).nullable().externalId().sensitive("pii").serverOnly(),
|
|
11
|
+
state: r().maxLength(16).oneOf(["active", "deleting"]).serverOnly(),
|
|
12
|
+
createdAt: i().serverOnly()
|
|
13
|
+
}).index(["tenantId", "ownerId"]).index(["state", "id"]).index(["tenantId", "storageId"]);
|
|
14
|
+
export { a as chatMediaTable };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { MEMBERS_TABLE as e } from "./contract.js";
|
|
2
|
+
import { boolean as t, id as n, json as r, table as i, text as a, timestamp as o } from "@voltro/database";
|
|
3
|
+
// src/members.entity.ts
|
|
4
|
+
var s = i(e, {
|
|
5
|
+
id: n({ prefix: "cmem" }),
|
|
6
|
+
tenantId: a().maxLength(256).externalId().sensitive("pii"),
|
|
7
|
+
channelId: a().maxLength(64).safe(),
|
|
8
|
+
subjectId: a().maxLength(256).externalId().sensitive("pii"),
|
|
9
|
+
role: a().maxLength(16).oneOf([
|
|
10
|
+
"member",
|
|
11
|
+
"mod",
|
|
12
|
+
"owner"
|
|
13
|
+
]).safe(),
|
|
14
|
+
mutedUntil: o().nullable().safe(),
|
|
15
|
+
lastReadAt: o().safe(),
|
|
16
|
+
lastReadId: a().maxLength(64).nullable().safe(),
|
|
17
|
+
lastSentAt: o().nullable().serverOnly(),
|
|
18
|
+
notify: t().safe(),
|
|
19
|
+
metadata: r().sensitive("pii")
|
|
20
|
+
}).index(["tenantId", "subjectId"]).index(["channelId"]);
|
|
21
|
+
export { s as chatMembersTable };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// src/mentionDraft.ts
|
|
2
|
+
var e = (e, t, n) => {
|
|
3
|
+
if (t !== n || !Number.isSafeInteger(t) || t < 0 || t > e.length || t < e.length && /[\p{L}\p{N}\p{M}_#]/u.test(String.fromCodePoint(e.codePointAt(t)))) return null;
|
|
4
|
+
let r = e.lastIndexOf("@", t - 1);
|
|
5
|
+
if (r < 0 || t <= r || r > 0 && !/[\s(\[{]/u.test(e[r - 1])) return null;
|
|
6
|
+
let i = e.slice(r + 1, t);
|
|
7
|
+
return i.length > 261 || /[@\r\n]/u.test(i) ? null : {
|
|
8
|
+
start: r,
|
|
9
|
+
end: t,
|
|
10
|
+
prefix: i
|
|
11
|
+
};
|
|
12
|
+
}, t = (t, n, r, i) => {
|
|
13
|
+
if (!r || r.length > 256 || /[@\r\n]/u.test(r) || !/#\d{4}$/u.test(r) || e(t, n.end, n.end)?.start !== n.start || t.slice(n.start + 1, n.end) !== n.prefix) return null;
|
|
14
|
+
let a = "@" + r + " ", o = t.slice(0, n.start) + a + t.slice(n.end);
|
|
15
|
+
return [...o].length <= i ? {
|
|
16
|
+
body: o,
|
|
17
|
+
caret: n.start + a.length
|
|
18
|
+
} : null;
|
|
19
|
+
};
|
|
20
|
+
export { t as insertMentionDraft, e as mentionDraftToken };
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { chatError as e } from "./errors.js";
|
|
2
|
+
import { MESSAGES_TABLE as t } from "./contract.js";
|
|
3
|
+
import { chatEventTenant as n, committedChatMentions as r } from "./eventProducer.js";
|
|
4
|
+
import { ChatAdministration as i } from "./server.js";
|
|
5
|
+
import { Effect as a, Schema as o } from "effect";
|
|
6
|
+
import { createHash as s } from "node:crypto";
|
|
7
|
+
import { defineOutboxHandler as c, defineSubscriber as l } from "@voltro/runtime";
|
|
8
|
+
import { defineNotification as u, notificationOutboxHandler as d } from "@voltro/plugin-notifications";
|
|
9
|
+
// src/mentionNotifications.ts
|
|
10
|
+
var f = "voltro.chat.mention.notify", p = "chat.mention", m = o.NonEmptyString.pipe(o.check(o.isMaxLength(256)), o.check(o.makeFilter((e) => e.trim().length > 0 && !e.includes("\0") && e !== "~"))), h = o.Struct({
|
|
11
|
+
tenantId: m,
|
|
12
|
+
channelId: m,
|
|
13
|
+
messageId: m,
|
|
14
|
+
recipientId: m
|
|
15
|
+
}), g = o.Struct({
|
|
16
|
+
tenantId: m,
|
|
17
|
+
channelId: m,
|
|
18
|
+
messageId: m,
|
|
19
|
+
recipientId: m,
|
|
20
|
+
locale: o.NonEmptyString.pipe(o.check(o.isMaxLength(80)))
|
|
21
|
+
}), _ = u({
|
|
22
|
+
name: "chat.mentioned",
|
|
23
|
+
audience: o.Struct({ userId: m }),
|
|
24
|
+
payload: o.Struct({
|
|
25
|
+
channelId: m,
|
|
26
|
+
messageId: m
|
|
27
|
+
}),
|
|
28
|
+
render: (e, t) => ({
|
|
29
|
+
title: t.locale.startsWith("de") ? "Du wurdest erwähnt" : "You were mentioned",
|
|
30
|
+
body: t.locale.startsWith("de") ? "Öffne den Chat, um die Nachricht zu lesen." : "Open the chat to read the message.",
|
|
31
|
+
data: {
|
|
32
|
+
channelId: e.channelId,
|
|
33
|
+
messageId: e.messageId
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
}), v = (u) => {
|
|
37
|
+
let m = structuredClone(u.subject), v = u.run, y = u.service, b = u.outboxForTenant, x = u.localeFor, S = (e) => v(a.flatMap(i, (t) => t.canNotifyMention(m, e))), C = async (e) => {
|
|
38
|
+
let t = o.decodeUnknownSync(h)(e.payload);
|
|
39
|
+
return e.tenantId !== t.tenantId || e.recipientId !== t.recipientId ? !1 : S(t);
|
|
40
|
+
}, w = async (e) => {
|
|
41
|
+
let t = o.decodeUnknownSync(h)(structuredClone(e));
|
|
42
|
+
if (!await S(t)) return null;
|
|
43
|
+
let n = await x?.(t.recipientId, t.tenantId) ?? "en", r = o.decodeUnknownSync(g)({
|
|
44
|
+
...t,
|
|
45
|
+
locale: n
|
|
46
|
+
}), i = s("sha256").update(JSON.stringify([
|
|
47
|
+
t.tenantId,
|
|
48
|
+
t.messageId,
|
|
49
|
+
t.recipientId
|
|
50
|
+
])).digest("hex");
|
|
51
|
+
return b(t.tenantId).enqueue(f, { ...r }, {
|
|
52
|
+
idempotencyKey: "chat.mention:" + i,
|
|
53
|
+
maxAttempts: 5
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
deliveryGuard: C,
|
|
58
|
+
enqueue: w,
|
|
59
|
+
outboxHandler: c({
|
|
60
|
+
effect: f,
|
|
61
|
+
maxAttempts: 5,
|
|
62
|
+
handler: async (t) => {
|
|
63
|
+
let n = o.decodeUnknownSync(g)(t.payload);
|
|
64
|
+
if (t.tenantId !== n.tenantId) throw e("identity_required");
|
|
65
|
+
if (!await S(n)) return { skipped: !0 };
|
|
66
|
+
let r = y();
|
|
67
|
+
if (!r) throw e("unavailable");
|
|
68
|
+
return d({
|
|
69
|
+
notifications: [_],
|
|
70
|
+
service: () => ({ send: async (t) => {
|
|
71
|
+
let i = await r.send({
|
|
72
|
+
...t,
|
|
73
|
+
deliveryGuard: {
|
|
74
|
+
id: p,
|
|
75
|
+
payload: {
|
|
76
|
+
tenantId: n.tenantId,
|
|
77
|
+
channelId: n.channelId,
|
|
78
|
+
messageId: n.messageId,
|
|
79
|
+
recipientId: n.recipientId
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
if (i.failed.length > 0) throw e("unavailable");
|
|
84
|
+
return i;
|
|
85
|
+
} })
|
|
86
|
+
}).handler({ payload: {
|
|
87
|
+
notification: _.name,
|
|
88
|
+
recipientId: n.recipientId,
|
|
89
|
+
locale: n.locale,
|
|
90
|
+
tenantId: n.tenantId,
|
|
91
|
+
payload: {
|
|
92
|
+
channelId: n.channelId,
|
|
93
|
+
messageId: n.messageId
|
|
94
|
+
}
|
|
95
|
+
} });
|
|
96
|
+
}
|
|
97
|
+
}),
|
|
98
|
+
subscriber: l({
|
|
99
|
+
table: t,
|
|
100
|
+
on: ["insert"],
|
|
101
|
+
once: !0,
|
|
102
|
+
eventTenant: n,
|
|
103
|
+
handler: async (e) => {
|
|
104
|
+
for (let t of r(e)) await w(t);
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
export { f as CHAT_MENTION_NOTIFICATION_EFFECT, p as CHAT_MENTION_NOTIFICATION_GUARD, v as createChatMentionNotifications };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { MESSAGES_TABLE as e } from "./contract.js";
|
|
2
|
+
import { boolean as t, id as n, integer as r, json as i, table as a, text as o, timestamp as s } from "@voltro/database";
|
|
3
|
+
// src/messages.entity.ts
|
|
4
|
+
var c = a(e, {
|
|
5
|
+
id: n({ prefix: "cmsg" }),
|
|
6
|
+
tenantId: o().maxLength(256).externalId().sensitive("pii"),
|
|
7
|
+
channelId: o().maxLength(64).safe(),
|
|
8
|
+
authorId: o().maxLength(256).nullable().externalId().sensitive("pii"),
|
|
9
|
+
requestKey: o().maxLength(64).serverOnly(),
|
|
10
|
+
kind: o().maxLength(16).oneOf([
|
|
11
|
+
"text",
|
|
12
|
+
"emote",
|
|
13
|
+
"system",
|
|
14
|
+
"media"
|
|
15
|
+
]).safe(),
|
|
16
|
+
body: o().sensitive("pii"),
|
|
17
|
+
originalBody: o().sensitive("pii").serverOnly(),
|
|
18
|
+
payload: i().sensitive("pii"),
|
|
19
|
+
replyToId: o().maxLength(64).nullable().safe(),
|
|
20
|
+
editedAt: s().nullable().safe(),
|
|
21
|
+
deletedAt: s().nullable().safe(),
|
|
22
|
+
flagged: t().safe(),
|
|
23
|
+
createdAt: s().safe(),
|
|
24
|
+
version: r().safe(),
|
|
25
|
+
metadata: i().sensitive("pii")
|
|
26
|
+
}).index(["channelId", "createdAt"]).index(["tenantId", "authorId"]);
|
|
27
|
+
export { c as chatMessagesTable };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ChatAdministration as e } from "./server.js";
|
|
2
|
+
import { Effect as t } from "effect";
|
|
3
|
+
import { sanctionsError as n } from "@voltro/plugin-sanctions/errors";
|
|
4
|
+
// src/reportEvidence.ts
|
|
5
|
+
var r = (r) => t.gen(function* () {
|
|
6
|
+
if (r.context.channelId === void 0) return [];
|
|
7
|
+
if (!Array.isArray(r.context.messageIds) || r.context.messageIds.length !== 1) return yield* t.fail(n("invalid_input"));
|
|
8
|
+
let i = yield* t.serviceOption(e);
|
|
9
|
+
return i._tag === "None" ? yield* t.fail(n("unavailable")) : [{ ...yield* i.value.collectReportContext(r.subject, {
|
|
10
|
+
channelId: r.context.channelId,
|
|
11
|
+
messageId: r.context.messageIds[0],
|
|
12
|
+
targetId: r.targetId
|
|
13
|
+
}).pipe(t.mapError((e) => n(e.code === "unavailable" ? "unavailable" : "forbidden")), t.catchDefect(() => t.fail(n("unavailable")))) }];
|
|
14
|
+
});
|
|
15
|
+
export { r as chatSanctionEvidenceCollector };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { id as e, integer as t, table as n, text as r, timestamp as i } from "@voltro/database";
|
|
2
|
+
// src/retention.entity.ts
|
|
3
|
+
var a = n("_voltro_chat_retention", {
|
|
4
|
+
id: e({ prefix: "cret" }),
|
|
5
|
+
tenantId: r().maxLength(256).nullable().serverOnly(),
|
|
6
|
+
afterChannelId: r().maxLength(64).nullable().serverOnly(),
|
|
7
|
+
admission: t().serverOnly(),
|
|
8
|
+
updatedAt: i().serverOnly()
|
|
9
|
+
});
|
|
10
|
+
export { a as chatRetentionTable };
|