feat: analytics

This commit is contained in:
Nevo David 2025-05-30 00:43:48 +07:00
parent 808589ae36
commit 8314cc58a2
2 changed files with 134 additions and 3 deletions

View File

@ -22,6 +22,7 @@ const allowedIntegrations = [
'youtube',
'pinterest',
'threads',
'x',
];
export const PlatformAnalytics = () => {
@ -66,6 +67,7 @@ export const PlatformAnalytics = () => {
'pinterest',
'youtube',
'threads',
'x',
].indexOf(currentIntegration.identifier) !== -1
) {
arr.push({
@ -83,6 +85,7 @@ export const PlatformAnalytics = () => {
'pinterest',
'youtube',
'threads',
'x',
].indexOf(currentIntegration.identifier) !== -1
) {
arr.push({
@ -92,7 +95,7 @@ export const PlatformAnalytics = () => {
}
if (
['facebook', 'linkedin-page', 'pinterest', 'youtube'].indexOf(
['facebook', 'linkedin-page', 'pinterest', 'youtube', 'x'].indexOf(
currentIntegration.identifier
) !== -1
) {

View File

@ -1,5 +1,6 @@
import { TwitterApi } from 'twitter-api-v2';
import { TweetV2, TwitterApi } from 'twitter-api-v2';
import {
AnalyticsData,
AuthTokenDetails,
PostDetails,
PostResponse,
@ -14,6 +15,9 @@ import { Plug } from '@gitroom/helpers/decorators/plug.decorator';
import { Integration } from '@prisma/client';
import { timer } from '@gitroom/helpers/utils/timer';
import { PostPlug } from '@gitroom/helpers/decorators/post.plug';
import { number, string } from 'yup';
import dayjs from 'dayjs';
import { uniqBy } from 'lodash';
export class XProvider extends SocialAbstract implements SocialProvider {
identifier = 'x';
@ -314,7 +318,7 @@ export class XProvider extends SocialAbstract implements SocialProvider {
}));
}
communities(accessToken: string, data: {search: string}) {
communities(accessToken: string, data: { search: string }) {
const [accessTokenSplit, accessSecretSplit] = accessToken.split(':');
const client = new TwitterApi({
appKey: process.env.X_API_KEY!,
@ -333,4 +337,128 @@ export class XProvider extends SocialAbstract implements SocialProvider {
// }
// })
}
private loadAllTweets = async (
client: TwitterApi,
id: string,
until: string,
since: string,
token = ''
): Promise<TweetV2[]> => {
const tweets = await client.v2.userTimeline(id, {
'tweet.fields': ['id'],
'user.fields': [],
'poll.fields': [],
'place.fields': [],
'media.fields': [],
exclude: ['replies', 'retweets'],
start_time: since,
end_time: until,
max_results: 100,
...(token ? { pagination_token: token } : {}),
});
return [
...tweets.data.data,
...(tweets.data.data.length === 100
? await this.loadAllTweets(
client,
id,
until,
since,
tweets.meta.next_token
)
: []),
];
};
async analytics(
id: string,
accessToken: string,
date: number
): Promise<AnalyticsData[]> {
const until = dayjs().endOf('day');
const since = dayjs().subtract(date, 'day');
const [accessTokenSplit, accessSecretSplit] = accessToken.split(':');
const client = new TwitterApi({
appKey: process.env.X_API_KEY!,
appSecret: process.env.X_API_SECRET!,
accessToken: accessTokenSplit,
accessSecret: accessSecretSplit,
});
try {
const tweets = uniqBy(
await this.loadAllTweets(
client,
id,
until.format('YYYY-MM-DDTHH:mm:ssZ'),
since.format('YYYY-MM-DDTHH:mm:ssZ')
),
(p) => p.id
);
if (tweets.length === 0) {
return [];
}
console.log(tweets.map((p) => p.id));
const data = await client.v2.tweets(
tweets.map((p) => p.id),
{
'tweet.fields': ['public_metrics'],
},
);
const metrics = data.data.reduce(
(all, current) => {
all.impression_count =
(all.impression_count || 0) +
+current.public_metrics.impression_count;
all.bookmark_count =
(all.bookmark_count || 0) + +current.public_metrics.bookmark_count;
all.like_count =
(all.like_count || 0) + +current.public_metrics.like_count;
all.quote_count =
(all.quote_count || 0) + +current.public_metrics.quote_count;
all.reply_count =
(all.reply_count || 0) + +current.public_metrics.reply_count;
all.retweet_count =
(all.retweet_count || 0) + +current.public_metrics.retweet_count;
return all;
},
{
impression_count: 0,
bookmark_count: 0,
like_count: 0,
quote_count: 0,
reply_count: 0,
retweet_count: 0,
}
);
console.log(metrics);
console.log(JSON.stringify(data, null, 2));
return Object.entries(metrics).map(([key, value]) => ({
label: key.replace('_count', '').replace('_', ' ').toUpperCase(),
percentageChange: 5,
data: [
{
total: String(0),
date: since.format('YYYY-MM-DD'),
},
{
total: String(value),
date: until.format('YYYY-MM-DD'),
},
],
}));
} catch (err) {
console.log(err);
}
return [];
}
}