-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi.js
164 lines (150 loc) · 4.32 KB
/
api.js
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
const fetch = require('node-fetch');
const pkg = require('../../package.json');
const logger = require('./logger');
const HOST_URI = 'https://adventofcode.com';
const USER_AGENT = `node/${process.version} ${pkg.name}/${pkg.version}`;
const TOO_EARLY_REQUEST_TEXT =
"please don't repeatedly request this endpoint before it unlocks";
const CORRECT_ANSWER_TEXT = "that's the right answer";
const INCORRECT_ANSWER_TEXT = "that's not the right answer";
const TOO_RECENT_ANSWER_TEXT =
'you gave an answer too recently; you have to wait after submitting an answer before trying again.';
const INCORRECT_LEVEL_TEXT = "you don't seem to be solving the right level";
const fetchFromCacheOrAoC = async (cacheKey, uri, options, config, cache) => {
if (config.useCache) {
const cachedResponse = cache.get(cacheKey);
// use the cached response response if it exists
if (cachedResponse) {
logger.debug(
'Found a previously cached response, returning response from cache'
);
return Promise.resolve(cachedResponse);
}
}
// otherwise call AoC
logger.debug(
'No previously cached response found, fetching from Advent Of Code'
);
const response = await fetch(uri, options);
return response.text();
};
async function getInput(config, cache) {
const { year, day, token } = config;
const uri = `${HOST_URI}/${year}/day/${day}/input`;
const options = {
method: 'get',
headers: {
'Content-Type': 'text/plain',
Cookie: `session=${token}`,
'User-Agent': USER_AGENT
}
};
const cacheKey = JSON.stringify({
uri,
token
});
const textResponse = await fetchFromCacheOrAoC(
cacheKey,
uri,
options,
config,
cache
);
if (textResponse.toLowerCase().includes(TOO_EARLY_REQUEST_TEXT)) {
return Promise.reject(
new Error(
'This puzzle has not opened yet, please wait until the puzzle unlocks!'
)
);
}
const isValidInputResponse =
textResponse.length && !textResponse.match(/<[^>]+>/);
if (!isValidInputResponse) {
return Promise.reject(
new Error(
'An error occurred while fetching the input. Are you authenticated correctly?'
)
);
}
if (config.useCache && !cache.get(cacheKey)) {
// update cache if it had not been set previously
cache.set(cacheKey, textResponse);
}
return textResponse;
}
const postAnswer = async ({ part, answer }, config, cache) => {
const { year, day, token } = config;
const uri = `${HOST_URI}/${year}/day/${day}/answer`;
const options = {
method: 'post',
headers: {
Cookie: `session=${token}`,
'User-Agent': USER_AGENT,
'cache-control': 'no-cache',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
level: part,
answer
})
};
const cacheKey = JSON.stringify({
uri,
token,
part,
answer
});
const cachedResponse = cache.get(cacheKey);
const textResponse = await fetchFromCacheOrAoC(
cacheKey,
uri,
options,
config,
cache
);
const text = textResponse.toLowerCase();
if (text.includes(CORRECT_ANSWER_TEXT)) {
if (config.useCache && !cachedResponse) {
// Update cache if no previously cached response
cache.set(cacheKey, textResponse);
}
return Promise.resolve({ correct: true });
}
if (text.includes(INCORRECT_ANSWER_TEXT)) {
if (config.useCache && !cachedResponse) {
// Update cache if no previously cached response
cache.set(cacheKey, textResponse);
}
return Promise.resolve({ correct: false });
}
if (text.includes(TOO_RECENT_ANSWER_TEXT)) {
const leftToWaitText = text
.split(TOO_RECENT_ANSWER_TEXT)[1]
.split('.')[0]
.trim();
return Promise.reject(
new Error(`You gave an answer too recently, ${leftToWaitText}.`)
);
}
if (text.includes(TOO_EARLY_REQUEST_TEXT)) {
return Promise.reject(
new Error(
'This puzzle has not opened yet, please wait until the puzzle unlocks!'
)
);
}
if (text.includes(INCORRECT_LEVEL_TEXT)) {
return Promise.reject(
new Error(
"You don't seem to be solving the correct level. Did you already complete it?"
)
);
}
return Promise.reject(
new Error('Unknown response from AoC. Are you authenticated correctly?')
);
};
module.exports = {
getInput,
postAnswer
};