【無料】noteのフォロー管理用スクリプトを作ってみたよ。
追記:2023年11月17日
200スキ集まった場合、Google拡張機能として利用できるツールを開発するよ。
開発する場合はみんなにも意見を募集するよ。
開発して欲しい人はSNSにシェアして拡散してくれたら嬉しいです。
はじめに
noteのフォロー管理をするツールを販売している人が居たから、noteのフォロー管理をするスクリプトを作成してみたよ。
ツールの仕様
開発者ツールのコンソールに貼り付けてエンターキーを押すことで利用できるよ。
必ずフォローリストを開いた状態で実行してね。
機能
フォロー中 / 相互フォロー / 片思いフォローに表示を切り替え出来るよ。
自動で全てのアカウントのリスト化をするよ。
json形式での書き出しに対応しているよ。
無料で公開する理由
① 教育支援
前回はTwitterで自動いいねするスクリプトを公開したの。
今回はnoteを題材にしたってわけ。
JavaScriptの勉強に役立ってくれたら嬉しいわ。
② note運営に機能追加を求めたいから
noteは簡単に実装できる機能を追加していない事が多いから、機能追加を求める声を増やして、より使いやすいサービスになって欲しいと思うよ。
③ お金を払うまでも無いから
この手のツールやスクリプトは、ぼったくり価格で販売されているから、市場を健全化していきたいと考えたの。
スクリプト本体
(function() {
const modal = document.createElement('div');
modal.innerHTML = `
<div id="modalContainer" style="display: none;">
<h4>noteフォロー整理ツール</h4>
<div id="filterButtons">
<button id="allFollows">全て</button>
<button id="mutualFollows">相互フォロー (<span id="mutualCount">0</span>)</button>
<button id="oneSidedFollows">片思いフォロー (<span id="oneSidedCount">0</span>)</button>
<button id="jsonButton">JSON書き出し</button>
</div>
<span class="createdBy">作成者:<a href="https://note.com/honne_note/">ほんねのーと</a></span>
</div>
`;
document.body.appendChild(modal);
const style = document.createElement('style');
style.textContent = `
#modalContainer {
position: fixed;
right: 10px;
bottom: 10px;
padding: 15px;
background-color: white;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
#modalContainer h4 {
margin-top: 0;
margin-bottom: 10px;
font-size: 16px;
font-weight: bold;
color: #238f76;
}
#modalContainer .createdBy {
display: block;
margin-bottom: 10px;
font-size: 14px;
color: #666;
}
#filterButtons {
display: flex;
flex-direction: column;
gap: 10px;
margin-bottom: 20px;
}
#filterButtons button {
padding: 10px 15px;
border: none;
border-radius: 4px;
cursor: pointer;
background-color: #238f76;
color: white;
font-size: 16px;
font-weight: bold;
transition: background-color 0.2s cubic-bezier(1, 0, 0, 1);
}
`;
document.head.appendChild(style);
document.getElementById('modalContainer').style.display = 'block';
const updateCounts = () => {
const items = document.querySelectorAll('.o-contentFollow__item');
let mutualCount = 0;
let oneSidedCount = 0;
items.forEach(item => {
if (item.querySelector('.m-userListItem__followed')) {
mutualCount++;
} else {
oneSidedCount++;
}
});
document.getElementById('mutualCount').innerText = mutualCount;
document.getElementById('oneSidedCount').innerText = oneSidedCount;
};
const filterFollows = (type) => {
const items = document.querySelectorAll('.o-contentFollow__item');
items.forEach(item => {
const isMutual = item.querySelector('.m-userListItem__followed') !==
null;
if (type === 'all') {
item.style.display = 'block';
} else if (type === 'mutual' && isMutual) {
item.style.display = 'block';
} else if (type === 'oneSided' && !isMutual) {
item.style.display = 'block';
} else {
item.style.display = 'none';
}
});
};
document.getElementById('allFollows').addEventListener('click', () =>
filterFollows('all'));
document.getElementById('mutualFollows').addEventListener('click', () =>
filterFollows('mutual'));
document.getElementById('oneSidedFollows').addEventListener('click', () =>
filterFollows('oneSided'));
const observer = new MutationObserver(updateCounts);
observer.observe(document.querySelector('.o-contentFollow__userList'), {
childList: true
});
updateCounts();
let currentPage = 1;
let isLoading = false;
const interval = 500;
const paginationItems = document.querySelectorAll('.m-paginate__item');
const maxPages = parseInt(paginationItems[paginationItems.length - 1].innerText);
function modifyUserItems() {
document.querySelectorAll('.o-contentFollow__item').forEach(item => {
const followButton = item.querySelector('.m-userListItem__action');
if (followButton) {
followButton.remove();
}
const link = item.querySelector('.m-userListItem__link');
if (link) {
link.setAttribute('target', '_blank');
}
});
};
const loadMoreData = () => {
if (isLoading || currentPage >= maxPages) {
if (currentPage >= maxPages) {
document.getElementById('loadingStatus').remove();
showCompleteAlert();
}
return;
}
isLoading = true;
currentPage++;
const baseUrl = window.location.href.split('?')[0];
fetch(`${baseUrl}?page=${currentPage}`)
.then(response => response.text())
.then(data => {
const parser = new DOMParser();
const doc = parser.parseFromString(data, 'text/html');
const newItems = doc.querySelectorAll('.o-contentFollow__item');
const container = document.querySelector('.o-contentFollow__userList');
newItems.forEach(item => container.appendChild(item));
isLoading = false;
if (newItems.length > 0) {
setTimeout(() => loadMoreData(), interval);
}
})
.catch(error => console.error('Error loading more items:', error));
updateLoadingStatus(maxPages);
modifyUserItems();
};
loadMoreData();
function updateLoadingStatus(maxPages) {
let statusElement = document.getElementById('loadingStatus');
if (!statusElement) {
statusElement = document.createElement('div');
statusElement.id = 'loadingStatus';
statusElement.style.position = 'fixed';
statusElement.style.bottom = '10px';
statusElement.style.right = '10px';
document.body.appendChild(statusElement);
}
statusElement.innerText = `Loading... (${currentPage} of ${maxPages})`;
};
const showCompleteAlert = () => {
const statusElement = document.getElementById('loadingStatus');
if (statusElement) {
statusElement.remove();
}
alert('すべて取得しました');
};
document.getElementById('jsonButton').addEventListener('click', createAndDownloadJson);
function createAndDownloadJson() {
const items = document.querySelectorAll('.o-contentFollow__item');
const data = Array.from(items).map(item => {
const nameLabel = item.querySelector('.m-userListItem__nameLabel');
const name = nameLabel ? nameLabel.innerText.trim() : '不明';
const mutual = item.querySelector('.m-userListItem__followed') ? 1 : 0;
const linkElement = item.querySelector('.m-userListItem__link');
const link = linkElement ? linkElement.href : 'リンク不明';
const imageElement = item.querySelector('.m-userListItem__avatar img');
const imageUrl = imageElement ? (imageElement.getAttribute('data-src') ||
imageElement.src) : '画像URL不明';
return {
name, mutual, link, imageUrl
};
});
const jsonBlob = new Blob([JSON.stringify(data)], {
type: 'application/json'
});
const url = URL.createObjectURL(jsonBlob);
const a = document.createElement('a');
a.href = url;
a.download = 'followers.json';
a.click();
URL.revokeObjectURL(url);
}
loadMoreData();
})();
スクリプトの使い方
noteにアクセス
フォローリストを開く
ブラウザの開発者ツールを起動する
コンソールのタブを開いて、スクリプトをコピー&ペーストし、エンターキーを押す
リストの読み込み完了まで待機する
読み込みが完了したら、右下のポップアップからボタンで選んで使ってね。
注意
このスクリプトは、Javascriptの教育用に無償公開しているものです。
スクリプト及び改造済みのスクリプトを第三者に譲渡 / 販売 / 公開 / 配布する行為は禁止とします。
万が一、スクリプトの実行によって何らかの被害にあったとしても、私は責任をとれませんので、自己責任で実行してください。
最後に
「こんなスクリプトを書いてみて欲しい」
「こんなものを作りたいけど作り方を教えて欲しい」
といった要望があれば、コメントにお願いね。
私が時間のある時に記事にして公開するよ!!
使い方で分からないことがあれば、それも教えるよ。
PR
noteのフォローをよろしくね!!
コメントした人にはフォローを返すよ!!
Twitterもやっているから、フォローしてくれたら嬉しいです!!
この記事が気に入ったらサポートをしてみませんか?