|
2 | 2 |
|
3 | 3 | (require racket/contract/base |
4 | 4 | racket/runtime-path |
| 5 | + racket/system |
| 6 | + racket/port |
5 | 7 | racket/file |
6 | 8 | racket/string |
7 | | - advent-of-code/request |
8 | 9 | net/sendurl |
9 | | - net/url) |
| 10 | + net/url |
| 11 | + (for-syntax racket/base |
| 12 | + racket/path |
| 13 | + setup/getinfo)) |
10 | 14 |
|
11 | 15 | (provide (contract-out |
| 16 | + [aoc-url url?] |
| 17 | + [aoc-session? predicate/c] |
12 | 18 | [current-aoc-time (-> date?)] |
13 | 19 | [session-file path-string?] |
14 | | - [find-session (-> aoc-session?)])) |
| 20 | + [contact-info-file path-string?] |
| 21 | + [find-session (-> aoc-session?)] |
| 22 | + [find-contact-info (-> string?)] |
| 23 | + [contact-info->user-agent (-> string? bytes?)])) |
| 24 | + |
| 25 | +(define (aoc-session? s) (string? s)) |
| 26 | +(define aoc-url (string->url "https://adventofcode.com")) |
15 | 27 |
|
16 | 28 | (define (current-aoc-time) |
17 | 29 | (define aoc-offset (- (* 5 60 60))) |
|
20 | 32 | #f)) |
21 | 33 |
|
22 | 34 | (define-runtime-path session-file "session.key") |
| 35 | +(define-runtime-path contact-info-file "contact.txt") |
23 | 36 |
|
24 | 37 | (define (y-or-n prompt [default #f]) |
25 | 38 | (printf "~a [~a/~a] " |
|
42 | 55 | (parameterize ([current-output-port (current-error-port)]) |
43 | 56 | (displayln "Session key unset or expired.") |
44 | 57 | (printf "Please set ~a~n" session-file) |
45 | | - (when (not (eq? 'y (y-or-n "Set interactively now?" 'y))) |
46 | | - (exit 1)) |
| 58 | + (unless (eq? 'y (y-or-n "Set interactively now?" 'y)) |
| 59 | + (raise-user-error "Session key unset or expired")) |
47 | 60 | (when (eq? 'y (y-or-n "Open Advent of Code in browser?" 'y)) |
48 | 61 | (send-url (url->string aoc-url))) |
49 | 62 | (displayln "Hint: Your cookies can typically be found in the developer console.") |
50 | 63 | (displayln "- Firefox: \"Storage\" tab (Shift + F9)") |
51 | 64 | (displayln "- Chrome: \"Application\" tab") |
52 | 65 | (displayln "Enter the contents of your session cookie below:") |
53 | 66 | (define session (read-line (current-input-port) 'any)) |
54 | | - (when (eof-object? session) |
55 | | - (exit 1)) |
56 | | - (with-output-to-file session-file |
57 | | - #:exists 'replace |
58 | | - (lambda () |
59 | | - (display session) |
60 | | - (flush-output))) |
| 67 | + (when (or (eof-object? session) (string=? "" session)) |
| 68 | + (raise-user-error "No input provided")) |
| 69 | + (display-to-file session session-file #:exists 'replace) |
61 | 70 | (displayln "Session set successfully!"))) |
62 | 71 |
|
| 72 | +(define month-seconds (* 31 24 60 60)) |
| 73 | +(define (older-than-seconds? path seconds) |
| 74 | + (let/ec return |
| 75 | + (define csec (current-seconds)) |
| 76 | + (define file-seconds |
| 77 | + (file-or-directory-modify-seconds |
| 78 | + path #f (lambda () (return #t)))) |
| 79 | + (<= seconds (- csec file-seconds)))) |
| 80 | + |
63 | 81 | (define (find-session) |
64 | | - (define csec (current-seconds)) |
65 | | - (define month-seconds (* 31 24 60 60)) |
66 | | - (when (<= |
67 | | - month-seconds |
68 | | - (- |
69 | | - csec |
70 | | - (file-or-directory-modify-seconds |
71 | | - session-file |
72 | | - #f |
73 | | - (lambda () (- csec month-seconds))))) |
| 82 | + (when (older-than-seconds? session-file month-seconds) |
74 | 83 | (refresh-session!)) |
75 | 84 | (string-trim (file->string session-file))) |
| 85 | + |
| 86 | +(define (raw-find-contact-info) |
| 87 | + (and (file-exists? contact-info-file) |
| 88 | + (string-trim (file->string contact-info-file)))) |
| 89 | + |
| 90 | +(define (refresh-contact-info!) |
| 91 | + (let/ec return |
| 92 | + (parameterize ([current-output-port (current-error-port)]) |
| 93 | + (displayln "Contact information unset or possibly outdated.") |
| 94 | + (displayln "Eric Wastl asks that you include a way to be contacted in automated requests.") |
| 95 | + (cond |
| 96 | + [(raw-find-contact-info) |
| 97 | + => |
| 98 | + (lambda (contact-info) |
| 99 | + (printf "Your current contact information at ~a is:~n" contact-info-file) |
| 100 | + (printf " ~a~n" contact-info))] |
| 101 | + [else |
| 102 | + (printf "Please set ~a to a suitable email address or URL.~n" contact-info-file)]) |
| 103 | + (unless (eq? 'y (y-or-n "Set interactively now?" 'y)) |
| 104 | + (cond |
| 105 | + [(raw-find-contact-info) |
| 106 | + (file-or-directory-modify-seconds contact-info-file (current-seconds) |
| 107 | + #;fail void) |
| 108 | + (return (void))] |
| 109 | + [else |
| 110 | + (raise-user-error "Could not find contact information")])) |
| 111 | + (define contact-info |
| 112 | + (cond |
| 113 | + [(and (find-executable-path "git") |
| 114 | + (eq? 'y (y-or-n "Use contact information from git?" 'y)) |
| 115 | + (or (contact-info-from-git) |
| 116 | + (begin |
| 117 | + (displayln "Error calling git") |
| 118 | + #f))) |
| 119 | + => values] |
| 120 | + [else |
| 121 | + (displayln "Enter your contact information below:") |
| 122 | + (read-line (current-input-port) 'any)])) |
| 123 | + (when (or (eof-object? contact-info) (string=? "" contact-info)) |
| 124 | + (raise-user-error "No input provided")) |
| 125 | + (displayln "This library will send the following User-Agent to Advent of Code:") |
| 126 | + (printf " ~s~n" (contact-info->user-agent contact-info)) |
| 127 | + (unless (eq? 'y (y-or-n "Is that okay?" 'y)) |
| 128 | + (raise-user-error "Not accepted")) |
| 129 | + (display-to-file contact-info contact-info-file #:exists 'replace) |
| 130 | + (displayln "Contact information set successfully!")))) |
| 131 | + |
| 132 | +(define (guess-interactive?) |
| 133 | + (terminal-port? (current-input-port))) |
| 134 | + |
| 135 | +(define (find-contact-info) |
| 136 | + (when (and (guess-interactive?) |
| 137 | + (older-than-seconds? contact-info-file month-seconds)) |
| 138 | + (refresh-contact-info!)) |
| 139 | + (cond |
| 140 | + [(raw-find-contact-info) => values] |
| 141 | + [else |
| 142 | + (define-logger advent-of-code-racket) |
| 143 | + (log-advent-of-code-racket-error |
| 144 | + "No contact information set. Please set ~a to an email address or URL." |
| 145 | + contact-info-file) |
| 146 | + "unknownlibraryuser@example.com"])) |
| 147 | + |
| 148 | +(define (contact-info-from-git) |
| 149 | + (define git (find-executable-path "git")) |
| 150 | + (define (git-config key) |
| 151 | + (let/ec return |
| 152 | + (parameterize ([current-error-port (open-output-nowhere)]) |
| 153 | + (string-trim |
| 154 | + (with-output-to-string |
| 155 | + (lambda () |
| 156 | + (unless (zero? (system*/exit-code git "config" key)) |
| 157 | + (return #f)))))))) |
| 158 | + (git-config "user.email")) |
| 159 | + |
| 160 | +;; Taken from http-easy |
| 161 | +(begin-for-syntax |
| 162 | + (define this-path (path-only (syntax-source #'here))) |
| 163 | + (define info-ref (get-info/full this-path))) |
| 164 | +(define-syntax (get-lib-version stx) |
| 165 | + (datum->syntax stx (info-ref 'version) stx)) |
| 166 | +(define lib-version (get-lib-version)) |
| 167 | + |
| 168 | +(define (contact-info->user-agent contact-info) |
| 169 | + (call-with-output-bytes |
| 170 | + (lambda (out) |
| 171 | + (fprintf out |
| 172 | + "advent-of-code-racket/~a (~a; racket[~a] ~a; +~a)" |
| 173 | + lib-version |
| 174 | + (system-type 'os) |
| 175 | + (case (system-type 'vm) |
| 176 | + [(chez-scheme) 'CS] |
| 177 | + [else 'BC]) |
| 178 | + (version) |
| 179 | + contact-info)))) |
0 commit comments