Skip to content

Commit

Permalink
Send message to welcome users (#4765)
Browse files Browse the repository at this point in the history
* Send message to welcome users

* Fix CI

* Add dependency on internal package

* Add the script for the waitlist tier invites

* Cleanup

* Fix types

* Cleanup messages and types

* Update scripts
  • Loading branch information
motechFR authored Oct 23, 2024
1 parent 8def357 commit a2928f8
Show file tree
Hide file tree
Showing 23 changed files with 264 additions and 39 deletions.
4 changes: 4 additions & 0 deletions .ebstalk.apps.env/scoutgame.env
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ REACT_APP_GITHUB_CLIENT_ID="{{pull:secretsmanager:/io.cv.app/prd/github:SecretSt
GITHUB_CLIENT_SECRET="{{pull:secretsmanager:/io.cv.app/prd/github:SecretString:waitlist_oauth_client_secret}}"
GITHUB_ACCESS_TOKEN="{{pull:secretsmanager:/io.cv.app/prd/github:SecretString:scoutgame_github_access_token}}"
BUILDER_SMART_CONTRACT_OWNER_PRIVKEY="{{pull:secretsmanager:/io.cv.app/prd/buildernft:SecretString:builder_smart_contract_owner_privkey}}"

NEYNAR_SIGNER_ID="{{pull:secretsmanager:/io.cv.app/prd/neynar:SecretString:neynar_signer_id}}"
NEYNAR_SIGNER_PUBLIC_KEY="{{pull:secretsmanager:/io.cv.app/prd/neynar:SecretString:neynar_signer_public_key}}"

SCOUTGAME_S3_BUCKET="scoutgame.public"
NFT_ARTWORK_S3_PATH="prd"
REACT_APP_SCOUTGAME_INVITE_CODE="1337"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import env from '@beam-australia/react-env';
import { log } from '@charmverse/core/log';
import { ActionType } from '@decent.xyz/box-common';
import type { BoxActionRequest, BoxActionResponse } from '@decent.xyz/box-common';
import { ActionType } from '@decent.xyz/box-common';
import {
builderNftChain,
getBuilderContractAddress,
usdcOptimismMainnetContractAddress,
getDecentApiKey,
builderNftChain,
optimismUsdcContractAddress
} from '@packages/scoutgame/builderNfts/constants';
import { GET } from '@packages/utils/http';
Expand Down Expand Up @@ -72,7 +70,6 @@ export function useDecentTransaction({
dstChainId: builderNftChain.id,
slippage: 1,
actionType: ActionType.NftMint,
// @ts-ignore
actionConfig: {
chainId: optimism.id,
contractAddress: getBuilderContractAddress(),
Expand Down
66 changes: 66 additions & 0 deletions apps/scoutgamecron/src/scripts/welcomeWaitlistTier.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { log } from "@charmverse/core/log";
import { Prisma, prisma } from "@charmverse/core/prisma-client";
import { ConnectWaitlistTier, getWaitlistRange } from "@packages/scoutgame/waitlist/scoring/constants";
import { welcomeFromWaitlistToScoutgame } from '@packages/scoutgame/waitlist/welcomeToScoutgame';


async function welcomeWaitlistTier(tier: ConnectWaitlistTier) {

const tierInfo = getWaitlistRange(tier);

const whereQuery: Prisma.ConnectWaitlistSlotWhereInput = tier === 'legendary' ? {
percentile: {
gte: tierInfo.min,
}
} : tier === 'common' ? {
percentile: {
lte: tierInfo.max
}

}
// Other tiers have a minMax range
: {
percentile: {
gte: tierInfo.min,
lte: tierInfo.max
}
};

const existingScouts = await prisma.scout.findMany({
where: {
farcasterId: {
gte: 1
}
},
select: {
farcasterId: true
}
})

// Only message users who are not already scouts
const users = await prisma.connectWaitlistSlot.findMany({
where: {...whereQuery, fid: {notIn: existingScouts.map(scout => scout.farcasterId).filter(Boolean) as number[]}, isPartnerAccount: {not: true}},
orderBy: {
fid: 'asc'
},
skip: 2
});

const totalUsersInTier = users.length;

log.info(`Processing ${totalUsersInTier} users in tier ${tier}`);


const limit = totalUsersInTier;
// const limit = 1;

for (let i = 0; i < limit; i++) {
const user = users[i];
console.log(`Processing FID:${user.fid} ${user.username} user ${i + 1} of ${totalUsersInTier}`);
await welcomeFromWaitlistToScoutgame({fid: user.fid});
}

}


// welcomeWaitlistTier('rare').then(console.log).catch(console.error);
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { prisma } from '@charmverse/core/prisma-client';
import { jest } from '@jest/globals';
import type { Season } from '@packages/scoutgame/dates';
import { mockRepo, mockBuilder } from '@packages/scoutgame/testing/database';
import { mockSeason } from '@packages/scoutgame/testing/generators';
import { DateTime } from 'luxon';
Expand Down Expand Up @@ -66,7 +67,7 @@ describe('processBuilderActivity', () => {
builderId: builder.id,
githubUser: builder.githubUser,
createdAfter: new Date(),
season: mockSeason
season: mockSeason as Season
});

const builderEvents = await prisma.builderEvent.count({
Expand Down Expand Up @@ -114,7 +115,7 @@ describe('processBuilderActivity', () => {
builderId: builder.id,
githubUser: builder.githubUser,
createdAfter: new Date(),
season: mockSeason
season: mockSeason as Season
});

const builderEvents = await prisma.builderEvent.count({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ export async function getPullRequestsByUser({ login, after }: { login: string; a

while (hasNextPage) {
try {
// @ts-ignore
// @ts-expect-error tbd what error is
const { search } = await octokit.graphql.paginate<GraphQLSearchResponse>(prSearchQuery, {
queryString,
first: 100,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { randomLargeInt, randomIntFromInterval } from '@packages/scoutgame/testi
import type { ConnectWaitlistTier, TierChange } from '@packages/scoutgame/waitlist/scoring/constants';
import { refreshUserScore } from '@packages/scoutgame/waitlist/scoring/refreshUserScore';

import { refreshPercentilesForEveryone } from '../refreshPercentilesForEveryone'; // Adjust the import to the correct module
import { refreshPercentilesForEveryone } from '../refreshPercentilesForEveryone';

// Function to shuffle an array deterministically using a seeded random number generator
function seededShuffle(array: number[], seed: number): number[] {
Expand Down Expand Up @@ -92,7 +92,7 @@ describe('refreshPercentilesForEveryone', () => {

// Everyone starts in the 'common' tier
// Now, 70% of 150 records should be out of the common tier
expect(tierChangeResults.length).toBe(104);
expect(tierChangeResults.length).toBe(106);

const firstChangedUser = tierChangeResults[0];
expect(fids.indexOf(firstChangedUser.fid)).toBe(131);
Expand Down Expand Up @@ -153,7 +153,7 @@ describe('refreshPercentilesForEveryone', () => {

expect(thirdUserWaitlistSlot.percentile).toBe(thirdChangedUser.percentile);

expect(fourthChangedUser.percentile).toBe(50);
expect(fourthChangedUser.percentile).toBe(53);
expect(fourthChangedUser.score).toBe(210);
expect(fourthChangedUser.newTier).toBe<ConnectWaitlistTier>('rare');
expect(fourthChangedUser.tierChange).toBe<TierChange>('up');
Expand Down Expand Up @@ -249,7 +249,7 @@ describe('refreshPercentilesForEveryone', () => {

// Everyone starts in the 'common' tier
// Now, 70% of 150 records should be out of the common tier
expect(tierChangeResults.length).toBe(104);
expect(tierChangeResults.length).toBe(106);

const firstChangedUser = tierChangeResults[0];
expect(fids.indexOf(firstChangedUser.fid)).toBe(131);
Expand Down Expand Up @@ -310,7 +310,7 @@ describe('refreshPercentilesForEveryone', () => {

expect(thirdUserWaitlistSlot.percentile).toBe(thirdChangedUser.percentile);

expect(fourthChangedUser.percentile).toBe(50);
expect(fourthChangedUser.percentile).toBe(53);
expect(fourthChangedUser.score).toBe(210);
expect(fourthChangedUser.newTier).toBe<ConnectWaitlistTier>('rare');
expect(fourthChangedUser.tierChange).toBe<TierChange>('up');
Expand Down Expand Up @@ -506,8 +506,8 @@ describe('refreshPercentilesForEveryone', () => {
expect(tierChangeResults.some((tierChange) => fidsWithReferral.includes(tierChange.fid)));

// Everyone starts in the 'common' tier
// Now, 70% (-1) of 100 records should be out of the common tier
expect(tierChangeResults.length).toBe(69);
// Now, 70% of 100 records should be out of the common tier
expect(tierChangeResults.length).toBe(70);

const firstChangedUser = tierChangeResults[0];
expect(expectedFids.indexOf(firstChangedUser.fid)).toBe(50);
Expand Down Expand Up @@ -568,7 +568,7 @@ describe('refreshPercentilesForEveryone', () => {

expect(thirdUserWaitlistSlot.percentile).toBe(thirdChangedUser.percentile);

expect(fourthChangedUser.percentile).toBe(50);
expect(fourthChangedUser.percentile).toBe(59);
expect(fourthChangedUser.score).toBe(-909);
expect(fourthChangedUser.newTier).toBe<ConnectWaitlistTier>('rare');
expect(fourthChangedUser.tierChange).toBe<TierChange>('up');
Expand Down
6 changes: 3 additions & 3 deletions apps/waitlist/lib/scoring/notifyNewScore.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NEYNAR_SIGNER_ID } from '@packages/farcaster/constants';
import { writeToFarcaster } from '@packages/farcaster/messaging/writeToFarcaster';
import type { ConnectWaitlistTier } from '@packages/scoutgame/waitlist/scoring/constants';
import { baseUrl, isDevEnv, isProdEnv, isStagingEnv, isTestEnv } from '@root/config/constants';
import { NEYNAR_SIGNER_ID } from '@root/lib/farcaster/constants';
import { writeToFarcaster } from '@root/lib/farcaster/messaging/writeToFarcaster';
import { baseUrl, isDevEnv, isProdEnv, isStagingEnv, isTestEnv } from '@packages/utils/env';

import type { TierChangeResult } from './refreshPercentilesForEveryone';

Expand Down
4 changes: 0 additions & 4 deletions apps/waitlist/lib/scoring/refreshPercentilesForEveryone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,6 @@ import { roundNumberInRange } from '@packages/utils/numbers';

import { notifyNewScore } from './notifyNewScore';

// const prisma = new PrismaClient({
// log: ['query']
// });

export type TierChangeResult = Pick<ConnectWaitlistSlot, 'fid' | 'username' | 'percentile' | 'score'> & {
newTier: ConnectWaitlistTier;
tierChange: TierChange;
Expand Down
3 changes: 3 additions & 0 deletions apps/waitlist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
"typecheck": "../../node_modules/typescript/bin/tsc --project ./tsconfig.json --noEmit"
},
"dependencies": {
"@packages/farcaster": "^0.0.0",
"@packages/github": "file:../../packages/github",
"@packages/scoutgame": "^0.0.0",
"@packages/utils": "^1.0.0",
"next-safe-action": "~7.4.2"
}
}
8 changes: 8 additions & 0 deletions package-lock.json

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

6 changes: 6 additions & 0 deletions packages/farcaster/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const NEYNAR_API_BASE_URL = 'https://api.neynar.com/v2/farcaster';

export const NEYNAR_API_KEY = process.env.NEYNAR_API_KEY as string;

export const NEYNAR_SIGNER_ID = process.env.NEYNAR_SIGNER_ID as string;
export const NEYNAR_SIGNER_PUBLIC_KEY = process.env.NEYNAR_SIGNER_PUBLIC_KEY as string;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { User as FarcasterUserProfileFromNeynar } from '@neynar/nodejs-sdk/build/neynar-api/v2/openapi-farcaster';
import { GET } from '@root/adapters/http';
import { GET } from '@packages/utils/http';

import { NEYNAR_API_BASE_URL, NEYNAR_API_KEY } from './constants';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
import { InvalidInputError } from '@charmverse/core/errors';
import { log } from '@charmverse/core/log';
import type { Signer } from '@neynar/nodejs-sdk/build/neynar-api/v2/openapi-farcaster';
import { GET, POST } from '@root/adapters/http';
import { getPublicClient } from '@root/lib/blockchain/publicClient';
import { getWalletClient } from '@root/lib/blockchain/walletClient';
import { sleep } from '@root/lib/utils/sleep';
import { getPublicClient } from '@packages/onchain/getPublicClient';
import { getWalletClient } from '@packages/onchain/getWalletClient';
import { GET, POST } from '@packages/utils/http';
import { sleep } from '@packages/utils/sleep';
import type { Address } from 'viem';
import { encodeAbiParameters } from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { optimism } from 'viem/chains';
Expand Down Expand Up @@ -220,7 +221,7 @@ export async function getApprovedSignerId(): Promise<{ signerId: string; signerP
abi: keyGatewayAbi, // ABI of the contract
functionName: 'addFor', // Function to call
args: [
farcasterDeveloper.custody_address as `0x${string}`, // fidOwner address
farcasterDeveloper.custody_address as Address, // fidOwner address
1, // keyType (uint32)
signerPublicKey as `0x${string}`, // key (bytes)
1, // metadataType (uint8)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
// docs.neynar.com/reference/publish-message

import { ExternalServiceError } from '@charmverse/core/errors';
import { log } from '@charmverse/core/log';
import type { Cast } from '@neynar/nodejs-sdk/build/neynar-api/v2/openapi-farcaster';
import { POST } from '@root/adapters/http';
import { NEYNAR_API_BASE_URL, NEYNAR_API_KEY } from '@root/lib/farcaster/constants';
import { POST } from '@packages/utils/http';

// TBD - https://github.com/neynarxyz/farcaster-examples/tree/main/managed-signers
import { NEYNAR_API_BASE_URL, NEYNAR_API_KEY } from '../constants';

// https://docs.neynar.com/reference/post-cast
// https://github.com/neynarxyz/farcaster-examples/tree/main/gm-bot
// Copied from @neynar/nodejs-sdk/build/neynar-api/v2/openapi-farcaster - Fails in CI so ported the type here
type Cast = {
hash: string;
parent_hash: string | null;
parent_url: string | null;
root_parent_url: string | null;
parent_author: any;
author: any;
text: string;
timestamp: string;
embeds: any[];
type?: any;
};

export async function writeToFarcaster({
neynarSignerId,
Expand Down
1 change: 1 addition & 0 deletions packages/scoutgame/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"./builderNfts/artwork/generateNftImage": "./src/builderNfts/artwork/generateNftImage.tsx"
},
"dependencies": {
"@packages/farcaster": "^0.0.0",
"@packages/onchain": "^0.0.0",
"@packages/utils": "^1.0.0",
"@packages/github": "^0.0.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getWaitlistRange, waitlistTiers } from '../constants';

describe('getWaitlistRange', () => {
it('should return the correct range for each tier', () => {
const expectedRanges = {
common: { min: 1, max: 30 },
rare: { min: 31, max: 60 },
epic: { min: 61, max: 80 },
mythic: { min: 81, max: 95 },
legendary: { min: 96, max: 100 }
};

waitlistTiers.forEach((tier) => {
const { min, max } = getWaitlistRange(tier);
expect({ min, max }).toEqual(expectedRanges[tier]);
});
});

it('should throw an error for an invalid tier', () => {
expect(() => getWaitlistRange('invalid-tier' as any)).toThrow('Invalid tier: invalid-tier');
});
});
Loading

0 comments on commit a2928f8

Please sign in to comment.