Merge pull request #230 from FixTweet/analytics-engine

Merge Cloudflare Analytics Engine
This commit is contained in:
dangered wolf 2023-04-13 12:32:16 -04:00 committed by GitHub
commit d3e255d911
Signed by: DevComp
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 8 deletions

View file

@ -143,17 +143,45 @@ const populateTweetProperties = async (
return apiTweet; return apiTweet;
}; };
const writeDataPoint = (event: FetchEvent, language: string | undefined, nsfw: boolean, returnCode: string, flags?: InputFlags) => {
console.log('Writing data point...');
if (typeof AnalyticsEngine !== 'undefined') {
const flagString = Object.keys(flags || {})
// @ts-expect-error - TypeScript doesn't like iterating over the keys, but that's OK
.filter(flag => flags?.[flag])[0] || 'standard';
console.log(flagString)
AnalyticsEngine.writeDataPoint({
blobs: [
event.request.cf?.colo as string, /* Datacenter location */
event.request.cf?.country as string, /* Country code */
event.request.headers.get('user-agent') ?? '', /* User agent (for aggregating bots calling) */
returnCode, /* Return code */
flagString, /* Type of request */
language ?? '', /* For translate feature */
],
doubles: [
nsfw ? 1 : 0 /* NSFW media = 1, No NSFW Media = 0 */
]
});
}
}
/* API for Twitter statuses (Tweets) /* API for Twitter statuses (Tweets)
Used internally by FixTweet's embed service, or Used internally by FixTweet's embed service, or
available for free using api.fxtwitter.com. */ available for free using api.fxtwitter.com. */
export const statusAPI = async ( export const statusAPI = async (
status: string, status: string,
language: string | undefined, language: string | undefined,
event: FetchEvent event: FetchEvent,
flags?: InputFlags
): Promise<APIResponse> => { ): Promise<APIResponse> => {
let conversation = await fetchConversation(status, event); let conversation = await fetchConversation(status, event);
let tweet = conversation?.globalObjects?.tweets?.[status] || {}; let tweet = conversation?.globalObjects?.tweets?.[status] || {};
let wasMediaBlockedNSFW = false;
if (tweet.retweeted_status_id_str) { if (tweet.retweeted_status_id_str) {
tweet = conversation?.globalObjects?.tweets?.[tweet.retweeted_status_id_str] || {}; tweet = conversation?.globalObjects?.tweets?.[tweet.retweeted_status_id_str] || {};
} }
@ -170,6 +198,7 @@ export const statusAPI = async (
if (typeof tweet.full_text !== 'undefined') { if (typeof tweet.full_text !== 'undefined') {
console.log('Successfully loaded Tweet using elongator'); console.log('Successfully loaded Tweet using elongator');
wasMediaBlockedNSFW = true;
} else if ( } else if (
typeof tweet.full_text === 'undefined' && typeof tweet.full_text === 'undefined' &&
conversation.timeline?.instructions?.length > 0 conversation.timeline?.instructions?.length > 0
@ -177,20 +206,24 @@ export const statusAPI = async (
console.log( console.log(
'Tweet could not be accessed with elongator, must be private/suspended' 'Tweet could not be accessed with elongator, must be private/suspended'
); );
writeDataPoint(event, language, wasMediaBlockedNSFW, 'PRIVATE_TWEET', flags);
return { code: 401, message: 'PRIVATE_TWEET' }; return { code: 401, message: 'PRIVATE_TWEET' };
} }
} else { } else {
/* {"errors":[{"code":34,"message":"Sorry, that page does not exist."}]} */ /* {"errors":[{"code":34,"message":"Sorry, that page does not exist."}]} */
if (conversation.errors?.[0]?.code === 34) { if (conversation.errors?.[0]?.code === 34) {
writeDataPoint(event, language, wasMediaBlockedNSFW, 'NOT_FOUND', flags);
return { code: 404, message: 'NOT_FOUND' }; return { code: 404, message: 'NOT_FOUND' };
} }
/* Tweets object is completely missing, smells like API failure */ /* Tweets object is completely missing, smells like API failure */
if (typeof conversation?.globalObjects?.tweets === 'undefined') { if (typeof conversation?.globalObjects?.tweets === 'undefined') {
writeDataPoint(event, language, wasMediaBlockedNSFW, 'API_FAIL', flags);
return { code: 500, message: 'API_FAIL' }; return { code: 500, message: 'API_FAIL' };
} }
/* If we have no idea what happened then just return API error */ /* If we have no idea what happened then just return API error */
writeDataPoint(event, language, wasMediaBlockedNSFW, 'API_FAIL', flags);
return { code: 500, message: 'API_FAIL' }; return { code: 500, message: 'API_FAIL' };
} }
} }
@ -217,5 +250,7 @@ export const statusAPI = async (
/* Finally, staple the Tweet to the response and return it */ /* Finally, staple the Tweet to the response and return it */
response.tweet = apiTweet; response.tweet = apiTweet;
writeDataPoint(event, language, wasMediaBlockedNSFW, 'OK', flags);
return response; return response;
}; };

View file

@ -30,7 +30,7 @@ export const handleStatus = async (
): Promise<StatusResponse> => { ): Promise<StatusResponse> => {
console.log('Direct?', flags?.direct); console.log('Direct?', flags?.direct);
const api = await statusAPI(status, language, event as FetchEvent); const api = await statusAPI(status, language, event as FetchEvent, flags);
const tweet = api?.tweet as APITweet; const tweet = api?.tweet as APITweet;
/* Catch this request if it's an API response */ /* Catch this request if it's an API response */

8
src/types/env.d.ts vendored
View file

@ -15,9 +15,5 @@ declare const RELEASE_NAME: string;
declare const TEST: boolean | undefined; declare const TEST: boolean | undefined;
declare const TwitterProxy: { declare const TwitterProxy: Fetcher;
fetch: ( declare const AnalyticsEngine: AnalyticsEngineDataset;
input: RequestInfo<unknown, CfProperties<unknown>>,
init?: RequestInit<RequestInitCfProperties> | undefined
) => Promise<Response>;
};

View file

@ -6,6 +6,9 @@ send_metrics = false
services = [ services = [
{ binding = "TwitterProxy", service = "elongator" } { binding = "TwitterProxy", service = "elongator" }
] ]
analytics_engine_datasets = [
{ binding = "AnalyticsEngine" }
]
[build] [build]
command = "npm run build" command = "npm run build"