Add outbound message opt-in for apprise

This commit is contained in:
Jack Kingsman
2026-06-11 20:42:29 -07:00
parent 907a0e4d14
commit fb848d2e8d
7 changed files with 199 additions and 11 deletions
@@ -287,6 +287,7 @@ const CREATE_INTEGRATION_DEFINITIONS: readonly CreateIntegrationDefinition[] = [
config: {
urls: '',
preserve_identity: true,
include_outgoing: false,
markdown_format: true,
body_format_dm: '**DM:** {sender_name}: {text} **via:** [{hops_backticked}]',
body_format_channel:
@@ -2590,6 +2591,23 @@ function AppriseConfigEditor({
</div>
</label>
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={config.include_outgoing === true}
onChange={(e) => onChange({ ...config, include_outgoing: e.target.checked })}
className="h-4 w-4 rounded border-border"
/>
<div>
<span className="text-sm">Forward RemoteTerm-sent messages</span>
<p className="text-[0.8125rem] text-muted-foreground">
Include DMs and channel messages sent by this RemoteTerm instance, including manual
sends and bot replies. Outgoing messages carry no routing path or signal data, so
path-related format fields render as direct and RSSI/SNR are empty.
</p>
</div>
</label>
<Separator />
<h3 className="text-base font-semibold tracking-tight">Message Format</h3>
+101
View File
@@ -561,6 +561,107 @@ describe('SettingsFanoutSection', () => {
);
});
it('creates Apprise with outgoing forwarding disabled by default', async () => {
const createdApprise: FanoutConfig = {
id: 'ap-new',
type: 'apprise',
name: 'Apprise #1',
enabled: true,
config: {
urls: '',
preserve_identity: true,
include_outgoing: false,
markdown_format: true,
body_format_dm: '**DM:** {sender_name}: {text} **via:** [{hops_backticked}]',
body_format_channel:
'**{channel_name}:** {sender_name}: {text} **via:** [{hops_backticked}]',
},
scope: { messages: 'all', raw_packets: 'none' },
sort_order: 0,
created_at: 2000,
};
mockedApi.createFanoutConfig.mockResolvedValue(createdApprise);
mockedApi.getFanoutConfigs.mockResolvedValueOnce([]).mockResolvedValueOnce([createdApprise]);
renderSection();
await openCreateIntegrationDialog();
selectCreateIntegration('Apprise');
confirmCreateIntegration();
await waitFor(() => expect(screen.getByText('← Back to list')).toBeInTheDocument());
expect(screen.getByLabelText(/Forward RemoteTerm-sent messages/)).not.toBeChecked();
expect(
screen.getByText(/Outgoing messages carry no routing path or signal data/)
).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Save as Enabled' }));
await waitFor(() =>
expect(mockedApi.createFanoutConfig).toHaveBeenCalledWith({
type: 'apprise',
name: 'Apprise #1',
config: {
urls: '',
preserve_identity: true,
include_outgoing: false,
markdown_format: true,
body_format_dm: '**DM:** {sender_name}: {text} **via:** [{hops_backticked}]',
body_format_channel:
'**{channel_name}:** {sender_name}: {text} **via:** [{hops_backticked}]',
},
scope: { messages: 'all', raw_packets: 'none' },
enabled: true,
})
);
});
it('can enable outgoing forwarding for an existing Apprise integration', async () => {
const appriseConfig: FanoutConfig = {
id: 'ap-1',
type: 'apprise',
name: 'Apprise Feed',
enabled: true,
config: {
urls: 'discord://abc',
preserve_identity: true,
markdown_format: true,
},
scope: { messages: 'all', raw_packets: 'none' },
sort_order: 0,
created_at: 1000,
};
mockedApi.getFanoutConfigs.mockResolvedValue([appriseConfig]);
mockedApi.updateFanoutConfig.mockResolvedValue({
...appriseConfig,
config: { ...appriseConfig.config, include_outgoing: true },
});
renderSection();
await waitFor(() => expect(screen.getByText('Apprise Feed')).toBeInTheDocument());
fireEvent.click(screen.getByRole('button', { name: 'Edit' }));
await waitFor(() => expect(screen.getByText('← Back to list')).toBeInTheDocument());
const includeOutgoing = screen.getByLabelText(/Forward RemoteTerm-sent messages/);
expect(includeOutgoing).not.toBeChecked();
fireEvent.click(includeOutgoing);
fireEvent.click(screen.getByRole('button', { name: 'Save as Enabled' }));
await waitFor(() =>
expect(mockedApi.updateFanoutConfig).toHaveBeenCalledWith('ap-1', {
name: 'Apprise Feed',
config: {
urls: 'discord://abc',
preserve_identity: true,
markdown_format: true,
include_outgoing: true,
},
scope: { messages: 'all', raw_packets: 'none' },
enabled: true,
})
);
});
it('new draft names increment within the integration type', async () => {
mockedApi.getFanoutConfigs.mockResolvedValue([
webhookConfig,