;;;; Copyright 2018 Reddit, Inc. ;;;; ;;;; Permission is hereby granted, free of charge, to any person obtaining a copy of ;;;; this software and associated documentation files (the "Software"), to deal in ;;;; the Software without restriction, including without limitation the rights to ;;;; use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies ;;;; of the Software, and to permit persons to whom the Software is furnished to do ;;;; so, subject to the following conditions: ;;;; ;;;; The above copyright notice and this permission notice shall be included in all ;;;; copies or substantial portions of the Software. ;;;; ;;;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ;;;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ;;;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ;;;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ;;;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ;;;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ;;;; SOFTWARE. (in-package #:reddit) (defparameter *title* (create-scanner "(?s)(?i)(.+?)")) (defparameter *toplevel* (create-scanner "https?://(?:www.)?([^/]*)")) (defun website-stream (url) (third (http-get url))) (defun website-string (url) (let ((in (third (http-get url)))) (with-output-to-string (s) (when in (loop for line = (read-line in nil) while line do (format s "~a~%" line)) (close in)) s))) (defun website-title (url) (log-message* "downloading title for ~a" url) (ignore-errors (register-groups-bind (title) (*title* (website-string url)) (remove #\Newline (remove #\Return title))))) (defun tl-domain (url) (ignore-errors (register-groups-bind (tl-dom) (*toplevel* url) tl-dom))) (defun replace-alist (alist newlist) (remove nil (append newlist (mapcar #'(lambda (param) (unless (assoc (car param) newlist) param)) alist)))) (defun create-url (base params) (with-output-to-string (s) (format s "~a?" base) (loop for param in params for x = 0 then (1+ x) do (when (> x 0) (format s "&")) (format s "~a=~a" (car param) (cdr param))))) (defun minutes (seconds) (floor (/ seconds 60))) (defun hours (seconds) (floor (/ seconds 3600))) (defun days (seconds) (floor (/ seconds 86400))) (defun age-str (date) (multiple-value-bind (usec second minute hour day month year) (decode-time date) (let* ((utime (encode-universal-time second minute hour day month year)) (seconds (- (get-universal-time) utime))) (cond ((< seconds 7199) (format nil "~a minute~:p" (minutes seconds))) ((< seconds 86400) (format nil "~a hour~:p" (hours seconds))) (t (format nil "~a day~:p" (days seconds))))))) (defun sanitize (in type &optional valid-inputs) (when in (case type (int (parse-integer in :junk-allowed t)) (string (if valid-inputs (when (member in valid-inputs :test #'string=) in) in)) (sym (let ((ustr (intern (string-upcase in) :keyword))) (if valid-inputs (when (member ustr valid-inputs) ustr) ustr)))))) (defun add-rlist (val lst size) (if (member val lst) lst (cons val (subseq lst 0 (1- size))))) ;;user url (defparameter *user-url* "/user/([^/]+)/?([^/]*)") (defun decode-user-url (url) (register-groups-bind (name fn) (*user-url* url) (values name fn))) ;;cookies (defun 2weeks () (+ (get-universal-time) 1209600)) (defun -2weeks () (- (get-universal-time) 1209600)) (defun 5minutes () (+ (get-universal-time) 300)) (defun set-cookie-list (name lst) (set-cookie name :value (format nil "~{~a~^:~}" lst) :expires (2weeks))) (defun add-to-cookie-list (val name lst) (let ((lst (if (member val lst) lst (cons val lst)))) (set-cookie-list name lst) lst)) (defun get-cookie-list (name) (mapcar #'(lambda (val) (sanitize val 'int)) (split ":" (cookie-in name)))) ;;ny times link (defparameter *userland* "rssuserland") (defparameter *goodnytimes* "(?s)