├── setup.sh ├── Gemfile ├── .gitignore ├── .gitdown ├── Gemfile.lock ├── arduino_gitdown └── arduino_gitdown.ino ├── README.md └── commit-msg /setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/sh 2 | 3 | cp commit-msg .git/hooks/ 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'serialport' 4 | gem 'tumblr_client' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | .bundle 4 | .config 5 | coverage 6 | InstalledFiles 7 | lib/bundler/man 8 | pkg 9 | rdoc 10 | spec/reports 11 | test/tmp 12 | test/version_tmp 13 | tmp 14 | 15 | # YARD artifacts 16 | .yardoc 17 | _yardoc 18 | doc/ 19 | 20 | .gitdown 21 | *.swp 22 | *.swo 23 | -------------------------------------------------------------------------------- /.gitdown: -------------------------------------------------------------------------------- 1 | # config file for gitdown 2 | 3 | # possible modes: "normal", "krunk", "baller", "math" 4 | mode: normal 5 | 6 | # change this to the device name of your Arduino's USB connection 7 | # (this can be found easily in the Arduino IDE) 8 | port: /dev/tty.usbmodem1421 9 | 10 | verbose: true 11 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | faraday (0.8.7) 5 | multipart-post (~> 1.1) 6 | faraday_middleware (0.9.0) 7 | faraday (>= 0.7.4, < 0.9) 8 | json (1.7.7) 9 | multipart-post (1.2.0) 10 | oauth (0.4.7) 11 | serialport (1.1.0) 12 | tumblr_client (0.7.1) 13 | faraday (>= 0.8) 14 | faraday_middleware (>= 0.8) 15 | json 16 | oauth 17 | 18 | PLATFORMS 19 | ruby 20 | 21 | DEPENDENCIES 22 | serialport 23 | tumblr_client 24 | -------------------------------------------------------------------------------- /arduino_gitdown/arduino_gitdown.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Sample DrinkShield Sketch 3 | * 4 | * Used for simple standalone usuage of the drinkshield. 5 | * Records results at all times (no ready lights) 6 | * 7 | * Craig Smith 8 | */ 9 | 10 | #include "DrinkShield.h" 11 | 12 | //This is the ratio of sensor reading to (BAC% * 10) as integer. 13 | //For example, BAC_COEF of 2 means a reading of 4 returns 2 to the computer, 14 | //which corresponds to 0.02% BAC 15 | const float BAC_COEF = 5; //for now, no calibration 16 | 17 | unsigned long time; 18 | 19 | // 0.1 is the version written on the DrinkShield board 20 | DrinkShield ds(0,1); 21 | 22 | void setup() 23 | { 24 | // Take 20 air samples when we first turn on the system 25 | ds.autocalibrate(20); 26 | // Turn on the Ready light and turn off the rest 27 | ds.greenLight(OFF); 28 | ds.redLight(ON); 29 | ds.lightBarLevel(0, 0); 30 | 31 | Serial.begin(9600); 32 | } 33 | 34 | void loop() 35 | { 36 | int reading = 0; 37 | int bac = 0; 38 | int max_bac = 0; 39 | 40 | if(Serial.available() > 0){ //only breathalyze when the computer asks us to 41 | 42 | delay(50); //wait for characters to come down the line 43 | 44 | //for now we don't care about what gets sent, just eat up all the bytes 45 | while(Serial.available() > 0){ 46 | Serial.read(); 47 | } 48 | 49 | ds.greenLight(ON); 50 | ds.redLight(OFF); 51 | 52 | time = millis(); 53 | //do 10 readings/sec for 5 seconds 54 | while(millis() < (time + 5000)){ 55 | 56 | //these two lines will be changed once we figure out some calibration 57 | //val should be getReading, and light bars set accordingly 58 | reading = ds.getReading(); 59 | bac = reading / BAC_COEF; 60 | 61 | ds.lightBarLevel(min(bac, 11), 0); 62 | 63 | //overwrite the max reading if necessary 64 | if(bac > max_bac){ 65 | max_bac = bac; 66 | } 67 | 68 | //wait 100ms until next reading 69 | delay(100); 70 | } 71 | 72 | Serial.println(max_bac); //return max reading from 8 sec period 73 | 74 | ds.greenLight(OFF); 75 | ds.redLight(ON); 76 | ds.lightBarLevel(0,0); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gitdown 2 | ======= 3 | 4 | ![example of gitdown aborting a commit](http://geoffreylitt.com/files/gitdown-screenshot.png) 5 | 6 | __gitdown__ is a script which moderates your git committing activity based on your blood alcohol content (BAC). It uses the [DrinkShield](http://www.gfxhax.com/drinkshield/) for Arduino as a breathalyzer, and a Ruby script as the git hook. 7 | 8 | It was built for hackNY's spring 2013 hackathon by Alex Qin (@alexqin) and Geoffrey Litt (@geoffreylitt), and won first place there. 9 | 10 | You can watch a demo video of gitdown in action at [this Youtube link](http://www.youtube.com/watch?v=NnBb1wmHj5k). 11 | 12 | Modes 13 | ------ 14 | gitdown has three modes of operation: 15 | 16 | - __Normal mode__: Only lets you commit with a BAC of less than 0.05%. 17 | - __Krunk mode__: Only lets you commit with a BAC of _greater_ than 0.05%. 18 | - __Ball(m)er mode__: Only lets you commit with a BAC of [between 0.13% and 0.15%](http://xkcd.com/323/). 19 | 20 | In Krunk mode and Baller mode, gitdown will mangle your commit messages to make them appear more intoxicated, and will also post your name, current BAC, and the commit message to the official [gitdown Tumblr](http://gitdownhackny.tumblr.com), with a silly related photo attached. 21 | 22 | When the "verbose" configuration parameter is turned on as described below, gitdown also gives you helpful facts about your current level of impairment, based on your BAC. 23 | 24 | Usage 25 | ----- 26 | First, clone the repo and install necessary gems: 27 | 28 | git clone https://github.com/noidontdig/gitdown.git 29 | cd gitdown 30 | bundle install 31 | 32 | To use the script as a hook for the gitdown repo itself, copy it to the proper location: 33 | 34 | cp commit-msg .git/hooks/ 35 | 36 | Using this project requires an Arduino Uno with the [DrinkShield](http://www.gfxhax.com/drinkshield/) attachment. Open the `arduino_gitdown/arduino_gitdown.ino` file with the Arduino IDE and load it onto the Arduino. 37 | 38 | Then, open the `.gitdown` config file and edit the three configuration parameters: 39 | - __mode__: This should be set to either "normal", "krunk", or "baller", corresponding to the modes described above. 40 | - __port__: This should be the device name of the Arduino's USB connection on your machine. This can be found from the Arduino IDE's "Serial Port" Menu. The default value is `/dev/tty.usbmodem1421`, which happened to work on the Macbook Air used for development. 41 | - __verbose__: Set to "true" or "false", depending on whether you want the system to output helpful information about your current BAC level. 42 | 43 | Then, you can try editing a file in the gitdown repo and committing the change, to see the script in action. 44 | 45 | echo "random change" >> commit-msg 46 | git commit -a -m "messing up the repo" 47 | 48 | Depending on the active mode and your BAC, the script will either permit you to or prevent you from committing! 49 | 50 | If you want to actually use gitdown in another repository, copy the `commit-msg` script to that repo's `.git/hooks` directory, and make sure to copy the `.gitdown` config file to that repository as well. 51 | 52 | -------------------------------------------------------------------------------- /commit-msg: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # Git hook for breathalyzing before commmitting 4 | 5 | require 'serialport' 6 | require 'io/console' 7 | require 'tumblr_client' 8 | 9 | CONFIG = ".gitdown" 10 | 11 | NORMAL = 0.05 12 | BALLMER_MIN = 0.13 13 | BALLMER_MAX = 0.15 14 | 15 | mode_descriptions = {"normal" => "You best not be tipsy. Not even one bit.", 16 | "krunk" => "If you ain't WASTED, you ain't COMMITTIN'", 17 | "baller" => "BALL(m)ER PEAK: A narrow spike of blood alcohol content (0.13% - 0.15%) leading to high programming productivity.", 18 | "math" => "A hobbled mode for those who haven't acquired their DrinkShield yet. Answer math problems to gain commit privileges."} 19 | 20 | VOWELS = ['a', 'e', 'i', 'u', 'o', 'y', 'A', 'E', 'I', 'O', 'U', 'Y'] 21 | 22 | TUMBLRHOST = "gitdownhackny.tumblr.com" 23 | API_KEY = "ZDJOwTnJd3SnXlGWjo8NyFORqcyc7wyjg0pAMIAHO2shdw9S6N" 24 | API_SECRET = "p8R3nxfAG9QvOUCoOPmZz25N5YEnufwSdPt7aJ6gQDgfkdzqds" 25 | OAUTH_TOKEN = "yyGjyA0zXPVSfccadcotOzjyTZchB3T8njx1hlDHx5Y7Mt8dup" 26 | OAUTH_TOKEN_SECRET = "MLcD5STnDdhXrmBoKTHFZCJlEEOsiLmFmqIgPXc5FwmNTkfVlH" 27 | 28 | IMAGES = ['http://www.societyofrobots.com/images/programming_PID_drunkdog.jpg', 29 | 'http://24.media.tumblr.com/49863e872d499205a9960d87cdbc0e8e/tumblr_mkvzhgpYSS1s7qixko1_500.gif', 30 | 'http://i.imgur.com/ueVZc.jpg', 31 | 'http://3.bp.blogspot.com/-FYIrCemUVwY/TxWlcgw1J5I/AAAAAAAAAY8/NfOS5aiHyzA/s1600/beer-drinker.jpg', 32 | 'http://25.media.tumblr.com/tumblr_ljgx4s9l4x1qipm4jo1_500.jpg', 33 | 'http://img514.imageshack.us/img514/2074/drunk0124kj.jpg', 34 | 'http://farm1.staticflickr.com/178/452713612_01ecbce27a_z.jpg'] 35 | 36 | BALLER_IMAGE = 'http://scm-l3.technorati.com/11/01/31/25951/steve-ballmer.jpg' 37 | 38 | Tumblr.configure do |config| 39 | config.consumer_key = API_KEY 40 | config.consumer_secret = API_SECRET 41 | config.oauth_token = OAUTH_TOKEN 42 | config.oauth_token_secret = OAUTH_TOKEN_SECRET 43 | end 44 | 45 | mode = "" 46 | port_str = "" 47 | verbose = false 48 | if File.exist?(CONFIG) 49 | File.open(CONFIG).each do |line| 50 | if line =~ /^mode.*/ 51 | mode = line.split(':')[1].strip 52 | elsif line =~ /^port.*/ 53 | port_str = line.split(':')[1].strip 54 | elsif line =~ /^verbose.*/ 55 | if line.split(':')[1].strip.downcase == 'true' 56 | verbose = true 57 | else 58 | verbose = false 59 | end 60 | end 61 | end 62 | else 63 | puts "No .gitdown file found" 64 | exit 1 65 | end 66 | 67 | if mode.empty? or port_str.empty? 68 | puts ".gitdown file contains errors." 69 | puts "Make sure it contains a mode and port." 70 | exit 1 71 | end 72 | 73 | puts "---------------- Time to GITDOWN!!! ----------------" 74 | puts "You are in #{mode.upcase} mode." 75 | puts mode_descriptions[mode] 76 | puts "------------------------------------------------------" 77 | 78 | #Serial port init variables 79 | baud_rate = 9600 80 | data_bits = 8 81 | stop_bits = 1 82 | parity = SerialPort::NONE 83 | sp = SerialPort.new(port_str, baud_rate, data_bits, stop_bits, parity) 84 | # Taking control of the serial port resets the Arduino. 85 | # Wait 4 seconds for it to start up and autocalibrate 86 | puts "Calibrating breathalyzer sensor..." 87 | sleep(4) 88 | 89 | puts "Prepare to blow into the breathalyzer! Breathalyzing starting in 3 seconds..." 90 | sleep(3) 91 | 92 | puts "Blow blow blow!!!" 93 | 94 | #get bac from breathalizer 95 | sp.write "a" #For the moment, the specific letter sent doesn't matter 96 | sleep(6) #breathalyzer takes 5 seconds to measure, plus 1 for good measure 97 | bac = sp.gets.chomp.to_f / 100.0 98 | puts "\nYour BAC is: #{bac}%.\n" 99 | if verbose 100 | puts "BAC Effect Analysis:" 101 | if bac < 0.03 102 | puts "At this BAC, the average individual appears normal." 103 | elsif bac >= 0.03 and bac < 0.06 104 | puts "At this BAC, you are experiencing mild euphoria, relaxation, and talkativeness. Concentration is impaired." 105 | elsif bac >= 0.06 and bac < 0.09 106 | puts "At this BAC, you are experiencing disinhibition and extroversion. Reasoning, depth perception, and peripheral vision are impaired." 107 | elsif bac >= 0.09 and bac < 0.20 108 | puts "At this BAC, you are experiencing anger or sadness, boisterousness, and decreased libido. Reflexes and motor control are impaired. Slurred speech is possible." 109 | elsif bac >= 0.20 110 | puts "At this BAC, you are experiencing stupor, loss of understanding, and impaired sensations. Loss of consciousness and memory blackout are possible. Don't drink more!" 111 | end 112 | puts "\n" 113 | end 114 | 115 | commit_file = ARGV[0] 116 | message = File.read(commit_file).strip 117 | 118 | case mode 119 | when "normal" 120 | if bac > NORMAL 121 | puts "I'm sorry Dave, I can't let you do that...you're too drunk." 122 | puts "COMMIT ABORTED" 123 | exit 1 124 | else 125 | puts "You are sober. Good job!" 126 | puts "COMMIT SUCCESS" 127 | exit 0 128 | end 129 | when "krunk" 130 | if bac < NORMAL 131 | puts "Sorry, you still have #{NORMAL - bac}%BAC to go until you're properly drunk..." 132 | puts "COMMIT ABORTED" 133 | exit 1 134 | else 135 | client = Tumblr::Client.new 136 | puts "You are drunk! Good job." 137 | puts "COMMIT SUCCESS" 138 | name = `git config user.name` 139 | now = DateTime.now 140 | date = now.strftime("%B %e, %l:%M%p") 141 | title = "#{name} - #{date} - BAC: #{bac}%" 142 | drunk_message = "

