Your browser is very old. You might enjoy surfing the web more if you used something newer like:

Google Chrome

Even Firefox would be OK.

If you're being forced at gunpoint to use Internet Explorer, you should at least upgrade it. Version 8 is tolerable and 9 will be OK when it comes out.

Posts from August 2009

Locking Down Facebook

Before we talk about some basic tools for locking down Facebook, let’s go over the #1 rule of Social Media:

If you don’t want the whole world to see it, don’t put it online.

Things happen. Facebook has messed up privacy settings before and exposed private information. Accounts get hacked. Companies go out of business and sell data. I could go on and on. If it’s going to get you in trouble at work/home/wherever, think twice before putting it online because the internet is forever.

That said, there are a few easy ways to tighten up your Facebook account.

Step 1: Create a Friend List for limited access Facebook Friends

First, click on the Friends link from your home page.

Next, click the “Create New List” button.

Give your list a meaningful name. Mine’s called “Minimal Access People”.

Step 2: Lock down your profile

Go to the “Privacy Settings” of your account.

Click on “Profile”

Here’s what my Profile privacy settings look like. You can see how some items are open to all of my Friends and some are open to all of my Friends except those on the Minimal Access People list.

The above is achieved by editing the “Custom Settings” of each profile item. Here’s a drilldown of the Education settings.

Step 3: Lock down photos

The most important Photo privacy setting is the “Photos Tagged of You” setting (which is part of your Profile privacy settings).

This setting is great because it applies to any photo of you, no matter who the photo actually belongs to.

You can also control the privacy of individual Photo albums which is handy but, unfortunately, you have set the privacy of each album individually.

And lastly, you can completely lock certain people entirely out of your photos.

This is done via the “Applications Settings” of your “Privacy Settings”. Just click on “Edit Settings” next to the Photos Application

Once you’re there, you can set custom settings to control who can access your Facebook photos.

Step 4: Yay!

There you have it, a few basic ways to lock down your Facebook account. Hopefully this gives you a small degree of comfort about who gets to see what about you.

But always to try to keep in the back of your head what would happen if the above doesn’t work.

Automatically blocking Twitter spam accounts

At this point, I’m sure that everyone has been followed by one of many spam Twitter accounts.

It’s pretty obvious that these are bogus, if not by the lack of actual content, then by the small number of followers compared to the massive number of friends.

So let’s use the Twitter API to write a little script that detects these accounts and automatically blocks them.

The first thing to do is to get the list of followers for your Twitter account. All you have to do is send a GET request to http://twitter.com/statuses/followers.json (change the .json to .xml to get XML back instead). The request should send, via Basic Authentication, your Twitter username and password.

The above gives you a nice list of Twitter users with all sorts of information, including their Twitter user ID, number of followers, and number of friends. From there, it’s simple to loop over the list and check the ratio of followers to friends.

I found

if (Follower / Friends) * 100 < 5

to be a nice threshold.

Now that we’ve identified the bogus accounts, we can use the API call to block them. In this case, we do an HTTP POST to http://twitter.com/blocks/create/id.json, passing the user ID to block as the single POST value.

At the end of this post is a complete Python script that will do all of the above. In the process of writing the script, I got to learn about parsing Python script command-line arguments using optparse.

Using optparse makes handling command-line arguments dead simple.

    parser = OptionParser()
    parser.add_option("-d", "--dry-run", action="store_true", dest="dryrun", help="Displays accounts that would be blocked.")
    (options, args) = parser.parse_args()

    if options.dryrun:
      print "--dry-run or -d was found on the command-line."
    else:
      print "No --dry-run or -d found."

The other nice thing is that optparse automatically handles –help (or -h) and prints out a nice help message based on the help text passed to the add_option() method of optparse.

Here’s the complete script which I hope you find useful. I’m releasing it using under the WTFPL license. No warranties, blah blah, not my fault if it breaks you computer, squishes your kitten, etc.

#!/usr/bin/python

import urllib
import urllib2
import base64
import json
import sys
from optparse import OptionParser
#################################################################################

username = ''
password = ''
verbose = False

def twitterRequest(url, username, password, values=None):
    b64str = base64.encodestring( '%s:%s' % (username, password))[:-1]
    header = {'Authorization': "Basic %s" % b64str}

    if not values is None:
        values = urllib.urlencode(values)

    req = urllib2.Request( url, values, header)
    res = urllib2.urlopen(req)

    return json.loads(res.read())

