Misskeyに記事のURLをPocketへ追加するボタンを付けるUserScript

Misskeyの記事のURLを[Pocket](https://getpocket.com/ja/)サービスへ送信するボタンを付けるUserScriptです。
仮想DOM生成時に実行されるため、少し重くなるかもしれません。

<img width="400" alt="sample" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3346996/cbcd8f10-c031-79c2-ae7a-0a607aab5a4d.png">

正常に送信が行われると緑色になります。（API送信時に確認のため色を変えているため、Pocketへ保存されている記事の色が変わるわけではありません。）

## 準備
Pocketに登録し、API利用のため**カスタマーキー**と**アクセストークン**を取得します。
https://zenn.dev/kou_pg_0131/articles/pocket-api-usage
上記サイトを参考にして取得しました。
何故かよくわからないですが一度500エラーになるのですが上記記事通り「アクセストークンを発行する」から再度行うと取得できました。

## UserScriptコード
PCブラウザ, Android, iOSでのそれぞれの導入方法は[こちらの記事](https://qiita.com/myspleen/items/6d3c64074365f5a060e8)を参照ください。
下記のうち`@match`のURLを利用するMisskeyのサーバーへ、`consumer_key`、`access_token`を取得したPocket APIのカスタマーキーとアクセストークンへ書き換えてください。
```JavaScript
// ==UserScript==
// @name         Save URL to Pocket
// @namespace    @noc_turn@nijimiss.moe
// @version      1.0
// @description  Saves the URL to Pocket when the specified button is clicked
// @match        https://nijimiss.moe/*
// @run-at      document-idle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

// ボタンを生成する関数
function createPocketButton(article) {
    const footer = article.querySelector('footer');
    if (footer) {
        const pocketButton = document.createElement('button');
        pocketButton.classList.add('_button', 'xviCy', 'pocket-button');
        pocketButton.style.height = '32px';
        pocketButton.style.borderRadius = '6px';

        const pocketIcon = document.createElement('img');
        pocketIcon.setAttribute('src', 'https://estampie.mimoza.jp/pocket.svg');
        pocketIcon.setAttribute('width', '16');
        pocketIcon.setAttribute('height', '16');
        pocketIcon.classList.add('xeJ4G', 'x5kTm', 'x9Io4');

        pocketButton.appendChild(pocketIcon);

        pocketButton.addEventListener('click', () => {
            const noteElement = article.querySelector('a[href^="/notes/"]');
            if (noteElement) {
                const notePath = noteElement.getAttribute('href');
                const fullURL = 'http://nijimiss.moe' + notePath;

                GM_xmlhttpRequest({
                    method: 'POST',
                    url: 'https://getpocket.com/v3/add',
                    headers: {
                        'Content-Type': 'application/json; charset=UTF-8',
                        'X-Accept': 'application/json',
                    },
                    data: JSON.stringify({
                        url: fullURL,
                        consumer_key: 'カスタマーキー',
                        access_token: 'アクセストークン',
                    }),
                    onload: function(response) {
                        if (response.status >= 200 && response.status < 300) {
                            // API送信が成功した時の処理
                            pocketButton.style.backgroundColor = '#77b58c'; // ボタンの背景色を緑に変える
                        } else {
                            // API送信が失敗した時の処理
                            pocketButton.style.backgroundColor = '#FF0000'; // ボタンの背景色を赤に変える
                        }
                    }
                });
            }
        });

        footer.appendChild(pocketButton);
    }
}


// DOMが変更されたときに実行される関数
const observerCallback = function(mutationsList, observer) {
    for(let mutation of mutationsList) {
        if (mutation.type === 'childList') {
            const articles = document.querySelectorAll('article:not(.has-pocket-button)');
            articles.forEach(article => {
                createPocketButton(article);
                article.classList.add('has-pocket-button');
            });
        }
    }
};

// MutationObserverを設定
const observer = new MutationObserver(observerCallback);
observer.observe(document, { childList: true, subtree: true });

// 初期ページの記事にボタンを追加
const initialArticles = document.querySelectorAll('article');
initialArticles.forEach(article => {
    createPocketButton(article);
    article.classList.add('has-pocket-button');
});



```
## 補足
下記カスタムCSSを併用すると二行にならずに済むかもしれません。数値は任意で変更してください。
```CSS
.xviCy:not(:last-child) {
    margin-right: 14px !important;
}
```