import { delay } from '@std/async/delay'; import { shuffle } from '@std/random'; type User = { user: string; fullname: string; type: string; isFollowing?: true; _id: string; isPro: boolean; avatarUrl: string; }; export class HuggingFace { constructor(public cookie: string) { } async getOrganizationFollowers(organization: string, cursor?: string) { const url = new URL(`https://huggingface.co/api/organizations/${organization}/followers`); cursor && url.searchParams.append('cursor', cursor); const response = await fetch(url, { headers: { 'Cookie': this.cookie, }, }); if (!response.ok) throw Error(`${response.status} ${response.statusText}`); return await response.json() as User[]; } async getNonFollowingUsers(organization: string) { let cursor: string | undefined = undefined; while (true) { const users = await this.getOrganizationFollowers(organization, cursor); if (!users.length) throw Error('No users found'); const nonFollowing = users.filter((user) => !user.isFollowing); if (nonFollowing.length) return nonFollowing; cursor = users[users.length - 1]._id; } } async followRandomUsers(organization: string) { const users = await this.getNonFollowingUsers(organization); const shuffled = shuffle(users); for (let i = 0; i < shuffled.length; i++) { await delay(1000); const user = shuffled[i]; const res = await this.follow(user.user); if (res.status === 429) { console.error((await res.json()).error); return i; } const body = await res.text(); if (body.trim().toLowerCase() === 'ok') { console.log(`Followed ${user.user}`); } else { console.warn(`Failed to follow ${user.user}`); continue; } } } async follow(user: string) { return await fetch(`https://huggingface.co/api/users/${user}/follow`, { method: 'post', headers: { 'content-type': 'application/json', 'Cookie': this.cookie, }, }); } }