├── go ├── 3-meme ├── go └── meme.rb ├── .gitignore ├── 5-twilio ├── config.ru ├── Gemfile ├── heroku-spawn ├── Gemfile.lock └── app.rb ├── 4-facetime ├── run ├── facetime.rb └── recognize.rb ├── 2-vagrant ├── cookbooks │ └── run │ │ └── recipes │ │ └── default.rb └── Vagrantfile ├── config └── README.md ├── 1-earthquake └── quake └── README.md /go: -------------------------------------------------------------------------------- 1 | ./1-earthquake/quake 2 | -------------------------------------------------------------------------------- /3-meme/go: -------------------------------------------------------------------------------- 1 | ruby meme.rb -p 2424 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vagrant 2 | boxes 3 | config/* 4 | !config/README.md 5 | -------------------------------------------------------------------------------- /5-twilio/config.ru: -------------------------------------------------------------------------------- 1 | require 'app' 2 | run Sinatra::Application 3 | -------------------------------------------------------------------------------- /5-twilio/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | gem "sinatra" 3 | gem "twilio" 4 | -------------------------------------------------------------------------------- /4-facetime/run: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ruby 4-facetime/facetime.rb & macruby 4-facetime/recognize.rb 3 | -------------------------------------------------------------------------------- /2-vagrant/cookbooks/run/recipes/default.rb: -------------------------------------------------------------------------------- 1 | # install curl 2 | package "curl" 3 | 4 | # Hit our network on port 2424 to kick off the quake 5 | execute "curl `curl http://icanhazip.com`:2424/quake" 6 | -------------------------------------------------------------------------------- /4-facetime/facetime.rb: -------------------------------------------------------------------------------- 1 | number = File.read(File.expand_path("config/facetime-number")) 2 | `open facetime://#{number}` 3 | 4 | require 'rubygems' 5 | require 'appscript' 6 | 7 | Appscript::app('FaceTime').activate 8 | sleep(3) 9 | Appscript::app('System Events').keystroke("\r") 10 | -------------------------------------------------------------------------------- /5-twilio/heroku-spawn: -------------------------------------------------------------------------------- 1 | path=/tmp/heroku-spawn 2 | 3 | rm -rf $path 4 | mkdir -p $path 5 | cp app.rb $path 6 | cp config.ru $path 7 | cp Gemfile $path 8 | cp Gemfile.lock $path 9 | cp ../config/numbers-to-call $path 10 | cp ../config/twilio-credentials $path 11 | 12 | cd $path 13 | git init . 14 | git add . 15 | git commit -m "trololol" 16 | heroku create 17 | heroku rename vagranception 18 | git push heroku master 19 | curl vagranception.heroku.com/dial 20 | -------------------------------------------------------------------------------- /5-twilio/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | builder (2.1.2) 5 | crack (0.1.8) 6 | httparty (0.7.6) 7 | crack (= 0.1.8) 8 | rack (1.2.2) 9 | sinatra (1.2.0) 10 | rack (~> 1.1) 11 | tilt (< 2.0, >= 1.2.2) 12 | tilt (1.2.2) 13 | twilio (3.0.1) 14 | builder (~> 2.1.2) 15 | httparty (~> 0.7.4) 16 | 17 | PLATFORMS 18 | ruby 19 | 20 | DEPENDENCIES 21 | sinatra 22 | twilio 23 | -------------------------------------------------------------------------------- /2-vagrant/Vagrantfile: -------------------------------------------------------------------------------- 1 | Vagrant::Config.run do |config| 2 | config.vm.box = "base" 3 | config.vm.box_url = "http://files.vagrantup.com/lucid32.box" 4 | 5 | config.vm.customize do |vm| 6 | vm.memory_size = 728 7 | end 8 | 9 | # mount the entire project in /vagranception 10 | config.vm.share_folder "vagranception", "/vagranception", "../" 11 | 12 | config.vm.provision :chef_solo do |chef| 13 | chef.cookbooks_path = "cookbooks" 14 | chef.add_recipe "run" 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/README.md: -------------------------------------------------------------------------------- 1 | # Config 2 | 3 | This is where all of the config files go. No, I didn't just put my Twilio 4 | credentials in the repo or all of GitHub's personal numbers just chillin around 5 | here. All the crap from `/config` is kept locally and is pulled into scripts 6 | as they need them. 7 | 8 | ## Necessary Files 9 | 10 | These files should be in `/config`: 11 | 12 | - `facetime-number` - the number to FaceTime 13 | - `numbers-to-call` - the numbers we'll bomb over Twilio 14 | - `twilio-credentials` - a YAML file for Twilio: `sid` line, then a line for your `token`. 15 | -------------------------------------------------------------------------------- /4-facetime/recognize.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Modified from: https://gist.github.com/402759 3 | # 4 | # Mr. Aimonetti also gave a more expanded version of this during his 2010 5 | # RubyConf talk, which maybe my subconscious drummed up enough to give me the 6 | # idea for parts of this segment. MAYBE. 7 | # 8 | # In your Speech system preferences, make sure it's listening without keypress, 9 | # and make sure your sound input is correct. 10 | framework 'AppKit' 11 | 12 | app = NSApplication.sharedApplication 13 | 14 | class AppDelegate 15 | def speechRecognizer(sender, didRecognizeCommand:command) 16 | puts "command: #{command}" 17 | system "osascript -e 'tell application \"FaceTime\" to quit'" 18 | exit!(0) 19 | end 20 | end 21 | 22 | recognizer = NSSpeechRecognizer.alloc.init 23 | recognizer.commands = ["ellen", "page", "ellen page"] 24 | recognizer.delegate = AppDelegate.new 25 | recognizer.ListensInForegroundOnly = false 26 | recognizer.startListening 27 | 28 | app.run 29 | -------------------------------------------------------------------------------- /1-earthquake/quake: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # curls the last quakes in the world and re-checks it every five seconds to see 4 | # if anything else happens. 5 | # 6 | # POSSIBLE BUG: if it's a really huge earthquake that rips the Earth's crust 7 | # and/or the fabric of space-time in half, this script could be interrupted 8 | # prior to entering the next loop (perhaps my living room just exploded, for 9 | # example). 10 | 11 | url='http://earthquake.usgs.gov/earthquakes/catalogs/eqs1hour-M0.txt' 12 | chsum1=`curl --silent $url | sed -n 2p | md5` 13 | chsum2=$chsum1 14 | 15 | while [ 1 ] 16 | do 17 | chsum2=`curl --silent $url | sed -n 2p` 18 | if [ "$chsum2" != "" ] 19 | then 20 | chsum2=`echo $chsum2 | md5` 21 | if [ "$chsum1" == "$chsum2" ] 22 | then 23 | echo "None yet! We're still alive!" 24 | else 25 | echo "Holy shit! A fucking earthquake!" 26 | say "Holy shit! A fucking earthquake!" 27 | cd 2-vagrant && vagrant up 28 | exit 0 29 | fi 30 | else 31 | echo "None yet! We're still alive!" 32 | fi 33 | sleep 5 34 | done -------------------------------------------------------------------------------- /3-meme/meme.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'sinatra' 3 | `rm -f /tmp/quake-meme.jpg` 4 | 5 | get "/" do 6 | if File.exist?('/tmp/quake-meme.jpg') 7 | fork { sleep(10) && `cd .. && 4-facetime/run` } 8 | s = < 10 | 11 | quake 12 | 13 | 14 | 15 | 16 | 17 | HTMLLOL 18 | 19 | else 20 | 21 | < 23 | 24 | quake 25 | 26 | 31 | 32 | 33 | 34 | 35 | HTMLLOL 36 | 37 | end 38 | end 39 | 40 | # gem install memegen 41 | # https://github.com/cmdrkeene/memegen 42 | get "/quake" do 43 | file = `memegen y_u_no 'EARTH' 'Y U NO STAY STILL'`.chomp 44 | system "mv #{file} /tmp/quake-meme.jpg" 45 | end 46 | 47 | get "/quake.jpg" do 48 | send_file '/tmp/quake-meme.jpg', :type => 'image/jpeg', :disposition => 'inline' 49 | end 50 | -------------------------------------------------------------------------------- /5-twilio/app.rb: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'bundler/setup' 3 | require 'yaml' 4 | 5 | require 'sinatra' 6 | require 'twilio' 7 | 8 | yaml = YAML::load(File.open("twilio-credentials")) 9 | 10 | Twilio.connect(yaml['sid'], yaml['token']) 11 | 12 | class Helper 13 | def self.numbers 14 | File.open("numbers-to-call", "rb").read.split("\n") 15 | end 16 | 17 | def self.text 18 | [ 19 | "Ellen Page once scaled a single memcached instance up to overthrow a Middle East dictatorship. Is Ellen Page the best hire for GitHub?", 20 | "Ellen Page is writing a Node.js interface to Git. Should GitHub hire Ellen Page?", 21 | "Ellen Page forked Mislav's will-paginate and called it Ellen Page-in-ate. Should Mislav accept the pull request?", 22 | "Ellen Page animates GIFs with her bare hands while listening to Daft Punk at tumultuous volumes. Should GitHub steal her?", 23 | "Zach Holman is going to make voracious love to Ellen Page. I just wanted to let you know.", 24 | "Even though Chris Wanstrath has flowing locks of gorgeous hair, Ellen Page's hair has unicorns in it. Should GitHub hire Ellen Page as our new CEO?", 25 | "I just wanted to ask you: should GitHub hire Ellen Page? Just think about it. She knows all about Pull Requests and about git re re re.", 26 | "Boy, if GitHub hired Ellen Page, they could probably IPO at 30 billion dollars. Groupon don't know about it." 27 | ].shuffle.first 28 | end 29 | end 30 | 31 | get '/' do 32 | "hi" 33 | end 34 | 35 | get '/dial' do 36 | Helper.numbers.each do |number| 37 | puts "_NUMBER: #{number}" 38 | Twilio::Call.make(yaml['number'], number, 'http://vagranception.heroku.com/response', :timelimit => 30) 39 | end 40 | "trololol?" 41 | end 42 | 43 | post '/response' do 44 | Twilio::Verb.new do |v| 45 | text = Helper.text 46 | puts "_TEXT: #{text}" 47 | v.say text 48 | v.record 49 | end.response 50 | end 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # V A G R A N C E P T I O N 2 | 3 | This is the convoluted source code to my screencast about Vagrant. You should 4 | probably [watch it first][screencast]. You may want to sit down for this. 5 | 6 | ## This 7 | 8 | Just to point this out: most of this code is just for illustrative purposes. 9 | It's not great code, but it is, well, code. There is some cool stuff in there 10 | though. 11 | 12 | ## The Process 13 | 14 | As a recap from the screencast: 15 | 16 | - Mac #1 runs [`quake`][quake], a small shell script to monitor earthquakes 17 | - Once a quake happens, that kicks off a Vagrant build 18 | - That Vagrant build creates a VM that curls a URL running on Mac #2 19 | - Mac #2 routes the request to a tiny Sinatra app that builds a new meme using 20 | [memegen][memegen] 21 | - The meme, when loaded, kicks off a [FaceTime call][facetime] 22 | - Mac #2's FaceTime process uses MacRuby to monitor for the keywords "Ellen" or 23 | "Page". When that happens, it (should) curl a URL to Mac #1 (although this 24 | didn't, well, happen). 25 | - Mac #1 gets the request and [spins up a fresh Heroku instance][heroku] 26 | - That Heroku instance talks to Twilio and dials everyone in the world ever 27 | 28 | ## Config 29 | 30 | There's a decent amount API keys, phone numbers, and stuff like that to set up; 31 | that's (mostly) all in the [config README][readme]. 32 | 33 | ## lol 34 | 35 | Don't take this too seriously; I didn't. It's fun to hack around sometimes 36 | though, no? 37 | 38 | [screencast]: http://zachholman.com/screencast/vagranception 39 | [quake]: https://github.com/holman/vagranception/blob/master/1-earthquake/quake 40 | [vagrant]: https://github.com/holman/vagranception/tree/master/2-vagrant 41 | [meme]: https://github.com/holman/vagranception/blob/master/3-meme/meme.rb 42 | [memegen]: https://github.com/cmdrkeene/memegen 43 | [facetime]: https://github.com/holman/vagranception/tree/master/4-facetime 44 | [heroku]: https://github.com/holman/vagranception/blob/master/5-twilio/heroku-spawn 45 | [readme]: https://github.com/holman/vagranception/tree/master/config 46 | --------------------------------------------------------------------------------