Misskeyの記事のURLをPocketサービスへ送信するボタンを付けるUserScriptです。
仮想DOM生成時に実行されるため、少し重くなるかもしれません。
![sample](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F3346996%2Fcbcd8f10-c031-79c2-ae7a-0a607aab5a4d.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=03fb142cb587dc303c1bb48a59898c47)
正常に送信が行われると緑色になります。(API送信時に確認のため色を変えているため、Pocketへ保存されている記事の色が変わるわけではありません。)
準備
Pocketに登録し、API利用のためカスタマーキーとアクセストークンを取得します。
https://zenn.dev/kou_pg_0131/articles/pocket-api-usage
上記サイトを参考にして取得しました。
何故かよくわからないですが一度500エラーになるのですが上記記事通り「アクセストークンを発行する」から再度行うと取得できました。
UserScriptコード
PCブラウザ, Android, iOSでのそれぞれの導入方法はこちらの記事を参照ください。
下記のうち@match
のURLを利用するMisskeyのサーバーへ、consumer_key
、access_token
を取得したPocket APIのカスタマーキーとアクセストークンへ書き換えてください。
// ==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を併用すると二行にならずに済むかもしれません。数値は任意で変更してください。
.xviCy:not(:last-child) {
margin-right: 14px !important;
}