Add per-subreddit R4R conversation modes
This commit is contained in:
1 parent
23d4c3a320
commit
0f76bcf59a
14 files changed
+343
-24
No files matched your search
@@ -76,6 +76,13 @@ export function SettingsEditor({ config, section }: SettingsEditorProps) {
|
||||
if (validation.data.maxMessagesPerHour > 20 && validation.data.maxMessagesPerHour > persistedLimit) {
|
||||
reasons.push(`The limit will allow ${validation.data.maxMessagesPerHour} messages per hour.`);
|
||||
}
|
||||
const enablesAutonomousR4R = validation.data.payload.subreddits?.some((subreddit, index) => (
|
||||
subreddit.conversation.mode === 'r4r'
|
||||
&& persistedDraft.subreddits[index]?.conversationMode !== 'r4r'
|
||||
));
|
||||
if (enablesAutonomousR4R) {
|
||||
reasons.push('Autonomous R4R will send conversational replies without approval for the selected subreddit. Existing conversations keep their snapshotted policy.');
|
||||
}
|
||||
|
||||
if (reasons.length > 0) {
|
||||
setPendingRiskySave({ payload: validation.data.payload, reasons });
|
||||
|
||||
@@ -14,6 +14,8 @@ export interface SubredditDraft {
|
||||
weight: string;
|
||||
tone: z.infer<typeof ToneKeySchema>;
|
||||
scripted: string;
|
||||
conversationMode: 'legacy' | 'r4r' | 'manual';
|
||||
maxAutomatedTurns: string;
|
||||
}
|
||||
|
||||
export interface SettingsDraft {
|
||||
@@ -73,6 +75,8 @@ export function createSettingsDraft(config: ConfigResponse): SettingsDraft {
|
||||
weight: subreddit.weight === undefined ? '' : String(subreddit.weight),
|
||||
tone: subreddit.tone,
|
||||
scripted: subreddit.scripted ?? '',
|
||||
conversationMode: subreddit.conversation.mode,
|
||||
maxAutomatedTurns: String(subreddit.conversation.maxAutomatedTurns),
|
||||
})),
|
||||
advancedConfigJson: config.advanced ? JSON.stringify(config.advanced, null, 2) : '',
|
||||
};
|
||||
@@ -89,6 +93,9 @@ export function validateSettingsDraft(draft: SettingsDraft): SettingsValidationR
|
||||
if (subreddit.weight.trim() !== '' && Number(subreddit.weight) <= 0) {
|
||||
requiredErrors[`subreddits.${index}.weight`] = 'Subreddit weight must be greater than 0.';
|
||||
}
|
||||
if (subreddit.conversationMode === 'r4r' && subreddit.maxAutomatedTurns.trim() === '') {
|
||||
requiredErrors[`subreddits.${index}.maxAutomatedTurns`] = 'Maximum automated turns is required.';
|
||||
}
|
||||
});
|
||||
if (Object.keys(requiredErrors).length > 0) return { errors: requiredErrors };
|
||||
|
||||
@@ -119,6 +126,12 @@ export function validateSettingsDraft(draft: SettingsDraft): SettingsValidationR
|
||||
weight: subreddit.weight.trim() === '' ? undefined : Number(subreddit.weight),
|
||||
tone: subreddit.tone,
|
||||
scripted: subreddit.scripted.trim() === '' ? undefined : subreddit.scripted,
|
||||
conversation: {
|
||||
mode: subreddit.conversationMode,
|
||||
maxAutomatedTurns: subreddit.conversationMode === 'r4r'
|
||||
? Number(subreddit.maxAutomatedTurns)
|
||||
: 3,
|
||||
},
|
||||
})),
|
||||
});
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ export function BehaviourSection({ config, draft, errors, onChange }: SectionPro
|
||||
...draft,
|
||||
subreddits: [
|
||||
...draft.subreddits,
|
||||
{ name: '', enabled: true, type: 'image', weight: '1', tone: 'default', scripted: '' },
|
||||
{ name: '', enabled: true, type: 'image', weight: '1', tone: 'default', scripted: '', conversationMode: 'legacy', maxAutomatedTurns: '3' },
|
||||
],
|
||||
})}
|
||||
type="button"
|
||||
@@ -190,6 +190,43 @@ export function BehaviourSection({ config, draft, errors, onChange }: SectionPro
|
||||
{config.tones.map((tone) => <option key={tone} value={tone}>{tone}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
<label className="settings-field">
|
||||
<span>Conversation behaviour</span>
|
||||
<select
|
||||
aria-label={`Subreddit ${index + 1} conversation behaviour`}
|
||||
onChange={(event) => onChange(updateSubreddit(draft, index, {
|
||||
...subreddit,
|
||||
conversationMode: event.target.value as SubredditDraft['conversationMode'],
|
||||
}))}
|
||||
value={subreddit.conversationMode}
|
||||
>
|
||||
<option value="legacy">Legacy media asks</option>
|
||||
<option value="r4r">Autonomous R4R conversation</option>
|
||||
<option value="manual">Manual only</option>
|
||||
</select>
|
||||
<small>{subreddit.conversationMode === 'r4r'
|
||||
? 'Continues general conversation autonomously. It will not request sexual media.'
|
||||
: subreddit.conversationMode === 'manual'
|
||||
? 'Incoming replies are marked for human handling and are never answered automatically.'
|
||||
: 'Preserves the existing three-turn media-request flow.'}</small>
|
||||
</label>
|
||||
{subreddit.conversationMode === 'r4r' ? (
|
||||
<label className="settings-field">
|
||||
<span>Maximum automated turns</span>
|
||||
<input
|
||||
aria-describedby={errors[`subreddits.${index}.maxAutomatedTurns`] ? `subreddit-${index}-max-turns-error` : undefined}
|
||||
aria-invalid={errors[`subreddits.${index}.maxAutomatedTurns`] ? 'true' : undefined}
|
||||
aria-label={`Subreddit ${index + 1} maximum automated turns`}
|
||||
max="20"
|
||||
min="1"
|
||||
onChange={(event) => onChange(updateSubreddit(draft, index, { ...subreddit, maxAutomatedTurns: event.target.value }))}
|
||||
step="1"
|
||||
type="number"
|
||||
value={subreddit.maxAutomatedTurns}
|
||||
/>
|
||||
{errors[`subreddits.${index}.maxAutomatedTurns`] ? <small className="field-error" id={`subreddit-${index}-max-turns-error`}>{errors[`subreddits.${index}.maxAutomatedTurns`]}</small> : null}
|
||||
</label>
|
||||
) : null}
|
||||
<label className="settings-field scripted-field">
|
||||
<span>Scripted message (optional)</span>
|
||||
<textarea
|
||||
|
||||
Reference in new issue
Block a user