跳至主要內容
入門

NIP-36 敏感內容

Nostr 敏感內容標記機制,用於標示需要警告的內容。

5 分鐘

概述

NIP-36 定義了敏感內容標記機制,讓發布者可以為可能令人不適的內容添加警告。 這類似於其他平台的「內容警告」(Content Warning)或「不適合工作場所」(NSFW)標記。 客戶端應該在顯示標記內容前要求用戶確認。

Content-Warning 標籤

使用 content-warning 標籤標記敏感內容,可選擇性地附帶原因說明:

{`["content-warning", "<警告原因>"]`}

事件範例

{`{
  "kind": 1,
  "tags": [
    ["content-warning", "暴力內容"]
  ],
  "content": "這則貼文包含可能令人不適的內容...",
  ...
}`}

無原因的警告

如果不想說明具體原因,可以留空:

{`{
  "kind": 1,
  "tags": [
    ["content-warning", ""]
  ],
  "content": "敏感內容...",
  ...
}`}

實作

建立敏感內容事件

{`import { finalizeEvent } from 'nostr-tools';

// 建立帶有內容警告的事件
function createSensitiveEvent(
  privateKey: Uint8Array,
  content: string,
  warningReason?: string
) {
  const event = {
    kind: 1,
    created_at: Math.floor(Date.now() / 1000),
    tags: [
      ['content-warning', warningReason || ''],
    ],
    content,
  };

  return finalizeEvent(event, privateKey);
}

// 使用範例
const nsfwPost = createSensitiveEvent(
  privateKey,
  '這是一則包含敏感圖片的貼文...',
  'NSFW 圖片'
);

const spoilerPost = createSensitiveEvent(
  privateKey,
  '電影結局是...',
  '劇透警告'
);

const triggerPost = createSensitiveEvent(
  privateKey,
  '討論心理健康議題...',
  '可能引發不適'
);`}

檢查敏感內容

{`interface ContentWarning {
  hasSensitiveContent: boolean;
  reason: string | null;
}

// 檢查事件是否有敏感內容標記
function checkContentWarning(event: Event): ContentWarning {
  const cwTag = event.tags.find(t => t[0] === 'content-warning');

  if (!cwTag) {
    return { hasSensitiveContent: false, reason: null };
  }

  return {
    hasSensitiveContent: true,
    reason: cwTag[1] || null,
  };
}

// 使用範例
const { hasSensitiveContent, reason } = checkContentWarning(event);

if (hasSensitiveContent) {
  console.log(\`此內容已標記為敏感\${reason ? \`: \${reason}\` : ''}\`);
}`}

過濾敏感內容

{`// 過濾掉所有敏感內容
function filterSensitiveContent(events: Event[]): Event[] {
  return events.filter(event => {
    const { hasSensitiveContent } = checkContentWarning(event);
    return !hasSensitiveContent;
  });
}

// 只保留敏感內容
function getSensitiveContent(events: Event[]): Event[] {
  return events.filter(event => {
    const { hasSensitiveContent } = checkContentWarning(event);
    return hasSensitiveContent;
  });
}

// 按敏感程度分類
function categorizeContent(events: Event[]): {
  safe: Event[];
  sensitive: Event[];
} {
  const safe: Event[] = [];
  const sensitive: Event[] = [];

  for (const event of events) {
    const { hasSensitiveContent } = checkContentWarning(event);
    if (hasSensitiveContent) {
      sensitive.push(event);
    } else {
      safe.push(event);
    }
  }

  return { safe, sensitive };
}`}

UI 顯示建議

客戶端應該:

  • 預設隱藏敏感內容
  • 顯示警告訊息和原因(如有)
  • 提供「顯示內容」按鈕
  • 允許用戶設定偏好(總是隱藏、總是顯示等)
{`// React 組件範例
function SensitiveNote({ event }: { event: Event }) {
  const [revealed, setRevealed] = useState(false);
  const { hasSensitiveContent, reason } = checkContentWarning(event);

  // 如果沒有敏感標記,直接顯示
  if (!hasSensitiveContent) {
    return ;
  }

  // 顯示警告覆蓋層
  if (!revealed) {
    return (
      
⚠️
{reason ? \`敏感內容:\${reason}\` : '此內容已被標記為敏感'}
); } // 已確認顯示 return (
⚠️ {reason || '敏感內容'}
); }`}

用戶偏好設定

{`// 用戶敏感內容偏好
type SensitiveContentPreference =
  | 'always_hide'      // 總是隱藏
  | 'show_warning'     // 顯示警告(預設)
  | 'always_show';     // 總是顯示

interface UserSettings {
  sensitiveContent: SensitiveContentPreference;
}

// 根據用戶設定決定顯示方式
function shouldShowContent(
  event: Event,
  settings: UserSettings
): { show: boolean; showWarning: boolean } {
  const { hasSensitiveContent } = checkContentWarning(event);

  if (!hasSensitiveContent) {
    return { show: true, showWarning: false };
  }

  switch (settings.sensitiveContent) {
    case 'always_hide':
      return { show: false, showWarning: false };
    case 'always_show':
      return { show: true, showWarning: true };
    case 'show_warning':
    default:
      return { show: false, showWarning: true };
  }
}`}

常見警告原因

一些常用的警告原因標籤:

原因 說明
NSFW 不適合工作場所的內容
nudity 裸露內容
violence 暴力內容
spoiler 劇透內容
sensitive 一般敏感內容

媒體內容處理

對於包含媒體的敏感內容,建議額外處理:

{`// 敏感媒體處理
function processSensitiveMedia(event: Event): {
  content: string;
  mediaUrls: string[];
  blurMedia: boolean;
} {
  const { hasSensitiveContent } = checkContentWarning(event);

  // 提取媒體 URL
  const mediaUrls = extractMediaUrls(event.content);

  return {
    content: event.content,
    mediaUrls,
    blurMedia: hasSensitiveContent,
  };
}

// 在 CSS 中模糊處理
const styles = \`
  .blurred-media {
    filter: blur(20px);
    transition: filter 0.3s ease;
  }

  .blurred-media:hover,
  .blurred-media.revealed {
    filter: none;
  }
\`;`}

中繼器處理

中繼器可以根據自己的政策處理敏感內容:

  • 接受所有內容(預設)
  • 拒絕有敏感標記的內容
  • 僅接受有敏感標記的成人內容

最佳實踐

發布者應該誠實標記敏感內容,這有助於建立健康的社群環境。 客戶端則應尊重這些標記,讓用戶自行決定是否查看。

注意事項

  • 自願性:標記是發布者自願的,無法強制執行
  • 主觀性:「敏感」的定義因人而異
  • 回覆內容:回覆敏感貼文不會自動繼承敏感標記
  • 轉發處理:轉發時應保留原始的敏感標記
  • NIP-01 - 基本協議與事件結構
  • NIP-18 - 轉發(處理敏感內容轉發)
  • NIP-94 - 檔案中繼資料(媒體標記)

參考資源

已複製連結
已複製到剪貼簿