Skip to content

Instantly share code, notes, and snippets.

@Bossett
Last active October 18, 2024 09:35
Show Gist options
  • Save Bossett/c012ed574d96114569af7ba3b25693f1 to your computer and use it in GitHub Desktop.
Save Bossett/c012ed574d96114569af7ba3b25693f1 to your computer and use it in GitHub Desktop.
bsky snippets
// ADDS YOURSELF TO A LIST
// -----------------------
// Navigate from https://bsky.app/ to the list you want to add yourself to
// Open the developer tools, and copy/paste this into the console tab:
(async () => {
const [_, handle, rkey] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*?)\/lists\/(.*?)(?:\/|$)/) || [];
if (!handle) return console.error("The URL doesn't match the expected format.");
const did = !handle.startsWith("did:") ? (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`)).json()).did : handle;
const { session } = JSON.parse(localStorage.root);
const { accessJwt, did: repo } = session.accounts.find(a => a.did === session.data.did) || {};
await fetch("https://bsky.social/xrpc/com.atproto.repo.createRecord", {
method: "POST", headers: {"Content-Type": "application/json", Authorization: `Bearer ${accessJwt}`},
body: JSON.stringify({collection: "app.bsky.graph.listitem", repo, record: {subject: repo, list: `at://${did}/app.bsky.graph.list/${rkey}`, createdAt: new Date().toISOString()}})
});
location.reload();
})();
// **BLOCK** ALL MEMBERS OF A LIST
// -------------------------------
// Navigate from https://bsky.app/ to the list of users you want to block
// Open the developer tools, and copy/paste this into the console tab:
// It may take a little while to run - the page will reload when finished
(async () => {
const [_, handle, rkey] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*?)\/lists\/(.*?)(?:\/|$)/) || [];
if (!handle) return console.error("The URL doesn't match the expected format.");
const did = !handle.startsWith("did:") ? (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`)).json()).did : handle;
const { session } = JSON.parse(localStorage.BSKY_STORAGE);
const { accessJwt, did: repo } = session.accounts.find((a) => a.did === session.currentAccount.did) || {};
let totalRetrieved = 0, currentCursor = "", members = [];
do {
const list = await (await fetch(`https://bsky.social/xrpc/app.bsky.graph.getList?list=at://${did}/app.bsky.graph.list/${rkey}&limit=100&cursor=${currentCursor}`, {
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` }
})).json();
currentCursor = list.cursor;
totalRetrieved = list.items.length;
members = [...members, ...list.items];
} while (currentCursor !== undefined && currentCursor !== '');
for (let i = 0; i < members.length; i += 3) {
await Promise.all(members.slice(i, i + 3).map((member) => fetch("https://bsky.social/xrpc/com.atproto.repo.createRecord", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` },
body: JSON.stringify({ collection: "app.bsky.graph.block", repo, record: { subject: member.subject.did, createdAt: new Date().toISOString() } })
})));
}
location.reload();
})();
// CONVERTS A MOD LIST TO A CURATION LIST
// -----------------------
// Navigate from https://bsky.app/ to the list you want to convert
// Open the developer tools, and copy/paste this into the console tab:
(async () => {
const [_, handle, rkey] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*?)\/lists\/(.*?)(?:\/|$)/) || [];
if (!handle) return console.error("The URL doesn't match the expected format.");
const did = !handle.startsWith("did:") ? (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`)).json()).did : handle;
const { session } = JSON.parse(localStorage.BSKY_STORAGE);
const { accessJwt, did: repo } = session.accounts.find((a) => a.did === session.currentAccount.did) || {};
const listRecord = (await (await fetch(`https://bsky.social/xrpc/com.atproto.repo.getRecord?repo=${repo}&rkey=${rkey}&collection=app.bsky.graph.list`)).json()).value;
listRecord.purpose = "app.bsky.graph.defs#curatelist";
await fetch("https://bsky.social/xrpc/com.atproto.repo.putRecord", {
method: "POST", headers: {"Content-Type": "application/json", Authorization: `Bearer ${accessJwt}`},
body: JSON.stringify({collection: "app.bsky.graph.list", repo, rkey, record: listRecord})
});
location.reload();
})();
// COPIES ALL MEMBERS OF A LIST TO ANOTHER LIST
// --------------------------------------------
// 1. Navigate from https://bsky.app/ to the list you want to copy TO and copy its url
// (e.g. https://bsky.app/profile/bossett.social/lists/3k22v6pbfk32t)
// 2. Change the value of DESTINATION_LIST to your value from 1.
// 3. Navigate from https://bsky.app/ to the list you want to copy FROM
// 4. Open the developer tools, and copy/paste this into the console tab & ENTER
// It may take a little while to run - the page will reload when finished
(async () => {
const DESTINATION_LIST = "https://bsky.app/profile/bossett.social/lists/3k22v6pbfk32t";
const [unused_1, src_handle, src_rkey] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*?)\/lists\/(.*?)(?:\/|$)/) || [];
if (!src_handle || !src_rkey) return console.error("The source URL doesn't match the expected format.");
const [unused_2, dst_handle, dst_rkey] = DESTINATION_LIST.match(/https:\/\/bsky.app\/profile\/(.*?)\/lists\/(.*?)(?:\/|$)/) || [];
if (!dst_handle || !dst_rkey) return console.error("The destination URL doesn't match the expected format.");
const { session } = JSON.parse(localStorage.BSKY_STORAGE);
const { accessJwt, did: repo } = session.accounts.find((a) => a.did === session.currentAccount.did) || {};
const src_did = !src_handle.startsWith("did:") ? (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${src_handle}`)).json()).did : src_handle;
let totalRetrieved = 0, currentCursor = "", members = [];
do {
const list = await (await fetch(`https://bsky.social/xrpc/app.bsky.graph.getList?list=at://${src_did}/app.bsky.graph.list/${src_rkey}&limit=100&cursor=${currentCursor}`, {
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` }
})).json();
currentCursor = list.cursor;
totalRetrieved = list.items.length;
members = [...members, ...list.items];
} while (currentCursor !== undefined && currentCursor !== '');
for (let i = 0; i < members.length; i += 3) {
await Promise.all(members.slice(i, i + 3).map((member) => fetch("https://bsky.social/xrpc/com.atproto.repo.createRecord", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` },
body: JSON.stringify({ collection: "app.bsky.graph.listitem", repo, record: { subject: member.subject.did, list: `at://${repo}/app.bsky.graph.list/${dst_rkey}` ,createdAt: new Date().toISOString() } })
})));
}
location.reload();
})();
// DELETES A FOLLOW RECORD
// -----------------------
// Navigate from https://bsky.app/ to the profile you want to unfollow
// Open the developer tools, and copy/paste this into the console tab:
(async () => {
const [_, handle] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*)/) || [];
if (!handle) return console.error("The URL doesn't match the expected format.");
const targetDid = !handle.startsWith("did:") ? (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`)).json()).did : handle;
const { session } = JSON.parse(localStorage.BSKY_STORAGE);
const { accessJwt, did, pdsUrl } = session.currentAccount;
let currentCursor = undefined;
let targetDidUris = [];
do {
const rec = await (await fetch(`${pdsUrl}xrpc/com.atproto.repo.listRecords?repo=${did}&collection=app.bsky.graph.follow&limit=100&cursor=${currentCursor||''}`, {
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` }
})).json();
currentCursor = rec.cursor;
targetDidUris = [...targetDidUris,...rec.records.filter((r)=>r.value.subject === targetDid)]
} while (currentCursor !== undefined && currentCursor !== '');
targetDidUris.forEach(rec=>{
const [_,recordDid,rkey] = rec.uri.match(/at:\/\/(.*?)\/app\.bsky\.graph\.follow\/([^\/]+)/);
if(rkey && recordDid === did) {
fetch(`${pdsUrl}xrpc/com.atproto.repo.deleteRecord`, {
method: "POST", headers: {"Content-Type": "application/json", Authorization: `Bearer ${accessJwt}`},
body: JSON.stringify({collection: "app.bsky.graph.follow", repo: did, rkey})
});
}})
})();
// DELETES A RECORD
// -----------------------
// DELETES the record at urlToDelete - just set that to an at:// URL
(async () => {
const urlToDelete = "at://did:plc:jfhpnnst6flqway4eaeqzj2a/app.bsky.feed.like/3kxdu4cjco22n";
const [_,repo,collection,rkey] = urlToDelete.match(/at:\/\/(.*?)\/(.*?)\/(.*)/);
const { session } = JSON.parse(localStorage.BSKY_STORAGE);
const { accessJwt, did, pdsUrl } = session.currentAccount;
if (did !== repo) return;
const rec = await(
await fetch(
`${pdsUrl}xrpc/com.atproto.repo.getRecord?repo=${repo}&collection=${collection}&rkey=${rkey}`,
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessJwt}`,
},
}
)
).json();
if(rec.uri !== urlToDelete) return;
console.log(`found ${urlToDelete} to delete`)
fetch(`${pdsUrl}xrpc/com.atproto.repo.deleteRecord`, {
method: "POST", headers: {"Content-Type": "application/json", Authorization: `Bearer ${accessJwt}`},
body: JSON.stringify({collection: collection, repo: did, rkey})
});
})();
// FOLLOWS ALL MEMBERS OF A LIST
// -----------------------------
// Navigate from https://bsky.app/ to the list you want to follow
// Open the developer tools, and copy/paste this into the console tab:
// It may take a little while to run - the page will reload when finished
(async () => {
const [_, handle, rkey] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*?)\/lists\/(.*?)(?:\/|$)/) || [];
if (!handle) return console.error("The URL doesn't match the expected format.");
const did = !handle.startsWith("did:") ? (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`)).json()).did : handle;
const { session } = JSON.parse(localStorage.BSKY_STORAGE);
const { accessJwt, did: repo } = session.accounts.find((a) => a.did === session.currentAccount.did) || {};
let totalRetrieved = 0, currentCursor = "", members = [];
do {
const list = await (await fetch(`https://bsky.social/xrpc/app.bsky.graph.getList?list=at://${did}/app.bsky.graph.list/${rkey}&limit=100&cursor=${currentCursor}`, {
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` }
})).json();
currentCursor = list.cursor;
totalRetrieved = list.items.length;
members = [...members, ...list.items];
} while (currentCursor !== undefined && currentCursor !== '');
for (let i = 0; i < members.length; i += 3) {
await Promise.all(members.slice(i, i + 3).map((member) => fetch("https://bsky.social/xrpc/com.atproto.repo.createRecord", {
method: "POST",
headers: { "Content-Type": "application/json", Authorization: `Bearer ${accessJwt}` },
body: JSON.stringify({ collection: "app.bsky.graph.follow", repo, record: { subject: member.subject.did, createdAt: new Date().toISOString() } })
})));
}
location.reload();
})();
// GETS THE LIST OF HANDLES THAT HAVE LIKED A FEED
// -----------------------------------------------
// Navigate from https://bsky.app/ to the feed you want to get likes for
// Open the developer tools, and copy/paste this into the console tab:
(async () => {
const [_, handle, rkey] =
window.location.href.match(
/https:\/\/bsky.app\/profile\/(.*?)\/feed\/(.*?)(?:\/|$)/
) || [];
if (!handle)
return console.error("The URL doesn't match the expected format.");
const did = !handle.startsWith("did:")
? (
await (
await fetch(
`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`
)
).json()
).did
: handle;
const { session } = JSON.parse(localStorage.root);
const { accessJwt, did: repo } =
session.accounts.find((a) => a.did === session.data.did) || {};
let totalRetrieved = 0, currentCursor = '', likes = [];
do {
let result = await (
await fetch(
`https://bsky.social/xrpc/app.bsky.feed.getLikes?uri=at://${did}/app.bsky.feed.generator/${rkey}&cursor=${currentCursor}`,
{
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessJwt}`,
},
}
)
).json();
currentCursor = result.cursor;
totalRetrieved = result.likes.length;
likes = [...likes, ...result.likes];
} while (totalRetrieved > 0);
for (const like of likes) {
console.log(like.actor.handle);
}
})();
const [_, handle, rkey] = window.location.href.match(/https:\/\/bsky.app\/profile\/(.*?)\/post\/(.*?)(?:\/|$)/) || [];
if (!handle) {
console.error("The URL doesn't match the expected format.");
} else {
const did = handle.startsWith("did:") ? handle : (await (await fetch(`https://bsky.social/xrpc/com.atproto.identity.resolveHandle?handle=${handle}`)).json()).did;
const posts = await (await fetch(`https://public.api.bsky.app/xrpc/app.bsky.feed.getPosts?uris=at://${did}/app.bsky.feed.post/${rkey}`)).json();
const parentPost = posts.posts[0].record.reply.root.uri;
console.log(`muting ${parentPost}`)
if (!parentPost) {
console.log("Failed to find parent post.");
} else {
const storage = JSON.parse(localStorage.getItem('BSKY_STORAGE'));
storage.mutedThreads.push(parentPost);
localStorage.setItem('BSKY_STORAGE', JSON.stringify(storage));
const broadcast = new BroadcastChannel('BSKY_BROADCAST_CHANNEL');
broadcast.postMessage({ event: 'BSKY_UPDATE' });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment