Skip to content

Commit

Permalink
feat(federation): 特定の連合サーバーのメディアを全てセンシティブとして設定する機能を追加 (#340)
Browse files Browse the repository at this point in the history
  • Loading branch information
u1-liquid authored Jan 7, 2024
1 parent ce58adc commit d4a8e9a
Show file tree
Hide file tree
Showing 15 changed files with 105 additions and 5 deletions.
3 changes: 3 additions & 0 deletions locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ perDay: "Per Day"
stopActivityDelivery: "Stop sending activities"
blockThisInstance: "Block this instance"
silenceThisInstance: "Silence this instance"
sensitiveMediaThisInstance: "Mark media from this instance as sensitive"
operations: "Operations"
software: "Software"
version: "Version"
Expand All @@ -218,6 +219,8 @@ blockedInstances: "Blocked Instances"
blockedInstancesDescription: "List the hostnames of the instances you want to block separated by linebreaks. Listed instances will no longer be able to communicate with this instance."
silencedInstances: "Silenced instances"
silencedInstancesDescription: "List the hostnames of the instances that you want to silence. All accounts of the listed instances will be treated as silenced, can only make follow requests, and cannot mention local accounts if not followed. This will not affect blocked instances."
sensitiveMediaInstances: "Instances with sensitive media"
sensitiveMediaInstancesDescription: "List the hostnames of the instances that you want to mark all media from this instance as sensitive."
muteAndBlock: "Mutes and Blocks"
mutedUsers: "Muted users"
blockedUsers: "Blocked users"
Expand Down
3 changes: 3 additions & 0 deletions locales/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export interface Locale {
"stopActivityDelivery": string;
"blockThisInstance": string;
"silenceThisInstance": string;
"sensitiveMediaThisInstance": string;
"operations": string;
"software": string;
"version": string;
Expand All @@ -225,6 +226,8 @@ export interface Locale {
"blockedInstancesDescription": string;
"silencedInstances": string;
"silencedInstancesDescription": string;
"sensitiveMediaInstances": string;
"sensitiveMediaInstancesDescription": string;
"muteAndBlock": string;
"mutedUsers": string;
"blockedUsers": string;
Expand Down
3 changes: 3 additions & 0 deletions locales/ja-JP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ perDay: "1日ごと"
stopActivityDelivery: "アクティビティの配送を停止"
blockThisInstance: "このサーバーをブロック"
silenceThisInstance: "サーバーをサイレンス"
sensitiveMediaThisInstance: "このサーバーのメディアを全てセンシティブとして設定"
operations: "操作"
software: "ソフトウェア"
version: "バージョン"
Expand All @@ -222,6 +223,8 @@ blockedInstances: "ブロックしたサーバー"
blockedInstancesDescription: "ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。"
silencedInstances: "サイレンスしたサーバー"
silencedInstancesDescription: "サイレンスしたいサーバーのホストを改行で区切って設定します。サイレンスされたサーバーに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。"
sensitiveMediaInstances: "センシティブなメディアを含むサーバー"
sensitiveMediaInstancesDescription: "センシティブなメディアを含むサーバーのホストを改行で区切って設定します。このサーバーからのメディアは全てセンシティブとして扱われます。"
muteAndBlock: "ミュートとブロック"
mutedUsers: "ミュートしたユーザー"
blockedUsers: "ブロックしたユーザー"
Expand Down
3 changes: 3 additions & 0 deletions locales/ko-KR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ perDay: "1일마다"
stopActivityDelivery: "액티비티 보내지 않기"
blockThisInstance: "이 서버를 차단"
silenceThisInstance: "서버를 사일런스"
sensitiveMediaThisInstance: "이 서버의 미디어를 모두 민감한 미디어로 표시"
operations: "작업"
software: "소프트웨어"
version: "버전"
Expand All @@ -222,6 +223,8 @@ blockedInstances: "차단된 서버"
blockedInstancesDescription: "차단하려는 서버의 호스트 이름을 줄바꿈으로 구분하여 설정합니다. 차단된 인스턴스는 이 인스턴스와 통신할 수 없게 됩니다."
silencedInstances: "사일런스한 서버"
silencedInstancesDescription: "사일런스하려는 서버의 호스트명을 한 줄에 하나씩 입력합니다. 사일런스된 서버에 소속된 유저는 모두 '사일런스'된 상태로 취급되며, 이 서버로부터의 팔로우가 프로필 설정과 무관하게 승인제로 변경되고, 팔로워가 아닌 로컬 유저에게는 멘션할 수 없게 됩니다. 정지된 서버에는 적용되지 않습니다."
sensitiveMediaInstances: "민감한 미디어를 포함한 서버"
sensitiveMediaInstancesDescription: "민감한 미디어를 포함한 서버의 호스트명을 한 줄에 하나씩 입력합니다. 이 서버에 소속된 유저가 업로드한 미디어는 모두 민감한 미디어로 표시됩니다."
muteAndBlock: "뮤트 및 차단"
mutedUsers: "뮤트한 유저"
blockedUsers: "차단한 유저"
Expand Down
16 changes: 16 additions & 0 deletions packages/backend/migration/1704622962215-sensitive-media-hosts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class SensitiveMediaHosts1704622962215 {
name = 'SensitiveMediaHosts1704622962215'

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "sensitiveMediaHosts" character varying(1024) array NOT NULL DEFAULT '{}'`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "sensitiveMediaHosts"`);
}
}
6 changes: 6 additions & 0 deletions packages/backend/src/core/UtilityService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ export class UtilityService {
return silencedHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
}

@bindThis
public isSensitiveMediaHost(sensitiveMediaHosts: string[] | undefined, host: string | null): boolean {
if (!sensitiveMediaHosts || host == null) return false;
return sensitiveMediaHosts.some(x => `.${host.toLowerCase()}`.endsWith(`.${x}`));
}

@bindThis
public isSensitiveWordIncluded(text: string, sensitiveWords: string[]): boolean {
if (sensitiveWords.length === 0) return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,14 +173,17 @@ export class ApNoteService {
const apMentions = await this.apMentionService.extractApMentions(note.tag, resolver);
const apHashtags = extractApHashtags(note.tag);

const meta = await this.metaService.fetch();
const isSensitiveMediaHost = this.utilityService.isSensitiveMediaHost(meta.blockedHosts, this.utilityService.extractDbHost(note.id ?? entryUri));

// 添付ファイル
// TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない
const limit = promiseLimit<MiDriveFile>(2);
const files = (await Promise.all(toArray(note.attachment).map(attach => (
limit(() => this.apImageService.resolveImage(actor, {
...attach,
sensitive: note.sensitive, // Noteがsensitiveなら添付もsensitiveにする
sensitive: isSensitiveMediaHost || note.sensitive, // Noteがsensitiveなら添付もsensitiveにする
}))
))));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export class InstanceEntityService {
maintainerName: instance.maintainerName,
maintainerEmail: instance.maintainerEmail,
isSilenced: this.utilityService.isSilencedHost(meta.silencedHosts, instance.host),
isSensitiveMedia: this.utilityService.isSensitiveMediaHost(meta.sensitiveMediaHosts, instance.host),
iconUrl: instance.iconUrl,
faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor,
Expand Down
5 changes: 5 additions & 0 deletions packages/backend/src/models/Meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ export class MiMeta {
})
public silencedHosts: string[];

@Column('varchar', {
length: 1024, array: true, default: '{}',
})
public sensitiveMediaHosts: string[];

@Column('varchar', {
length: 1024,
nullable: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ export const packedFederationInstanceSchema = {
type: 'boolean',
optional: false, nullable: false,
},
isSensitiveMedia: {
type: 'boolean',
optional: false, nullable: false,
},
iconUrl: {
type: 'string',
optional: false, nullable: true,
Expand Down
11 changes: 11 additions & 0 deletions packages/backend/src/server/api/endpoints/admin/meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,16 @@ export const meta = {
nullable: false,
},
},
sensitiveMediaHosts: {
type: 'array',
optional: true,
nullable: false,
items: {
type: 'string',
optional: false,
nullable: false,
},
},
pinnedUsers: {
type: 'array',
optional: false, nullable: false,
Expand Down Expand Up @@ -491,6 +501,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
hiddenTags: instance.hiddenTags,
blockedHosts: instance.blockedHosts,
silencedHosts: instance.silencedHosts,
sensitiveMediaHosts: instance.sensitiveMediaHosts,
sensitiveWords: instance.sensitiveWords,
preservedUsernames: instance.preservedUsernames,
hcaptchaSecretKey: instance.hcaptchaSecretKey,
Expand Down
17 changes: 16 additions & 1 deletion packages/backend/src/server/api/endpoints/admin/update-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ export const paramDef = {
type: 'string',
},
},
sensitiveMediaHosts: {
type: 'array', nullable: true, items: {
type: 'string',
},
},
urlPreviewDenyList: { type: 'array', nullable: true, items: {
type: 'string',
} },
Expand Down Expand Up @@ -173,13 +178,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (Array.isArray(ps.sensitiveWords)) {
set.sensitiveWords = ps.sensitiveWords.filter(Boolean);
}

if (Array.isArray(ps.silencedHosts)) {
let lastValue = '';
set.silencedHosts = ps.silencedHosts.sort().filter((h) => {
const lv = lastValue;
lastValue = h;
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
});
}).map(x => x.toLowerCase());
}

if (Array.isArray(ps.sensitiveMediaHosts)) {
let lastValue = '';
set.sensitiveMediaHosts = ps.sensitiveMediaHosts.sort().filter((h) => {
const lv = lastValue;
lastValue = h;
return h !== '' && h !== lv && !set.blockedHosts?.includes(h);
}).map(x => x.toLowerCase());
}

if (Array.isArray(ps.urlPreviewDenyList)) {
Expand Down
11 changes: 11 additions & 0 deletions packages/frontend/src/pages/admin/instance-block.vue
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<span>{{ i18n.ts.silencedInstances }}</span>
<template #caption>{{ i18n.ts.silencedInstancesDescription }}</template>
</MkTextarea>
<MkTextarea v-else-if="tab === 'sensitive'" v-model="sensitiveMediaHosts" class="_formBlock">
<span>{{ i18n.ts.sensitiveMediaInstances }}</span>
<template #caption>{{ i18n.ts.sensitiveMediaInstancesDescription }}</template>
</MkTextarea>
<MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
</FormSuspense>
</MkSpacer>
Expand All @@ -35,18 +39,21 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
const blockedHosts = ref<string>('');
const silencedHosts = ref<string>('');
const sensitiveMediaHosts = ref<string>('');
const tab = ref('block');
async function init() {
const meta = await os.api('admin/meta');
blockedHosts.value = meta.blockedHosts.join('\n');
silencedHosts.value = meta.silencedHosts.join('\n');
sensitiveMediaHosts.value = meta.sensitiveMediaHosts.join('\n');
}
function save() {
os.apiWithDialog('admin/update-meta', {
blockedHosts: blockedHosts.value.split('\n') || [],
silencedHosts: silencedHosts.value.split('\n') || [],
sensitiveMediaHosts: sensitiveMediaHosts.value.split('\n') || [],
}).then(() => {
fetchInstance();
Expand All @@ -63,6 +70,10 @@ const headerTabs = computed(() => [{
key: 'silence',
title: i18n.ts.silence,
icon: 'ti ti-eye-off',
}, {
key: 'sensitive',
title: i18n.ts.sensitive,
icon: 'ti ti-photo-exclamation',
}]);
definePageMetadata({
Expand Down
13 changes: 13 additions & 0 deletions packages/frontend/src/pages/instance-info.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch>
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
<MkSwitch v-model="isSensitiveMedia" :disabled="!meta || !instance" @update:modelValue="toggleSensitiveMedia">{{ i18n.ts.sensitiveMediaThisInstance }}</MkSwitch>
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
</div>
</FormSection>
Expand Down Expand Up @@ -149,6 +150,7 @@ const instance = ref<Misskey.entities.FederationInstance | null>(null);
const suspended = ref(false);
const isBlocked = ref(false);
const isSilenced = ref(false);
const isSensitiveMedia = ref(false);
const faviconUrl = ref<string | null>(null);
const usersPagination = {
Expand All @@ -172,6 +174,7 @@ async function fetch(): Promise<void> {
suspended.value = instance.value?.isSuspended ?? false;
isBlocked.value = instance.value?.isBlocked ?? false;
isSilenced.value = instance.value?.isSilenced ?? false;
isSensitiveMedia.value = instance.value?.isSensitiveMedia ?? false;
faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview');
}
Expand All @@ -194,6 +197,16 @@ async function toggleSilenced(): Promise<void> {
});
}
async function toggleSensitiveMedia(): Promise<void> {
if (!meta.value) throw new Error('No meta?');
if (!instance.value) throw new Error('No instance?');
const { host } = instance.value;
const sensitiveMediaHosts = meta.value.sensitiveMediaHosts ?? [];
await os.api('admin/update-meta', {
sensitiveMediaHosts: isSensitiveMedia.value ? sensitiveMediaHosts.concat([host]) : sensitiveMediaHosts.filter(x => x !== host),
});
}
async function toggleSuspend(): Promise<void> {
if (!instance.value) throw new Error('No instance?');
await os.api('admin/federation/update-instance', {
Expand Down
9 changes: 6 additions & 3 deletions packages/misskey-js/src/autogen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
/* eslint @typescript-eslint/no-explicit-any: 0 */

/*
* version: 2023.12.2-io
* generatedAt: 2023-12-28T08:11:12.906Z
* version: 2023.11.1-io.3a
* generatedAt: 2024-01-07T10:20:39.681Z
*/

/**
Expand Down Expand Up @@ -4209,6 +4209,7 @@ export type components = {
maintainerName: string | null;
maintainerEmail: string | null;
isSilenced: boolean;
isSensitiveMedia: boolean;
/** Format: url */
iconUrl: string | null;
/** Format: url */
Expand Down Expand Up @@ -4560,6 +4561,7 @@ export type operations = {
enableServiceWorker: boolean;
translatorAvailable: boolean;
silencedHosts?: string[];
sensitiveMediaHosts?: string[];
pinnedUsers: string[];
hiddenTags: string[];
blockedHosts: string[];
Expand Down Expand Up @@ -8676,6 +8678,7 @@ export type operations = {
perUserListTimelineCacheMax?: number;
notesPerOneAd?: number;
silencedHosts?: string[] | null;
sensitiveMediaHosts?: string[] | null;
urlPreviewDenyList?: string[] | null;
};
};
Expand Down Expand Up @@ -25241,7 +25244,7 @@ export type operations = {
* @default other
* @enum {string}
*/
category?: 'nsfw' | 'spam' | 'explicit' | 'phishing' | 'personalInfoLeak' | 'selfHarm' | 'criticalBreach' | 'otherBreach' | 'violationRights' | 'violationRightsOther' | 'other' | 'personalinfoleak' | 'selfharm' | 'criticalbreach' | 'otherbreach' | 'violationrights' | 'violationrightsother' | 'notlike';
category?: 'nsfw' | 'spam' | 'explicit' | 'phishing' | 'personalInfoLeak' | 'selfHarm' | 'criticalBreach' | 'otherBreach' | 'violationRights' | 'violationRightsOther' | 'other';
};
};
};
Expand Down

0 comments on commit d4a8e9a

Please sign in to comment.