Full Code of ftrain/anxietybox for AI

master cee928693146 cached
17 files
37.5 KB
9.9k tokens
6 symbols
1 requests
Download .txt
Repository: ftrain/anxietybox
Branch: master
Commit: cee928693146
Files: 17
Total size: 37.5 KB

Directory structure:
gitextract_lviy8w2b/

├── .gitignore
├── README.md
├── anxietybox/
│   ├── .gitignore
│   ├── README.md
│   ├── dot-lein-env
│   ├── project.clj
│   ├── src/
│   │   ├── anxietybox/
│   │   │   ├── bot.clj
│   │   │   ├── data.clj
│   │   │   ├── handler.clj
│   │   │   ├── mail.clj
│   │   │   └── style.clj
│   │   └── resources/
│   │       └── templates/
│   │           ├── activate.mustache
│   │           ├── anxiety.mustache
│   │           └── delete.mustache
│   └── test/
│       └── anxietybox/
│           └── test/
│               └── handler.clj
├── bin/
│   └── anxiety-piper.pl
└── schema.sql

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
*~
#*

================================================
FILE: README.md
================================================
# AnxietyBox

This is the Clojure project code to run anxietybox.com.

This project was discussed on the Reply All podcast in early 2015.
http://gimletmedia.com/episode/the-anxiety-box/

It runs at http://anxietybox.com.

## TODO

This thing is a hacky mess; it was my first attempt to get something
working on a website using Clojure. It worked, but it's badly
organized and undocumented. It's also tiny, and since I created it I
wrote probably 20,000 more lines of Clojure/Clojurescript, so it
should be possible to refactor.


## Prerequisites

You will need [Leiningen][1] 1.7.0 or above installed.

[1]: https://github.com/technomancy/leiningen

## Running

To start a web server for the application, run:

    lein ring server

## License

Copyright © 2014 Paul Ford


================================================
FILE: anxietybox/.gitignore
================================================
/target
/lib
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port


================================================
FILE: anxietybox/README.md
================================================
# anxietybox

FIXME

## Prerequisites

You will need [Leiningen][1] 1.7.0 or above installed.

[1]: https://github.com/technomancy/leiningen

## Running

To start a web server for the application, run:

    lein ring server

## License

Copyright © 2014 FIXME


================================================
FILE: anxietybox/dot-lein-env
================================================
; Remove this line and copy this file to .lein-env, then update the values
{:mailgun-api-key "the-mailgun-api-key"
:log-file "/Users/JohnMcCarthy/anxietybox/logs/clojure.log"
:postgres-database "anxietybox"
:postgres-user "horacepippin"
:postgres-password "dominos"}


================================================
FILE: anxietybox/project.clj
================================================
(defproject anxietybox "0.1.0-SNAPSHOT"
  :description "Code for Anxietybox.com"
  :url "http://anxietybox.com/source"
  :dependencies [[org.clojure/clojure "1.5.1"]
                  [compojure "1.3.1"]
                  [org.clojure/java.jdbc "0.3.6"]
                  [enlive "1.1.5"]
                  [postgresql/postgresql "8.4-702.jdbc4"]
                  [hiccup "1.0.5"]
                  [garden "1.2.5"]
                  [clj-http "1.0.1"]
                  [cheshire "5.4.0"]
                  [com.taoensso/timbre "3.3.1"]
                  [lein-environ "1.0.0"]
                  [environ "1.0.0"]
                                        ; project.clj
                  ]
  :plugins [[lein-ring "0.9.0"]]
  :ring {:handler anxietybox.handler/app}
  :resource-paths ["src/resources"]
  )

;; :profiles {:dev [[javax.servlet/servlet-api "2.5"]
;;                     [ring-mock "0.1.5"]]}


================================================
FILE: anxietybox/src/anxietybox/bot.clj
================================================
(ns anxietybox.bot
  (:require
    [clojure.string :as string]))

(def interrogatories [
                      "a lot of people have done this already."
                      "a lot of the people who succeed here are pretty awful people, so it's better that you don't even try."
                      "can you handle the rejection if you do this?"
                      "do you think anyone will ever take you seriously if you try this?"
                      "don't over-index on success here. Because it might not happen."
                      "have you ever finished anything?"
                      "have you ever made progress on something like that?"
                      "have you ever made something like this work?"
                      "I don't know. Can you do any part of this? Even a small part?"
                      "I don't meant to second-guess you."
                      "I don't want to doubt you, but..."
                      "I doubt it can ever work."
                      "I doubt you can think of a place to get started, can you?"
                      "I doubt you'll make progress here."
                      "I have no idea what success would look like here, well, except that this is obviously not success."
                      "I mean shouldn't it be done by now?"
                      "I'd love to see you prove me wrong here."
                      "I'm guessing this all feels beyond you, which is normal for people who are risking everything without any real strategy."
                      "Is there anything you can do to keep this from being a total disaster?"
                      "we all acknowledge it's a long shot for you even to attempt this."
                      "let's be honest, your track record is pretty weak."
                      "maybe it's better to just put all of this behind you?"
                      "there's no chance you can pull this off, is there?"
                      "let's just hope that past results don't equal future success."
                      "well, where's probably too much attention in life paid to actually doing things, don't you think?"
                      "this problem is a barrier to success, but you can't have it all."
                      "where would you put your chances for success? Zero percent? Greater?"
                      "you might just want to give yourself credit for good intentions."
                      "you probably are used to being at the front of the class, and this is a wake-up call that you're not even in the middle."
                      "you'll probably just screw it up, right?"
                      "you're probably putting too much pressure on yourself to do something good."
                      "you're ready to buckle down, maybe. so that's good. If you do it."
                      "you should at least try."
])