#{title}:

\"" 143 | new_commit = "" 144 | message.each_char do |c| 145 | if VOWELS.include?(c) 146 | new_commit += c * Random.rand(2..5) 147 | else 148 | new_commit += c 149 | end 150 | end 151 | drunk_message += "#{new_commit}\"

" 152 | img_url = IMAGES[Random.rand(IMAGES.count - 1)] 153 | drunk_message += "" 154 | File.open(commit_file, 'w') { |f| f.write(new_commit) } 155 | post = client.text(TUMBLRHOST, body: drunk_message) 156 | exit 0 157 | end 158 | when "baller" 159 | if bac >= BALLMER_MIN && bac <= BALLMER_MAX 160 | client = Tumblr::Client.new 161 | name = `git config user.name` 162 | now = DateTime.now 163 | date = now.strftime("%B %e, %l:%M%p") 164 | title = "#{name} (Ballin') - #{date} - BAC: #{bac}%" 165 | drunk_message = "

#{title}:

\"" 166 | new_commit = "HITTING THE BALLMER PEAK YO #{message}augifeoifh" 167 | drunk_message += "#{new_commit}\"

" 168 | img_url = BALLER_IMAGE 169 | drunk_message += "" 170 | File.open(commit_file, 'w') { |f| f.write(new_commit) } 171 | post = client.text(TUMBLRHOST, body: drunk_message) 172 | puts "You are at the Ballmer peak. Awesome!" 173 | puts "COMMIT SUCCESS" 174 | exit 0 175 | else 176 | puts "You're not at the Ballmer peak...get between 0.13% and 0.15% to get in the zone!" 177 | puts "COMMIT ABORTED" 178 | exit 1 179 | end 180 | else 181 | puts "Incorrect mode, check .gitdown file for examples" 182 | exit 1 183 | end 184 | --------------------------------------------------------------------------------