Avatar

syncretizm

5 public vals
Joined November 10, 2023
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import process from 'node:process';
import { YouTube } from 'https://deno.land/x/youtube@v0.3.0/mod.ts';
export async function fetchVideoDetails(req) {
const url = new URL(req.url);
let yturl = url.searchParams.get("yturl") || "";
let mode = url.searchParams.get("mode") || "";
let responseContent;
let publishedDate, currentDate, timeDiff, daysDiff; // Declare variables outside the switch
yturl = decodeURIComponent(yturl);
const videoIdMatch = yturl.match(/(?:https?:\/\/)?(?:www\.)?youtube\.com\/(?:watch\?v=|live\/)([a-zA-Z0-9_-]+)|youtu\.be\/([a-zA-Z0-9_-]+)/);
let videoId = videoIdMatch ? videoIdMatch[1] || videoIdMatch[2] : null;
if (videoId && videoId.includes('&')) {
videoId = videoId.split('&')[0];
}
if (!videoId) {
videoId = `0iaU9VZXKUQ`;
}
if (!mode) {
mode = `transcript`;
}
const apiKey = process.env.ytapiKey;
let apiUrl = `https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=${videoId}&key=${apiKey}`;
try {
let response = await fetch(apiUrl);
let data = await response.json();
if (!data.items || data.items.length === 0) {
throw new Error('No video details found');
}
const item = data.items[0];
let responseContent;
switch (mode) {
case 'thumbnail':
responseContent = `![](${item.snippet.thumbnails.high.url})`;
break;
case 'channel':
const channelLink = `https://www.youtube.com/channel/${item.snippet.channelId}`;
responseContent = `[${item.snippet.channelTitle}](${channelLink})`;
break;
case 'publishDate':
responseContent = item.snippet.publishedAt;
break;
case 'viewCount':
responseContent = item.statistics.viewCount.toString();
break;
case 'viewsPerDay':
publishedDate = new Date(item.snippet.publishedAt);
currentDate = new Date();
timeDiff = currentDate.getTime() - publishedDate.getTime();
daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
const viewsPerDay = daysDiff > 0 ? (item.statistics.viewCount / daysDiff).toFixed(2) : 'N/A';
responseContent = viewsPerDay.toString();
break;
case 'videoAge':
publishedDate = new Date(item.snippet.publishedAt);
currentDate = new Date();
timeDiff = currentDate.getTime() - publishedDate.getTime();
daysDiff = Math.floor(timeDiff / (1000 * 3600 * 24));
responseContent = daysDiff.toString();
break;
case 'videoTitle':
responseContent = item.snippet.title;
break;
case 'engagementMetrics':
const likeCount = item.statistics.likeCount || 'N/A';
const dislikeCount = item.statistics.dislikeCount || 'N/A';
const commentCount = item.statistics.commentCount || 'N/A';
responseContent = `Likes: ${likeCount}, Dislikes: ${dislikeCount}, Comments: ${commentCount}`;
break;
case 'averageViewDuration':
const duration = item.contentDetails.duration;
const durationInSeconds = parseISO8601Duration(duration);
const averageViewDuration = (durationInSeconds / item.statistics.viewCount).toFixed(2);
responseContent = `Average View Duration: ${averageViewDuration} seconds`;
break;
case 'videoCategory':
const categoryId = item.snippet.categoryId;
apiUrl = `https://www.googleapis.com/youtube/v3/videoCategories?part=snippet&id=${categoryId}&key=${apiKey}`;
response = await fetch(apiUrl);
data = await response.json();
const category = data.items && data.items.length > 0 ? data.items[0].snippet.title : 'N/A';
responseContent = `Category: ${category}`;
break;
case 'tags':
const tags = item.snippet.tags || [];
responseContent = `Tags: ${tags.join(', ')}`;
break;
case 'transcript':
async function fetchcaptions(videoId) {
const WATCH_URL = `https://www.youtube.com/watch?v=${videoId}`;
try {
const response = await fetch(WATCH_URL);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import { PuppeteerDeno } from "https://deno.land/x/puppeteer@16.2.0/src/deno/Puppeteer.ts";
import process from "node:process";
export async function extractTwitterContent(req) {
try {
console.log("Starting to extract Twitter content...");
const url = new URL(req.url);
const twurl = decodeURIComponent(url.searchParams.get("twurl") || "");
if (twurl.length == 0) { return new Response("Error: No twitter url provided", { status: 400 }); }
console.log(`Received twitter URL: ${twurl}`);
// convert twurl to threadurl via regex
const postURLRegex = /https:\/\/twitter\.com\/(.*)\/status\/(\d+)/;
const matches = twurl.match(postURLRegex);
console.log("Matching URL against Twitter post URL regex...");
let threadurl = "";
if (matches) {
const authorHandle = matches[1];
const postID = matches[2];
threadurl = `https://twitter.com/${authorHandle}/thread/${postID}`;
console.log(`Thread URL constructed: ${threadurl}`);
} else {
threadurl = twurl;
console.log("No match found, using original URL.");
}
const puppeteer = new PuppeteerDeno({ productName: "chrome" });
console.log("Connecting to browser...");
const browser = await puppeteer.connect({
browserWSEndpoint: `wss://chrome.browserless.io?token=${process.env.browserlessKey}`,
defaultViewport: null,
args: ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"],
});
const page = await browser.newPage();
console.log(`Navigating to thread URL: ${threadurl}`);
await page.goto(threadurl, { waitUntil: "domcontentloaded" }).catch(e => {
console.error("Navigation to thread URL failed:", e);
browser.close();
return new Response("Error: Navigation to Twitter thread URL timed out", { status: 504 });
});
await page.screenshot({ path: "threadurl-screenshot.png" });
// Check if login required
const loginSelector = "div[data-testid=\"loginButton\"]";
console.log("Checking if login is required...");
const login = await page.$(loginSelector);
let status = "";
if (login) {
status = "login_required";
console.log("Login is required.");
} else {
status = "ok";
console.log("No login required.");
}
if (status == "login_required") {
console.log("Attempting to log in...");
await page.goto("https://twitter.com/i/flow/login", { waitUntil: "domcontentloaded" }).catch(e => {
console.error("Navigation to login page failed:", e);
browser.close();
return new Response("Error: Navigation to Twitter login page timed out", { status: 504 });
});
// if login required, click the login button which has data-testid="loginButton"
const loginButton = "div[data-testid=\"loginButton\"]";
await page.click(loginButton).catch(e => {
console.error("Clicking login button failed:", e);
browser.close();
return new Response("Error: Clicking Twitter login button failed", { status: 500 });
});
console.log("Clicked on login button.");
// wait for the login form to appear, input with autocomplete="username" and name="text"
await page.waitForSelector("input[autocomplete=\"username\"][name=\"text\"]").catch(e => {
console.error("Waiting for username input failed:", e);
browser.close();
return new Response("Error: Waiting for Twitter username input failed", { status: 500 });
});
console.log("Typing in username...");
await page.type("input[autocomplete=\"username\"][name=\"text\"]", process.env.twitterUsername).catch(e => {
console.error("Typing in username failed:", e);
browser.close();
return new Response("Error: Typing in Twitter username failed", { status: 500 });
});
// look for the div with role="button" that is somewhere below the input
const loginButtonSelector = "div[role=\"button\"]";
await page.waitForSelector(loginButtonSelector).catch(e => {
console.error("Waiting for next button after username failed:", e);
browser.close();
return new Response("Error: Waiting for next button after username failed", { status: 500 });
});
console.log("Clicking on next button after username...");
await page.click(loginButtonSelector).catch(e => {
console.error("Clicking next button after username failed:", e);
browser.close();
return new Response("Error: Clicking next button after username failed", { status: 500 });
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
const functionMap = {
// Basic Arithmetic Operations, with checks for type as numbers
"add": (a, b) => typeof a === "number" && typeof b === "number" ? a + b : null,
"subtract": (a, b) => typeof a === "number" && typeof b === "number" ? a - b : null,
"multiply": (a, b) => typeof a === "number" && typeof b === "number" ? a * b : null,
"mod": (a, b) => typeof a === "number" && typeof b === "number" ? a % b : null,
"pow": (a, b) => typeof a === "number" && typeof b === "number" ? Math.pow(a, b) : null,
"divide": (a, b) => (typeof a === "number" && typeof b === "number" && b !== 0) ? a / b : null,
// Comparison Operations, with checks for type as numbers
"equal": (a, b) => typeof a === "number" && typeof b === "number" ? a === b : null,
"notequal": (a, b) => typeof a === "number" && typeof b === "number" ? a !== b : null,
"greaterthan": (a, b) => typeof a === "number" && typeof b === "number" ? a > b : null,
"lessthan": (a, b) => typeof a === "number" && typeof b === "number" ? a < b : null,
"greaterthanorequal": (a, b) => typeof a === "number" && typeof b === "number" ? a >= b : null,
"lessthanorequal": (a, b) => typeof a === "number" && typeof b === "number" ? a <= b : null,
// Logical Operations, with checks
"and": (a, b, c) => typeof a === "boolean" && typeof b === "string" && typeof c === "string" ? a ? b : c : null,
"or": (a, b) => typeof a === "boolean" && typeof b === "boolean" ? a || b : null,
"not": (a) => typeof a === "boolean" ? !a : null,
"xor": (a, b) => typeof a === "boolean" && typeof b === "boolean" ? a !== b : null,
"nand": (a, b) => typeof a === "boolean" && typeof b === "boolean" ? !(a && b) : null,
"nor": (a, b) => typeof a === "boolean" && typeof b === "boolean" ? !(a || b) : null,
"xnor": (a, b) => typeof a === "boolean" && typeof b === "boolean" ? a === b : null,
// Boolean Values
// true receives a boolean, b is if true, c is if false
"true": (a, b, c) => typeof a === "boolean" && typeof b === "string" && typeof c === "string" ? a ? b : c : null,
"false": (a, b, c) => typeof a === "boolean" && typeof b === "string" && typeof c === "string" ? a ? c : b : null,
// String and Array Operations with checks
"contains": (a, b) => typeof a === "string" || typeof a === "number" || Array.isArray(a) ? a.includes(b) : null,
"length": (a) => typeof a === "string" || typeof a === "number" || Array.isArray(a) ? a.length : null,
"empty": (a) => typeof a === "string" || typeof a === "number" || Array.isArray(a) ? a === "" : null,
"test": (a, b) => typeof a === "string" ? b.includes(a) : null,
"match": (a, b) => typeof a === "string" ? a.match(b) : null,
"replace": (a, b, c) => typeof a === "string" ? a.replace(b, c) : null,
"replaceAll": (a, b, c) => typeof a === "string" ? a.replaceAll(b, c) : null,
"lowercase": (a) => typeof a === "string" ? a.toLowerCase() : null,
"uppercase": (a) => typeof a === "string" ? a.toUpperCase() : null,
"repeat": (a, b) => typeof a === "string" ? a.repeat(b) : null,
"stripformatting": (a) => typeof a === "string" ? a.replace(/<[^>]*>/g, "") : null,
"min": (a) => Array.isArray(a) ? Math.min(...a) : null,
"max": (a) => Array.isArray(a) ? Math.max(...a) : null,
"absolute": (a) => typeof a === "number" ? Math.abs(a) : null,
"round": (a) => typeof a === "number" ? Math.round(a) : null,
"ceiling": (a) => typeof a === "number" ? Math.ceil(a) : null,
"floor": (a) => typeof a === "number" ? Math.floor(a) : null,
"squareroot": (a) => typeof a === "number" ? Math.sqrt(a) : null,
"cuberoot": (a) => typeof a === "number" ? Math.cbrt(a) : null,
"exponent": (a) => typeof a === "number" ? Math.exp(a) : null,
"ln": (a) => typeof a === "number" ? Math.log(a) : null,
"log10": (a) => typeof a === "number" ? Math.log10(a) : null,
"log2": (a) => typeof a === "number" ? Math.log2(a) : null,
"sin": (a) => typeof a === "number" ? Math.sin(a) : null,
"cos": (a) => typeof a === "number" ? Math.cos(a) : null,
"tan": (a) => typeof a === "number" ? Math.tan(a) : null,
"asin": (a) => typeof a === "number" ? Math.asin(a) : null,
"acos": (a) => typeof a === "number" ? Math.acos(a) : null,
"atan": (a) => typeof a === "number" ? Math.atan(a) : null,
"atan2": (a, b) => typeof a === "number" && typeof b === "number" ? Math.atan2(a, b) : null,
"degrees": (a) => typeof a === "number" ? a * 180 / Math.PI : null,
"radians": (a) => typeof a === "number" ? a * Math.PI / 180 : null,
"random": (a, b) => typeof a === "number" && typeof b === "number" ? Math.random() * (b - a) + a : null,
"randomint": (a, b) =>
typeof a === "number" && typeof b === "number" ? Math.floor(Math.random() * (b - a + 1)) + a : null,
"pi": () => Math.PI,
"e": () => Math.E,
// Date and Time Operations, with checks
"now": (a) => typeof a === "date" ? a.getTime() : null,
"minute": (a) => typeof a === "date" ? a.getMinutes() : null,
"hour": (a) => typeof a === "date" ? a.getHours() : null,
"day": (a) => typeof a === "date" ? a.getDay() : null,
"date": (a) => typeof a === "date" ? a.getDate() : null,
"week": (a) => typeof a === "date" ? a.getWeek() : null,
"month": (a) => typeof a === "date" ? a.getMonth() : null,
"year": (a) => typeof a === "date" ? a.getFullYear() : null,
"dateadd": (a, b) =>
typeof a === "date" && typeof b === "number" ? new Date(a.getTime() + b * 24 * 60 * 60 * 1000) : null,
"datesubtract": (a, b) =>
typeof a === "date" && typeof b === "number" ? new Date(a.getTime() - b * 24 * 60 * 60 * 1000) : null,
"datebetween": (startDate, endDate, unit) => {
if (!startDate || !endDate || typeof unit !== "string") {
return new Response("Invalid arguments for datebetween function");
}
let difference = Math.abs(endDate - startDate);
switch (unit) {
case "days":
return difference / (1000 * 60 * 60 * 24);
case "hours":
return difference / (1000 * 60 * 60);
case "minutes":
return difference / (1000 * 60);
case "seconds":
return difference / 1000;
case "milliseconds":
return difference;
case "weeks":
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
export async function formulaEndpoint(req) {
if (req.method !== "GET") {
return new Response("Method Not Allowed", { status: 405 });
}
// PARSE URL
const params = new URLSearchParams(req.url.split("?")[1]);
// Extract the method and remove it from the parameters
const method = params.get("method");
params.delete("method");
// Initialize an object to store the variables
const vars = {};
// Iterate through the parameters
params.forEach((value, key) => {
if (value) {
vars[key] = value;
}
});
// now params is a dictionary of all the parameters that are not 'method'
// if params is empty, then we need to respond with an error.
if (Object.keys(vars).length === 0) {
return new Response("No parameters provided", { status: 400 });
}
// the params contain either numbers or strings, depending on the method passed.
// methods/functions include: add, subtract, multiply, mod, pow, divide, equal, =/=, >, <, >=, <=, and, or, not, xor, nand, nor, xnor, true, false, if_empty, length, contains, test, match, replace, replaceAll, lowercase, uppercase, repeat(#), strip_forma
// variable types include: number, string, boolean, date, array, object, null, undefined
// the return type is always a string.
// EXECUTE REQUEST
let result = null;
// DICTIONARY OF ALL FUNCTIONS, MAX NUMBER OF PARAMETERS, AND ACCEPTED TYPE
const functions = {
"add": ["infinite", "number"],
"subtract": ["infinite", "number"],
"multiply": ["infinite", "number"],
"mod": [2, "number"],
"pow": [2, "number"],
"divide": [2, "number"],
"equal": [2, "any"],
"!=": [2, "any"],
">": [2, "number"],
"<": [2, "number"],
">=": [2, "number"],
"<=": [2, "number"],
"and": [2, "boolean"],
"or": [2, "boolean"],
"not": [1, "boolean"],
"xor": [2, "boolean"],
"nand": [2, "boolean"],
"nor": [2, "boolean"],
"xnor": [2, "boolean"],
"true": [3, "string"],
"false": [3, "string"],
"if_empty": [1, "any"],
"length": [1, "string"],
"contains": [2, "string"],
"test": [2, "string"], // test whether a string contains a substring, the latter of which can be a regular expression
"match": [2, "string"],
"replace": [3, "string"],
"replaceAll": [3, "string"],
"lowercase": [1, "string"],
"uppercase": [1, "string"],
"repeat": [2, "string", "number"],
"strip_formatting": [1, "string"],
"min": ["infinite", "number"],
"max": ["infinite", "number"],
"absolute": [1, "number"],
"round": [1, "number"],
"ceiling": [1, "number"],
"floor": [1, "number"],
"squareroot": [1, "number"],
"cuberoot": [1, "number"],
"exponent": [2, "number"],
"ln": [1, "number"],
"log10": [1, "number"],
"log2": [1, "number"],
"now": [1, "number"], // receives UTC offset
"minute": [1, "date"],
"hour": [1, "date"],
"day": [1, "date"],
"date": [1, "date"],
"week": [1, "date"],
"month": [1, "date"],
"year": [1, "date"],
"dateAdd": [3, "date", "number", "string"],
"dateSubtract": [3, "date", "number", "string"],
"dateBetween": [3, "date", "date", "string"],
// 'dateRange': [3, 'date'],
// 'dateStart': [2, 'date'],
// 'dateEnd': [2, 'date'],
"timestamp": [1, "date"],
"fromTimestamp": [1, "number"],
"formatDate": [2, "string"],
"parseDate": [2, "string"],
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
const timeZone = "Asia/Singapore";
const State = {
New: 0,
Learning: 1,
Review: 2,
Relearning: 3,
};
const Rating = {
Again: 1,
Hard: 2,
Good: 3,
Easy: 4,
};
class ReviewLog {
rating;
elapsed_days;
scheduled_days;
review;
state;
constructor(rating, elapsed_days, scheduled_days, review, state) {
this.rating = rating;
this.elapsed_days = elapsed_days;
this.scheduled_days = Math.abs(scheduled_days);
this.review = review;
this.state = state;
}
}
class Card {
due;
stability;
difficulty;
elapsed_days;
scheduled_days;
reps;
lapses;
state;
last_review;
constructor() {
this.due = new Date();
this.stability = 0;
this.difficulty = 0;
this.elapsed_days = 0;
this.scheduled_days = 0;
this.reps = 0;
this.lapses = 0;
this.state = State.New;
this.last_review = new Date();
}
}
class SchedulingInfo {
card;
review_log;
constructor(card, review_log) {
this.card = card;
this.review_log = review_log;
}
}
class SchedulingCards {
again;
hard;
good;
easy;
constructor(card) {
this.again = { ...card };
this.hard = { ...card };
this.good = { ...card };
this.easy = { ...card };
}
update_state(state) {
if (state == State.New) {
this.again.state = State.Learning;
this.hard.state = State.Learning;
this.good.state = State.Learning;
this.easy.state = State.Review;
this.again.lapses += 1;
} else if (state == State.Learning || state == State.Relearning) {
this.again.state = state;
this.hard.state = state;
this.good.state = State.Review;
this.easy.state = State.Review;
} else if (state == State.Review) {
this.again.state = State.Relearning;
this.hard.state = State.Review;
this.good.state = State.Review;
this.easy.state = State.Review;
this.again.lapses += 1;
}
}
schedule(now, hard_interval, good_interval, easy_interval) {
this.again.scheduled_days = 0;
this.hard.scheduled_days = hard_interval;
this.good.scheduled_days = good_interval;
this.easy.scheduled_days = easy_interval;
Next