(def offers [
             "In saying"
             "Telling people about how you're going to"
             "Offering that you're going to"
             "Insisting you can"
             "Demanding people believe you when you say that you will"
             "Being so sure you can"
             "Telling yourself that soon you are going to"
             "Being the guy who always says you will"
             ])
(def contemplatives
  ["I was thinking about your attempts to"
   "Let's talk about where you are at with your desire to"
   "A moment to talk about how you want to"
   "Just checking in talk about your thing where you"
   "Wondering about the status with your project to"
   "I don't know if you were serious when you said you wanted to"
   "I want to think about your plan to"
   "I heard you when you talked about how you wanted to"
   "So you want to"
   "You're finally ready to"
   "After what seems like forever you want to"
   "You wake up today and you're expecting to"
   "People are expecting you to"
   "So everyone is really curious to see if you can"
   "We were all talking about how you want to"
   "I understand that it is your goal to"])

(def returns ["Anyway,"
               "So basically,"
               "what it comes down to is,"
               "what I want to know is,"
               "essentially,"
               "okay so"
               "the upshot is"
               "the thing that matters is:"
               "the fact to not here is,"
               "can you tell me: "
               "are you ready to answer: "
               "look at it like this:"
               "consider--"
               "reflect:"
               "tell me,"
               "drop a line,"
               "keep me in the loop,"
               "I'm curious,"
               "I'd be interested in hearing your take,"
               "I'm all ears--"
               "let me know: "
               "inform me: "])

(def call-to-action [
                     "what do you think?"
                     "what is your take?"
                     "can you do any of this?"
                     "are you ready?"
                     "will you even try?"
                     "is this another one of your \"things\"?"
                     "will you be able to change everyone's minds?"])

(def indicators ["all signs point to you being"
                  "You:"
                  "Your issues in a nutshell: you're"
                  "just relax and stop pretending to be anything other than"
                  "your friends accept you even though you are undeniably"
                  "it's unfair that so many people say you're"
                  "you don't really deserve to be seen as"
                  "no one wants to tell you that you are"
                  "i wish you could accept that you are"
                  "all of your friends were trying to describe you and what came up was:"
                  "people are only tired of you because you are so"
                  "all the people you meet immediately think:"
                  "a tag cloud of your personality would include things like"
                  "i wonder how can you succeed at anything given that you are so"
                  "When I look at you, I think:"
                  "no one can blame you for being born"
                  "you mom and dad would never say anything but they so want to know why you choose to be"
                  "it's clear that you don't know what to do besides be"
                  "i respect that you just live your life and don't care if people think you are"
                  "most of your friends are doing okay, which makes me wonder why are you so"
                  "the only explanation for your career is that you are essentially"
                  "when you meet successful people they think of you as"
                  "Your total lack of self-control has left you"
                  "people pretend to be nice to you but they're thinking:"
                  "when you aren't around your friends say you are"
                  "it's true that you are basically"
                  "history will forget you because history forgets people who are"
                  "unlike successful people you are totally"
                  "compared to you, people who succeed are not so"
                  "unlike you, your 'friends' are not"
                  "the reason no one notices your work is because you are"
                  "the reason you are not famous is because you are"
                  "obviously you would go further in life except for your tendency to be"
                  "you could be successful if you were not"
                  "you might succeed if you didn't seem so"
                  "your parents deserve credit for loving someone so"
                  "compared to the rest of your family you sure are"
                  "compared with everyone else you grew up with you definitely turned out"
                  "Your whole life you've just been"
                  "your friends all say you are"
                  "like most people who procrastinate, you're"
                  "i don't agree with all of the people who say that you are"
                  "your mother always worried that you would turn out"
                  "if your grandparents were here right now they'd tell you that you are"
                  "people on Facebook look at your picture and think:"
                  "the minute you got in the cab your cab driver thought: I'm"
                  "it's a shame that people have painted you as"
                  "it's too bad everyone has decided you are"
                  "it's not entirely on you because people with your background are typically"
                  "it's just totally obvious that you are"
                  "the simple reason you are not happy is that you are"
                  "no doubt in my mind that you are"
                  "people in your neighborhood think of you as"
                  "ask yourself, do you always want to be"
                  "you do have something to offer, but it's too bad you're"
                  "it's hard to be a leader when you're"
                  "relax, there are worse things to be than so"
                  "you are too hard on yourself. I can think of worse ways to be than"
                  "don't beat yourself up for being"
                  "congratulate yourself. Most people would give up if they looked in the mirror and thought:"
                  "you might as well forgive yourself for being so incredibly"
                  "of all humans who ever lived you are the most"])

