|
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,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|