Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update to use cookies instead of headers #14

Merged
merged 3 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ browsers' developer tools.
1. Navigate to <https://www.nytimes.com/crosswords>
1. Look for a request for some kind of json file e.g. `progress.json`, `mini-stats.json`, or
`stats-and-streaks.json`.
1. In the headers pane, find the value of `nyt-s` in the list of request headers. That is your
token. If you can't find the `nyt-s` header in the request, try a different json file.
1. In the headers pane, find the list of cookies, and fine `NYT-S` in that string. That is your
token. If you can't find the `NYT-S` cookie in the request, try a different json file.

### Under the hood

Expand All @@ -108,7 +108,7 @@ in your browser. Alternatively, you can supposedly extract your session cookie f
send that instead (see linked reddit post below), but I haven't tried it myself.

```sh
curl 'https://www.nytimes.com/svc/crosswords/v6/game/{id}.json' -H 'accept: application/json' -H 'nyt-s: {subscription_header}'
curl 'https://www.nytimes.com/svc/crosswords/v6/game/{id}.json' -H 'accept: application/json' --cookie 'NYT-S={subscription_header}'
```

1. Check out the `calcs` and `firsts` field of this response to get information like solve duration,
Expand Down
19 changes: 12 additions & 7 deletions plot/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@

DAYS = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

YEAR_TO_SPLIT = datetime.datetime.now().year - 1

A = argparse.ArgumentParser(
description='Generates plots of crossword statistics from a CSV',
formatter_class=argparse.ArgumentDefaultsHelpFormatter
Expand All @@ -32,7 +34,7 @@
help="The path to a CSV file generated from the crossword crate")
A.add_argument('out_file', action='store', type=str,
help="The output path where plots should be saved (e.g. trends.png)")
A.add_argument('-c', '--ceiling', action='store', type=int, default=0,
A.add_argument('-c', '--ceiling', action='store', type=int, default=None,
help='Max Y Value in minutes; use 0 to compute from data.')
A.add_argument('-s', '--style', action='store', default="Solarize_Light2",
type=str, # choices=plt.style.available, # looks gross
Expand Down Expand Up @@ -124,18 +126,21 @@ def save_vln_plot(df, out_path, ymax):

def save_split_vln_plot(df, out_path, ymax):
"""
Splits the violin plot into pre-2021 and 2021+ sections to look
Splits the violin plot into pre- and post- YEAR_TO_SPLIT sections to look
at progress over time.

df: dataframe containing crossword times
out_path: filename to save plot to
ceiling: max y-value to show
"""
df['solve_time_m'] = df['solve_time_secs'] / 60.0
# TODO: should probably not hard-code 2021 and instead pass in a date.
df['In 2021'] = df['Solved datetime'] > datetime.datetime(2021, 1, 1)
ax = sns.violinplot(x="weekday", y="solve_time_m", hue='In 2021',
split=True, data=df, bw=.25, order=DAYS)
df[f'In {YEAR_TO_SPLIT}'] = df['Solved datetime'] > datetime.datetime(YEAR_TO_SPLIT, 1, 1)
try:
ax = sns.violinplot(x="weekday", y="solve_time_m", hue=f'In {YEAR_TO_SPLIT}',
split=True, data=df, bw=.25, order=DAYS)
except:
# Happens if there is no data from last year
return

date = max(df['Solved datetime']).strftime("%b %d, %Y")
ax.set_title("%d NYT Crossword Solve Times by Day of Week as of %s" % (len(df), date))
Expand All @@ -147,7 +152,7 @@ def save_split_vln_plot(df, out_path, ymax):

ax.legend() # Seems to have the effect of removing the title of the legend
handles, labels = ax.get_legend_handles_labels()
ax.legend(handles, ["Before 2021", "2021"], loc="upper left")
ax.legend(handles, [f"Before {YEAR_TO_SPLIT}", f"{YEAR_TO_SPLIT}+"], loc="upper left")

plt.savefig(out_path)
plt.close()
Expand Down
10 changes: 8 additions & 2 deletions src/api_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use governor::state::direct::NotKeyed;
use governor::state::InMemoryState;
use governor::{Quota, RateLimiter};
use log::error;
use reqwest::header::{self, HeaderMap};
use reqwest::header::{self, HeaderMap, HeaderValue};
use reqwest::IntoUrl;
use serde::Deserialize;
use std::collections::HashMap;
Expand Down Expand Up @@ -119,9 +119,15 @@ impl RateLimitedClient {
/// * `quota` - Outgoing request quota in requests per second
pub fn new(nyt_s: &str, quota: NonZeroU32) -> Self {
kesyog marked this conversation as resolved.
Show resolved Hide resolved
let mut headers = HeaderMap::new();
headers.insert("nyt-s", nyt_s.parse().unwrap());
headers.insert(header::ACCEPT, "application/json".parse().unwrap());
headers.insert(header::DNT, "1".parse().unwrap());
if nyt_s.len() == 162 {
headers.insert(header::COOKIE, HeaderValue::from_str(&format!("NYT-S={}", nyt_s)).unwrap());
} else if nyt_s.len() == 142 {
headers.insert("nyt-s", nyt_s.parse().unwrap());
} else {
panic!("NYT-S must be either 162 characters (for NYT-S cookie) or 142 characters (for nyt-s header)");
}

let client = reqwest::ClientBuilder::new()
.user_agent("Scraping personal stats")
Expand Down