(def person {:failure ["stupid" "vile" "fake" "soulless" "a bad friend" "childish" "selfish" "a fraud" "incapable of coming up with anything new" "unoriginal" "affected" "a loser" "foolish" "lumpy" "weak" "deeply flawed"  "dishonest" "hypocritical" "slow" "unable to finish anything" "badly prepared" "a terrible communicator"]
              :fear ["inadequate" "not funny" "not smart" "not cool" "not interesting" "boring" "impossible to like" "living in fear" "afraid" "pitiful" "terrified of everything" "perpetually scared" "full of self-hatred" "shaky" "nervous" "afflicted by nerves" "weak-kneed" "deficient" "lacking in empathy" "lacking in intelligence" "devoid of brilliance" "not even clever" "incapable" "incompetent" "unequal
    to the task" "barely tolerable" "deservedly alone" "so easily forgotten" "likely to die soon" "diseased-looking" "at risk of a fatal disease" "a burden on others" "awkward" "socially weird" "hard to talk with"]
              :loathing
              ["poison" "wasteful" "fraudulent"  "like garbage" "cheesy" "fake" "unworthy of saving" "irredeemable" "phony" "faux-intelligent" "a liar" "a cheater" "deviant" "cretinous" "evil-looking" "awful" "unsightly" "unloveable" "lazy" "stupid" "devoid of willpower" "unambitious" "third-rate" "z-list" "boring" "exhausting to know" "predictable" "whiny" "needy"]
              :appearance ["ugly" "hideous" "misshapen" "awkward" "oddly-proportioned" "weird" "strange" "repulsive" "monstrish" "disgusting" "weird-faced" "unsexy" "untouchable" "crooked" "in posession of a weird nose" "undesirable" "strangely repulsive" "unkempt" "slobby" "weird-nosed" "sneaky-looking"]
              })



(def project
  {:failure ["waste of time" "stupid" "meaningless" "pointless" "empty"]})

(def youknow ["you know,"
              "which reminds me,"
              "speaking of that,"
              "not to beat a dead horse,"
              "and to take it a little further,"
              "and on that--"
              "okay, so--"
              "relatedly,"
              "I noticed that,"
              "I saw that,"
              "I made a note that"])

(def action ["you wrote"
             "you told me"
             "you emailed me"
             "you said to me"
             "you said"
             "you made a comment"
             "you impulsively wrote back"])

(def datespan ["not long ago"
               "pretty recently"
               "it wasn't too long ago"
               "the other day"
               "a little while ago"
               "just a little time ago"
               "recently"])


(def person-keys (keys person))

(defn andjoin [s]
  (str (string/join ", " (butlast s))
    (cond
      (> (count s) 2) ", and "
      (= (count s) 2) " and ")
        (last s)))

(defn monster [k m]
  (andjoin (into #{}
             (take (+ 1 (rand-int 2)) (repeatedly
                       (fn [] (rand-nth ((rand-nth k) m))))))))

(defn ucfirst [s]
  (apply str (concat (string/upper-case (first s)) (rest s))))

(defn sentence [s]
  (apply str (ucfirst s) "."))

(defn indicator []
  (rand-nth indicators))

(defn description []
  (monster person-keys person))

(defn crit []
  (apply str (indicator) " " (description)))

(defn ps []
  (sentence (apply str (indicator) " " (description))))

(defn change-tense [anxiety]
  (string/replace anxiety #"my" "your"))
(defn q [s] (str "\"" s "\""))
(defn make-reply [reply]
  (if reply  (str
              "\n\n"
              (sentence
               (str
                (rand-nth youknow)
                " "
                (rand-nth datespan)
                " "
                (rand-nth action)
                ", "
                (q reply)
                "--and "
                (rand-nth youknow)
                " "
                (crit)))
              " ")))

(defn compose [anxiety reply]
  (str
   (sentence (str (rand-nth contemplatives)
                  " "
                  (change-tense anxiety)))
   "\n\n"
   (ucfirst (rand-nth interrogatories))
   " "
   (rand-nth offers)
   " \""
   anxiety
   "\"--"
   (rand-nth interrogatories)
   (make-reply reply)
   (ucfirst (rand-nth returns))
   " "
   (rand-nth interrogatories)
   " "
   (ucfirst (rand-nth returns))
   " "
   (rand-nth call-to-action)))






================================================
FILE: anxietybox/src/anxietybox/data.clj
================================================
(ns anxietybox.data
  (:require
    [environ.core :as env]
    [taoensso.timbre :as timbre]    
    [clojure.java.jdbc :as sql]))

;; Logging prefix
(timbre/refer-timbre)
(timbre/set-config! [:appenders :spit :enabled?] true)
(timbre/set-config! [:shared-appender-config :spit-filename] (env/env :log-file))
;;

(defn uuid
  "Generate a UUID.
   (uuid)
   =>#uuid \"f6411771-a11e-40ed-acc8-a844ca2e59cd\"" 
  [] (java.util.UUID/randomUUID))


(def pg {:subprotocol "postgresql"
          :subname "anxietybox"
          :user (env/env :postgres-user)
          :password (env/env :postgres-password)
          :stringtype "unspecified"})

(defn anxiety-insert
  ""
  [box anxiety]
  (sql/insert! pg "anxiety"
    {:description (second anxiety) :box_id (:id box)}))

(defn box-insert
  [box]
  (log "[box]" box)
  (try
    (let [db-box (first (sql/insert! pg "box" (dissoc box :project)))]
      (doall (map (partial anxiety-insert db-box) (:project box)))
      db-box)
    (catch Exception e e)))

(defn reply-insert [reply]
  (sql/insert! pg "reply" reply))

(defn reply-select [confirm]
  (let [box-id (:id (first (sql/query pg ["select id from box where confirm = ?" confirm])))]
    (sql/query pg ["select * from reply where box_id = ? ORDER BY created_time DESC" box-id])))

(defn reply-select-by-box [box]
  (vec (sql/query pg ["select * from reply where box_id = ? ORDER BY created_time DESC" (:id box)])))

(defn anxiety-select [box]
  (vec (sql/query pg
    ["select * from anxiety where box_id = ?" (:id box)])))

(defn box-relate [box]
  (merge box 
    {:replies (reply-select-by-box box)}
    {:anxieties (anxiety-select box)}))

(defn box-select-by-confirm
  [confirm]
  (prn confirm)
  (box-relate
    (first
      (sql/query pg
        ["select * from box where confirm=?" confirm]))))

(defn box-select
  "Fetch a full record for a box.

   (box-select \"ford@ftrain.com\")

   => {:anxieties [{:description \"finishing my book\", :box_id
   2, :id 1} {:description \"losing weight\", :box_id 2, :id 2}
   {:description \"making friends\", :box_id 2, :id 3}], :active
   true, :confirm #uuid
   \"26ed5e80-ff88-424d-a57b-7e4359ad56bf\", :count 0, :email
   \"ford@ftrain.com\", :name \"Paul\", :id 2, :created_time #inst
   \"2014-03-01T11:55:39.631064000-00:00\"}"
  
  [email]
  (box-relate (first (sql/query pg ["select * from box where lower(email) = lower(?)" email]))))



(defn box-update [box]
  (sql/update! pg "box" (dissoc box :id) ["id=?" (:id box)]))

(defn anxiety-update [anxiety box]
  (sql/insert! pg "anxiety"
    (assoc (dissoc anxiety :id) {:box_id (:id box)})))

(defn toggle-block [code bool]
  (sql/update! pg "box"
    {:active bool :confirm (uuid)}
    ["confirm=?" code]))

(defn box-activate
  [code]
  (toggle-block code true))

(defn box-deactivate
  [code]
  (toggle-block code false))

(defn box-delete
  [code]
  (sql/delete! pg "box" ["confirm=?" code]))

(defn anxiety-enhance [box]
  (merge box {:anxieties (anxiety-select box)}))

(defn boxes-for-update []
  (map anxiety-enhance (sql/query pg ["SELECT * from box where active=?" true])))

;(boxes-for-update)


================================================
FILE: anxietybox/src/anxietybox/handler.clj
================================================
(ns anxietybox.handler
  (:use compojure.core)
  (:require
    [taoensso.timbre :as timbre]    
    [anxietybox.data :as data]
    [anxietybox.style :as style] 
    [anxietybox.bot :as bot]
    [anxietybox.mail :as mail]
    [clojure.string :as string]
    [compojure.handler :as handler]
    [compojure.route :as route]
    [cheshire.core :as cheshire]    
    [hiccup.core :as html]))

;; Logging prefix
(timbre/refer-timbre)
(timbre/set-config! [:appenders :spit :enabled?] true)
;(timbre/set-config! [:shared-appender-config :spit-filename] (env/env :log-file))

(def site-prefix "http://localhost:3000/")

(defn nest
  "Take a key-value map and nest it by splitting on dashes.

   (nest {:type \"Student\" :name-first \"John\" :name-last
   \"Doe\" :class-1 \"English\" :class-2 \"Biology\" :class-3
   \"Gym\"})

   => {:type \"Student\", :class {:3 \"Gym\", :2 \"Biology\", :1
   \"English\"}, :name {:first \"John\", :last \"Doe\"}}"
  [params]
  (reduce (fn [m [ks v]] (assoc-in m ks v)) {}
        (map (fn [[k v]]
               (list (map keyword (string/split (name k) #"-")) v)) params)))

(defn check-params
  "A dumb parameter checker."
  [params]
  (filter identity
    (list
           (if (= (:project-0 params) "") "You need to put something in the anxiety box.")
           (if (not (:project-0 params)) "No anxiety entered.")      
           (if (= (:name params) "") "You need to put something in the full name box.")
           (if (not (:name params)) "No full name.")            
           (if (= (:email params) "") "You need to put something in the email box.")
           (if (not (:email params)) "No email."))))


(defn make-js
  "TK"
  [& params]
(str "$(document).ready(function(){ 

var values=
"
  (if (ffirst params) (cheshire/generate-string ((comp first first first) params) {:pretty true}) {})
  
  ";

$('input.field').bind('focus click', function(s){ 
        $(this).addClass('visited');
        $(this).attr('value','');
}); 

$('#submit').submit(function() {
  $('div#anxieties-wrapper:last-child').delete();
  return true;
});

var no = 0;

var anxieties = ['lose weight', 'eat right', 'make friends', 'find love', 'answer email', 'get intimate', 'go to school', 'ask for money', 'break up', 'meet people', 'speak in public', 'be honest', 'tell mom no'];

function getAnxiety(num) {
  if (num > anxieties.length) {return \"that bad, huh?\"}
  else {return anxieties[num - 1];}
}

function form() {
  $('div#anxieties-wrapper').append(
    $('<input class=\"field\" type=\"text\" name=\"project-' + no++ + '\" value=\"' + getAnxiety(no) + '\"/>').bind('focus click',
       function(s) { 
         $(this).addClass('visited');
         $(this).attr('value',''); 
         if ($(this).is(':last-child')) form();}));}
form();
});
"))

(defn make-page
  "Make an HTML page"
  [title body & params]
  (html/html
   [:html
    [:head
      [:script {:src "//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"}]
      [:style {:type "text/css"} style/css]      
      [:script (make-js params)]
     [:link {:href "http://fonts.googleapis.com/css?family=Gentium+Basic:400,400italic,700,700italic|Alfa+Slab+One|Akronim" :rel "stylesheet" :type "text/css"}] 
     [:meta {:type "charset"}]
     [:title title]
      [:body body
        [:div#footer "An anxiety simulator. &copy;2014 "
          [:a {:href "http://twitter.com/ftrain"} "Paul Ford."]
          " All rights reserved. "]]]]))

;; TK make this templates
(defn make-home [& params]
  (make-page "Anxiety Box"
    (html/html [:div#main
                 [:h1 "What’s in Your" [:br] [:span.sitename "Anxiety Box?"] ]
                 [:h2#quote "&ldquo;"
                   (bot/ps)
                   "&rdquo;"
                   [:nobr
                     [:i "&mdash;"
                       [:a
                         {:href "http://twitter.com/anxietyboxbot"}
                         "@anxietyboxbot" ]]]]
                 [:form
                   {:id "signup" :method "post" :action "/"}
                   [:div#info
                     [:p "Stop making yourself anxious&mdash;that's
                     our job! <b>Fill out the form to the left and
                     leave your anxiety with us.</b>"]
                     
                     [:p "When you're anxious <b>your anxiety spams
                     your mind</b> and leads to a condition known as
                     <b>procrastinatory shame despair</b>."]
                     
                     [:p "We will take over and three or four times
                     a day send you <b>anxious, urgent, deeply
                     upsetting emails</b>. Delete the email (or
                     reply) and <b>POOF! the anxiety goes
                     away</b>. Delete your account any time."]

                     [:p "Relief is here if you want it."]]

                     [:div#form
                       [:h3 "my name is"]
                       [:input.field {:type "text"
                                       :value "First name only"
                                       :name "name"}]
                       [:div.gloss "So that we can personalize the terrible emails."]

                       [:h3 "my email is"]                       
                       [:input.field {:type "text"
                                       :value "your@email.com"
                                       :name "email"}]
                       [:div.gloss "Don't worry, every email you get has a link that lets you delete your account."]
                       [:h3 "my anxiety won't let me"]
                       [:div#anxieties-wrapper]
                       [:div.gloss "Finish my book. Manage my eating. Respond to email. Enter as many as you want."]]
                     [:div.submit-wrapper
                       [:input#submit.submit {:type "submit" :value "Click here to start!"}]]]])
    params))


(defroutes app-routes

  (GET "/" [] (make-home))

  (POST "/" {params :params}
    (let [errors (check-params params)
           id (data/uuid)]
      (info params errors id)
      (if (not= errors ())
        
        (make-page "Anxiety Box: Errors"
          (html/html
            [:div#main        
              [:h1 "Looks like you failed, which is typical of you. Please back up and try again."]
              [:h2 errors]]))

        (let [b (data/box-insert
                  (merge params {:confirm id}))]
              (if (not= (type b) "org.postgresql.util.PSQLException")
                (do
                  (mail/send-confirmation (data/box-insert (merge (nest params) {:confirm id})))
                  (make-page "Anxiety Box: Anxiety received"
                             (html/html
                              [:div#main        
                               [:h1 "Anxiety Received"]
                               [:h2 "We received your anxiety, so at least you are able to fill out a form. Look for a confirmation email from anxiety@anxietybox.com"]])))
                (do
                  (mail/send-reminder (data/box-insert params))
                  (make-page "Anxiety Box: Duplicate account"
                             (html/html
                              [:div#main                          
                               [:h1 "Duplicate account"]
                               [:h2 "You've already signed up. We sent you an account reminder. It has a link that lets you delete your account and start again."]]))))))))

  (GET "/edit/:confirm" [confirm]
    (make-home (data/box-select-by-confirm confirm)))
  
  (GET "/activate/:id" [id]
    (do (let [res (first (data/box-activate id))]
    (make-page "Anxiety Box"
      (html/html [:div#main
                   [:h1 
                     (if (= 1 res) "Activated" "Already activated")]])))))

  (GET "/delete/:id" [id]
    (do (let [res (first (data/box-delete id))]
          (make-page "Anxiety Box: Account deleted"
            (html/html
              [:div#main
                [:h1 "Anxiety Box"]
                [:h2
                (if (= 1 res) "Your account was deleted"
                  "Error: That confirmation has expired.")]])))))

  (GET "/responses" []
    {:headers {"Content-Type" "application/json;charset=UTF-8"}
      :body  (cheshire/generate-string {:statements (take 10 (repeatedly bot/ps))})})

  (GET "/bot" []
    {:headers {"Content-Type" "application/json;charset=UTF-8"}
      :body  (cheshire/generate-string {:statements (take 10 (repeatedly bot/ps))})})

  (GET "/sendmail" []
    {:headers {"Content-Type" "application/json;charset=UTF-8"}
      :body (map mail/send-anxiety (data/boxes-for-update))})


  ; Receives mailgun posts
  (POST "/receive" {params :params} 
        {:headers {"Content-Type" "application/json;charset=UTF-8"}
         :body  (cheshire/generate-string
                 (do (data/reply-insert 
                      {:box_id 
                       (:id  (let [box (data/box-select (:sender params))]
                               (if box box 
                                   (let [conf
                                         (drop 1 (re-find #"/delete/([\S\"]+)" (:body-plain params)))]
                                     (if conf (data/box-select-by-confirm (first conf)))))))
                       :description (:stripped-text params)})))})

  (route/resources "/")
  (route/not-found "Not Found"))

(def app
  (handler/site app-routes))

;(mail/send-anxiety (data/box-select "ford@ftrain.com"))
;(map mail/send-anxiety (data/boxes-for-update))


================================================
FILE: anxietybox/src/anxietybox/mail.clj
================================================
(ns anxietybox.mail
  (:require
    [environ.core :as env]    
    [taoensso.timbre :as timbre]    
    [anxietybox.bot :as bot]    
    [clj-http.client :as client]))

(timbre/refer-timbre)
(timbre/set-config! [:appenders :spit :enabled?] true)
;; TODO put this in env.
(timbre/set-config! [:shared-appender-config :spit-filename] "/Users/ford/Desktop/logs/clojure.log")

(def mailgun-auth ["api" (env/env :mailgun-api-key)])
(def mailgun-site "anxietybox.com")
(def mailgun-uri (str "https://api.mailgun.net/v2/" mailgun-site "/messages"))
(def from-email "Your Anxiety <anxiety@anxietybox.com>")
(def closing "\n\nSincerely,\n\nYour Anxiety\n\nhttp://anxietybox.com // http://twitter.com/anxietyboxbot")

(defn mailgun-send 
  "Post an email to the mailgun gateway."
  [form]
  (info form)
  (client/post mailgun-uri
    {:basic-auth mailgun-auth
      :throw-entire-message? true
      :form-params (merge {:from from-email} form)}))

(defn send-confirmation
  ""
  [box]
  (info box)
  (mailgun-send { :to (:email box)
          :subject "Confirmation requested"
          :text (str "Dear " (:name box) ",
\nYou just signed up for Anxietybox.com. Click here to confirm your email:
\n\thttp://anxietybox.com/activate/" (:confirm box) "
\nIf you didn't sign up, ignore this email." closing)}))

(defn send-reminder [box]
  (mailgun-send { :to (:email box)
          :subject "Account information"
          :text (str "Dear " (:name box) ",
\nClick here to delete your account:
\n\thttp://anxietybox.com/delete/" (:confirm box) "
\nYou can start a new account any time." closing)}))

(defn anxiety-text [box]
  (str "Dear " (:name box) ",\n\n"
    (bot/compose 
      (if (:anxieties box) (:description (rand-nth (:anxieties box))))
      (if (:reply box) (:description (rand-nth (:replies box)))))
    closing
    "\n\nP.S. Click here to delete your account:"
    "\n\thttp://anxietybox.com/delete/"
    (:confirm box) 
    "\nYou can start a new account any time."
    ))

(defn send-anxiety [box]
  (mailgun-send { :to (:email box)
          :subject (bot/ps)
          :text (anxiety-text box)}))


;(send-anxiety (data/box-select "ford@ftrain.com"))





================================================
FILE: anxietybox/src/anxietybox/style.clj
================================================
(ns anxietybox.style
  (:require
    [garden.core :as css]))

(def css
  (let [font "'Gentium Basic', serif"
        title-font "'Alfa Slab One', cursive"
        display-font "'Akronim', cursive"]
    (css/css {:pretty-print? false }
             [:body
              {:margin-left "auto"
               :margin-right "auto"
               :background "red"
               :padding "1em"
               :max-width "800px"

               :font-family font
               :font-size "12pt"
               :text-align "center"}]
             [:a {:color "inherit"}]
             [:h1 :h2
              {:font-family title-font
               :font-weight "normal"
               :margin 0
               :padding 0
               :color "black"
               :text-transform "uppercase"
               :font-size "220%"
               :line-height "175%"
               }]
             [:span.sitename
              {:font-family display-font
               :font-size "300%"
               :z-index "-1"
               :text-transform "uppercase"
               :color "red"
               }]
             [:b {:color "red"}]
             [:h2
              {:font-size "130%"
               :font-family font
               :font-weight "bold"
               :margin "1em 5em 1em 5em"
               :padding 0
               :text-transform "none"
               :line-height "129%"
               :color "#888"
               }]
             [:h3 {:margin 0
                   :padding 0
                   :font-variant "Italic"
                   :font-weight "normal"
                   :color "#88A"
                   }]
             [:div#main
              {
               :padding "1em"
               :border-radius "1em"
               :border-left "20px solid #C00"
               :border-top "20px solid #D00"
               :border-right "20px solid #B00"
               :border-bottom "20px solid #A00"
               :box-shadow "4px 4px 10px 0px rgba(0, 0, 0, 0.9)"
               :background "white"}]
             [:div#info
              { :width "50%"
               :padding "-1em 0 0 0"
               :text-align "left"
               :font-size "13pt"
               :float "right"
          
               }]
             [:div#form
              {:color "#666"
               :padding "0 1em 0 0"
               :margin "0 1em 0 0"
               :width "45%"
               :text-align "right"
               :border-right "1px dotted #ccc"
               :font-style "italic"
               }]
             [:div.gloss
              {:padding ".5em 0 1em 0"
               :line-height "129%"
               :font-size "90%"
               }]
             [:input.field
              { :font-size "12pt"
               :background "#ffe"
               :text-align "right"
                :clear "both"
               :font-family font
               :font-weight "bold"
               :padding ".25em"
               :color "#777"}]
             [:input.visited
              { :background "#fff"
                :color "#222"
                :font-weight "bold"
               }]
             [:div#footer {:margin-top "20px" :text-align "center" :color "pink"}]
             [:div.submit-wrapper
              {:width "100%"
               :padding-top "1em"
               :text-align "center"}
              ]
             [:input.submit
              {:margin-left "auto"
               :margin-right "auto"
               :display "block"
               :font-size "25pt"
               :border "3px solid red"
               :font-family title-font
               :background "red"
               :color "white"
               :border-radius "1em"
               :border-left "4px solid #C00"
               :border-top "4px solid #D00"
               :border-right "4px solid #B00"
               :border-bottom "4px solid #A00"          
               :text-transform "uppercase"
               :padding "0 .5em 0 .5em"}]

             [:input.submit:hover :input.submit:focus
              {:background "#F66"}]
             [:input.submit:active
              {:background "#F66"
               :border-right "4px solid #C00"
               :border-bottom "4px solid #D00"
               :border-left "4px solid #B00"
               :border-top "4px solid #A00"          
               }]      
             )))


================================================
FILE: anxietybox/src/resources/templates/activate.mustache
================================================
Dear {{fullname}},


You just signed up for Anxietybox.com. Click here to confirm your email:

http://anxietybox.com/activate/{{confirm}}

If you didn't sign up, ignore this email.
    
Best wishes,

Your Anxiety


================================================
FILE: anxietybox/src/resources/templates/anxiety.mustache
================================================
Dear {{name}},

I've been thinking a lot about your goal of "{{project}}."

I don't think you'll ever make any progress. It's pretty much
hopeless. 

Can you name ONE THING you have ever finished? Think about it.  You've
never finished anything, as far as I can tell.

Just prove me wrong by telling me about ONE THING that you completed
and how you did it. No one has every been worse at finishing things
than you have.

Sincerely,

Anxiety

Delete your account: http://anxietybox.com/delete/{{confirm}}


================================================
FILE: anxietybox/src/resources/templates/delete.mustache
================================================
<html>
  <body>
    <p>Dear {{fullname}},</p>
    
    <p><a href="http://anxietybox.com/delete/{{confirm}}">Click here to delete your account (you can start a new account any time)</a>
    http://localhost:3000/delete/{{confirm}}</p>
  </body>
</html>


================================================
FILE: anxietybox/test/anxietybox/test/handler.clj
================================================
(ns anxietybox.test.handler
  (:use clojure.test
        ring.mock.request  
        anxietybox.handler))

(deftest test-app
  (testing "main route"
    (let [response (app (request :get "/"))]
      (is (= (:status response) 200))
      (is (= (:body response) "Hello World"))))
  
  (testing "not-found route"
    (let [response (app (request :get "/invalid"))]
      (is (= (:status response) 404)))))

;; (defn bootstrap []
;;   (do (db/box-insert {:fullname "Paul Ford"
;;                     :email "ford@ftrain.com"
;;                     :project "The Secret Lives of Web Pages"
;;                     :confirm (db/uuid)
;;                     })))

;; (bootstrap)


================================================
FILE: bin/anxiety-piper.pl
================================================
#!/bin/env perl
$email = "";
while (<STDIN>) { 
    die "TOO MANY LINES" if 1000 < $x++; 
    $email .= $_;
}
$email =~ s/'/\\'/;
$code = "215b7fa466431ae07ac879aad6ba7576";
    
open (F, "|curl -F'auth=$code&email=$email' --url http://anxietybox.com/reply");


================================================
FILE: schema.sql
================================================
DROP TABLE IF EXISTS account CASCADE;
DROP TABLE IF EXISTS anxiety CASCADE;
DROP TABLE IF EXISTS reply CASCADE;

DROP TABLE IF EXISTS accounts CASCADE;
DROP TABLE IF EXISTS anxieties CASCADE;
DROP TABLE IF EXISTS replies CASCADE;

CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

-- The account of a user
CREATE TABLE accounts (
       created_time TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
       id SERIAL PRIMARY KEY,
       name VARCHAR(100) NOT NULL,
       email VARCHAR(100) UNIQUE NOT NULL,
       count INT NOT NULL DEFAULT 0,
       confirm uuid DEFAULT UUID_GENERATE_V4(),
       active BOOLEAN DEFAULT FALSE
);

CREATE UNIQUE INDEX lower_email_index ON accounts (lower(email));
CREATE UNIQUE INDEX confirm_index ON accounts (confirm);

-- Track anxieties
CREATE TABLE anxieties (
       id SERIAL PRIMARY KEY,
       account_id int REFERENCES accounts(id) ON DELETE CASCADE,
       tracker uuid DEFAULT UUID_GENERATE_V4(),
       description text UNIQUE NOT NULL
);

CREATE UNIQUE INDEX track_index ON accounts (confirm);

-- Track replies
CREATE TABLE replies (
       created_time TIMESTAMP WITHOUT TIME ZONE DEFAULT NOW(),
       id SERIAL PRIMARY KEY,
       account_id int REFERENCES accounts(id),
       anxiety_id int REFERENCES anxieties(id) ON DELETE CASCADE,
       description text
);
Download .txt
gitextract_lviy8w2b/

├── .gitignore
├── README.md
├── anxietybox/
│   ├── .gitignore
│   ├── README.md
│   ├── dot-lein-env
│   ├── project.clj
│   ├── src/
│   │   ├── anxietybox/
│   │   │   ├── bot.clj
│   │   │   ├── data.clj
│   │   │   ├── handler.clj
│   │   │   ├── mail.clj
│   │   │   └── style.clj
│   │   └── resources/
│   │       └── templates/
│   │           ├── activate.mustache
│   │           ├── anxiety.mustache
│   │           └── delete.mustache
│   └── test/
│       └── anxietybox/
│           └── test/
│               └── handler.clj
├── bin/
│   └── anxiety-piper.pl
└── schema.sql
Download .txt
SYMBOL INDEX (6 symbols across 1 files)

FILE: schema.sql
  type accounts (line 12) | CREATE TABLE accounts (
  type lower_email_index (line 22) | CREATE UNIQUE INDEX lower_email_index ON accounts (lower(email))
  type confirm_index (line 23) | CREATE UNIQUE INDEX confirm_index ON accounts (confirm)
  type anxieties (line 26) | CREATE TABLE anxieties (
  type track_index (line 33) | CREATE UNIQUE INDEX track_index ON accounts (confirm)
  type replies (line 36) | CREATE TABLE replies (
Condensed preview — 17 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (42K chars).
[
  {
    "path": ".gitignore",
    "chars": 5,
    "preview": "*~\n#*"
  },
  {
    "path": "README.md",
    "chars": 773,
    "preview": "# AnxietyBox\n\nThis is the Clojure project code to run anxietybox.com.\n\nThis project was discussed on the Reply All podca"
  },
  {
    "path": "anxietybox/.gitignore",
    "chars": 89,
    "preview": "/target\n/lib\n/classes\n/checkouts\npom.xml\npom.xml.asc\n*.jar\n*.class\n/.lein-*\n/.nrepl-port\n"
  },
  {
    "path": "anxietybox/README.md",
    "chars": 260,
    "preview": "# anxietybox\n\nFIXME\n\n## Prerequisites\n\nYou will need [Leiningen][1] 1.7.0 or above installed.\n\n[1]: https://github.com/t"
  },
  {
    "path": "anxietybox/dot-lein-env",
    "chars": 267,
    "preview": "; Remove this line and copy this file to .lein-env, then update the values\n{:mailgun-api-key \"the-mailgun-api-key\"\n:log-"
  },
  {
    "path": "anxietybox/project.clj",
    "chars": 905,
    "preview": "(defproject anxietybox \"0.1.0-SNAPSHOT\"\n  :description \"Code for Anxietybox.com\"\n  :url \"http://anxietybox.com/source\"\n "
  },
  {
    "path": "anxietybox/src/anxietybox/bot.clj",
    "chars": 13740,
    "preview": "(ns anxietybox.bot\n  (:require\n    [clojure.string :as string]))\n\n(def interrogatories [\n                      \"a lot of"
  },
  {
    "path": "anxietybox/src/anxietybox/data.clj",
    "chars": 3114,
    "preview": "(ns anxietybox.data\n  (:require\n    [environ.core :as env]\n    [taoensso.timbre :as timbre]    \n    [clojure.java.jdbc :"
  },
  {
    "path": "anxietybox/src/anxietybox/handler.clj",
    "chars": 9520,
    "preview": "(ns anxietybox.handler\n  (:use compojure.core)\n  (:require\n    [taoensso.timbre :as timbre]    \n    [anxietybox.data :as"
  },
  {
    "path": "anxietybox/src/anxietybox/mail.clj",
    "chars": 2162,
    "preview": "(ns anxietybox.mail\n  (:require\n    [environ.core :as env]    \n    [taoensso.timbre :as timbre]    \n    [anxietybox.bot "
  },
  {
    "path": "anxietybox/src/anxietybox/style.clj",
    "chars": 4360,
    "preview": "(ns anxietybox.style\n  (:require\n    [garden.core :as css]))\n\n(def css\n  (let [font \"'Gentium Basic', serif\"\n        tit"
  },
  {
    "path": "anxietybox/src/resources/templates/activate.mustache",
    "chars": 213,
    "preview": "Dear {{fullname}},\n\n\nYou just signed up for Anxietybox.com. Click here to confirm your email:\n\nhttp://anxietybox.com/act"
  },
  {
    "path": "anxietybox/src/resources/templates/anxiety.mustache",
    "chars": 505,
    "preview": "Dear {{name}},\n\nI've been thinking a lot about your goal of \"{{project}}.\"\n\nI don't think you'll ever make any progress."
  },
  {
    "path": "anxietybox/src/resources/templates/delete.mustache",
    "chars": 253,
    "preview": "<html>\n  <body>\n    <p>Dear {{fullname}},</p>\n    \n    <p><a href=\"http://anxietybox.com/delete/{{confirm}}\">Click here "
  },
  {
    "path": "anxietybox/test/anxietybox/test/handler.clj",
    "chars": 673,
    "preview": "(ns anxietybox.test.handler\n  (:use clojure.test\n        ring.mock.request  \n        anxietybox.handler))\n\n(deftest test"
  },
  {
    "path": "bin/anxiety-piper.pl",
    "chars": 260,
    "preview": "#!/bin/env perl\n$email = \"\";\nwhile (<STDIN>) { \n    die \"TOO MANY LINES\" if 1000 < $x++; \n    $email .= $_;\n}\n$email =~ "
  },
  {
    "path": "schema.sql",
    "chars": 1305,
    "preview": "DROP TABLE IF EXISTS account CASCADE;\nDROP TABLE IF EXISTS anxiety CASCADE;\nDROP TABLE IF EXISTS reply CASCADE;\n\nDROP TA"
  }
]

About this extraction

This page contains the full source code of the ftrain/anxietybox GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 17 files (37.5 KB), approximately 9.9k tokens, and a symbol index with 6 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!