redditページ上の投稿をコピーするブックマークレット

サイトをclaudeに要約してほしいとき、普通だったらめんどいから必要なとこ全選択してclaudeに丸投げするんだけど、redditはなぜかコピーできなかったので作らなきゃいけなかった

claudeに作ってもらった

```js
javascript:(function() {
  let output = `# URL: ${window.location.href}\n\n`;

  function extractPostDetails() {
    /* タイトル取得 */
    const titleElement = document.querySelector('h1[slot="title"]') || document.querySelector('h1');
    const title = titleElement ? titleElement.textContent.trim() : "Reddit投稿";
    output += `# ${title}\n\n`;

    /* 投稿者名の取得 - href属性から取得 */
    let authorName = '';
    const userLinks = document.querySelectorAll('a[href*="/user/"]');
    for (const link of userLinks) {
      const href = link.getAttribute('href');
      if (href) {
        const match = href.match(/\/user\/([^/]+)\/?/);
        if (match && match[1] && !match[1].startsWith('u_') && match[1] !== 'undefined') {
          authorName = match[1];
          break;
        }
      }
    }
    
    /* 投稿日時の取得 */
    const postDateElement = document.querySelector('span[data-testid="post_timestamp"]') || 
                           document.querySelector('time');
    const creationDate = postDateElement ? postDateElement.textContent.trim().split(/\*|\(編集済み\)|\(edited\)/)[0].trim() : '不明';
    
    output += `**投稿者**: ${authorName}\n**作成日時**: ${creationDate}\n\n`;

    /* 投稿内容の取得 */
    const postContentElement = document.querySelector('div[data-click-id="text"]') || 
                              document.querySelector('[slot="post-body"]') || 
                              document.querySelector('[slot="text-body"]') ||
                              document.querySelector('div.usertext-body');
    
    if (postContentElement) {
      let postContent = postContentElement.textContent.trim();
      postContent = postContent.replace(/\s*Read more\s*$/, '');
      output += `## 投稿内容\n\n${postContent}\n\n`;
    }
    
    output += `## コメント\n\n`;
  }

  function getComments() {
    const comments = [];
    /* 様々なバージョンのRedditに対応するコメントセレクタ */
    const commentElements = document.querySelectorAll('.Comment, shreddit-comment, [data-testid="comment"]');
    
    commentElements.forEach(comment => {
      if (!comment || !comment.textContent) return;
      
      /* コメントの階層レベル取得 */
      let level = 0;
      if (comment.getAttribute('depth')) {
        level = parseInt(comment.getAttribute('depth') || '0') || 0;
      } else {
        const style = window.getComputedStyle(comment);
        const marginLeft = style.marginLeft;
        if (marginLeft) {
          level = Math.floor(parseInt(marginLeft) / 20);
        }
      }
      
      /* 投稿者名の取得 - href属性から取得 */
      let author = '';
      const userLinks = comment.querySelectorAll('a[href*="/user/"]');
      for (const link of userLinks) {
        const href = link.getAttribute('href');
        if (href) {
          const match = href.match(/\/user\/([^/]+)\/?/);
          if (match && match[1] && !match[1].startsWith('u_') && match[1] !== 'undefined') {
            author = match[1];
            break;
          }
        }
      }
      
      /* 日付の取得 */
      let date = '';
      const dateElement = comment.querySelector('time') || 
                         comment.querySelector('[data-testid="comment_timestamp"]') ||
                         comment.querySelector('faceplate-timeago');
      
      if (dateElement && dateElement.textContent) {
        date = dateElement.textContent.trim();
      } else {
        const fullText = comment.textContent || '';
        const match = fullText.match(/(\d+\s+(?:second|minute|hour|day|week|month|year)s?\s+ago)|(\d+\s*(?:mo|hr|min|sec|d|w|y)\.?\s+ago)/i);
        if (match) {
          date = match[0];
        }
      }
      
      /* コメント内容の取得 - より多くのセレクタを試す */
      let content = '';
      
      /* 様々なセレクタを試して内容を取得 */
      const contentSelectors = [
        '[slot="comment-body"]',
        '[slot="text-body"]',
        '.md',
        '.usertext-body',
        'p',
        '[data-testid="comment"] div'
      ];
      
      for (const selector of contentSelectors) {
        const contentElement = comment.querySelector(selector);
        if (contentElement && contentElement.textContent) {
          content = contentElement.textContent.trim();
          content = content.replace(/\s*Read more\s*$/, '');
          if (content) break;
        }
      }
      
      /* コメント内容が取得できない場合はコメント全体から投稿者名と日付を除いて推測 */
      if (!content && comment.textContent) {
        content = comment.textContent.trim();
        /* 投稿者名と日付を含む部分を削除する試み */
        if (author) content = content.replace(new RegExp(author, 'g'), '');
        if (date) content = content.replace(new RegExp(date, 'g'), '');
        content = content.replace(/\s+/g, ' ').trim();
      }
      
      if (author || date || content) {
        comments.push({ level, author, date, content });
      }
    });
    
    return comments;
  }

  try {
    extractPostDetails();
    const comments = getComments();
    
    if (comments.length > 0) {
      comments.forEach(comment => {
        const indent = '  '.repeat(comment.level);
        output += `${indent}- **${comment.author || "不明"}** | ${comment.date || "不明"}\n\n${indent}  ${comment.content.split('\n').join(`\n${indent}  `)}\n\n`;
      });
    } else {
      output += "コメントが見つかりませんでした。";
    }
    
    /* クリップボードコピー処理 */
    navigator.clipboard.writeText(output)
      .then(() => {
        alert("Redditコンテンツがクリップボードにコピーされました！");
      })
      .catch(err => {
        /* エラー時にフォールバックでウィンドウに表示 */
        const newWindow = window.open();
        if (newWindow) {
          newWindow.document.write(`<html><head><title>Reddit Markdown</title><style>body{font-family:monospace;white-space:pre-wrap;padding:20px;}button{padding:10px;margin:10px 0;}</style></head><body><button onclick="copyText()">テキストをコピー</button><div id="content">${output.replace(/</g,'&lt;').replace(/>/g,'&gt;')}</div><script>function copyText(){const text=document.getElementById('content').innerText;navigator.clipboard.writeText(text).then(()=>{alert('コピーしました！');}).catch(()=>{alert('コピーに失敗しました。テキストを手動で選択してコピーしてください。');});}</script></body></html>`);
        } else {
          alert("クリップボードへのコピーに失敗しました。ブラウザの権限設定を確認してください。");
        }
      });
  } catch (error) {
    alert(`エラーが発生しました: ${error.message}`);
  }
})();
```

実行結果:

```md
# URL: https://www.reddit.com/r/ObsidianMD/comments/16hgg0n/properties_creation_date_modification_date/

