Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create builders on sg #4683

Merged
merged 5 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/scoutgamecron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@packages/github": "^0.0.0",
"@packages/onchain": "^0.0.0",
"@packages/scoutgame": "^0.0.0",
"@packages/utils": "^1.0.0",
"web-push": "^3.6.7"
},
"devDependencies": {
Expand Down
107 changes: 107 additions & 0 deletions apps/scoutgamecron/src/scripts/createBuilders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { log } from '@charmverse/core/log';
import { prisma } from '@charmverse/core/prisma-client'
import { octokit } from '@packages/github/client';

import * as http from '@packages/utils/http';

export type FarcasterProfile = {
username: string;
fid: number;
display_name: string;
pfp_url: string;
profile: {
bio: {
text: string;
}
}
};

const profileApiUrl = 'https://api.neynar.com/v2/farcaster/user/bulk';

export async function getFarcasterProfileById(fid: number) {
const {users} = await http
.GET<{users: FarcasterProfile[]}>(
`${profileApiUrl}?fids=${fid}`,
{},
{
credentials: 'omit',
headers: {
'X-Api-Key': process.env.NEYNAR_API_KEY as string
}
}
);
return users[0] || null;
}


const builderWaitlistLogins: string[] = []

async function createBuilders() {
for (const login of builderWaitlistLogins) {
const waitlistSlot = await prisma.connectWaitlistSlot.findFirst({
where: {
OR: [
{
githubLogin: login,
},
{
githubLogin: login.toLowerCase(),
}
]
}
})
const fid = waitlistSlot?.fid;

if (!waitlistSlot || !fid) {
log.warn(`No waitlist slot or fid found for ${login}`)
continue
}

if (waitlistSlot && fid) {
try {
const githubUser = await octokit.rest.users.getByUsername({ username: login })
const profile = await getFarcasterProfileById(fid)
if (!profile) {
log.info(`No profile found for ${login}`)
continue
}
const displayName = profile.display_name;
const username = profile.username;
const avatarUrl = profile.pfp_url;
const bio = profile.profile.bio.text;
if (!username) {
log.info(`No username found for ${login} with fid ${fid}`)
continue
}
const builder = await prisma.scout.upsert({
where: {
username,
},
update: {},
create: {
displayName,
username,
avatar: avatarUrl,
bio,
builderStatus: "applied",
farcasterId: fid,
farcasterName: displayName,
githubUser: {
create: {
id: githubUser.data.id,
login,
displayName: githubUser.data.name,
email: githubUser.data.email,
}
}
}
})
log.info(`Created builder for ${login}`, { builderId: builder.id })
} catch (error) {
log.error(`Error creating builder for ${login}`, { error })
}
}
}
}

createBuilders()
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions packages/utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"./numbers": "./src/numbers.ts",
"./react": "./src/react.ts",
"./types": "./src/types.ts",
"./dates": "./src/dates.ts"
"./dates": "./src/dates.ts",
"./http": "./src/http.ts"
}
}
}
33 changes: 33 additions & 0 deletions packages/utils/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
type RequestInit = Parameters<typeof fetch>[1];

export async function transformResponse(response: Response) {
const contentType = response.headers.get('content-type');

if (response.status >= 400) {
// necessary to capture the regular response for embedded blocks
if (contentType?.includes('application/json')) {
try {
const jsonResponse = await response.json();
return Promise.reject({ status: response.status, ...jsonResponse });
} catch (error) {
// not valid JSON, content-type is lying!
}
}
// Note: 401 if user is logged out
return response.text().then((text) => Promise.reject({ status: response.status, message: text }));
}

if (contentType?.includes('application/json')) {
return response.json();
} else if (contentType?.includes('application/octet-stream')) {
return response.blob();
}
return response.text().then((_response) => {
// since we expect JSON, dont return the true value for 200 response
return _response === 'OK' ? null : _response;
});
}

export default function fetchWrapper<T>(resource: string, init?: RequestInit): Promise<T> {
return fetch(resource, init).then(transformResponse) as Promise<T>;
}
95 changes: 95 additions & 0 deletions packages/utils/src/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import fetch from './fetch';

type Params = { [key: string]: any };

export function GET<T = Response>(
_requestUrl: string,
data: Params = {},
{ headers = {}, credentials = 'include' }: { credentials?: RequestCredentials; headers?: any } = {}
): Promise<T> {
const requestUrl = _appendQuery(_requestUrl, data);
return fetch<T>(requestUrl, {
method: 'GET',
headers: new Headers({
Accept: 'application/json',
...headers
}),
credentials
});
}

export function DELETE<T>(
_requestUrl: string,
data: Params = {},
{ headers = {} }: { headers?: any } = {}
): Promise<T> {
const requestUrl = _appendQuery(_requestUrl, data);
return fetch<T>(requestUrl, {
method: 'DELETE',
headers: new Headers({
Accept: 'application/json',
'Content-Type': 'application/json',
...headers
}),
credentials: 'include'
});
}

export function POST<T>(
requestURL: string,
data: Params | string = {},
{
headers = {},
noHeaders,
skipStringifying,
credentials = 'include',
query
}: {
credentials?: RequestCredentials;
headers?: any;
noHeaders?: boolean;
skipStringifying?: boolean;
query?: any;
} = {}
): Promise<T> {
const urlWithQuery = query ? _appendQuery(requestURL, query || {}) : requestURL;

return fetch<T>(urlWithQuery, {
body: !skipStringifying ? JSON.stringify(data) : (data as string),
method: 'POST',
headers: noHeaders
? undefined
: new Headers({
Accept: 'application/json',
'Content-Type': 'application/json',
...headers
}),
credentials
});
}

export function PUT<T>(requestURL: string, data: Params = {}, { headers = {} }: { headers?: any } = {}): Promise<T> {
return fetch<T>(requestURL, {
body: JSON.stringify(data),
method: 'PUT',
headers: new Headers({
Accept: 'application/json',
'Content-Type': 'application/json',
...headers
}),
credentials: 'include'
});
}

function _appendQuery(path: string, data: Params) {
const queryString = Object.keys(data)
.filter((key) => !!data[key])
.map((key) => {
const value = data[key];
return Array.isArray(value)
? `${value.map((v: string) => `${key}=${v}`).join('&')}`
: `${key}=${encodeURIComponent(value)}`;
})
.join('&');
return `${path}${queryString ? `?${queryString}` : ''}`;
}
Loading