Skip to content

Commit

Permalink
feat: fetch all platform following social activities
Browse files Browse the repository at this point in the history
  • Loading branch information
dmoosocool committed Oct 30, 2023
1 parent 1c7a9c1 commit 4a62322
Show file tree
Hide file tree
Showing 9 changed files with 330 additions and 17 deletions.
2 changes: 1 addition & 1 deletion packages/site/src/pages/monitor/list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ const MonitorList = () => {
const [monitorList, setMonitorList] = useState<SocialMonitor[]>([]);
const handleGetState = async () => {
const snapState = await sendGetState();
console.log(snapState.monitor);
console.log(snapState);
// toast({
// title: 'You submitted the following values:',
// description: (
Expand Down
11 changes: 9 additions & 2 deletions packages/site/src/utils/snap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,19 @@ export type SocialActivity = {
activities: string[];
total: number;
};

export type CronActivity = {
id: string;
text: string;
owner?: string;
};

export type TProfile = {
handle: string;
address?: string;
avatar?: string;
activities?: SocialActivity[];
lastActivities?: SocialActivity[];
activities?: CronActivity[];
lastActivities?: CronActivity[];
};

export enum Platform {
Expand Down
2 changes: 1 addition & 1 deletion packages/snap/snap.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"url": "https://github.com/NaturalSelectionLabs/RSS3-MetaMask-Snap.git"
},
"source": {
"shasum": "4j3pb8CbYuEUdHa7PUICtBRVvCEjyPan3AwxoUujXj8=",
"shasum": "sgMIE/TzXLQiskQP4BnEoZdlDYt99Y9Wi1JAEvzHnAY=",
"location": {
"npm": {
"filePath": "dist/bundle.js",
Expand Down
84 changes: 81 additions & 3 deletions packages/snap/src/crossbel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { isValidWalletAddress } from './utils';
import { getMultiple } from './fetch';
// import { CronActivity } from './state';
import { CronActivity, getState } from './state';
import { Platform, TProfile, TRelationChainResult } from '.';

const API = `https://indexer.crossbell.io/v1`;
Expand Down Expand Up @@ -135,8 +138,14 @@ async function getCharacterId(handle: string) {
* Retrieves the followers for the given character ID from the Crossbell API.
*
* @param id - The character ID to retrieve the followers for.
* @param handle - The Handle.
* @param timestamp - The timestamp.
*/
export async function getFollowingByCharacterId(id: string) {
export async function getFollowingByCharacterId(
id: string,
handle: string,
timestamp?: string,
) {
const following: TProfile[] = [];
let hasNextPage = true;
let cursor: string | undefined;
Expand Down Expand Up @@ -168,7 +177,69 @@ export async function getFollowingByCharacterId(id: string) {
}
}

return following;
const addresses = following
.map((item) => item.address)
.filter((addr) => addr !== undefined)
.slice(0, 50) as string[];

// Each 50 addresses is a set of requests
const addressesGroup: string[][] = [];
for (let i = 0; i < addresses.length; i += 100) {
addressesGroup.push(addresses.slice(i, i + 100));
}

const groupAddresses: {
owner: string;
activities: CronActivity[];
oldActivities: CronActivity[];
}[] = [];

const addressGroupPromise = addressesGroup.map(async (group) => {
const activities = await getMultiple(group, timestamp);
const executeActivitiesPromise = activities.map(async (activity) => {
const state = await getState();
// async;
const { monitor } = state;
const cachedFollowing = monitor.find((item) => item.search === handle);
let oldActivities: CronActivity[] = [];
if (cachedFollowing?.activities) {
oldActivities =
cachedFollowing.activities.find((item) => item.address === handle)
?.activities ?? [];
}
// activity.oldActivities = oldActivities;
return {
...activity,
oldActivities,
};
});
const executeActivities = await Promise.all(executeActivitiesPromise);
groupAddresses.push(...executeActivities);
});

await Promise.all(addressGroupPromise);

const fetchedFollowing = following.map((item) => {
if (item.address !== undefined) {
const findOut = groupAddresses.find((addr) => {
if (addr.owner === undefined || item.address === undefined) {
return false;
}
return addr.owner.toLowerCase() === item.address.toLowerCase();
});

if (findOut) {
return {
...item,
activities: findOut.activities,
lastActivities: findOut.oldActivities,
};
}
}
return item;
});

return fetchedFollowing;
}

/**
Expand Down Expand Up @@ -239,7 +310,14 @@ export async function handler(
}

// 2. Get following
const following = await fetchMethod(data.characterId);

const { monitor } = await getState();

const timestamp =
monitor.find((item) => item.search === handle)?.latestUpdateTime ??
undefined;

const following = await fetchMethod(data.characterId, handle, timestamp);

// 3. Return result
return {
Expand Down
77 changes: 74 additions & 3 deletions packages/snap/src/farcaster.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { getMultiple } from './fetch';
import { CronActivity, getState } from './state';
import { Platform, TProfile, TRelationChainResult } from '.';

const API = 'https://api.warpcast.com/v2';
Expand Down Expand Up @@ -147,9 +149,15 @@ export async function format(data: TFarcasterUser[]): Promise<TProfile[]> {
* Returns the following profiles for a given Farcaster ID.
*
* @param fid - The Farcaster ID to get the following profiles for.
* @param handle - The handle.
* @param timestamp - The timestamp.
* @returns An array of TProfile objects representing the following profiles.
*/
export async function getFollowingByFid(fid: number) {
export async function getFollowingByFid(
fid: number,
handle: string,
timestamp?: string,
) {
let cursor: string | undefined;
let hasNextPage = true;
const following: TProfile[] = [];
Expand All @@ -168,7 +176,64 @@ export async function getFollowingByFid(fid: number) {
}
}

return following;
const addresses = following
.map((item) => item.address)
.filter((addr) => addr !== undefined) as string[];

// Each 100 addresses is a set of requests
const addressesGroup: string[][] = [];
for (let i = 0; i < addresses.length; i += 100) {
addressesGroup.push(addresses.slice(i, i + 100));
}

const groupAddresses: {
owner: string;
activities: CronActivity[];
oldActivities: CronActivity[];
}[] = [];

const addressGroupPromise = addressesGroup.map(async (group) => {
const activities = await getMultiple(group, timestamp);
const executeActivitiesPromise = activities.map(async (activity) => {
const state = await getState();
const { monitor } = state;
const cachedFollowing = monitor.find((item) => item.search === handle);
let oldActivities: CronActivity[] = [];
if (cachedFollowing?.activities) {
oldActivities =
cachedFollowing.activities.find((item) => item.address === handle)
?.activities ?? [];
}
return {
...activity,
oldActivities,
};
});
const executeActivities = await Promise.all(executeActivitiesPromise);
groupAddresses.push(...executeActivities);
});

await Promise.all(addressGroupPromise);
const fetchedFollowing = following.map((item) => {
if (item.address !== undefined) {
const findOut = groupAddresses.find((addr) => {
if (addr.owner === undefined || item.address === undefined) {
return false;
}
return addr.owner.toLowerCase() === item.address.toLowerCase();
});

if (findOut) {
return {
...item,
activities: findOut.activities,
lastActivities: findOut.oldActivities,
};
}
}
return item;
});
return fetchedFollowing;
}

/**
Expand All @@ -191,7 +256,13 @@ export async function handler(handle: string): Promise<TRelationChainResult> {
};
}

const following = await getFollowingByFid(owner.fid);
const { monitor } = await getState();

const timestamp =
monitor.find((item) => item.search === handle)?.latestUpdateTime ??
undefined;

const following = await getFollowingByFid(owner.fid, handle, timestamp);
return {
owner: {
handle: owner.handle,
Expand Down
95 changes: 94 additions & 1 deletion packages/snap/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import moment from 'moment';
import {
Activity,
type Activity,
formatAddressAndNS,
format as sdkFormat,
type Theme,
Expand Down Expand Up @@ -76,3 +76,96 @@ export function format(activity: Activity) {
};
return sdkFormat(activity, theme);
}

/**
* Get social count by rss3.
*
* @param addresses - The wallet address array.
* @param sinceTimestamp - The timestamp.
* @returns The social count array.
*/
export async function getMultiple(
addresses: (string | undefined)[],
sinceTimestamp?: string,
) {
const activities: CronActivity[] = [];
let hasNextPage = true;
let cursor: string | undefined;

const filtedAddresses = addresses.filter(
(addr) => addr !== undefined,
) as string[];

if (filtedAddresses.length === 0) {
return [];
}

const executeAddresses = filtedAddresses;

// 1 day ago
const timestamp =
sinceTimestamp === undefined
? moment().subtract(1, 'day').unix()
: moment(sinceTimestamp).subtract(1, 'day').unix();

while (hasNextPage) {
const params = {
action_limit: 10,
limit: 500,
account: executeAddresses,
tag: ['social'],
type: ['post', 'comment'],
direction: 'out',
since_timestamp: timestamp,
cursor,
};
const resp = await fetch(
'https://testnet.rss3.io/data/accounts/activities',
{
method: 'POST',
headers: {
accept: 'application/json',
'content-type': 'application/json',
},
body: JSON.stringify(params),
},
);
const { data, meta } = (await resp.json()) as {
data: Activity[];
meta: null | { cursor: string };
};

data.map((item) =>
activities.push({
id: item.id,
text: format(item).join(''),
owner: item.owner,
}),
);

if (meta === null) {
hasNextPage = false;
} else {
cursor = meta.cursor;
}
}

return executeAddresses.map((addr) => {
const groupBy = activities.filter(
(activity) =>
activity.owner?.toLocaleLowerCase() === addr?.toLocaleLowerCase(),
);
if (groupBy) {
return {
owner: addr,
activities: groupBy,
oldActivities: [],
};
}
return {
owner: addr,
activities: [],
oldActivities: [],
};
});
}
8 changes: 3 additions & 5 deletions packages/snap/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { assert } from '@metamask/utils';
import { Profile } from '@rss3/js-sdk';
import moment from 'moment';
import {
CronActivity,
SocialActivity,
State,
addAddressToState,
Expand Down Expand Up @@ -42,8 +43,8 @@ export type TProfile = {
handle: string;
address?: string;
avatar?: string;
activities?: SocialActivity[];
lastActivities?: SocialActivity[];
activities?: CronActivity[];
lastActivities?: CronActivity[];
};

export type FetchSocialCountParams = {
Expand Down Expand Up @@ -430,9 +431,6 @@ export const onCronjob: OnCronjobHandler = async ({ request }) => {
const handles = item.profiles
.filter((profile) => profile.handle !== undefined)
.map((profile) => {
// const execute = executeArray.find(
// (list) => list.platform === profile.platform,
// );
if (profile.platform === Platform.Crossbell) {
return {
handle: profile.handle,
Expand Down
Loading

0 comments on commit 4a62322

Please sign in to comment.