mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-19 05:55:34 +09:00
feat(push): ring Android calls and harden the relay (#2915)
This commit is contained in:
1 parent
b16989d567
commit
5fde6eb484
25 files changed
+1050
-120
No files matched your search
@@ -3,7 +3,7 @@
|
||||
import {Config} from '@app/api/Config';
|
||||
import {GatewayRpcMethodError, GatewayRpcMethodErrorCodes} from '@app/api/infrastructure/GatewayRpcError';
|
||||
import type {IGatewayRpcTransport} from '@app/api/infrastructure/IGatewayRpcTransport';
|
||||
import type {CallData} from '@app/api/infrastructure/IGatewayService';
|
||||
import {type CallCaller, type CallData, callCallerRpcParams} from '@app/api/infrastructure/IGatewayService';
|
||||
import {NatsGatewayRpcTransport} from '@app/api/infrastructure/NatsGatewayRpcTransport';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
import {NatsConnectionManager} from '@pkgs/nats/src/NatsConnectionManager';
|
||||
@@ -128,6 +128,7 @@ export class GatewayRpcClient {
|
||||
region: string,
|
||||
ringing: Array<string>,
|
||||
recipients: Array<string>,
|
||||
caller?: CallCaller,
|
||||
): Promise<CallData> {
|
||||
return this.call<CallData>('call.create', {
|
||||
channel_id: channelId,
|
||||
@@ -135,6 +136,7 @@ export class GatewayRpcClient {
|
||||
region,
|
||||
ringing,
|
||||
recipients,
|
||||
...callCallerRpcParams(caller),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -142,8 +144,8 @@ export class GatewayRpcClient {
|
||||
return this.call('call.update_region', {channel_id: channelId, region});
|
||||
}
|
||||
|
||||
async ringCallRecipients(channelId: string, recipients: Array<string>): Promise<boolean> {
|
||||
return this.call('call.ring', {channel_id: channelId, recipients});
|
||||
async ringCallRecipients(channelId: string, recipients: Array<string>, caller?: CallCaller): Promise<boolean> {
|
||||
return this.call('call.ring', {channel_id: channelId, recipients, ...callCallerRpcParams(caller)});
|
||||
}
|
||||
|
||||
async stopRingingCallRecipients(channelId: string, recipients: Array<string>): Promise<boolean> {
|
||||
|
||||
@@ -6,16 +6,18 @@ import {SYSTEM_USER_ID} from '@app/api/constants/Core';
|
||||
import type {GatewayDispatchEvent} from '@app/api/constants/Gateway';
|
||||
import {GatewayRpcClient} from '@app/api/infrastructure/GatewayRpcClient';
|
||||
import {GatewayRpcMethodError, GatewayRpcMethodErrorCodes} from '@app/api/infrastructure/GatewayRpcError';
|
||||
import type {
|
||||
CallData,
|
||||
GatewayChannelMention,
|
||||
GatewayGuildMemoryStats,
|
||||
GatewayMentionSources,
|
||||
GatewayMentionSourcesPage,
|
||||
GatewayNodeStats,
|
||||
GatewayVoiceStateCounts,
|
||||
GatewayVoiceStateEntry,
|
||||
GuildChannelAuthContext,
|
||||
import {
|
||||
type CallCaller,
|
||||
type CallData,
|
||||
callCallerRpcParams,
|
||||
type GatewayChannelMention,
|
||||
type GatewayGuildMemoryStats,
|
||||
type GatewayMentionSources,
|
||||
type GatewayMentionSourcesPage,
|
||||
type GatewayNodeStats,
|
||||
type GatewayVoiceStateCounts,
|
||||
type GatewayVoiceStateEntry,
|
||||
type GuildChannelAuthContext,
|
||||
} from '@app/api/infrastructure/IGatewayService';
|
||||
import {Logger} from '@app/api/Logger';
|
||||
import {APIErrorCodes} from '@fluxer/constants/src/ApiErrorCodes';
|
||||
@@ -1695,6 +1697,7 @@ export class GatewayService {
|
||||
region: string,
|
||||
ringing: Array<string>,
|
||||
recipients: Array<string>,
|
||||
caller?: CallCaller,
|
||||
): Promise<CallData> {
|
||||
return this.call<CallData>('call.create', {
|
||||
channel_id: channelId.toString(),
|
||||
@@ -1702,6 +1705,7 @@ export class GatewayService {
|
||||
region,
|
||||
ringing,
|
||||
recipients,
|
||||
...callCallerRpcParams(caller),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1709,8 +1713,12 @@ export class GatewayService {
|
||||
return this.call<boolean>('call.update_region', {channel_id: channelId.toString(), region});
|
||||
}
|
||||
|
||||
async ringCallRecipients(channelId: ChannelID, recipients: Array<string>): Promise<boolean> {
|
||||
return this.call<boolean>('call.ring', {channel_id: channelId.toString(), recipients});
|
||||
async ringCallRecipients(channelId: ChannelID, recipients: Array<string>, caller?: CallCaller): Promise<boolean> {
|
||||
return this.call<boolean>('call.ring', {
|
||||
channel_id: channelId.toString(),
|
||||
recipients,
|
||||
...callCallerRpcParams(caller),
|
||||
});
|
||||
}
|
||||
|
||||
async stopRingingCallRecipients(channelId: ChannelID, recipients: Array<string>): Promise<boolean> {
|
||||
|
||||
@@ -24,6 +24,19 @@ export interface CallData {
|
||||
voice_states: Array<VoiceState>;
|
||||
}
|
||||
|
||||
export interface CallCaller {
|
||||
id: string;
|
||||
name: string;
|
||||
avatar: string | null;
|
||||
}
|
||||
|
||||
export function callCallerRpcParams(caller: CallCaller | undefined): Record<string, unknown> {
|
||||
if (!caller) {
|
||||
return {};
|
||||
}
|
||||
return {caller_id: caller.id, caller_name: caller.name, caller_avatar: caller.avatar};
|
||||
}
|
||||
|
||||
export interface GatewayGuildMemoryStatsEntry {
|
||||
node_id: string;
|
||||
guild_id: string | null;
|
||||
@@ -381,11 +394,12 @@ export abstract class IGatewayService {
|
||||
region: string,
|
||||
ringing: Array<string>,
|
||||
recipients: Array<string>,
|
||||
caller?: CallCaller,
|
||||
): Promise<CallData>;
|
||||
|
||||
abstract updateCallRegion(channelId: ChannelID, region: string | null): Promise<boolean>;
|
||||
|
||||
abstract ringCallRecipients(channelId: ChannelID, recipients: Array<string>): Promise<boolean>;
|
||||
abstract ringCallRecipients(channelId: ChannelID, recipients: Array<string>, caller?: CallCaller): Promise<boolean>;
|
||||
|
||||
abstract stopRingingCallRecipients(channelId: ChannelID, recipients: Array<string>): Promise<boolean>;
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
import {createChannelID} from '@app/api/BrandedTypes';
|
||||
import {GatewayRpcClient} from '@app/api/infrastructure/GatewayRpcClient';
|
||||
import {GatewayService} from '@app/api/infrastructure/GatewayService';
|
||||
import type {IGatewayRpcTransport} from '@app/api/infrastructure/IGatewayRpcTransport';
|
||||
import {afterEach, describe, expect, it} from 'vitest';
|
||||
|
||||
const CHANNEL_ID = createChannelID(12n);
|
||||
|
||||
interface RecordedCall {
|
||||
method: string;
|
||||
params: Record<string, unknown>;
|
||||
}
|
||||
|
||||
function recordingService(recorded: Array<RecordedCall>): GatewayService {
|
||||
const transport: IGatewayRpcTransport = {
|
||||
async call(method: string, params: Record<string, unknown>): Promise<unknown> {
|
||||
recorded.push({method, params});
|
||||
return null;
|
||||
},
|
||||
async destroy(): Promise<void> {},
|
||||
};
|
||||
GatewayRpcClient.createForTests(transport);
|
||||
return new GatewayService();
|
||||
}
|
||||
|
||||
describe('call rpc caller params', () => {
|
||||
afterEach(async () => {
|
||||
await GatewayRpcClient.resetForTests();
|
||||
});
|
||||
|
||||
it('sends the caller to call.create as caller_id, caller_name and caller_avatar', async () => {
|
||||
const recorded: Array<RecordedCall> = [];
|
||||
const service = recordingService(recorded);
|
||||
await service.createCall(CHANNEL_ID, '99', 'automatic', ['2'], ['1', '2'], {
|
||||
id: '1',
|
||||
name: 'Elias',
|
||||
avatar: 'a1b2c3d4',
|
||||
});
|
||||
expect(recorded).toHaveLength(1);
|
||||
expect(recorded[0].method).toBe('call.create');
|
||||
expect(recorded[0].params.caller_id).toBe('1');
|
||||
expect(recorded[0].params.caller_name).toBe('Elias');
|
||||
expect(recorded[0].params.caller_avatar).toBe('a1b2c3d4');
|
||||
});
|
||||
|
||||
it('sends the caller to call.ring as caller_id, caller_name and caller_avatar', async () => {
|
||||
const recorded: Array<RecordedCall> = [];
|
||||
const service = recordingService(recorded);
|
||||
await service.ringCallRecipients(CHANNEL_ID, ['2'], {id: '1', name: 'Elias', avatar: 'a1b2c3d4'});
|
||||
expect(recorded).toHaveLength(1);
|
||||
expect(recorded[0].method).toBe('call.ring');
|
||||
expect(recorded[0].params.caller_id).toBe('1');
|
||||
expect(recorded[0].params.caller_name).toBe('Elias');
|
||||
expect(recorded[0].params.caller_avatar).toBe('a1b2c3d4');
|
||||
});
|
||||
|
||||
it('sends caller_avatar as null when the caller has no avatar', async () => {
|
||||
const recorded: Array<RecordedCall> = [];
|
||||
const service = recordingService(recorded);
|
||||
await service.ringCallRecipients(CHANNEL_ID, ['2'], {id: '1', name: 'Elias', avatar: null});
|
||||
expect(recorded[0].params.caller_avatar).toBeNull();
|
||||
expect(Object.hasOwn(recorded[0].params, 'caller_avatar')).toBe(true);
|
||||
});
|
||||
|
||||
it('omits every caller key from call.create when no caller was resolved', async () => {
|
||||
const recorded: Array<RecordedCall> = [];
|
||||
const service = recordingService(recorded);
|
||||
await service.createCall(CHANNEL_ID, '99', 'automatic', ['2'], ['1', '2']);
|
||||
expect(recorded[0].params).toEqual({
|
||||
channel_id: '12',
|
||||
message_id: '99',
|
||||
region: 'automatic',
|
||||
ringing: ['2'],
|
||||
recipients: ['1', '2'],
|
||||
});
|
||||
});
|
||||
|
||||
it('omits every caller key from call.ring when no caller was resolved', async () => {
|
||||
const recorded: Array<RecordedCall> = [];
|
||||
const service = recordingService(recorded);
|
||||
await service.ringCallRecipients(CHANNEL_ID, ['2']);
|
||||
expect(recorded[0].params).toEqual({channel_id: '12', recipients: ['2']});
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user