Avatar

@rlesser

7 likes21 public vals
Joined January 28, 2023

Get Favorites on Resy

This function fetches your favorite restaurants and venues, as specified in your Hit List.

Inputs

An object containing:

Returns

A Favorites object, containing the ids of your favorite venues, defined in the type below.

See other Resy vals I've made.

Readme
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
type Favorites = {
results: {
venues: {
venue: {
id: {
resy: number;
};
name: string;
};
}[];
};
};
const Resy_getFavorites = async (params: {
authToken: string;
}): Promise<Favorites> => {
const url = "https://api.resy.com";
const response = await fetch(`${url}/3/user/favorites`, {
headers: {
accept: "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9",
authorization: 'ResyAPI api_key="VbWk7s3L4KiK5fzlO7JD3Q5EYolJI7n5"',
"x-origin": "https://resy.com",
origin: "https://resy.com/",
dnt: "1",
referer: "https://resy.com/",
"content-type": "application/json",
"content-type": "application/x-www-form-urlencoded",
"x-resy-auth-token": params.authToken,
},
});
const data = await response.json();
return data;
};
0
4

Get a Venue's Calendar on Resy

This function fetches the calendar for a given venue on Resy, which gives information on which days the venue is available, sold-out, or closed.

Inputs

An object containing:

  • venue_id - The Resy venue id, either fetched from the website's network data or from @rlesser_getFavorites (Todo: add venue id from url function).
  • num_seats - The number of seats you are checking for availability for. Use 2 as a default if you just want to know when the restaurant is closed.

Returns

A VenueCalendar object, containing:

  • last_calendar_day - A string representing the last day the restaurant has made their calendar available for (normally a few weeks out from today).
  • scheduled - An object containing a list of dates and restaurant statuses. See type below.

See other Resy vals I've made.

Readme
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
type VenueCalendar = {
last_calendar_day: string;
scheduled: {
date: string;
inventory: {
reservation: "sold-out" | "available" | "closed";
};
}[];
};
const Resy_getVenueCalendar = async (params: {
venue_id: number;
num_seats: number;
}): Promise<VenueCalendar> => {
const url = "https://api.resy.com";
const query = new URLSearchParams({
venue_id: String(params.venue_id),
num_seats: String(params.num_seats),
start_date: (new Date()).toISOString().slice(0, 10),
end_date: new Date(new Date().setFullYear(new Date().getFullYear() + 1))
.toISOString().slice(0, 10),
});
const response = await fetch(`${url}/4/venue/calendar?${query.toString()}`, {
headers: {
accept: "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9",
authorization: 'ResyAPI api_key="VbWk7s3L4KiK5fzlO7JD3Q5EYolJI7n5"',
"x-origin": "https://resy.com",
origin: "https://resy.com/",
dnt: "1",
referer: "https://resy.com/",
"content-type": "application/json",
"content-type": "application/x-www-form-urlencoded",
},
});
const data = await response.json();
return data;
};
0
4
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
const Resy_setNotify = async (params: {
authToken: string;
notifies: {
venue_id: number;
day: string;
time_preferred_start: string;
time_preferred_end: string;
num_seats: number;
service_type_id: number;
}[];
}) => {
const url = "https://api.resy.com";
const response = await fetch(`${url}/3/notify`, {
method: "POST",
body: `struct_data=${
encodeURIComponent(JSON.stringify({ notifies: params.notifies }))
}`,
headers: {
accept: "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9",
authorization: 'ResyAPI api_key="VbWk7s3L4KiK5fzlO7JD3Q5EYolJI7n5"',
"x-origin": "https://resy.com",
origin: "https://resy.com/",
dnt: "1",
referer: "https://resy.com/",
"content-type": "application/json",
"content-type": "application/x-www-form-urlencoded",
"x-resy-auth-token": params.authToken,
},
});
const data = await response.json();
return data;
};
0
3

Authenticate with Resy

This function can be used to generate an authentication token for Resy, which can be used to view favorites, set notifies, or even make reservations.

Inputs

An AuthParams object containing:

  • email - The email address for your Resy account. Probably best to keep private.
  • password - The password for your Resy account. Definitely keep this private, and place it in a secret.

Returns

An AuthResponse object containing:

  • id - Your Resy account id.
  • token - Your Resy account token, used to authenticate you in subsequent API calls.

See other Resy vals I've made.

Readme
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
type AuthParams = {
email: string;
password: string;
};
type AuthResponse = {
id: string;
token: string;
[props: string]: unknown;
};
const Resy_authenticate = async (params: AuthParams): Promise<{
AuthResponse;
}> => {
const url = "https://api.resy.com";
const body = `email=${encodeURIComponent(params.email)}&password=${
encodeURIComponent(params.password)
}`;
const response = await fetch(`${url}/3/auth/password`, {
method: "POST",
body: body,
headers: {
accept: "application/json, text/plain, */*",
"accept-encoding": "gzip, deflate, br",
"accept-language": "en-US,en;q=0.9",
authorization: 'ResyAPI api_key="VbWk7s3L4KiK5fzlO7JD3Q5EYolJI7n5"',
"x-origin": "https://resy.com",
origin: "https://resy.com/",
dnt: "1",
referer: "https://resy.com/",
"content-type": "application/json",
"content-type": "application/x-www-form-urlencoded",
},
});
const data = await response.json();
return data;
};
0
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Send a pushover message.
// token, user, and other opts are as specified at https://pushover.net/api
async function pushover({ token, user, message, title, url, ...opts }) {
return await fetch("https://api.pushover.net/1/messages.json", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
token,
user,
message,
title,
url,
...opts,
}),
});
}
// Forked from @meatcar.pushover - added content type header
0
2
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
let googleSheetBatchUpdate = async (
serviceAccount: string,
spreadsheetId: string,
batchUpdateData:
import("npm:googleapis").sheets_v4.Params$Resource$Spreadsheets$Batchupdate[
"requestBody"
]["requests"],
) => {
const { getToken } = await import(
"https://deno.land/x/google_jwt_sa@v0.2.3/mod.ts"
);
const token = await getToken(serviceAccount, {
scope: ["https://www.googleapis.com/auth/spreadsheets"],
});
const requestBody = {
requests: batchUpdateData,
};
const result = await @stevekrouse.fetchJSON(
`https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}:batchUpdate`,
{
method: "POST",
headers: {
Authorization: `Bearer ${token.access_token}`,
},
body: JSON.stringify(requestBody),
},
);
return result;
};
0
1

