@standard-crypto/farcaster-js-neynar
Version:
A tool for interacting with Farcaster via Neynar APIs.
344 lines • 13.5 kB
JavaScript
import { CastApi, UserApi, VerificationApi, NotificationsApi, ReactionsApi, FollowsApi, Configuration, } from './openapi/index.js';
import axios, { AxiosError } from 'axios';
import { silentLogger } from '../logger.js';
const BASE_PATH = 'https://api.neynar.com/v1';
export class NeynarV1APIClient {
logger;
apis;
/**
* Instantiates a NeynarV1APIClient
*
* Note: A Wallet must be provided if the API client is to mint new AuthTokens
*/
constructor(apiKey, { logger = silentLogger, axiosInstance, } = {}) {
this.logger = logger;
if (apiKey === '') {
throw new Error('Attempt to use an authenticated API method without first providing an api key');
}
if (axiosInstance === undefined) {
axiosInstance = axios.create();
}
axiosInstance.defaults.decompress = true;
axiosInstance.interceptors.response.use((response) => response, (error) => {
if (NeynarV1APIClient.isApiErrorResponse(error)) {
const apiErrors = error.response.data;
this.logger.warn(`API errors: ${JSON.stringify(apiErrors)}`);
}
throw error;
});
const config = new Configuration({
basePath: BASE_PATH,
apiKey: apiKey,
});
this.apis = {
cast: new CastApi(config, undefined, axiosInstance),
user: new UserApi(config, undefined, axiosInstance),
verification: new VerificationApi(config, undefined, axiosInstance),
notifications: new NotificationsApi(config, undefined, axiosInstance),
reactions: new ReactionsApi(config, undefined, axiosInstance),
follows: new FollowsApi(config, undefined, axiosInstance),
};
}
/**
* Utility for parsing errors returned by the Neynar API servers. Returns true
* if the given error is caused by an error response from the server, and
* narrows the type of `error` accordingly.
*/
static isApiErrorResponse(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
error) {
if (!(error instanceof AxiosError))
return false;
return (error.response?.data !== undefined && 'message' in error.response.data);
}
/**
* Fetches casts in a given thread. See [Neynar documentation](https://docs.neynar.com/reference/get-all-casts-in-thread)
* Note that the parent provided by the caller is included in the response.
*/
async fetchCastsInThread(threadParentHash, viewerFid) {
const response = await this.apis.cast.allCastsInThread({
threadHash: threadParentHash,
viewerFid: viewerFid,
});
return response.data.result.casts;
}
/**
* Gets all casts (including replies and recasts) created by the specified user. See [Neynar documentation](https://docs.neynar.com/reference/get-all-casts-from-user)
*
*/
async *fetchCastsForUser(fid, options) {
let cursor;
while (true) {
const response = await this.apis.cast.casts({
fid: fid,
viewerFid: options?.viewerFid,
parentUrl: options?.parentUrl,
cursor: cursor,
limit: options?.pageSize,
});
// yield current page of casts
yield* response.data.result.casts;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Gets recent casts created by the specified user. See [Neynar documentation](https://docs.neynar.com/reference/get-recent-casts-from-protocol)
*
*/
async *fetchRecentCasts(options) {
let cursor;
while (true) {
// fetch one page of casts (with refreshed auth if necessary)
const response = await this.apis.cast.recentCasts({
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
yield* response.data.result.casts;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* A list of users in reverse chronological order based on sign up. See [Neynar documentation](https://docs.neynar.com/reference/get-recent-users-from-protocol)
*/
async *fetchRecentUsers(options) {
let cursor;
while (true) {
// fetch one page of casts (with refreshed auth if necessary)
const response = await this.apis.user.recentUsers({
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
yield* response.data.result.users;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Fetch all likes by a given user. See [Neynar documentation](https://docs.neynar.com/reference/get-user-cast-likes)
*/
async *fetchUserCastLikes(fid, options) {
let cursor;
while (true) {
// fetch one page of likes
const response = await this.apis.user.userCastLikes({
fid: fid,
viewerFid: options?.viewerFid,
limit: options?.pageSize,
cursor: cursor,
});
yield* response.data.result.likes;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Gets the specified user via their FID (if found). See [Neynar documentation](https://docs.neynar.com/reference/get-user-information-by-fid)
*/
async lookupUserByFid(fid, viewerFid) {
try {
const response = await this.apis.user.user({ fid, viewerFid });
return response.data.result.user;
}
catch (error) {
if (NeynarV1APIClient.isApiErrorResponse(error)) {
if (error.response.status === 404)
return null;
}
throw error;
}
}
/**
* Gets the specified user via their username (if found). See [Neynar documentation](https://docs.neynar.com/reference/get-user-information-by-username)
*/
async lookupUserByUsername(username, viewerFid) {
try {
const response = await this.apis.user.userByUsername({
username,
viewerFid,
});
return response.data.result.user;
}
catch (error) {
if (NeynarV1APIClient.isApiErrorResponse(error)) {
const status = error.response.status;
if (status === 404) {
return null;
}
}
throw error;
}
}
/**
* Gets the custody address for the specified user via their username (if found). See [Neynar documentation](https://docs.neynar.com/reference/get-custody-address)
*/
async fetchCustodyAddressForUser(fid) {
const response = await this.apis.user.custodyAddress({ fid });
return response.data.result.custodyAddress;
}
/**
* Gets all known verifications of a user. See [Neynar documentation](https://docs.neynar.com/reference/get-user-verifications)
*/
async fetchUserVerifications(fid) {
const response = await this.apis.verification.verifications({ fid });
return response.data.result;
}
/**
* Checks if a given Ethereum address has a Farcaster user associated with it.
* Note: if an address is associated with multiple users, the API will return
* the user who most recently published a verification with the address
* (based on when Warpcast received the proof, not a self-reported timestamp).
* See [Neynar documentation](https://docs.neynar.com/reference/get-user-by-verification)
*/
async lookupUserByVerification(address) {
try {
const response = await this.apis.verification.userByVerification({
address,
});
return response.data.result.user;
}
catch (error) {
if (NeynarV1APIClient.isApiErrorResponse(error)) {
const status = error.response.status;
if (status === 404) {
return null;
}
}
throw error;
}
}
/**
* Gets a list of mentions and replies to the user’s casts in reverse chronological order. See [Neynar documentation](https://docs.neynar.com/reference/get-user-mentions-and-replies)
*/
async *fetchMentionAndReplyNotifications(fid, options) {
let cursor;
while (true) {
// fetch one page of notifications
const response = await this.apis.notifications.mentionsAndReplies({
fid: fid,
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
// yield current page
yield* response.data.result.notifications;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
*Get a list of likes and recasts to the users’s casts in reverse chronological order. See [Neynar documentation](https://docs.neynar.com/reference/get-user-likes-and-recasts)
*/
async *fetchUserLikesAndRecasts(fid, options) {
let cursor;
while (true) {
// fetch one page of notifications
const response = await this.apis.notifications.reactionsAndRecasts({
fid: fid,
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
// yield current page
yield* response.data.result.notifications;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Lists a given cast's likes. See [Neynar documentation](https://docs.neynar.com/reference/get-all-like-reactions-for-a-cast)
*/
async *fetchCastLikes(castHash, options) {
let cursor;
while (true) {
const response = await this.apis.reactions.castLikes({
castHash: castHash,
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
yield* response.data.result.likes;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Get All Reactions For a Cast. See [Neynar documentation](https://docs.neynar.com/reference/get-cast-reactions)
*/
async *fetchCastReactions(castHash, options) {
let cursor;
while (true) {
const response = await this.apis.reactions.castReactions({
castHash: castHash,
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
yield* response.data.result.casts;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Get the list of users who have recasted a specific cast. See [Neynar documentation](https://docs.neynar.com/reference/get-list-of-recasters)
*/
async *fetchRecasters(castHash, options) {
let cursor;
while (true) {
const response = await this.apis.reactions.castRecasters({
castHash: castHash,
viewerFid: options?.viewerFid,
cursor: cursor,
limit: options?.pageSize,
});
yield* response.data.result.users;
// prep for next page
if (response.data.result.next.cursor === null) {
break;
}
cursor = response.data.result.next.cursor;
}
}
/**
* Get all users that follow the specified user. See [Neynar documentation](https://docs.neynar.com/reference/get-list-of-followers)
*/
async fetchUserFollowers(fid, viewerFid) {
const response = await this.apis.follows.followers({ fid, viewerFid });
return response.data.result.users;
}
/**
* Get all users the specified user is following. See [Neynar documentation](https://docs.neynar.com/reference/get-list-of-following)
*/
async fetchUserFollowing(fid, viewerFid) {
const response = await this.apis.follows.following({ fid, viewerFid });
return response.data.result.users;
}
}
//# sourceMappingURL=NeynarV1APIClient.js.map