@@ -66,7 +66,19 @@ type IcebreakerProfile = {
6666 guilds ?: IcebreakerGuildMembership [ ] ;
6767} ;
6868
69+ export type UserMetadata = {
70+ name : string ;
71+ value : string | number ;
72+ } ;
73+
74+ export type UserMetadataApiResponse =
75+ | {
76+ metadata ?: UserMetadata [ ] ;
77+ }
78+ | undefined ;
79+
6980const API_URL = "https://app.icebreaker.xyz/api/v1" ;
81+ const TWITTER_API_URL = "https://hook.us1.make.com/h4yqha7yee9juaosenjrmebd956va0ew?twitter=" ;
7082
7183async function request < T > ( path : string , options ?: RequestInit ) {
7284 try {
@@ -79,6 +91,34 @@ async function request<T>(path: string, options?: RequestInit) {
7991 }
8092}
8193
94+ async function getTwitterMetadata ( username ?: string ) {
95+ if ( ! username ) {
96+ return ;
97+ }
98+
99+ const response = await request < UserMetadataApiResponse > ( `${ TWITTER_API_URL } ${ username } ` ) ;
100+
101+ return response ?. metadata ;
102+ }
103+
104+ function extractTwitterFollowerCount ( metadata ?: UserMetadata [ ] ) {
105+ if ( ! metadata ) {
106+ return ;
107+ }
108+
109+ const followerCount = metadata ?. find ( ( { name } ) => name === "followers_count" ) ?. value as number | undefined ;
110+ return followerCount ;
111+ }
112+
113+ function extractTwitterCreatedAt ( metadata ?: UserMetadata [ ] ) {
114+ if ( ! metadata ) {
115+ return ;
116+ }
117+
118+ const createdAt = metadata ?. find ( ( { name } ) => name === "created_at" ) ?. value ;
119+ return createdAt ? new Date ( createdAt ) : undefined ;
120+ }
121+
82122type ProfileResponse = {
83123 profiles : IcebreakerProfile [ ] ;
84124} ;
@@ -212,6 +252,46 @@ async function hasIcebreakerLinkedAccount({ user: member, rule }: CheckFunctionA
212252 } ;
213253}
214254
255+ async function hasGreaterThanVerifiedTwitterFollowers ( { user : member , rule } : CheckFunctionArgs ) {
256+ const { threshold } = rule . args as { threshold : string } ;
257+
258+ if ( ! threshold || isNaN ( + threshold ) ) {
259+ return {
260+ result : false ,
261+ message : "Invalid threshold value" ,
262+ } ;
263+ }
264+ const thresholdNumber = + threshold ;
265+
266+ const user = await getIcebreakerbyFid ( member . fid ) ;
267+
268+ if ( ! user ) {
269+ return {
270+ result : false ,
271+ message : `@${ member . username } not found in Icebreaker` ,
272+ } ;
273+ }
274+ // get twitter followers for user verified twitter account
275+ const verifiedTwitterAccount = user . channels ?. find (
276+ ( channel ) => channel . type === "twitter" && channel . isVerified
277+ ) ?. value ;
278+
279+ const metadata = verifiedTwitterAccount ? await getTwitterMetadata ( verifiedTwitterAccount ) : undefined ;
280+
281+ const followerCount = metadata && extractTwitterFollowerCount ( metadata ) ;
282+
283+ const userHasGreaterThanThreshold = ! ! followerCount && followerCount > thresholdNumber ;
284+
285+ return {
286+ result : userHasGreaterThanThreshold ,
287+ message : followerCount
288+ ? `@${ member . username } has ${ followerCount } followers on Twitter`
289+ : metadata
290+ ? `Unable to retrieve follower count for @${ member . username } `
291+ : `Unable to find a verified Twitter account for @${ member . username } ` ,
292+ } ;
293+ }
294+
215295async function hasPOAP ( { user : member , rule } : CheckFunctionArgs ) {
216296 const { eventId } = rule . args as { eventId : string } ;
217297
@@ -265,7 +345,8 @@ type RuleName =
265345 | "hasIcebreakerVerified"
266346 | "hasIcebreakerLinkedAccount"
267347 | "hasPOAP"
268- | "hasGuildRole" ;
348+ | "hasGuildRole"
349+ | "hasGreaterThanVerifiedTwitterFollowers" ;
269350
270351const author = "Icebreaker" ;
271352const authorUrl = "https://icebreaker.xyz" ;
@@ -377,6 +458,29 @@ export const iceBreakerRulesDefinitions: Record<RuleName, RuleDefinition> = {
377458 } ,
378459 } ,
379460
461+ hasGreaterThanVerifiedTwitterFollowers : {
462+ name : "hasGreaterThanVerifiedTwitterFollowers" ,
463+ allowMultiple : true ,
464+ author,
465+ authorUrl,
466+ authorIcon,
467+ category : "all" ,
468+ friendlyName : "Icebreaker: Has Greater Than X Verified Twitter Followers" ,
469+ checkType : "user" ,
470+ description : "Check if the user has more than a certain number of followers on their verified Twitter account" ,
471+ hidden : false ,
472+ invertable : true ,
473+ args : {
474+ threshold : {
475+ type : "string" ,
476+ friendlyName : "Follower Threshold" ,
477+ description : "The minimum number of followers required" ,
478+ placeholder : "Enter a number..." ,
479+ required : true ,
480+ } ,
481+ } ,
482+ } ,
483+
380484 hasPOAP : {
381485 name : "hasPOAP" ,
382486 allowMultiple : true ,
@@ -441,4 +545,5 @@ export const iceBreakerRulesFunction: Record<RuleName, CheckFunction> = {
441545 hasIcebreakerLinkedAccount : hasIcebreakerLinkedAccount ,
442546 hasPOAP : hasPOAP ,
443547 hasGuildRole : hasGuildRole ,
548+ hasGreaterThanVerifiedTwitterFollowers : hasGreaterThanVerifiedTwitterFollowers ,
444549} as const ;
0 commit comments