Harden filter import: item-level validation and user feedback

importFromAyuGram now skips individual malformed entries (missing id/text)
instead of crashing, and validates the top-level structure properly.
Import shows a toast on success, empty result, or parse failure.
This commit is contained in:
Tianrong Zhang 2026-06-11 23:35:43 -04:00
parent 1913174e1c
commit ffb1ba8108
2 changed files with 26 additions and 14 deletions

View File

@ -33,7 +33,7 @@ const SettingsAyuLike = ({
messageFilters, messageFilters,
onReset, onReset,
}: OwnProps & StateProps) => { }: OwnProps & StateProps) => {
const { setSharedSettingOption } = getActions(); const { setSharedSettingOption, showNotification } = getActions();
const fileInputRef = useRef<HTMLInputElement>(); const fileInputRef = useRef<HTMLInputElement>();
const [isClearConfirmOpen, openClearConfirm, closeClearConfirm] = useFlag(false); const [isClearConfirmOpen, openClearConfirm, closeClearConfirm] = useFlag(false);
@ -59,11 +59,16 @@ const SettingsAyuLike = ({
reader.onload = () => { reader.onload = () => {
try { try {
const imported = importFromAyuGram(JSON.parse(reader.result as string)); const imported = importFromAyuGram(JSON.parse(reader.result as string));
setSharedSettingOption({ if (imported.length === 0) {
ayuLike: { hideSponsoredMessages, messageFilters: imported }, showNotification({ message: 'No valid filter rules found in file' });
}); } else {
setSharedSettingOption({
ayuLike: { hideSponsoredMessages, messageFilters: imported },
});
showNotification({ message: `Imported ${imported.length} filter rules` });
}
} catch { } catch {
// Silently ignore malformed files — user will see no change showNotification({ message: 'Invalid file: not a valid AyuGram filter export' });
} }
// Reset so the same file can be re-imported // Reset so the same file can be re-imported
e.target.value = ''; e.target.value = '';

View File

@ -47,16 +47,23 @@ interface AyuGramExport {
export function importFromAyuGram(json: unknown): MessageFilterRule[] { export function importFromAyuGram(json: unknown): MessageFilterRule[] {
const data = json as AyuGramExport; const data = json as AyuGramExport;
if (!Array.isArray(data?.filters)) throw new Error('Invalid AyuGram export format'); if (!data || typeof data !== 'object' || !Array.isArray(data.filters)) {
throw new Error('Invalid AyuGram export format');
}
return data.filters.map((f) => ({ return data.filters.flatMap((f) => {
id: f.id, if (!f || typeof f !== 'object') return [];
enabled: Boolean(f.enabled), if (typeof f.id !== 'string' || !f.id) return [];
reversed: Boolean(f.reversed), if (typeof f.text !== 'string' || !f.text) return [];
caseInsensitive: Boolean(f.caseInsensitive), return [{
regex: f.text, id: f.id,
chatIds: f.dialogId ? [String(f.dialogId)] : undefined, enabled: Boolean(f.enabled),
})); reversed: Boolean(f.reversed),
caseInsensitive: Boolean(f.caseInsensitive),
regex: f.text,
chatIds: f.dialogId ? [String(f.dialogId)] : undefined,
}];
});
} }
export function exportToAyuGram(rules: MessageFilterRule[]): string { export function exportToAyuGram(rules: MessageFilterRule[]): string {