├── .gitignore ├── .travis.yml ├── __tests__ └── Request.js ├── LICENSE.txt ├── package.json ├── Example.js ├── Test.js ├── README.md └── UntappdClient.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.8" 5 | - "0.6" 6 | -------------------------------------------------------------------------------- /__tests__/Request.js: -------------------------------------------------------------------------------- 1 | var sinon = require('sinon') 2 | var PassThrough = require('stream').PassThrough; 3 | var http = require('http') 4 | var UntappdClient = require('../UntappdClient'); 5 | 6 | describe('Requests', () => { 7 | beforeEach(() => { 8 | this.request = sinon.stub(http, 'request'); 9 | }) 10 | 11 | afterEach(() => { 12 | http.request.restore(); 13 | }) 14 | 15 | it('Handles parse errors', () => { 16 | var response = new PassThrough(); 17 | response.write('Something that is not JSON'); 18 | response.end() 19 | 20 | var request = new PassThrough(); 21 | 22 | this.request.callsArgWith(1, response).returns(request) 23 | 24 | var untappd = new UntappdClient(); 25 | untappd.setClientSecret('fake secret') 26 | untappd.setClientId('fake id') 27 | 28 | untappd.beerInfo(function(err, result){ 29 | if(err){ 30 | expect(err).not.toBeNull() 31 | } 32 | }, {BID : 12345}) 33 | }) 34 | }) -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Glen R. Goodwin (@areinet) 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Glen R. Goodwin (http://www.arei.net)", 3 | "name": "node-untappd", 4 | "keywords": [ 5 | "untappd", 6 | "api", 7 | "beer", 8 | "node-untappd", 9 | "untapped", 10 | "brew", 11 | "brewery", 12 | "untapped", 13 | "microbrew", 14 | "micro-brew", 15 | "micro brew", 16 | "craft beer", 17 | "yum", 18 | "drunk", 19 | "drunken", 20 | "trashed", 21 | "lindsey lohan", 22 | "wasted", 23 | "bombed", 24 | "innebriated" 25 | ], 26 | "description": "NodeJS API to the Untappd Service", 27 | "version": "0.6.1", 28 | "homepage": "http://github.com/arei/node-untappd", 29 | "bugs": { 30 | "url": "http://github.com/arei/node-untappd/issues" 31 | }, 32 | "licenses": [ 33 | { 34 | "type": "MIT", 35 | "url": "http://github.com/arei/node-untappd/blob/master/LICENSE.txt" 36 | } 37 | ], 38 | "scripts": { 39 | "test": "./node_modules/.bin/jest", 40 | "test-debug": "node debug --harmony ./node_modules/jest-cli/bin/jest.js --runInBand" 41 | }, 42 | "repository": { 43 | "type": "git", 44 | "url": "git://github.com/arei/node-untappd.git" 45 | }, 46 | "main": "UntappdClient.js", 47 | "engines": { 48 | "node": ">=0.6.11" 49 | }, 50 | "dependencies": {}, 51 | "devDependencies": { 52 | "jest": "^19.0.2", 53 | "jest-cli": "^19.0.2", 54 | "sinon": "^2.1.0" 55 | }, 56 | "optionalDependencies": {} 57 | } 58 | -------------------------------------------------------------------------------- /Example.js: -------------------------------------------------------------------------------- 1 | // An example of how to use the UntappdClient. 2 | // 3 | // By Glen R. Goodwin 4 | // twitter: @areinet 5 | 6 | // Imports 7 | var UntappdClient = require("./UntappdClient",false); 8 | 9 | // Definitions 10 | 11 | // Replace this with your CLIENT ID 12 | var clientId = "[ your api key goes here ]"; 13 | 14 | // Replace this with your CLIENT SECRET 15 | var clientSecret = "[ your client secret goes here ]"; 16 | 17 | // Set to true if you want to see all sort of nasty output on stdout 18 | var debug = false; 19 | 20 | // The user we want to lookup for this example 21 | var data = {}; 22 | data.USERNAME = "[ some user name ]"; 23 | 24 | // Create Client 25 | var untappd = new UntappdClient(debug); 26 | untappd.setClientId(clientId); 27 | untappd.setClientSecret(clientSecret); 28 | 29 | // EXAMPLE - List last 25 recent checkins of the given user 30 | untappd.userActivityFeed(function(err,obj){ 31 | if (debug) console.log(err,obj); 32 | if (obj && obj.response && obj.response.checkins && obj.response.checkins.items) { 33 | var beers = obj.response.checkins.items.forEach(function(checkin) { 34 | console.log(checkin); 35 | console.log(checkin.user.user_name, "drank", checkin.beer.beer_name); 36 | console.log("by", checkin.brewery.brewery_name); 37 | if (checkin.venue.venue_name) 38 | console.log("at", checkin.venue.venue_name); 39 | console.log("on", checkin.created_at); 40 | }); 41 | } else { 42 | console.log(err, obj); 43 | } 44 | }, data); 45 | -------------------------------------------------------------------------------- /Test.js: -------------------------------------------------------------------------------- 1 | // This tests a client connection to untapped. 2 | // We're only doing read-only tests. Anything which would modify you 3 | // can figure out for yourself. 4 | // 5 | // By Glen R. Goodwin 6 | // twitter: @areinet 7 | 8 | // Imports 9 | var UntappdClient = require("./UntappdClient"); 10 | 11 | // Definitions 12 | var clientId = process.env['UNTAPPD_CLIENT_ID'] || ''; 13 | var clientSecret = process.env['UNTAPPD_CLIENT_SECRET'] || ''; 14 | var accessToken = process.env['UNTAPPD_ACCESS_TOKEN'] || ''; 15 | 16 | var debug = false; 17 | 18 | var sampleUser = "arei"; 19 | var beer_id = "1"; 20 | var brewery_id = "1"; 21 | var venue_id = "1"; 22 | var foursquare_id = "4ccf5fec1ac7a1cd6a5c1392"; 23 | 24 | // Handles testing our results 25 | var goodbadResults = function(name) { 26 | return function(err, obj) { 27 | if (debug) console.log(name, err, obj); 28 | if (err || !obj || obj.meta.code>=400 || !obj.response) { 29 | console.log("[ FAIL ] " + name); 30 | console.error("\t" + obj.meta.error_detail); 31 | return; 32 | } 33 | console.log("[ PASS ] " + name); 34 | }; 35 | }; 36 | 37 | console.log("Testing all the READ-ONLY Services for Untappd"); 38 | console.log("----------------------------------------------"); 39 | 40 | // Create Client 41 | var untappd = new UntappdClient(debug); 42 | untappd.setClientId(clientId); 43 | untappd.setClientSecret(clientSecret); 44 | untappd.setAccessToken(accessToken); 45 | 46 | // get the url for getting an oauth token 47 | console.log("[ INFO ] OAUTH Url: "+untappd.getUserAuthenticationURL("http://localhost:3000/auth")); 48 | console.log(""); 49 | 50 | // Test userActivityFeed 51 | untappd.userActivityFeed(goodbadResults("userActivityFeed"),{USERNAME: sampleUser}); 52 | 53 | // Test pubFeed 54 | untappd.pubFeed(goodbadResults("pubFeed"), {lat: 40, lng: 74}); 55 | 56 | // Test venueActivityFeed 57 | untappd.venueActivityFeed(goodbadResults("venueActivityFeed"),{VENUE_ID:venue_id}); 58 | 59 | // Test beerActivityFeed 60 | untappd.beerActivityFeed(goodbadResults("beerActivityFeed"),{BID: beer_id}); 61 | 62 | // Test breweryFeed 63 | untappd.breweryActivityFeed(goodbadResults("breweryActivityFeed"),{BREWERY_ID: brewery_id}); 64 | 65 | // Test userInfo 66 | untappd.userInfo(goodbadResults("userInfo"),{USERNAME: sampleUser}); 67 | 68 | // Test userWishList 69 | untappd.userWishList(goodbadResults("userWishList"),{USERNAME: sampleUser}); 70 | 71 | // Test userFriends 72 | untappd.userFriends(goodbadResults("userFriends"),{USERNAME: sampleUser}); 73 | 74 | // Test userBadges 75 | untappd.userBadges(goodbadResults("userBadges"),{USERNAME: sampleUser}); 76 | 77 | // Test userDistinctBeers 78 | untappd.userDistinctBeers(goodbadResults("userDistinctBeers"),{USERNAME: sampleUser}); 79 | 80 | // Test brewerInfo 81 | untappd.breweryInfo(goodbadResults("brewerInfo"),{BREWERY_ID: brewery_id}); 82 | 83 | // Test beerInfo 84 | untappd.beerInfo(goodbadResults("beerInfo"),{BID: beer_id}); 85 | 86 | // Test venueInfo 87 | untappd.venueInfo(goodbadResults("venueInfo"),{VENUE_ID: venue_id}); 88 | 89 | // Test Beer Search 90 | untappd.beerSearch(goodbadResults("searchBeer"),{q:"Stout"}); 91 | 92 | // Test Brewery Search 93 | untappd.brewerySearch(goodbadResults("searchBrewery"),{q:"Stone"}); 94 | 95 | // Test foursquareVenueLookup 96 | untappd.foursquareVenueLookup(goodbadResults("foursquareVenueLookup"),{VENUE_ID: foursquare_id}); 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-untappd 2 | ------- 3 | NodeJS API to intergrate with [Untappd API](http://untappd.com/api/docs). 4 | 5 | [Untappd](http://untappd.com) is a social beer tracking application for most mobile devices and the greater internet at large. It supports a robust set of features for "checking into" beer as it is consumed including locations, ratings, comments, and social integration. 6 | 7 | This library provides NodeJS with an abstraction to the [Untappd API](http://untappd.com/api/docs) allowing developers to query and integrate Untappd services into their own applications. 8 | 9 | This library nor the authors have any formal relationship with Untappd other than the beer we drink. 10 | 11 | ## Build Status 12 | 13 | [![Build Status](https://secure.travis-ci.org/arei/node-untappd.png)](http://travis-ci.org/arei/node-untappd) 14 | 15 | ## Getting Started 16 | 17 | 1. If you have never done so, sign up for [Untappd](http://untappd.com) and download it to your favorite mobile device. 18 | 2. Have a nice craft beer and make sure to Checkin. 19 | 3. You need to get yourself an Untappd ClientID and ClientSecret. To do so, go complete the [Untappd API Key Form](http://untappd.com/api/register?register=new). 20 | 4. Have more nice craft beer, make more checkins. 21 | 5. Wait for Untappd to email you your key. This takes around two (2) business days. 22 | 6. Download node-untappd: `npm install node-untappd` 23 | 7. Look at the Example, beer is optional but encouraged. 24 | 25 | ## Access Tokens 26 | 27 | Untappd now support OAUTH for most operations and specifically for any operation that writes data to untappd. 28 | 29 | To get an Access Token, you can use our handy OAUTH URL call to get the untappd oauth url, and then use that to get the access token. For more information on getting access tokens, please refer to [Untappd's API Authentication page](http://untappd.com/api/docs/v4#authentication). 30 | 31 | ## The Example code 32 | 33 | The `Example.js` file provides a very simple example for working with UntappdClient. 34 | 35 | To use Example.js... 36 | 37 | 1. Set your CLIENT ID on line 12. 38 | 2. Set your CLIENT SECRET on line 15. 39 | 40 | To run the example: 41 | 42 | node Example.js 43 | 44 | ## Basic Usage 45 | 46 | There are four parts to using node-untappd: 47 | 48 | Import the library: 49 | 50 | var UntappdClient = require("node-untappd"); 51 | 52 | Creating the client: 53 | 54 | var debug = false; 55 | var untappd = new UntappdClient(debug); 56 | 57 | Set your credientials 58 | 59 | var clientId = "[ your api key goes here ]"; // Replace this with your CLIENT ID 60 | var clientSecret = "[ your client secret goes here ]"; // Replace this with your CLIENT SECRET 61 | var accessToken = "[ your access token goes here ]"; // Replace this with an Access Token, Optional 62 | 63 | untappd.setClientId(clientId); 64 | untappd.setClientSecret(clientSecret); 65 | untappd.setAccessToken(accessToken); // Optional 66 | 67 | Executing API calls, for example: 68 | 69 | var data = {}; 70 | data.USERNAME = "[ some user name ]"; 71 | untappd.userActivityFeed(function(err, obj) { 72 | var beers = obj.results.forEach(function(checkin) { 73 | console.log(username, "drank", checkin.beer_name); 74 | console.log("by", checkin.brewery_name); 75 | if (checkin.venue_name) 76 | console.log("at", checkin.venue_name); 77 | console.log("on", checkin.created_at); 78 | }); 79 | }, data); 80 | 81 | ## API Calls 82 | 83 | All of the API calls defined in the [Untappd API](http://untappd.com/api/docs) have been implemented into the UntappdClient. It's a long list, so please look at UntappdClient for usage. 84 | 85 | Each API call takes a callback function as its first argument. Upon a result this function is called with `err` as the first parameter and `obj`, an object of the results, as the second. 86 | 87 | function(err,obj) 88 | 89 | The `err` is only populated if an error occurs, otherwise it is null. 90 | 91 | The `obj` will be populated with the object returned from Untappd upon completion of the call. In some cases where an error occurs, both `err` and `obj` will be populated. 92 | 93 | ## Going beyond the API 94 | 95 | We are providing only the most basic API. Please use it and take it to strange and wonderful new places. Also, please share what you have done with us, any suggestions you have with us, and by all means any bugs you find with us. We are eager to hear from you and, more importantly, score free beer from you. 96 | 97 | If you are using node-untappd somewhere in production, I'd love to share it out. Please let me know! 98 | -------------------------------------------------------------------------------- /UntappdClient.js: -------------------------------------------------------------------------------- 1 | // 2 | // UntappdClient v4 3 | // 4 | // By Glen R. Goodwin 5 | // twitter: @areinet 6 | // 7 | 8 | var QS = require("querystring"); 9 | var HTTPS = require("https"); 10 | var Crypto = require("crypto"); 11 | 12 | var UntappdClient = function(debug) { 13 | var that = this; 14 | 15 | var id, secret, token; 16 | 17 | var setClientId = function(clientId) { 18 | id = clientId; 19 | return that; 20 | }; 21 | that.setClientId = setClientId; 22 | 23 | var getClientId = function() { 24 | return id; 25 | }; 26 | that.getClientId = getClientId; 27 | 28 | var setClientSecret = function(clientSecret) { 29 | secret = clientSecret; 30 | return that; 31 | }; 32 | that.setClientSecret = setClientSecret; 33 | 34 | var getClientSecret = function() { 35 | return secret; 36 | }; 37 | that.getClientSecret = getClientSecret; 38 | 39 | var setAccessToken = function(accessToken) { 40 | token = accessToken; 41 | return that; 42 | }; 43 | that.setAccessToken = setAccessToken; 44 | 45 | var getAccessToken = function() { 46 | return token; 47 | }; 48 | that.getAccessToken = getAccessToken; 49 | 50 | var post = function(path, params, data, callback) { 51 | return req("POST", path, params, data, callback); 52 | }; 53 | 54 | var get = function(path, params, callback) { 55 | return req("GET", path, params, null, callback); 56 | }; 57 | 58 | var req = function(method, path, params, data, callback) { 59 | if (params && params.constructor==="function" && !callback) { 60 | callback = params; 61 | params = {}; 62 | } 63 | if (!params) params = {}; 64 | 65 | var options = { 66 | host: "api.untappd.com", 67 | port: 443, 68 | path: path, 69 | method: method 70 | }; 71 | 72 | if (method == "POST") { 73 | data = QS.stringify(data); 74 | options.headers = { 75 | "Content-Type": "application/x-www-form-urlencoded", 76 | "Content-Length": data.length 77 | }; 78 | } 79 | 80 | Object.keys(params).forEach(function(k) { 81 | if (params[k]===undefined || params[k]===null) delete params[k]; 82 | }); 83 | 84 | if (token) { 85 | params.access_token = token; 86 | } else { 87 | if (id) params.client_id = id; 88 | if (secret) params.client_secret = secret; 89 | } 90 | if (params) options.path += "?" + QS.stringify(params); 91 | 92 | if (debug) console.log("node-untappd: get : " + options.path); 93 | 94 | if (debug){ 95 | console.log("\nRequest"); 96 | console.log(options); 97 | console.log(params); 98 | console.log(data); 99 | } 100 | 101 | var request = HTTPS.request(options, function(response) { 102 | response.setEncoding("utf8"); 103 | var data = ""; 104 | 105 | response.on("data", function(incoming) { 106 | if (debug) console.log("node-untappd: data: ", incoming.length); 107 | data += incoming; 108 | }); 109 | 110 | response.on("end",function(incoming){ 111 | if (debug) console.log("node-untappd: end: ",incoming?incoming.length:0); 112 | data += incoming?incoming:""; 113 | try{ 114 | var obj = JSON.parse(data); 115 | callback.call(that,null,obj); 116 | }catch(e){ 117 | callback.call(that, e); 118 | } 119 | 120 | }); 121 | 122 | response.on("error", function() { 123 | if (debug) console.log("node-untappd: error: ", arguments); 124 | callback.call(that, arguments, null); 125 | }); 126 | 127 | request.on("error", function() { 128 | if (debug) console.log("node-untappd: error: ", arguments); 129 | callback.call(that, arguments, null); 130 | }); 131 | 132 | if(method=="POST") { 133 | request.write(data); 134 | } 135 | 136 | request.end(); 137 | 138 | return request; 139 | }); 140 | }; 141 | 142 | var hasToken = function() { 143 | return !!token; 144 | }; 145 | 146 | var hasId = function() { 147 | return !!id; 148 | }; 149 | 150 | var hasSecret = function() { 151 | return !!secret; 152 | }; 153 | 154 | function validate(param, key) { 155 | var message = key + " cannot be undefined or null."; 156 | return (param) ? null : new Error(message); 157 | } 158 | 159 | function authorized(tokenOnly) { 160 | if (debug) { 161 | console.log(getClientId(), getClientSecret(), getAccessToken()); 162 | } 163 | 164 | tokenOnly = (tokenOnly === undefined) ? false : tokenOnly; 165 | var caller = arguments.callee.caller.name; 166 | 167 | if (tokenOnly && !hasToken()) throw new Error("UntappdClient." + caller + " requires an AccessToken."); 168 | if (!hasToken() && !(hasId() && hasSecret())) throw new Error("UntappdClient." + caller + " requires an AccessToken or a ClientId/ClientSecret pair."); 169 | } 170 | 171 | // OAUTH Stuff 172 | 173 | // We use the basic oauth redirect method from untappd. 174 | // this url can be used in the browser to get the access token 175 | that.getUserAuthenticationURL = function(returnRedirectionURL) { 176 | validate(returnRedirectionURL, "returnRedirectionURL"); 177 | if (!hasId()) throw new Error("UntappdClient.getUserAuthenticationURL requires a ClientId"); 178 | return "https://untappd.com/oauth/authenticate/?client_id="+id+"&response_type=token&redirect_url="+returnRedirectionURL; 179 | }; 180 | 181 | //this is for server-side, Step 1 - OAUTH Authentication 182 | that.getAuthenticationURL = function(returnRedirectionURL) { 183 | validate(returnRedirectionURL, "returnRedirectionURL"); 184 | if (!hasId()) throw new Error("UntappdClient.getUserAuthenticationURL requires a ClientId"); 185 | return "https://untappd.com/oauth/authenticate/?client_id="+id+"&response_type=code&redirect_url="+returnRedirectionURL+"&code=COD"; 186 | }; 187 | 188 | // Step 2 - OATUH Authorization 189 | that.getAuthorizationURL = function(returnRedirectionURL,code) { 190 | validate(returnRedirectionURL, "returnRedirectionURL"); 191 | if (!hasId() || !hasSecret()) throw new Error("UntappdClient.getUserAuthenticationURL requires a ClientId/ClientSecret pair."); 192 | return "https://untappd.com/oauth/authorize/?client_id="+id+"&client_secret="+secret+"&response_type=code&redirect_url="+returnRedirectionURL+"&code="+code; 193 | }; 194 | 195 | // The FEEDS 196 | 197 | // https://untappd.com/api/docs#activityfeed 198 | that.activityFeed = function(callback, data) { 199 | data = data || {}; 200 | validate(callback, "callback"); 201 | authorized(true); 202 | return get("/v4/checkin/recent", data, callback); 203 | }; 204 | 205 | // https://untappd.com/api/docs#useractivityfeed 206 | that.userActivityFeed = function(callback, data) { 207 | data = data || {}; 208 | validate(callback, "callback"); 209 | // username or token 210 | if (!hasToken()) validate(data.USERNAME, "USERNAME"); 211 | authorized(); 212 | return get("/v4/user/checkins/" + (data.USERNAME || ''), data, callback); 213 | }; 214 | 215 | // https://untappd.com/api/docs#theppublocal 216 | that.pubFeed = function(callback, data) { 217 | data = data || {}; 218 | validate(callback, "callback"); 219 | authorized(); 220 | return get("/v4/thepub/local", data, callback); 221 | }; 222 | 223 | // https://untappd.com/api/docs#venueactivityfeed 224 | that.venueActivityFeed = function(callback, data) { 225 | data = data || {}; 226 | validate(callback, "callback"); 227 | validate(data.VENUE_ID, "VENUE_ID"); 228 | authorized(); 229 | return get("/v4/venue/checkins/" + data.VENUE_ID, data, callback); 230 | }; 231 | 232 | // https://untappd.com/api/docs#beeractivityfeed 233 | that.beerActivityFeed = function(callback, data) { 234 | data = data || {}; 235 | validate(callback, "callback"); 236 | validate(data.BID, "BID"); 237 | authorized(); 238 | return get("/v4/beer/checkins/" + data.BID, data, callback); 239 | }; 240 | 241 | // https://untappd.com/api/docs#breweryactivityfeed 242 | that.breweryActivityFeed = function(callback, data) { 243 | data = data || {}; 244 | validate(callback, "callback"); 245 | validate(data.BREWERY_ID, "BREWERY_ID"); 246 | authorized(); 247 | return get("/v4/brewery/checkins/" + data.BREWERY_ID, data, callback); 248 | }; 249 | 250 | // https://untappd.com/api/docs#notifications 251 | that.notifications = function(callback) { 252 | data = data || {}; 253 | validate(callback, "callback"); 254 | authorized(true); 255 | return get("/v4/notifications", null, callback); 256 | }; 257 | 258 | // The INFO / SEARCH 259 | 260 | // https://untappd.com/api/docs#userinfo 261 | that.userInfo = function(callback, data) { 262 | data = data || {}; 263 | if (!hasToken()) validate(data.USERNAME, "USERNAME"); 264 | validate(callback, "callback"); 265 | authorized(); 266 | return get("/v4/user/info/" + (data.USERNAME || ''), data, callback); 267 | }; 268 | 269 | // https://untappd.com/api/docs#userwishlist 270 | that.userWishList = function(callback, data) { 271 | data = data || {}; 272 | if (!hasToken()) validate(data.USERNAME, "USERNAME"); 273 | validate(callback, "callback"); 274 | authorized(); 275 | return get("/v4/user/wishlist/" + (data.USERNAME || ''), data, callback); 276 | }; 277 | 278 | // https://untappd.com/api/docs#userfriends 279 | that.userFriends = function(callback, data) { 280 | data = data || {}; 281 | if (!hasToken()) validate(data.USERNAME, "USERNAME"); 282 | validate(callback, "callback"); 283 | authorized(); 284 | return get("/v4/user/friends/" + (data.USERNAME || ''), data, callback); 285 | }; 286 | 287 | // https://untappd.com/api/docs#userbadges 288 | that.userBadges = function(callback, data) { 289 | data = data || {}; 290 | if (!hasToken()) validate(data.USERNAME, "USERNAME"); 291 | validate(callback, "callback"); 292 | authorized(); 293 | return get("/v4/user/badges/" + (data.USERNAME || ''), data, callback); 294 | }; 295 | 296 | // https://untappd.com/api/docs#userbeers 297 | that.userDistinctBeers = function(callback, data) { 298 | data = data || {}; 299 | if (!hasToken()) validate(data.USERNAME, "USERNAME"); 300 | validate(callback, "callback"); 301 | authorized(); 302 | return get("/v4/user/beers/" + (data.USERNAME || ''), data, callback); 303 | }; 304 | 305 | // https://untappd.com/api/docs#breweryinfo 306 | that.breweryInfo = function(callback, data) { 307 | data = data || {}; 308 | validate(data.BREWERY_ID, "BREWERY_ID"); 309 | validate(callback, "callback"); 310 | authorized(); 311 | return get("/v4/brewery/info/" + data.BREWERY_ID, data, callback); 312 | }; 313 | 314 | // https://untappd.com/api/docs#beerinfo 315 | that.beerInfo = function(callback, data) { 316 | data = data || {}; 317 | validate(data.BID, "BID"); 318 | validate(callback, "callback"); 319 | authorized(); 320 | return get("/v4/beer/info/" + data.BID, data, callback); 321 | }; 322 | 323 | // https://untappd.com/api/docs#venueinfo 324 | that.venueInfo = function(callback, data) { 325 | data = data || {}; 326 | validate(data.VENUE_ID, "VENUE_ID"); 327 | validate(callback, "callback"); 328 | authorized(); 329 | return get("/v4/venue/info/" + data.VENUE_ID, data, callback); 330 | }; 331 | 332 | // https://untappd.com/api/docs#beersearch 333 | that.beerSearch = function(callback, data) { 334 | data = data || {}; 335 | validate(data.q, "q"); 336 | validate(callback, "callback"); 337 | authorized(); 338 | return get("/v4/search/beer", data, callback); 339 | }; 340 | 341 | // https://untappd.com/api/docs#brewerysearch 342 | that.brewerySearch = function(callback, data) { 343 | data = data || {}; 344 | validate(data.q, "searchTerms"); 345 | validate(callback, "callback"); 346 | authorized(); 347 | return get("/v4/search/brewery", data, callback); 348 | }; 349 | 350 | // CHECKIN calls 351 | // https://untappd.com/api/docs#checkin 352 | that.checkin = function(callback, data) { 353 | data = data || {}; 354 | validate(data.gmt_offset, "gmt_offset"); 355 | validate(data.timezone, "timezone"); 356 | validate(data.bid, "bid"); 357 | validate(callback, "callback"); 358 | authorized(true); 359 | return post("/v4/checkin/add", {} , data, callback); 360 | }; 361 | 362 | // https://untappd.com/api/docs#toast 363 | // If already toasted, this will untoast, otherwise it toasts. 364 | that.toast = function(callback, data) { 365 | data = data || {}; 366 | validate(data.CHECKIN_ID, "CHECKIN_ID"); 367 | validate(callback, "callback"); 368 | authorized(true); 369 | return get("/v4/checkin/toast" + data.CHECKIN_ID, data, callback); 370 | }; 371 | 372 | // https://untappd.com/api/docs#pendingfriends 373 | that.pendingFriends = function(callback, data) { 374 | data = data || {}; 375 | validate(callback, "callback"); 376 | authorized(true); 377 | return get("/v4/user/pending", data, callback); 378 | }; 379 | 380 | // https://untappd.com/api/docs#addfriend 381 | that.requestFriends = function(callback, data) { 382 | data = data || {}; 383 | validate(data.TARGET_ID, "TARGET_ID"); 384 | validate(callback, "callback"); 385 | authorized(true); 386 | return get("/v4/friend/request/" + data.TARGET_ID, data, callback); 387 | }; 388 | 389 | // https://untappd.com/api/docs#removefriend 390 | that.removeFriends = function(callback, data) { 391 | data = data || {}; 392 | validate(data.TARGET_ID, "TARGET_ID"); 393 | validate(callback, "callback"); 394 | authorized(true); 395 | return get("/v4/friend/remove/" + data.TARGET_ID, data, callback); 396 | }; 397 | 398 | // https://untappd.com/api/docs#acceptfriend 399 | that.acceptFriends = function(callback, data) { 400 | data = data || {}; 401 | validate(data.TARGET_ID, "TARGET_ID"); 402 | validate(callback, "callback"); 403 | authorized(true); 404 | return post("/v4/friend/accept/" + data.TARGET_ID, {}, data, callback); 405 | }; 406 | 407 | // https://untappd.com/api/docs#rejectfriend 408 | that.rejectFriends = function(callback, data) { 409 | data = data || {}; 410 | validate(data.TARGET_ID, "TARGET_ID"); 411 | validate(callback, "callback"); 412 | authorized(true); 413 | return post("/v4/friend/reject/" + data.TARGET_ID, {}, data, callback); 414 | }; 415 | 416 | // https://untappd.com/api/docs#addcomment 417 | that.addComment = function(callback, data) { 418 | data = data || {}; 419 | validate(data.CHECKIN_ID, "CHECKIN_ID"); 420 | validate(data.shout, "shout"); 421 | validate(callback, "callback"); 422 | authorized(true); 423 | return post("/v4/checkin/addcomment/" + data.CHECKIN_ID, {}, data, callback); 424 | }; 425 | 426 | // https://untappd.com/api/docs#removecommment 427 | that.removeComment = function(callback, data) { 428 | data = data || {}; 429 | validate(data.COMMENT_ID, "COMMENT_ID"); 430 | validate(callback, "callback"); 431 | authorized(true); 432 | return post("/v4/checkin/deletecomment/" + data.COMMENT_ID, {}, data, callback); 433 | }; 434 | 435 | // https://untappd.com/api/docs#addwish 436 | that.addToWishList = function(callback, data) { 437 | data = data || {}; 438 | validate(data.bid, "bid"); 439 | validate(callback, "callback"); 440 | authorized(true); 441 | return get("/v4/user/wishlist/add", data, callback); 442 | }; 443 | 444 | // https://untappd.com/api/docs#removewish 445 | that.removeFromWishList = function(callback, data) { 446 | data = data || {}; 447 | validate(data.bid, "bid"); 448 | validate(callback, "callback"); 449 | authorized(true); 450 | return get("/v4/user/wishlist/remove", data, callback); 451 | }; 452 | 453 | // https://untappd.com/api/docs#foursquarelookup 454 | that.foursquareVenueLookup = function(callback, data) { 455 | data = data || {}; 456 | validate(data.VENUE_ID, "VENUE_ID"); 457 | validate(callback, "callback"); 458 | authorized(); 459 | return get("/v4/venue/foursquare_lookup/" + data.VENUE_ID, data, callback); 460 | }; 461 | }; 462 | 463 | module.exports = UntappdClient; 464 | --------------------------------------------------------------------------------