# Properties: Creation Date & Modification Date?

**投稿者**: focusedgrowth
**作成日時**: 2 yr. ago

## 投稿内容

In my note template I have created: "{{date:YYMMDD HH:mm}}" which populates the `created` field within properties at the top of the file, but how do I display the updated modification date of a file whenever something is changed?
  created: "{{date:YYMMDD HH:mm}}"
modified:

## コメント

- **Lanszer** | 2y ago

  Linter--specifically the YAML-timestamp--and Update time on edit are some plugin options.

  - **DudPug** | 2y ago

    I'm using update time on edit. Did that after switching to Obsidian Sync which caused all the built in file dates to be reset, never again.

  - **focusedgrowth** | 2y ago

    this is perfect! thanks!

  - **-piz** | 7mo ago

    I'm late to this but man I've been struggling with this annoyance for so long, you just saved me a world of headache. Linter is amazing by the way, can't believe I hadn't installed this yet.

  - **hey_ulrich** | 2y ago

    I believe Linter already has an option to update on edit, no? I use it.

  - **nicolaslienart** | 1y ago

    Awesome combination

- **djlaustin** | 2y ago

  I started using Linter for this reason. So far works well.

- **Fri247** | 1y ago

  This plugin saved me a lot of time and stress -> https://github.com/alangrainger/obsidian-frontmatter-modified-date
    
      It modifies automatically notes with the modification date on a specified obsidian property. Several settings are existing.
    
      My (german) plugin settings are:
    
      Frontmatter property: updated
    
      Date format: DD.MM.YYYY HH:mm:ss
    
      Exclude folders: Templates
    
      Only update existing fields: true
    
      Use typing events instead of Obsidian events: false
    
      Of course not the 100% solution like Formatter with automatically updating the date. But almost nothing is working inside these properties with dynamic dates. Nor dataview, nor formatter. Excpet maybe the linter plugin, but this needs to be triggered manually. There is an other plugin out there (https://github.com/beaussan/update-time-on-edit-obsidian), similiar to the first one, but that is more risky to use because it either inerts the date always or never and you have to define single notes or folder to exclude. The plugin above can only instert the modification date when a property is existing with the defined name.

- **blueark99** | 2y ago

  `=this.file.mday`
    
      you can't execute dv inline in properties add this to the page just below the properties block
```

一回でポンとできたわけじゃなくて、出来上がったあと無駄な処理ばかりだったからリファクタリングしてもらったら処理失敗するようになったりでなんだかんだ1時間ぐらいかかった

reddit側の変更ですぐ動かなくなるかもだけどとりあえず使えたので貼っておく