Skip to content
This repository was archived by the owner on Jun 8, 2023. It is now read-only.
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions src/scripts/foursquare-locator.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Description:
# Get last checkin of your bot's friends
#
# Dependencies:
# "node-foursquare": "0.2.0"
#
# Configuration:
# FOURSQUARE_CLIENT_ID
# FOURSQUARE_CLIENT_SECRET
# FOURSQUARE_ACCESS_TOKEN
#
# Commands:
# hubot foursquare approve - Approves the bot user's pending friend requests
# hubot foursquare friends - Lists the friends of the bot
# hubot foursquare register - Tells how to friend the bot
# where is __? - Filters recent checkins to a particular subset of users.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For help commands, I usually use a convention like: where is <user>?. It's similar to the formatting used in man pages, bu t I have yet to find a name what that actually is.

#
# Notes:
# To obtain/set the FOURSQUARE_ACCESS_TOKEN, you will need to go through the OAuth handshake manually with your bot's credentials
#
# Authors:
# stephenyeargin, jfryman, brandonvalentine

Util = require "util"
moment = require "moment"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to add this to the Dependencies too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was a later contribution. Oops.

Tested to work with

#   "moment": "~2.5.0"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nice thing about moving to npm packages is you can specify real dependencies instead of relying on people to read documentation 😁


module.exports = (robot) ->

config = secrets:
clientId: process.env.FOURSQUARE_CLIENT_ID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll want to check that these are set at some point, and reply back with an message on how to fix it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Preference for on-load or on-command?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on-command, because on-load is hidden in logs, but on-command is more visible to end-users.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I get away with it just on the where is bob? command, as that's the most obvious use-case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd want to apply it to anything that hits the API and needs those. Otherwise, you'll have silent failures. Using pagerduty.coffee as example again:

clientSecret: process.env.FOURSQUARE_CLIENT_SECRET
accessToken: process.env.FOURSQUARE_ACCESS_TOKEN
redirectUrl: "localhost"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this localhost get used? Anything that should be configured per install of this script?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used AFAICT. I think I was just trying to keep the foursquare node module happy in case they ever try to check for those vars.


foursquare = require('node-foursquare')(config);

# Who are my friends?
robot.respond /foursquare friends/i, (msg) ->
params = {}
foursquare.Users.getFriends 'self', params, config.secrets.accessToken, (error, response) ->

# Loop through friends
if response.friends.items.length > 0
list = []
for own key, friend of response.friends.items
user_name = formatName friend
list.push user_name

msg.send list.join(", ")
else
msg.send "Your bot has no friends. :("

# Approve pending bot friend quests
robot.respond /foursquare approve/i, (msg) ->
foursquare.Users.getRequests config.secrets.accessToken, (error, response) ->

# Loop through requests
if response.requests.length > 0

for own key, friend of response.requests
msg.http("https://api.foursquare.com/v2/users/#{friend.id}/approve?oauth_token=#{config.secrets.accessToken}").post() (err, res, body) ->
user_name = formatName friend
msg.send "Approved: #{user_name}"

# Your bot is lonely
else
msg.send "No friend requests to approve."

# Tell people how to friend the bot
robot.respond /foursquare register/i, (msg) ->
foursquare.Users.getUser 'self', config.secrets.accessToken, (error, response) ->
profile_url = "https://foursquare.com/user/#{response.user.id}"
user_name = response.user
msg.send "Head to #{profile_url} and friend #{user_name}!"
msg.send response.user.bio if response.user.bio?

# Find your friends
robot.hear /where[ ']i?s ([a-zA-Z0-9 ]+)(\?)?/i, (msg) ->
searchterm = msg.match[1].toLowerCase()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if username doesn't match between chat and foursquare?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question. We hit this problem as well. Foursquare doesn't emit the screen-name of the account either, and their compact response doesn't return the Twitter handle either. It's annoying. The only thing that it could do is do a key/value pair with redis-brain, but that's adding an extra requirement I wanted to avoid. Might be non-negotiable though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One trick we use in pagerduty.coffee is a command like hubot foursquare me as <something>, and store that value in the brain. That way, you can associate back a foursquare thing back to the chat thing.


params =
limit: 100

foursquare.Checkins.getRecentCheckins params, config.secrets.accessToken, (error, response) ->

# Loop through friends
found = 0
for own key, checkin of response.recent

# Skip if no string match
user_name = formatName checkin.user
user_name = user_name.toLowerCase()
if ~user_name.indexOf searchterm
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also do if user_name in searchTerm.

Hilariously, I didn't even know ~ was a thing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neither did I. Can't even remember how I came across it. ❇️

timeFormatted = moment(new Date(checkin.createdAt*1000)).fromNow()
msg.send "#{checkin.user.firstName} #{checkin.user.lastName} was at #{checkin.venue.name} #{timeFormatted}"
found++

# If loop failed to come up with a result, tell them
if found is 0
msg.send "Could not find a recent checkin from #{searchterm}."

# Format a name string
formatName = (user) ->
if user.lastName?
return "#{user.firstName} #{user.lastName}"
else if user.firstName?
return user.firstName
else
return "(No Name)"