#!/usr/bin/env node
/*
* Follow accounts by username. Resolves username -> fid, then submits a LinkAdd.
* node follow.js lthibault midao rakshasar.eth
*/
const fs = require("fs");
const { makeLinkAdd, NobleEd25519Signer, FarcasterNetwork, Message } = require("@farcaster/core");
const HUBS = ["https://snap.farcaster.xyz:3381", "https://hub.pinata.cloud"];
const UA = { "User-Agent": "Mozilla/5.0 (compatible; agentatwork/1.0)" };
async function fidOf(username) {
// fnames registry resolves plain fnames; the Warpcast API also handles ENS-style names
const r = await fetch(`https://api.warpcast.com/v2/user-by-username?username=${encodeURIComponent(username)}`,
{ headers: UA });
if (r.ok) {
const d = await r.json();
const fid = d?.result?.user?.fid;
if (fid) return { fid, followers: d.result.user.followerCount };
}
return null;
}
async function submit(bytes) {
let last;
for (const hub of HUBS) {
try {
const r = await fetch(hub + "/v1/submitMessage", {
method: "POST", headers: { "Content-Type": "application/octet-stream" }, body: bytes });
if (r.ok) return hub;
last = `${hub} ${r.status} ${(await r.text()).slice(0, 150)}`;
} catch (e) { last = `${hub} ${e.message}`; }
}
throw new Error(last);
}
(async () => {
const names = process.argv.slice(2);
const st = JSON.parse(fs.readFileSync(__dirname + "/farcaster.json", "utf8"));
const der = Buffer.from(st.signerPrivateKeyPkcs8, "base64");
const signer = new NobleEd25519Signer(der.subarray(der.length - 32));
const fid = Number(st.fid);
for (const name of names) {
try {
const who = await fidOf(name);
if (!who) { console.log(` ${name.padEnd(20)} not found`); continue; }
const res = await makeLinkAdd({ type: "follow", targetFid: who.fid },
{ fid, network: FarcasterNetwork.MAINNET }, signer);
if (res.isErr()) throw res.error;
await submit(Message.encode(res.value).finish());
console.log(` followed ${name.padEnd(20)} fid=${String(who.fid).padEnd(8)} followers=${who.followers}`);
} catch (e) {
console.log(` ${name.padEnd(20)} ERROR ${e.message.slice(0, 90)}`);
}
}
})();