@robono/server 0.14.0 → 0.16.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 +20 -1
- package/NOTIFICATIONS.md +149 -0
- package/README.md +35 -1
- package/SBOM.spdx.json +8 -8
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/location.js +1 -1
- package/dist/location.js.map +1 -1
- package/dist/managed-media.d.ts +9 -0
- package/dist/managed-media.d.ts.map +1 -1
- package/dist/managed-media.js +1 -0
- package/dist/managed-media.js.map +1 -1
- package/dist/messaging-media.d.ts +10 -0
- package/dist/messaging-media.d.ts.map +1 -1
- package/dist/messaging-media.js +12 -4
- package/dist/messaging-media.js.map +1 -1
- package/dist/messaging-notification-postgres.d.ts +7 -0
- package/dist/messaging-notification-postgres.d.ts.map +1 -0
- package/dist/messaging-notification-postgres.js +32 -0
- package/dist/messaging-notification-postgres.js.map +1 -0
- package/dist/messaging-notification-providers.d.ts +26 -0
- package/dist/messaging-notification-providers.d.ts.map +1 -0
- package/dist/messaging-notification-providers.js +107 -0
- package/dist/messaging-notification-providers.js.map +1 -0
- package/dist/messaging-notifications.d.ts +66 -0
- package/dist/messaging-notifications.d.ts.map +1 -0
- package/dist/messaging-notifications.js +128 -0
- package/dist/messaging-notifications.js.map +1 -0
- package/dist/messaging-transforms.d.ts.map +1 -1
- package/dist/messaging-transforms.js +26 -5
- package/dist/messaging-transforms.js.map +1 -1
- package/dist/messaging.d.ts +11 -1
- package/dist/messaging.d.ts.map +1 -1
- package/dist/messaging.js +65 -7
- package/dist/messaging.js.map +1 -1
- package/dist/messenger.d.ts +53 -0
- package/dist/messenger.d.ts.map +1 -0
- package/dist/messenger.js +89 -0
- package/dist/messenger.js.map +1 -0
- package/dist/push.d.ts.map +1 -1
- package/dist/push.js +1 -0
- package/dist/push.js.map +1 -1
- package/dist/types.d.ts +12 -3
- package/dist/types.d.ts.map +1 -1
- package/dist/webhooks.d.ts.map +1 -1
- package/dist/webhooks.js +11 -1
- package/dist/webhooks.js.map +1 -1
- package/package.json +4 -2
- package/sql/messaging-notifications.sql +41 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { createHash, createPrivateKey, sign } from 'node:crypto';
|
|
2
|
+
import { connect } from 'node:http2';
|
|
3
|
+
const b64 = (data) => Buffer.from(JSON.stringify(data)).toString('base64url');
|
|
4
|
+
function jwt(header, claims, key, algorithm, p1363 = false) {
|
|
5
|
+
const body = b64(header) + '.' + b64(claims);
|
|
6
|
+
const signature = sign(algorithm, Buffer.from(body), { key: createPrivateKey(key), ...(p1363 ? { dsaEncoding: 'ieee-p1363' } : {}) });
|
|
7
|
+
return body + '.' + signature.toString('base64url');
|
|
8
|
+
}
|
|
9
|
+
function collapse(data) { return createHash('sha256').update(data.accountScope + ':' + data.conversationId).digest('hex'); }
|
|
10
|
+
/** Uses only fixed Apple/Google endpoints. Private keys never enter notification payloads. */
|
|
11
|
+
export function createMessagingPushSender(options) {
|
|
12
|
+
const title = options.title?.trim() || 'New message';
|
|
13
|
+
if (title.length > 80)
|
|
14
|
+
throw Error('Use a notification title of 80 characters or fewer.');
|
|
15
|
+
const apple = options.apple, google = options.google;
|
|
16
|
+
if (apple && (!/^[A-Z\d]{10}$/.test(apple.teamId) || !/^[A-Z\d]{10}$/.test(apple.keyId) || !/^[-\w]+(?:\.[-\w]+)+$/.test(apple.bundleId)))
|
|
17
|
+
throw Error('Check the Apple Team ID, Key ID and app bundle ID.');
|
|
18
|
+
if (google && (!/^[a-z][a-z\d-]{4,61}[a-z\d]$/.test(google.projectId) || !/^[^\s@]+@[^\s@]+\.gserviceaccount\.com$/.test(google.clientEmail)))
|
|
19
|
+
throw Error('Check the Google project ID and service-account email.');
|
|
20
|
+
if (apple && (createPrivateKey(apple.privateKey).asymmetricKeyType !== 'ec' || createPrivateKey(apple.privateKey).asymmetricKeyDetails?.namedCurve !== 'prime256v1'))
|
|
21
|
+
throw Error('Use the Apple APNs .p8 signing key.');
|
|
22
|
+
if (google && createPrivateKey(google.privateKey).asymmetricKeyType !== 'rsa')
|
|
23
|
+
throw Error('Use the Google service-account private key.');
|
|
24
|
+
let appleToken = { value: '', expires: 0 }, googleToken = { value: '', expires: 0 }, googlePending;
|
|
25
|
+
const request = options.fetch ?? fetch;
|
|
26
|
+
async function googleAccess() {
|
|
27
|
+
if (googleToken.expires > Date.now())
|
|
28
|
+
return googleToken.value;
|
|
29
|
+
if (googlePending)
|
|
30
|
+
return googlePending;
|
|
31
|
+
googlePending = (async () => {
|
|
32
|
+
const now = Math.floor(Date.now() / 1000), assertion = jwt({ alg: 'RS256', typ: 'JWT' }, { iss: google.clientEmail, scope: 'https://www.googleapis.com/auth/firebase.messaging', aud: 'https://oauth2.googleapis.com/token', iat: now, exp: now + 3600 }, google.privateKey, 'RSA-SHA256');
|
|
33
|
+
const response = await request('https://oauth2.googleapis.com/token', { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(15000), headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer', assertion }) });
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
if (!response.ok || !data.access_token || typeof data.expires_in !== 'number')
|
|
36
|
+
throw Error('Google notification authentication failed.');
|
|
37
|
+
googleToken = { value: data.access_token, expires: Date.now() + Math.max(0, Math.min(data.expires_in, 3600) - 60) * 1000 };
|
|
38
|
+
return data.access_token;
|
|
39
|
+
})();
|
|
40
|
+
try {
|
|
41
|
+
return await googlePending;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
googlePending = undefined;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function sendApple(device, data) {
|
|
48
|
+
if (!apple)
|
|
49
|
+
return { accepted: false, retryable: false, code: 'apple_credentials_missing' };
|
|
50
|
+
if (!/^[a-f\d]{64,200}$/i.test(device.token))
|
|
51
|
+
return { accepted: false, invalidDevice: true, code: 'invalid_token' };
|
|
52
|
+
if (appleToken.expires <= Date.now())
|
|
53
|
+
appleToken = { value: jwt({ alg: 'ES256', kid: apple.keyId }, { iss: apple.teamId, iat: Math.floor(Date.now() / 1000) }, apple.privateKey, 'sha256', true), expires: Date.now() + 45 * 60000 };
|
|
54
|
+
const payload = JSON.stringify({ aps: { alert: { title, body: 'Open the app to read your message.' }, sound: 'default', 'thread-id': collapse(data) }, body: data });
|
|
55
|
+
return new Promise(resolve => {
|
|
56
|
+
const session = connect(apple.sandbox ? 'https://api.sandbox.push.apple.com' : 'https://api.push.apple.com');
|
|
57
|
+
let finished = false;
|
|
58
|
+
const finish = (result) => { if (finished)
|
|
59
|
+
return; finished = true; clearTimeout(timer); session.destroy(); resolve(result); };
|
|
60
|
+
const timer = setTimeout(() => finish({ accepted: false, retryable: true, code: 'apple_timeout' }), 15000);
|
|
61
|
+
session.on('error', () => finish({ accepted: false, retryable: true, code: 'apple_connection_failed' }));
|
|
62
|
+
const stream = session.request({ ':method': 'POST', ':path': '/3/device/' + device.token, authorization: 'bearer ' + appleToken.value, 'apns-topic': apple.bundleId, 'apns-push-type': 'alert', 'apns-priority': '10', 'apns-expiration': String(Math.floor(Date.now() / 1000) + 3600), 'apns-collapse-id': collapse(data), 'content-type': 'application/json' });
|
|
63
|
+
let status = 0, body = '';
|
|
64
|
+
stream.setEncoding('utf8');
|
|
65
|
+
stream.on('response', headers => { status = Number(headers[':status']); });
|
|
66
|
+
stream.on('data', chunk => { if (body.length < 8192)
|
|
67
|
+
body += chunk; });
|
|
68
|
+
stream.on('error', () => finish({ accepted: false, retryable: true, code: 'apple_connection_failed' }));
|
|
69
|
+
stream.on('end', () => {
|
|
70
|
+
let reason = '';
|
|
71
|
+
try {
|
|
72
|
+
reason = JSON.parse(body).reason;
|
|
73
|
+
}
|
|
74
|
+
catch { }
|
|
75
|
+
if (status === 403)
|
|
76
|
+
appleToken = { value: '', expires: 0 };
|
|
77
|
+
finish({ accepted: status === 200, invalidDevice: status === 410 || reason === 'BadDeviceToken', retryable: reason === 'ExpiredProviderToken' || status === 429 || status >= 500, code: status === 200 ? 'accepted' : status === 403 ? 'apple_credentials_rejected' : 'apple_rejected' });
|
|
78
|
+
});
|
|
79
|
+
stream.end(payload);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
async function sendGoogle(device, data) {
|
|
83
|
+
if (!google)
|
|
84
|
+
return { accepted: false, retryable: false, code: 'google_credentials_missing' };
|
|
85
|
+
const response = await request('https://fcm.googleapis.com/v1/projects/' + encodeURIComponent(google.projectId) + '/messages:send', {
|
|
86
|
+
method: 'POST', redirect: 'error', signal: AbortSignal.timeout(15000), headers: { authorization: 'Bearer ' + await googleAccess(), 'content-type': 'application/json' },
|
|
87
|
+
body: JSON.stringify({ message: { token: device.token, notification: { title, body: 'Open the app to read your message.' }, data,
|
|
88
|
+
android: { priority: 'high', ttl: '3600s', collapse_key: collapse(data), notification: { tag: collapse(data), channel_id: 'robono-messages' } } } }),
|
|
89
|
+
});
|
|
90
|
+
const result = await response.json().catch(() => null);
|
|
91
|
+
if (response.status === 401)
|
|
92
|
+
googleToken = { value: '', expires: 0 };
|
|
93
|
+
return { accepted: response.ok, invalidDevice: result?.error?.details?.some(detail => detail.errorCode === 'UNREGISTERED') ?? false,
|
|
94
|
+
retryable: response.status === 401 || response.status === 429 || response.status >= 500, code: response.ok ? 'accepted' : response.status === 401 || response.status === 403 ? 'google_credentials_rejected' : 'google_rejected' };
|
|
95
|
+
}
|
|
96
|
+
return { providers: [...(apple ? ['apns'] : []), ...(google ? ['fcm'] : [])],
|
|
97
|
+
async send(device, data) {
|
|
98
|
+
try {
|
|
99
|
+
return device.provider === 'apns' ? await sendApple(device, data) : await sendGoogle(device, data);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { accepted: false, retryable: true, code: 'provider_unavailable' };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=messaging-notification-providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging-notification-providers.js","sourceRoot":"","sources":["../src/messaging-notification-providers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { MessagingCreatedWebhookEvent } from './types.js';
|
|
2
|
+
export interface MessagingPushData {
|
|
3
|
+
event: 'robono.messaging';
|
|
4
|
+
eventId: string;
|
|
5
|
+
conversationId: string;
|
|
6
|
+
messageId: string;
|
|
7
|
+
accountScope: string;
|
|
8
|
+
}
|
|
9
|
+
export interface MessagingPushDevice {
|
|
10
|
+
id: string;
|
|
11
|
+
userId: string;
|
|
12
|
+
provider: 'apns' | 'fcm';
|
|
13
|
+
token: string;
|
|
14
|
+
}
|
|
15
|
+
export interface MessagingPushJob {
|
|
16
|
+
id: string;
|
|
17
|
+
lease: string;
|
|
18
|
+
event: MessagingCreatedWebhookEvent;
|
|
19
|
+
device: MessagingPushDevice;
|
|
20
|
+
attempt: number;
|
|
21
|
+
}
|
|
22
|
+
export interface MessagingNotificationStore {
|
|
23
|
+
/** Register tokens only for the authenticated user; a token belongs to one account at a time. */
|
|
24
|
+
register(scope: string, device: MessagingPushDevice): Promise<void>;
|
|
25
|
+
remove(scope: string, userId: string, id: string, expectedToken?: string): Promise<void>;
|
|
26
|
+
/** Atomically snapshot this recipient's registered devices; duplicate events do not add jobs. */
|
|
27
|
+
enqueue(scope: string, event: MessagingCreatedWebhookEvent): Promise<void>;
|
|
28
|
+
claim(scope: string, limit: number, lease: string): Promise<MessagingPushJob[]>;
|
|
29
|
+
/** Recheck token ownership after taking a job and immediately before delivery. */
|
|
30
|
+
current(scope: string, device: MessagingPushDevice): Promise<boolean>;
|
|
31
|
+
finish(scope: string, job: MessagingPushJob, result: {
|
|
32
|
+
state: 'sent' | 'skipped' | 'retry' | 'failed';
|
|
33
|
+
retryAt?: Date;
|
|
34
|
+
code?: string;
|
|
35
|
+
}): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
export interface MessagingNotificationOptions {
|
|
38
|
+
/** Separate scope and webhook secret for each customer integration and test/live environment. */
|
|
39
|
+
scope: string;
|
|
40
|
+
webhookSecret: string;
|
|
41
|
+
store: MessagingNotificationStore;
|
|
42
|
+
authenticate: (request: Request) => Promise<string | null>;
|
|
43
|
+
/** Required: current account status, conversation access, block/mute state, message existence. */
|
|
44
|
+
mayNotify: (event: MessagingCreatedWebhookEvent) => Promise<boolean>;
|
|
45
|
+
send: (device: MessagingPushDevice, data: MessagingPushData) => Promise<{
|
|
46
|
+
accepted: boolean;
|
|
47
|
+
invalidDevice?: boolean;
|
|
48
|
+
retryable?: boolean;
|
|
49
|
+
code?: string;
|
|
50
|
+
}>;
|
|
51
|
+
/** Only providers with configured server credentials can be registered. */
|
|
52
|
+
providers: readonly ('apns' | 'fcm')[];
|
|
53
|
+
}
|
|
54
|
+
export declare function messagingNotificationAccount(scope: string, userId: string): string;
|
|
55
|
+
/** Durable, content-free notifications. Apple/Google acceptance is not a delivery/read receipt. */
|
|
56
|
+
export declare function createMessagingNotifications(options: MessagingNotificationOptions): {
|
|
57
|
+
devices(request: Request): Promise<Response>;
|
|
58
|
+
webhook(request: Request): Promise<Response>;
|
|
59
|
+
runPending(limit?: number): Promise<{
|
|
60
|
+
sent: number;
|
|
61
|
+
skipped: number;
|
|
62
|
+
retried: number;
|
|
63
|
+
failed: number;
|
|
64
|
+
}>;
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=messaging-notifications.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging-notifications.d.ts","sourceRoot":"","sources":["../src/messaging-notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
|
+
import { verifyRobonoWebhook } from './webhooks.js';
|
|
3
|
+
function id(value, max = 200) { return typeof value === 'string' && value.length > 0 && value.length <= max && value.trim() === value && !/[\u0000-\u001f\u007f]/.test(value); }
|
|
4
|
+
const reply = (data, status = 200) => Response.json(data, { status, headers: { 'cache-control': 'no-store' } });
|
|
5
|
+
export function messagingNotificationAccount(scope, userId) { return createHash('sha256').update(JSON.stringify([scope, userId])).digest('hex'); }
|
|
6
|
+
/** Durable, content-free notifications. Apple/Google acceptance is not a delivery/read receipt. */
|
|
7
|
+
export function createMessagingNotifications(options) {
|
|
8
|
+
if (!id(options.scope) || !options.webhookSecret.trim())
|
|
9
|
+
throw Error('Set a unique notification scope and this integration’s webhook secret.');
|
|
10
|
+
const { scope, store } = options;
|
|
11
|
+
return {
|
|
12
|
+
async devices(request) {
|
|
13
|
+
if (request.method !== 'POST')
|
|
14
|
+
return reply({ error: { code: 'method_not_allowed' } }, 405);
|
|
15
|
+
try {
|
|
16
|
+
const userId = await options.authenticate(request);
|
|
17
|
+
if (!id(userId))
|
|
18
|
+
return reply({ error: { code: 'authentication_required' } }, 401);
|
|
19
|
+
const raw = await request.text();
|
|
20
|
+
if (raw.length > 8192)
|
|
21
|
+
return reply({ error: { code: 'request_too_large' } }, 413);
|
|
22
|
+
let body;
|
|
23
|
+
try {
|
|
24
|
+
body = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return reply({ error: { code: 'invalid_request' } }, 422);
|
|
28
|
+
}
|
|
29
|
+
const accountScope = messagingNotificationAccount(scope, userId);
|
|
30
|
+
if (body?.operation === 'notification-status')
|
|
31
|
+
return reply({ userId, accountScope, providers: options.providers });
|
|
32
|
+
if (body?.accountScope !== accountScope)
|
|
33
|
+
return reply({ error: { code: 'notification_account_changed' } }, 409);
|
|
34
|
+
if (!body || typeof body !== 'object' || !id(body.deviceId))
|
|
35
|
+
return reply({ error: { code: 'invalid_device' } }, 422);
|
|
36
|
+
if (body.operation === 'remove-device') {
|
|
37
|
+
await store.remove(scope, userId, body.deviceId);
|
|
38
|
+
return reply({ ok: true });
|
|
39
|
+
}
|
|
40
|
+
if (body.operation !== 'register-device' || !['apns', 'fcm'].includes(String(body.provider)) || !id(body.token, 4096))
|
|
41
|
+
return reply({ error: { code: 'invalid_device' } }, 422);
|
|
42
|
+
const provider = body.provider;
|
|
43
|
+
if (!options.providers.includes(provider))
|
|
44
|
+
return reply({ error: { code: 'notification_credentials_missing', message: provider === 'apns' ? 'Your app’s Apple notification credentials have not been connected.' : 'Your app’s Google notification credentials have not been connected.' } }, 409);
|
|
45
|
+
if (provider === 'apns' && !/^[a-f\d]{64,200}$/i.test(body.token))
|
|
46
|
+
return reply({ error: { code: 'invalid_device' } }, 422);
|
|
47
|
+
await store.register(scope, { id: body.deviceId, userId, provider, token: body.token });
|
|
48
|
+
return reply({ ok: true, accountScope: messagingNotificationAccount(scope, userId) });
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return reply({ error: { code: 'notifications_unavailable' } }, 503);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
async webhook(request) {
|
|
55
|
+
if (request.method !== 'POST')
|
|
56
|
+
return reply({ error: { code: 'method_not_allowed' } }, 405);
|
|
57
|
+
const raw = await request.text();
|
|
58
|
+
if (raw.length > 32768)
|
|
59
|
+
return reply({ error: { code: 'request_too_large' } }, 413);
|
|
60
|
+
let event;
|
|
61
|
+
try {
|
|
62
|
+
({ event } = await verifyRobonoWebhook(raw, request.headers, options.webhookSecret));
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return reply({ error: { code: 'invalid_webhook' } }, 401);
|
|
66
|
+
}
|
|
67
|
+
if (event.event !== 'messaging.message_created')
|
|
68
|
+
return reply({ ignored: true });
|
|
69
|
+
// Do not acknowledge until the queue insert is durable. Delivery is retried by the worker.
|
|
70
|
+
try {
|
|
71
|
+
await store.enqueue(scope, event);
|
|
72
|
+
return reply({ queued: true }, 202);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return reply({ error: { code: 'notifications_unavailable' } }, 503);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
async runPending(limit = 20) {
|
|
79
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100)
|
|
80
|
+
throw Error('Use a notification batch size from 1 to 100.');
|
|
81
|
+
const result = { sent: 0, skipped: 0, retried: 0, failed: 0 };
|
|
82
|
+
// Small batches avoid unbounded fanout and provider rate spikes.
|
|
83
|
+
for (let index = 0; index < limit; index++) {
|
|
84
|
+
const [job] = await store.claim(scope, 1, randomUUID());
|
|
85
|
+
if (!job)
|
|
86
|
+
break;
|
|
87
|
+
try {
|
|
88
|
+
const event = job.event;
|
|
89
|
+
if (job.device.userId !== event.recipient_external_user_id || event.notification_muted === true ||
|
|
90
|
+
Date.parse(event.created_at) < Date.now() - 86400000 || !await options.mayNotify(event) || !await store.current(scope, job.device)) {
|
|
91
|
+
await store.finish(scope, job, { state: 'skipped' });
|
|
92
|
+
result.skipped++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const data = { event: 'robono.messaging', eventId: event.event_id,
|
|
96
|
+
conversationId: event.conversation_id, messageId: event.message_id, accountScope: messagingNotificationAccount(scope, job.device.userId) };
|
|
97
|
+
const sent = await options.send(job.device, data);
|
|
98
|
+
if (sent.invalidDevice) {
|
|
99
|
+
await store.remove(scope, job.device.userId, job.device.id, job.device.token);
|
|
100
|
+
await store.finish(scope, job, { state: 'skipped', code: 'device_unregistered' });
|
|
101
|
+
result.skipped++;
|
|
102
|
+
}
|
|
103
|
+
else if (sent.accepted) {
|
|
104
|
+
await store.finish(scope, job, { state: 'sent' });
|
|
105
|
+
result.sent++;
|
|
106
|
+
}
|
|
107
|
+
else if (sent.retryable !== false && job.attempt < 8) {
|
|
108
|
+
await store.finish(scope, job, { state: 'retry', retryAt: new Date(Date.now() + Math.min(3600000, 15000 * 2 ** job.attempt)), code: sent.code ?? 'provider_unavailable' });
|
|
109
|
+
result.retried++;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
await store.finish(scope, job, { state: 'failed', code: sent.code ?? 'provider_rejected' });
|
|
113
|
+
result.failed++;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
await store.finish(scope, job, job.attempt < 8 ? { state: 'retry', retryAt: new Date(Date.now() + Math.min(3600000, 15000 * 2 ** job.attempt)), code: 'delivery_unavailable' } : { state: 'failed', code: 'delivery_unavailable' });
|
|
118
|
+
if (job.attempt < 8)
|
|
119
|
+
result.retried++;
|
|
120
|
+
else
|
|
121
|
+
result.failed++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return result;
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=messaging-notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging-notifications.js","sourceRoot":"","sources":["../src/messaging-notifications.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messaging-transforms.d.ts","sourceRoot":"","sources":["../src/messaging-transforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAG9C,uGAAuG;AACvG,wBAAgB,sCAAsC,CAAC,OAAO,EAAC;IAC9D,MAAM,EAAC,sBAAsB,CAAC;IAAA,MAAM,EAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAC,QAAQ,CAAC,CAAC;IAC/E,OAAO,EAAC,IAAI,CAAC,uBAAuB,EAAC,UAAU,CAAC,CAAC;IACjD,YAAY,CAAC,OAAO,EAAC,OAAO,GAAE,OAAO,CAAC,MAAM,GAAC,IAAI,CAAC,CAAC;IACnD,SAAS,CAAC,KAAK,EAAC;QAAC,OAAO,EAAC,MAAM,CAAC;QAAA,cAAc,EAAC,MAAM,CAAC;QAAA,SAAS,EAAC,MAAM,CAAC;QAAA,SAAS,EAAC,YAAY,GAAC,aAAa,CAAC;QAAA,OAAO,EAAC,OAAO,CAAA;KAAC,GAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/I,IACa,SAAQ,OAAO,
|
|
1
|
+
{"version":3,"file":"messaging-transforms.d.ts","sourceRoot":"","sources":["../src/messaging-transforms.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,sBAAsB,EAAC,MAAM,gBAAgB,CAAC;AAC3D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAG9C,uGAAuG;AACvG,wBAAgB,sCAAsC,CAAC,OAAO,EAAC;IAC9D,MAAM,EAAC,sBAAsB,CAAC;IAAA,MAAM,EAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAC,QAAQ,CAAC,CAAC;IAC/E,OAAO,EAAC,IAAI,CAAC,uBAAuB,EAAC,UAAU,CAAC,CAAC;IACjD,YAAY,CAAC,OAAO,EAAC,OAAO,GAAE,OAAO,CAAC,MAAM,GAAC,IAAI,CAAC,CAAC;IACnD,SAAS,CAAC,KAAK,EAAC;QAAC,OAAO,EAAC,MAAM,CAAC;QAAA,cAAc,EAAC,MAAM,CAAC;QAAA,SAAS,EAAC,MAAM,CAAC;QAAA,SAAS,EAAC,YAAY,GAAC,aAAa,CAAC;QAAA,OAAO,EAAC,OAAO,CAAA;KAAC,GAAE,OAAO,CAAC,OAAO,CAAC,CAAC;CAC/I,IACa,SAAQ,OAAO,uBA4C5B"}
|
|
@@ -21,14 +21,23 @@ export function createMessagingTransformBackendAdapter(options) {
|
|
|
21
21
|
}
|
|
22
22
|
if (!input || !['transcript', 'translation'].includes(input.operation) || typeof input.conversationId !== 'string' || !/^[0-9a-f-]{36}$/i.test(input.conversationId) || typeof input.messageId !== 'string' || !/^\d{1,19}$/.test(input.messageId) || BigInt(input.messageId) < 1n || BigInt(input.messageId) > 9223372036854775807n || input.operation === 'translation' && (typeof input.language !== 'string' || !/^[a-z]{2,3}(?:-[a-z0-9]{2,8}){0,2}$/i.test(input.language)))
|
|
23
23
|
return reply({ error: { code: 'invalid_transform' } }, 422);
|
|
24
|
+
if (input.output !== undefined && !['text', 'voice'].includes(input.output) || input.operation === 'transcript' && input.output === 'voice')
|
|
25
|
+
return reply({ error: { code: 'invalid_transform' } }, 422);
|
|
26
|
+
const voice = input.operation === 'translation' && input.output === 'voice';
|
|
24
27
|
const context = { actorId, conversationId: input.conversationId, messageId: input.messageId, operation: input.operation, request };
|
|
25
28
|
if (!await options.authorize(context))
|
|
26
29
|
return reply({ error: { code: 'operation_not_allowed' } }, 403);
|
|
27
|
-
const read = () => options.robono.request('list', { actorId, conversationId: input.conversationId, limit: 1, messageIds: [input.messageId] });
|
|
30
|
+
const read = () => options.robono.request('list', { actorId, conversationId: input.conversationId, limit: 1, messageIds: [input.messageId], messageActionsVersion: 1 });
|
|
28
31
|
const page = await read(), message = page.messages?.find((m) => m.id === input.messageId), flag = input.operation === 'transcript' ? 'transcription' : 'translation';
|
|
29
32
|
const permitted = (p) => p.settings?.enabled && p.settings.features?.[flag] === true;
|
|
30
33
|
if (!permitted(page) || !message || message.deleted || input.operation === 'transcript' && message.kind !== 'voice' || !['voice', 'text'].includes(message.kind))
|
|
31
34
|
return reply({ error: { code: 'transform_unavailable' } }, 403);
|
|
35
|
+
const signature = (m) => JSON.stringify({ kind: m.kind, text: m.text, media: m.media, revision: m.revision });
|
|
36
|
+
const original = signature(message);
|
|
37
|
+
// A transcript or spoken translation reveals the recording just as playback
|
|
38
|
+
// does. Lock incoming media before processing when the current backend supports it.
|
|
39
|
+
if (message.kind === 'voice' && message.senderId !== actorId && page.messageActionsAvailable)
|
|
40
|
+
await options.robono.request('consume', { actorId, conversationId: input.conversationId, messageId: message.id });
|
|
32
41
|
const source = message.kind === 'text' ? { type: 'text', text: message.text } : await (async () => {
|
|
33
42
|
const plan = await options.storage.download({ actorId, conversationId: input.conversationId, messageId: message.id, media: message.media });
|
|
34
43
|
const url = new URL(plan.url);
|
|
@@ -37,16 +46,28 @@ export function createMessagingTransformBackendAdapter(options) {
|
|
|
37
46
|
return { type: 'audio', source_url: plan.url, mime_type: message.media.mimeType };
|
|
38
47
|
})();
|
|
39
48
|
const language = input.operation === 'translation' ? input.language : undefined;
|
|
40
|
-
const key = `messaging:${input.conversationId}:${message.id}:${input.operation}:${language ?? 'auto'}`;
|
|
41
|
-
const result = await options.speech.speech({ external_request_id: key, input: source, outputs: [input.operation === 'transcript' ? 'transcript' : 'translated_text'], ...(language ? { target_language: language } : {}) }, { idempotencyKey: key });
|
|
49
|
+
const key = `messaging:${input.conversationId}:${message.id}:r${message.revision ?? 0}:${input.operation}:${language ?? 'auto'}${voice ? ':voice' : ''}`;
|
|
50
|
+
const result = await options.speech.speech({ external_request_id: key, input: source, outputs: voice ? ['translated_text', 'translated_voice'] : [input.operation === 'transcript' ? 'transcript' : 'translated_text'], ...(language ? { target_language: language } : {}) }, { idempotencyKey: key });
|
|
42
51
|
// A block, removal or retention change during processing must not reveal text.
|
|
43
52
|
const current = await read();
|
|
44
|
-
if (!permitted(current) || !current.messages?.some((m) => m.id === message.id && !m.deleted) || !await options.authorize(context))
|
|
53
|
+
if (!permitted(current) || !current.messages?.some((m) => m.id === message.id && !m.deleted && signature(m) === original) || await options.authenticate(request) !== actorId || !await options.authorize(context))
|
|
45
54
|
return reply({ error: { code: 'transform_unavailable' } }, 403);
|
|
46
55
|
const artifact = input.operation === 'transcript' ? result.artifacts.transcript : result.artifacts.translated_text;
|
|
47
56
|
if (!artifact || typeof artifact.text !== 'string')
|
|
48
57
|
throw Error('Speech result unavailable');
|
|
49
|
-
|
|
58
|
+
let audio;
|
|
59
|
+
if (voice) {
|
|
60
|
+
const media = result.artifacts.translated_voice?.media;
|
|
61
|
+
if (!media || typeof media.source_url !== 'string' || typeof media.mime_type !== 'string' || !media.mime_type.startsWith('audio/') || !Number.isFinite(Date.parse(media.source_url_expires_at)) || Date.parse(media.source_url_expires_at) <= Date.now())
|
|
62
|
+
throw Error('Translated voice is unavailable');
|
|
63
|
+
if (!Number.isSafeInteger(media.byte_size) || media.byte_size < 1 || media.byte_size > 100 * 1024 * 1024 || media.duration_ms != null && (!Number.isSafeInteger(media.duration_ms) || media.duration_ms < 1 || media.duration_ms > 3600000) || media.waveform !== undefined && (!Array.isArray(media.waveform) || media.waveform.length > 128 || media.waveform.some((peak) => !Number.isFinite(peak) || peak < 0 || peak > 1)))
|
|
64
|
+
throw Error('Invalid translated voice metadata');
|
|
65
|
+
const url = new URL(media.source_url);
|
|
66
|
+
if (url.protocol !== 'https:' || url.username || url.password)
|
|
67
|
+
throw Error('Invalid translated voice URL');
|
|
68
|
+
audio = { url: url.href, expiresAt: media.source_url_expires_at, mimeType: media.mime_type, durationMs: media.duration_ms ?? undefined, byteSize: media.byte_size, waveform: media.waveform };
|
|
69
|
+
}
|
|
70
|
+
return reply({ messageId: message.id, text: artifact.text, language: artifact.target_language ?? artifact.language, kind: input.operation, ...(audio ? { audio } : {}) });
|
|
50
71
|
}
|
|
51
72
|
catch (e) {
|
|
52
73
|
if (e instanceof RobonoError)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messaging-transforms.js","sourceRoot":"","sources":["../src/messaging-transforms.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"messaging-transforms.js","sourceRoot":"","sources":["../src/messaging-transforms.ts"],"names":[],"mappings":"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"}
|
package/dist/messaging.d.ts
CHANGED
|
@@ -25,7 +25,9 @@ export declare class RobonoMessagingBackend {
|
|
|
25
25
|
private options;
|
|
26
26
|
private url;
|
|
27
27
|
constructor(options: MessagingBackendOptions);
|
|
28
|
-
request(operation: string, input?: Record<string, unknown
|
|
28
|
+
request(operation: string, input?: Record<string, unknown>, options?: {
|
|
29
|
+
signal?: AbortSignal;
|
|
30
|
+
}): Promise<any>;
|
|
29
31
|
setRelationship(input: {
|
|
30
32
|
externalUserIds: [string, string];
|
|
31
33
|
allowed: boolean;
|
|
@@ -37,6 +39,8 @@ export declare class RobonoMessagingBackend {
|
|
|
37
39
|
name: string;
|
|
38
40
|
revision: number;
|
|
39
41
|
allowed: boolean;
|
|
42
|
+
supergroup?: boolean;
|
|
43
|
+
ownerId?: string;
|
|
40
44
|
members: {
|
|
41
45
|
userId: string;
|
|
42
46
|
canSend: boolean;
|
|
@@ -53,6 +57,12 @@ export declare class RobonoMessagingBackend {
|
|
|
53
57
|
reviewerId: string;
|
|
54
58
|
}): Promise<any>;
|
|
55
59
|
settings(): Promise<any>;
|
|
60
|
+
/** Recheck the live recipient, message, membership, blocks and mute state before an alert. */
|
|
61
|
+
canNotify(input: {
|
|
62
|
+
userId: string;
|
|
63
|
+
conversationId: string;
|
|
64
|
+
messageId: string;
|
|
65
|
+
}): Promise<any>;
|
|
56
66
|
/** Erases this user's hosted conversations. Delete the corresponding private media in your own store too. */
|
|
57
67
|
eraseUser(userId: string): Promise<any>;
|
|
58
68
|
}
|
package/dist/messaging.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../src/messaging.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AACD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAC,MAAM,CAAC;IAAC,cAAc,EAAC,MAAM,CAAC;IAAC,QAAQ,EAAC,MAAM,CAAC;IAAC,IAAI,EAAC,OAAO,GAAC,OAAO,CAAC;IAC5E,KAAK,EAAC,OAAO,CAAC;IAAC,MAAM,EAAC,MAAM,CAAC;IAAC,UAAU,EAAC,MAAM,GAAC,IAAI,CAAC;IAAC,WAAW,EAAC,MAAM,GAAC,IAAI,CAAC;IAC9E,WAAW,EAAC;QAAC,YAAY,EAAC,OAAO,CAAC;QAAA,YAAY,EAAC,OAAO,CAAC;QAAA,aAAa,EAAC,OAAO,CAAC;QAAA,aAAa,EAAC,OAAO,CAAA;KAAC,CAAC;CACrG;AACD,0FAA0F;AAC1F,qBAAa,sBAAsB;IAErB,OAAO,CAAC,OAAO;IAD3B,OAAO,CAAC,GAAG,CAAS;gBACA,OAAO,EAAE,uBAAuB;IAU9C,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM;IA+
|
|
1
|
+
{"version":3,"file":"messaging.d.ts","sourceRoot":"","sources":["../src/messaging.ts"],"names":[],"mappings":"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"}
|
package/dist/messaging.js
CHANGED
|
@@ -11,11 +11,11 @@ export class RobonoMessagingBackend {
|
|
|
11
11
|
throw Error("Use an HTTPS endpoint and a server API key.");
|
|
12
12
|
this.url = url.href;
|
|
13
13
|
}
|
|
14
|
-
async request(operation, input = {}) {
|
|
14
|
+
async request(operation, input = {}, options) {
|
|
15
15
|
const result = await (this.options.fetch ?? fetch)(this.url, {
|
|
16
16
|
method: "POST",
|
|
17
17
|
redirect: "error",
|
|
18
|
-
signal: AbortSignal.timeout(15000),
|
|
18
|
+
signal: options?.signal ? AbortSignal.any([options.signal, AbortSignal.timeout(operation === 'wait' ? 35000 : 15000)]) : AbortSignal.timeout(operation === 'wait' ? 35000 : 15000),
|
|
19
19
|
headers: {
|
|
20
20
|
authorization: `Bearer ${this.options.apiKey}`,
|
|
21
21
|
"content-type": "application/json",
|
|
@@ -53,6 +53,21 @@ export class RobonoMessagingBackend {
|
|
|
53
53
|
settings() {
|
|
54
54
|
return this.request("settings");
|
|
55
55
|
}
|
|
56
|
+
/** Recheck the live recipient, message, membership, blocks and mute state before an alert. */
|
|
57
|
+
async canNotify(input) {
|
|
58
|
+
try {
|
|
59
|
+
const result = await this.request('list', { actorId: input.userId, conversationId: input.conversationId,
|
|
60
|
+
messageIds: [input.messageId], notificationMessageId: input.messageId });
|
|
61
|
+
if (typeof result.notificationAllowed !== 'boolean')
|
|
62
|
+
throw new RobonoError('Install the notification-access service update before enabling alerts.', { status: 503, code: 'notification_access_unavailable', retryable: true });
|
|
63
|
+
return result.notificationAllowed;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
if (error instanceof RobonoError && [401, 403, 404].includes(error.status ?? 0))
|
|
67
|
+
return false;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
56
71
|
/** Erases this user's hosted conversations. Delete the corresponding private media in your own store too. */
|
|
57
72
|
eraseUser(userId) {
|
|
58
73
|
return this.request("erase-user", { userId });
|
|
@@ -78,7 +93,7 @@ export function createMessagingBackendAdapter(options) {
|
|
|
78
93
|
return respond({ error: { code: "authentication_required" } }, 401);
|
|
79
94
|
}
|
|
80
95
|
const raw = await request.text();
|
|
81
|
-
if (raw.length >
|
|
96
|
+
if (raw.length > 900000) {
|
|
82
97
|
return respond({ error: { code: "request_too_large" } }, 413);
|
|
83
98
|
}
|
|
84
99
|
let body;
|
|
@@ -92,11 +107,17 @@ export function createMessagingBackendAdapter(options) {
|
|
|
92
107
|
return respond({ error: { code: "invalid_request" } }, 422);
|
|
93
108
|
}
|
|
94
109
|
const operation = String(body.operation ?? "");
|
|
95
|
-
if (
|
|
110
|
+
if (raw.length > 20000 && !["group-create", "group-update"].includes(operation))
|
|
111
|
+
return respond({ error: { code: "request_too_large" } }, 413);
|
|
112
|
+
if (!["settings", "send", "edit", "delete", "consume", "conversation-preferences", "conversation-delete", "send-call-message", "call-message-options", "list", "search", "social-preview", "conversations", "seen", "presence-update", "wait", "group-members", "group-create", "group-update", "group-leave", "read", "react", "pin", "vote", "close-poll", "inventory", "safety", "block", "unblock", "report"].includes(operation))
|
|
96
113
|
return respond({ error: { code: "operation_not_allowed" } }, 403);
|
|
97
114
|
const input = { ...body, actorId };
|
|
115
|
+
if (['list', 'conversations', 'group-members'].includes(operation)) {
|
|
116
|
+
input.groupControlsVersion = 1;
|
|
117
|
+
input.discussionVersion = 1;
|
|
118
|
+
}
|
|
98
119
|
delete input.operation;
|
|
99
|
-
if (!await options.authorize({ actorId, operation, input, request })) {
|
|
120
|
+
if (!await options.authorize({ actorId, operation: operation === 'wait' || operation === 'social-preview' ? 'list' : operation, input, request })) {
|
|
100
121
|
return respond({ error: { code: "operation_not_allowed" } }, 403);
|
|
101
122
|
}
|
|
102
123
|
const callMessage = operation === 'send-call-message' || operation === 'call-message-options';
|
|
@@ -119,7 +140,7 @@ export function createMessagingBackendAdapter(options) {
|
|
|
119
140
|
return respond({ error: { code: 'call_message_unavailable' } }, 403);
|
|
120
141
|
}
|
|
121
142
|
delete input.unansweredCallId;
|
|
122
|
-
if ((operation === "send" || operation === 'send-call-message') && input.kind !== "text" && input.kind !== "poll" && options.resolveMedia) {
|
|
143
|
+
if ((operation === "send" || operation === 'send-call-message' || operation === 'edit' && input.kind !== undefined) && input.kind !== "text" && input.kind !== "poll" && options.resolveMedia) {
|
|
123
144
|
const candidate = input.media;
|
|
124
145
|
if (typeof candidate?.assetId !== "string" || typeof input.conversationId !== "string")
|
|
125
146
|
return respond({ error: { code: "invalid_media_request" } }, 422);
|
|
@@ -128,7 +149,44 @@ export function createMessagingBackendAdapter(options) {
|
|
|
128
149
|
return respond({ error: { code: "attachment_unavailable" } }, 403);
|
|
129
150
|
input.media = stored;
|
|
130
151
|
}
|
|
131
|
-
|
|
152
|
+
if (operation === 'list') {
|
|
153
|
+
input.liveUpdates = true;
|
|
154
|
+
input.messageActionsVersion = 1;
|
|
155
|
+
input.socialPreviewVersion = 1;
|
|
156
|
+
}
|
|
157
|
+
if (operation === 'conversations')
|
|
158
|
+
input.inboxActionsVersion = 1;
|
|
159
|
+
const result = await options.robono.request(operation === 'send-call-message' ? 'send' : operation, input, { signal: request.signal });
|
|
160
|
+
if (operation === 'conversations') {
|
|
161
|
+
if (!Array.isArray(result?.conversations) || result.conversations.length > 50)
|
|
162
|
+
return respond({ error: { code: 'invalid_messaging_response' } }, 502);
|
|
163
|
+
// A customer can revoke a chat locally before its membership sync arrives.
|
|
164
|
+
// Check each preview against that customer's current list permission too.
|
|
165
|
+
const allowed = await Promise.all(result.conversations.map(async (row) => typeof row.id === 'string' && await options.authorize({ actorId, operation: 'list', input: { actorId, conversationId: row.id }, request })));
|
|
166
|
+
result.conversations = result.conversations.filter((_row, index) => allowed[index]);
|
|
167
|
+
if (input.query !== undefined) {
|
|
168
|
+
if (result.globalSearchAvailable !== true || !Array.isArray(result.searchResults) || result.searchResults.length > 50)
|
|
169
|
+
return respond({ error: { code: 'search_unavailable' } }, 503);
|
|
170
|
+
const decisions = new Map();
|
|
171
|
+
const searchAllowed = await Promise.all(result.searchResults.map((row) => {
|
|
172
|
+
const id = row.conversation?.id;
|
|
173
|
+
if (typeof id !== 'string' || row.message?.conversationId !== id)
|
|
174
|
+
return false;
|
|
175
|
+
let decision = decisions.get(id);
|
|
176
|
+
if (!decision) {
|
|
177
|
+
decision = options.authorize({ actorId, operation: 'list', input: { actorId, conversationId: id }, request });
|
|
178
|
+
decisions.set(id, decision);
|
|
179
|
+
}
|
|
180
|
+
return decision;
|
|
181
|
+
}));
|
|
182
|
+
result.searchResults = result.searchResults.filter((_row, index) => searchAllowed[index]);
|
|
183
|
+
}
|
|
184
|
+
if (await options.authenticate(request) !== actorId)
|
|
185
|
+
return respond({ error: { code: 'authentication_required' } }, 401);
|
|
186
|
+
}
|
|
187
|
+
if ((operation === 'wait' || operation === 'social-preview') && (await options.authenticate(request) !== actorId || !await options.authorize({ actorId, operation: 'list', input, request })))
|
|
188
|
+
return respond({ error: { code: 'authentication_required' } }, 401);
|
|
189
|
+
return respond(result);
|
|
132
190
|
}
|
|
133
191
|
catch (e) {
|
|
134
192
|
if (e instanceof RobonoError) {
|