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

Misskeyの記事のURLをPocketサービスへ送信するボタンを付けるUserScriptです。

Google KeepはAPI非公開、EvernoteはAPIがありますがあまりUserScript向きではないようでした。


仮想DOM生成時に実行されるため、少し重くなるかもしれません。

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

準備

Pocketに登録し、API利用のためカスタマーキーアクセストークンを取得します。

上記サイトを参考にして取得しました。

UserScriptコード

PCブラウザ, Android, iOSでのそれぞれの導入方法はこちらの記事を参照ください。
下記のうち@matchのURLを利用するMisskeyのサーバーへ、consumer_keyaccess_tokenを取得したPocket APIのカスタマーキーとアクセストークンへ書き換えてください。

// ==UserScript==
// @name         Save URL to Pocket
// @namespace    @[email protected]
// @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://blog.estampie.work/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を併用すると二行にならずに済むかもしれません。数値は任意で変更してください。

.xviCy:not(:last-child) {
    margin-right: 14px !important;
}

Misskey,UserScript