def blockExists(username, password):
    try:
        twitterRequest('http://twitter.com/blocks/exists/%(id)s.json' % {'id': follower['id']}, username, password)
        return True
    except urllib2.HTTPError, e:
        return False

def blockUser(id, username, password):
    if not blockExists(username, password):
        twitterRequest('http://twitter.com/blocks/create/%(id)s.json' % {'id': follower['id']}, username, password, values={'id': id})

def vMsg(msg):
    if verbose:
        print msg
#################################################################################
if __name__ == "__main__":
    cliError = False
    doBlock = False

    parser = OptionParser()
    parser.add_option("-d", "--dry-run", action="store_true", dest="dryrun", help="Displays accounts that would be blocked.")
    parser.add_option("-b", "--block", action="store_true", dest="block", help="Blocks accounts that fall under the specified threshold")
    parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="Print detailed status messages")
    parser.add_option("-t", "--threshold", dest="threshold", help="The threshold accounts must fall under before they're blocked (default is 5)")
    parser.add_option("-u", "--username", dest="username", help="Twitter username")
    parser.add_option("-p", "--password", dest="password", help="Twitter password")

    (options, args) = parser.parse_args()

    #Handle command-line argument log to make sure this is a valid call
    if options.threshold is None:
        threshold = 5
    else:
        threshold = int(options.threshold)

    if (options.dryrun is None and options.block is None) or (not options.dryrun is None and not options.block is None):
        print "You must select either --dry-run or --block."
        cliError = True

    if options.username is None:
        print "Username required."
        cliError = True

    if options.password is None:
        print "Password required."
        cliError = True

    if cliError:
        print ""
        parser.print_help()
        sys.exit(1)

    username = options.username
    password = options.password
    verbose = options.verbose
    doBlock = options.block

    followers = twitterRequest('http://twitter.com/statuses/followers.json', username, password)
    spamCount = 0

    #All of the command-line stuff was OK so continue with the scanning & blocking
    for follower in followers:
        followers = float(follower['followers_count'])
        friends = float(follower['friends_count'])
        ratio = (followers / friends) * 100

        if ratio < threshold:
            spamCount = spamCount + 1
            if doBlock:
                prefix = "Blocking Account:\t"
                blockUser(follower['id'], username, password)
            else:
                prefix = "Possible Spam Account:\t"

            print prefix + str(follower['id']) + "\t\t" + follower['screen_name'] + "\t\t" + str(followers) + "\t\t" + str(friends) + "\t\t" + str(ratio)

    if spamCount <= 0:
        vMsg("No followers were flagged as potential spam accounts.")

    sys.exit(0)

File modification date/times in Python

A while back, I asked on StackOverflow, how to get file creation/modification date/times in Python.

It turns out, it’s pretty easy via the os.stat() function.

Pass os.stat() a filename!

print os.stat("/tmp/file")

Get back a bunch of numbers!

posix.stat_result(st_mode=33188, st_ino=515666L, st_dev=64512L,
st_nlink=1, st_uid=1000, st_gid=1000, st_size=0L,
st_atime=1249609170, st_mtime=1249609170, st_ctime=1249609170)

Umm…so…what do all of those numbers actually mean?

ST_MODE
Inode protection mode.
ST_INO
Inode number.
ST_DEV
Device inode resides on.
ST_NLINK
Number of links to the inode.
ST_UID
User id of the owner.
ST_GID
Group id of the owner.
ST_SIZE
Size in bytes of a plain file; amount of data waiting on some special files.
ST_ATIME
Time of last access.
ST_MTIME
Time of last modification.
ST_CTIME
The “ctime” as reported by the operating system. On some systems (like Unix) is the time of the last metadata change, and, on others (like Windows), is the creation time (see platform documentation for details).

My whole reason for finding out about os.stat() was to figure out which copy of a file was newer than another which can be done like this:

stat1 = os.stat(file1)
stat2 = os.stat(file2)

diff = stat1.st_mtime - stat2.st_mtime

if diff > 0:
    #file1 was modified more recently
elif diff < 0:
    #file2 was modified more recently
else
    #files have the same modification date/time

My next goal is to work this into my Google Docs backup script.