getNextSSR - fetch data from Next.js SSR-based sites

Many modern websites use Next.js and Server-side Rendering (SSR) to serve their website and the data to populate it. Instead of standard API calls, this manifests in the browser as calls routed through dynamic endpoints.

This val handles the url construction and response parsing, giving you access to the endpoint and data.

Input

  • websiteUrl - The website's url, like google.com or val.town let getNextSSR: (websiteUrl: string) => Promise<(endpoint: string) => Promise<Record<string, any>>>

Returns

  • fetching function - A function that takes in an endpoint and returns the endpoint's response data:
    • Input
    • endpoint - The endpoint string, like results.json or trending.json?page=2
    • Returns
    • data - The endpoint response data, without NextJS artifacts.

Example

const fetch = await @rlesser.getNextSSR("example.com");
const data = fetch("results.json");
return data;
Readme
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let getNextSSR = async (websiteUrl: string) => {
const html = await @stevekrouse.fetchText(websiteUrl);
const staticId = html.split("/_buildManifest")[0].split("/").slice(-1)[0];
return async (endpoint: string) => {
const responseData = await @stevekrouse.fetchJSON(
`https://${websiteUrl}/_next/data/${staticId}/${endpoint}`,
);
// one possible data path is response.pageProps.ssrResponses.[some_path]
const ssrResponses = responseData?.pageProps?.ssrResponses;
if (ssrResponses == undefined) {
throw Error(
"Unexpected response structure - leave a comment with the expected structure!",
);
}
const realData = Object.values(ssrResponses)[0];
return realData as Record<string, any>;
};
};
0
2
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
type AuthorData = {
avatarUrl: string;
fullname: string;
name: string;
type: string;
isHf: boolean;
};
interface Repo {
author: string;
id: string;
isLikedByUser: boolean;
lastModified: string;
likes: number;
private: boolean;
}
type ModelRepo = Repo & {
authorData: AuthorData;
downloads: number;
gated: boolean;
pipeline_tag: string;
repoType: "model";
};
type DatasetRepo = Repo & {
downloads: number;
gated: boolean;
previewable: boolean;
repoType: "dataset";
};
type SpaceRepo = Repo & {
authorData: AuthorData;
colorFrom: string;
colorTo: string;
emoji: string;
pinned: boolean;
runtime: object;
title: string;
repoType: "space";
};
type TrendingRepo = {
likes: number;
typeRank: number;
allRank?: number;
repoType: "model" | "dataset" | "space";
repoData: ModelRepo | DatasetRepo | SpaceRepo;
};
const HuggingFace_getTrending = async (params: {
type: "all" | "model" | "dataset" | "space";
}): Promise<TrendingRepo[]> => {
const url = "https://huggingface.co/api/trending";
const query = new URLSearchParams({ type: params.type, limit: String(100) });
const json = await @stevekrouse.fetchJSON(
url + "?" + query.toString(),
);
const data: TrendingRepo[] = json.recentlyTrending;
["model", "dataset", "space"].forEach((type) =>
data.filter((repo) => repo.repoType == type).forEach((repo, i) => {
repo.typeRank = i + 1;
})
);
if (params.type == "all") {
data.forEach((repo, i) => repo.allRank = i + 1);
}
return data;
};
0
2
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
type RepoData = {
date: string;
rank: number;
full_name: string;
description: string;
language: string;
stars: number;
forks: number;
stars_recent: number;
};
const Github_ScrapeTrendingRepos = async (
timeframe: string,
): Promise<RepoData[]> => {
const { cheerioJsonMapper: cjm } = await import("npm:cheerio-json-mapper");
const res = await fetch(`https://github.com/trending?since=${timeframe}`);
const html = await res.text();
const template = [
{
$: "article",
full_name: " > h2 > a | attr:href",
description: " > p",
language: " span[itemprop]",
stars: " > .f6 > a:first",
forks: " > .f6 > a:last",
stars_recent: " > .f6 > span:last",
},
];
const data = await cjm(html, template);
const date = new Date();
return data.map((d, i) => ({
date: date.toISOString(),
rank: i + 1,
full_name: d.full_name.slice(1),
description: d.description,
language: d.language,
stars: +d.stars.replace(",", ""),
forks: +d.forks.replace(",", ""),
stars_recent: +d.stars_recent.split(" ")[0].replace(",", ""),
}));
};
0
1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Github_SearchTopRepos = async (
start: string | Date,
days: number = 1,
) => {
const { Octokit } = await import("npm:@octokit/rest");
const octokit = new Octokit();
if (typeof start == "string")
start = new Date(start);
const end = new Date(start);
end.setDate(start.getDate() + days);
return await octokit.rest.search.repos({
q: `created:${start.toISOString()}..${end.toISOString()}`,
sort: "stars",
order: "desc",
per_page: 100,
});
};
0
1