Automate Adding to Trello on iOS with Launch Center Pro and Pythonista

I love using Trello to organize my life and work. While I kind of like it's iOS app, sometimes navigating to a board to add a card can be a little time consuming.  I want this process to be faster. I also use Launch Center Pro to get some quick actions that I miss from Android. Mostly around quick communication, adding events, and now, adding to Trello.  LCP uses x-callback-urls for inter-app communication, but Trello doesn't have any that I could see. Inspired by Federico Viticci's article on automating his iOS workflow, I decided to take a crack at it using pythonista. All code shown below can be found on GitHub.

2013-02-1114.32.57

2013-02-11
14.33.00

Pythonista lets you edit and run python scripts on your phone. There are a few ways to get scripts onto your device:

  • Edit right in the app
  • Import from a gist
  • Use pastebot. A great app that gives you multiple clipboards on your device that will also sync with your mac. This is my preferred method.

The endgame of all this: I want to be able to add a card to a specific board, with as few taps as possible. Check out this video for what we are going to build. Digging into the Trello API, we can see that to add a card, we'll need a name for the card and the ID of the list to add it to. We also see that we'll need a token for any of the API calls. To get all of our lists, we'll also need to know about all of our boards.  This breaks our tasks out as follows:

  1. Get an App API Key
  2. Get an API Token
  3. Get a list of every board we heve
  4. Get the IDs of every list on each of these boards
  5. Add a new card to our list of choice

To get an App API Key, you can generate one here.  In theory, the one I distribute with these scripts should be ok. Get this first script into Pythonista and run it. It will generate a permanant API token for you that you can leave in Launch Center Pro when running these scripts.

#GetTrelloToken

import webbrowser

url = 'https://trello.com/1/authorize?key=3e2cd730f3dcccbe15eaf0d39d219a37&name=PythonistaTrello&expiration=never&response_type=token&scope=read,write'

#open web browser to get a permanant Trello API Token
webbrowser.open(url)

2013-02-11 14.54.35

2013-02-1114.54.40

Now that we have our key and tokens, let's get get all of our boards and the lists on each one. Run this script and pass in your token and your email address as arguments. (In pythonista hold down run to get the "Run with arguments dialog"). Add your arguments separated by spaces. For me, this could look something like: "EGYlyiOdggDOaPoTUxaTQobmS6gtiX timothy.broder@gmail.com"

#List all lists by user

import urllib2
import urllib
import json
import sys

if len(sys.argv) != 3:
    raise Exception("Usage: list_trello_lists.py [token] [email]")

key = "3e2cd730f3dcccbe15eaf0d39d219a37"
args = { 'key': key,
    'token': sys.argv[1],
    'filter': 'open'}

#build out api url
username = sys.argv[2]
boards_url = "https://api.trello.com/1/members/%s/boards/?%s" % (username, urllib.urlencode(args))

#get board data from api
try:
    data = urllib2.urlopen(boards_url)
except urllib2.HTTPError as inst:
    raise Exception("Key or Token incorrect")

boards = json.loads(data.read())

#loop through each board
for board in boards:
    board_id = board['id']
    lists_url = "https://api.trello.com/1/boards/%s/lists?%s" % (board_id, urllib.urlencode(args))
    data = urllib2.urlopen(lists_url)
    lists = json.loads(data.read())

    print "-- %s" % board['name']
    #output each list in board
    for lizt in lists:
        print "\"%s\" %s" % (lizt['name'], lizt['id'])
    print "\n"

Running this, we'll get the IDs of the lists we need

2013-02-11 15.06.39

2013-02-1115.07.13

Finally, we'll need to take our key, the id of the list, what we want our card to say, and hit the API with them.  I also add in a position so the cards I add this way will appear at the top of their list. We'll pass these in as parameters:

"EGYlyiOdggDOaPoTUxaTQobmS6gtiX V12t3k6RQQ2r8X top the name of my card goes here"

Two things to point out inthis script:

  • we put the name of the card at the end so we can just loop through all the remaining words and not have to worry about wrapping the name in quotes to deal with spaces.
  • since the end game of this is to run from Launch center pro, we want to jump back to Launch Center Pro at the end using it's x-callback-url
#Add card to List

import urllib2
import urllib
import json
import sys
import webbrowser

arglen = len(sys.argv)

if arglen < 5:     raise Exception("Usage: list_trello_lists.py [token] [list_id] [position] [card_name]") name = sys.argv[4] #take care of spaces if arglen > 5:
    for i in range(5,arglen):
        name = "%s %s" % (name, sys.argv[i])

key = "3e2cd730f3dcccbe15eaf0d39d219a37"
token = sys.argv[1]
args = { 'key': key,
    'token': sys.argv[1],
    'name': name,
    'pos': sys.argv[3],
    'idList': sys.argv[2]
    }
card_add_url = "https://api.trello.com/1/cards"

try:
    data = urllib2.urlopen(card_add_url, urllib.urlencode(args))
except urllib2.HTTPError as inst:
    raise Exception("Key or Token incorrect")

#jump back to Launch Center Pro
webbrowser.open("launchpro:")

To run this in Launch Center Pro, Add an Action > Pythonista > Run Script with Arguments. Put in the name of the script, then in arguments, but everything from above except the name: "EGYlyiOdggDOaPoTUxaTQobmS6gtiX V12t3k6RQQ2r8X top," then hit the button that says input prompt. Now, when you run the script, Launch Center Pro will prompt you for text, enter the name of the card and hit go.

Add as many of these as you want! Just swap out the list ID.

Getting Started with Solr and Django

Solr is a very powerful search tool and it is pretty easy to get the basics, such as full text search, facets, and related assets up and running pretty quickly. We will be using haystack to do the communication between Django and Solr. All code for this can be viewed on github.

Install

Assuming you already have Django up and running, the first thing we need to do is install Solr.

curl -O http://mirrors.gigenet.com/apache/lucene/solr/4.0.0-BETA/apache-solr-4.0.0-BETA.zip
unzip apache-solr-4.0.0-BETA.zip
cd apache-solr-4.0.0-BETA
cd example
java -jar start.jar

Next install pysolr and haystack. (At the time of this writing the git checkout of haystack works better with the Solr 4.0 beta then the 1.2.7 that's in pip.)

pip install pysolr
pip install -e https://github.com/toastdriven/django-haystack.git

Add 'haystack' to INSTALLED_APPS in settings.py and add the following haystack connection:

HAYSTACK_CONNECTIONS = { 
    'default': { 
        'ENGINE': 'haystack.backends.solr_backend.SolrEngine', 
        'URL': 'http://127.0.0.1:8983/solr' 
    }, 
}

For the example, we're going to create a simple job database that a recruiter might use. Here is the model:

from django.db import models
from django.contrib.localflavor.us
import models as us_models

JOB_TYPES = (
    ('pt', 'Part Time'),
    ('ft', 'Full Time'),
    ('ct', 'Contract')
)

class Company(models.Model):
    name = models.CharField(max_length=64)
    address = models.TextField(blank=True, null=True)
    contact_email = models.EmailField()

    def __unicode__(self):
        return self.name

class Location(models.Model):
    city = models.CharField(max_length=64)
    state = us_models.USStateField()

    def __unicode__(self):
        return "%s, %s" % (self.city, self.state)

class Job(models.Model):
    name = models.CharField(max_length=64)
    description = models.TextField()
    salary = models.CharField(max_length=64, blank=True, null=True)
    type = models.CharField(max_length=2, choices=JOB_TYPES)
    company = models.ForeignKey(Company, related_name='jobs')
    location = models.ForeignKey(Location, related_name='location_jobs')
    contact_email = models.EmailField(blank=True, null=True)
    added_at = models.DateTimeField(auto_now=True)

    def __unicode__(self):
        return self.name

    def get_contact_email(self):
        if self.contact_email:
            return self.contact_email

        return self.company.contact_email

The next step is to create the SearchIndex object that will be used to transpose to data to Solr. save this as search_indexes.py in the same folder as your models.py. The text field with its template will be used for full text search on Solr. The other two fields will be used to faceted (drill down) navigation. For more details on this file, check out the [haystack tutorial](http://django- haystack.readthedocs.org/en/latest/tutorial.html#handling-data).

class JobIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.CharField(document=True, use_template=True)
    type = indexes.CharField(model_attr='type', faceted=True)
    location = indexes.CharField(model_attr='location', faceted=True)

    def get_model(self):
        return Job

    def index_queryset(self):
        return self.get_model().objects.all()

Create the search index template in your template folder with the following naming convention: search/indexes/[app]/[model]_text.txt

For us, this is templates/search/indexes/jobs/job_text.txt


{{ object.name }}
{{ object.description }}
{{ object.salary }}
{{ object.type }}
{{ object.added_at }}

Now, lets get our data into Solr. Run ./manage.py build_solr_schema to generate a schema.xml file. Move this into example\solr\conf in your Solr install. Note: if using Solr 4, edit this file and replace stopwords_en.txt with lang/stopwords_en.txt in all locations. To test everything and load your data, run: manage.py rebuild_index Subsequent updates can be made with: manage.py update_index.

If that all worked we can start working on the front-end to see the data in Django. Add this to your urls.py

(r'^$', include('haystack.urls')),

At this point there are at least two templates we'll need. One for the search results page, and a sub-template to represent each item we are pulling back. My example uses twitter bootstrap for some layout help and styling, see my base.html [here](https://github.com/broderboy/django-solr- demo/blob/master/templates/base.html) if interested.

Create templates/search/search.html This gives you a basic search form, the results, and pagination for a number of results


{% extends 'base.html' %}

{% block hero_text %}Search{% endblock %}
{% block header %}<p>Click around!</p>{% endblock %}


{% block content %}
<div class="span12">
    <h1>Search</h1>
    <form method="get" action="." class=".form-search">
        <table>
            {{ form.as_table }}
        </table>
        <input type="submit" value="Search">
    </form>
</div>
        {% if query %}
            <div class="span2">
                <h3>Filter</h3>
                {% if facets.fields.type %}
                    <div>
                        <h4>Type</h4>
                        <ul>
                        {% for type in facets.fields.type %}
                            <li><a href="{{ request.get_full_path }}&amp;selected_facets=type_exact:{{ type.0|urlencode }}">{{ type.0 }}</a> ({{ type.1 }})</li>
                        {% endfor %}
                        </ul>
                    </div>
                {% endif %}
                {% if facets.fields.location %}
                    <div>
                        <h4>Location</h4>
                        <ul>
                        {% for location in facets.fields.location %}
                            <li><a href="{{ request.get_full_path }}&amp;selected_facets=location_exact:{{ location.0|urlencode }}">{{ location.0 }}</a> ({{ location.1 }})</li>
                        {% endfor %}
                        </ul>
                    </div>
                {% endif %}
            </div>
            <div class="span6">
                <h3>Results</h3>
                <div class="accordion" id="accordion2">
                    {% for result in page.object_list %}
                        {% include 'search/_result_object.html' %}
                    {% empty %}
                        <p>No results found.</p>
                    {% endfor %}
                </div>

                {% if page.has_previous or page.has_next %}
                    <div>
                        {% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; Previous{% if page.has_previous %}</a>{% endif %}
                        |
                        {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Next &raquo;{% if page.has_next %}</a>{% endif %}
                    </div>
                {% endif %}
            </div>
        {% else %}
            <div class="span6">
                {# Show some example queries to run, maybe query syntax, something else? #}
            </div>
        {% endif %}
{% endblock %}

And the templates/search/_result_object.txt


{% load more_like_this %}


{% with obj=result.object %}
<div class="accordion-group">
    <div class="accordion-heading">
        <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapse_{{ obj.id }}">
            {{ obj.name }}
        </a>
        <div style="padding: 8px 15px;">
            <p>Company: {{ obj.company }}</p>
            <p>Type: {{ obj.type }}</p>
            {% if obj.salary %}<p>Salary: {{ obj.salary }}</p>{% endif %}
            <p>Location: {{ obj.location }}</p>
        </div>
    </div>
    <div id="collapse_{{ obj.id }}" class="accordion-body collapse in">
        <div class="accordion-inner">
            <p>Contact: <a href="mailto:{{ obj.get_contact_email }}">{{ obj.get_contact_email }}</a></p>
            {{ obj.description }}
            {% more_like_this obj as related_content limit 5  %}
            {% if related_content %}
                <div>
                    <br>
                    <p><strong>Related:</strong></p>
                    <ul>
                        {% for related in related_content %}
                            <li><a>{{ related.object.name }}</a></li>
                        {% endfor %}
                    </ul>
                </div>
            {% endif %}
        </div>
    </div>
</div>
{% endwith %}

Start up your dev server for search!

Adding Related Items is as simple as using the related_content tag in the haystack more_like_this tag library and tweaking out Solr config. Open up solrconfig.xml and add a MoreLikeThisHandler within thetag:

<requesthandler name="/mlt" class="solr.MoreLikeThisHandler"></requesthandler>

Our full _result_object.html now looks like this:


{% load more_like_this %}


{% with obj=result.object %}
<div class="accordion-group">
    <div class="accordion-heading">
        <a class="accordion-toggle" data-toggle="collapse" data-parent="#accordion2" href="#collapse_{{ obj.id }}">
            {{ obj.name }}
        </a>
        <div style="padding: 8px 15px;">
            <p>Company: {{ obj.company }}</p>
            <p>Type: {{ obj.type }}</p>
            {% if obj.salary %}<p>Salary: {{ obj.salary }}</p>{% endif %}
            <p>Location: {{ obj.location }}</p>
        </div>
    </div>
    <div id="collapse_{{ obj.id }}" class="accordion-body collapse in">
        <div class="accordion-inner">
            <p>Contact: <a href="mailto:{{ obj.get_contact_email }}">{{ obj.get_contact_email }}</a></p>
            {{ obj.description }}
            {% more_like_this obj as related_content limit 5  %}
            {% if related_content %}
                <div>
                    <br />
                    <p><strong>Related:</strong></p>
                    <ul>
                        {% for related in related_content %}
                            <li><a>{{ related.object.name }}</a></li>
                        {% endfor %}
                    </ul>
                </div>
            {% endif %}
        </div>
    </div>
</div>
{% endwith %}

Facets

To get our type and location facets, we'll have to add them to a queryset and pass this to a FacetedSearchView instead of the default one. urls.py now looks like this:

from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
from haystack.forms import FacetedSearchForm
from haystack.query import SearchQuerySet
from haystack.views import FacetedSearchView

sqs = SearchQuerySet().facet('type').facet('location')

urlpatterns = patterns('haystack.views',
    url(r'^$', FacetedSearchView(form_class=FacetedSearchForm, searchqueryset=sqs), name='haystack_search'),
)

urlpatterns = urlpatterns + patterns('',
    url(r'^admin/', include(admin.site.urls)),
    #(r'^', include('haystack.urls')),
)
- See more at: http://timbroder.com/2012/08/getting-started-with-solr-and-django.html?preview=true&preview_id=1167&preview_nonce=f6c209628d#sthash.2NVpbeDJ.dpuf

Then, we can use the generated facets in the search template in the facets variable


{% extends 'base.html' %}

{% block hero_text %}Search{% endblock %}
{% block header %}</p><p>Click around!</p>{% endblock %}


{% block content %}
<div class="span12">
    <h1>Search</h1>
    <form method="get" action="." class=".form-search">
        <table>
            {{ form.as_table }}
        </table>
        <input type="submit" value="Search"/>
    </form>
</div>
        {% if query %}
            <div class="span2">
                <h3>Filter</h3>
                {% if facets.fields.type %}
                    <div>
                        <h4>Type</h4>
                        <ul>
                        {% for type in facets.fields.type %}
                            <li><a href="{{ request.get_full_path }}&amp;selected_facets=type_exact:{{ type.0|urlencode }}">{{ type.0 }}</a> ({{ type.1 }})</li>
                        {% endfor %}
                        </ul>
                    </div>
                {% endif %}
                {% if facets.fields.location %}
                    <div>
                        <h4>Location</h4>
                        <ul>
                        {% for location in facets.fields.location %}
                            <li><a href="{{ request.get_full_path }}&amp;selected_facets=location_exact:{{ location.0|urlencode }}">{{ location.0 }}</a> ({{ location.1 }})</li>
                        {% endfor %}
                        </ul>
                    </div>
                {% endif %}
            </div>
            <div class="span6">
                <h3>Results</h3>
                <div class="accordion" id="accordion2">
                    {% for result in page.object_list %}
                        {% include 'search/_result_object.html' %}
                    {% empty %}
                        <p>No results found.</p>
                    {% endfor %}
                </div>

                {% if page.has_previous or page.has_next %}
                    <div>
                        {% if page.has_previous %}<a href="?q={{ query }}&amp;page={{ page.previous_page_number }}">{% endif %}&laquo; Previous{% if page.has_previous %}</a>{% endif %}
                        |
                        {% if page.has_next %}<a href="?q={{ query }}&amp;page={{ page.next_page_number }}">{% endif %}Next &raquo;{% if page.has_next %}</a>{% endif %}
                    </div>
                {% endif %}
            </div>
        {% else %}
            <div class="span6">
                {# Show some example queries to run, maybe query syntax, something else? #}
            </div>
        {% endif %}
{% endblock %}

And we're done! As I said, check out the [haystack documentation](http ://django-haystack.readthedocs.org/en/latest/index.html) for more information. Leave any questions in the comments and I'll be sure to answer them

[![](http://timbroder.com/wp- content/uploads/2012/08/solr2.png)](http://timbroder.com/wp- content/uploads/2012/08/solr2.png)

Hiding fields in the Django admin based on permission

Quick and easy if you have one group of users that can only edit an asset, and another group that has publishing rights

def remove_from_fieldsets(fieldsets, fields):
    for fieldset in fieldsets:
        for field in fields:
            if field in fieldset[1]['fields']:
                new_fields = []
                for new_field in fieldset[1]['fields']:
                    if not new_field in fields:
                        new_fields.append(new_field)

                fieldset[1]['fields'] = tuple(new_fields)
                break

class PositionAdmin(admin.ModelAdmin):
    ...
    def get_fieldsets(self, request, obj=None):
        fieldsets = super(PositionAdmin, self).get_fieldsets(request, obj)

        if not request.user.is_superuser and request.user.groups.filter(name='publisher').count() == 0:
            remove_from_fieldsets(fieldsets, ('is_published',))
        return fieldsets

Making virtualenv on windows with powershell a little cleaner

While I code on a mac at home, I can't live without my giant dual screens and solid state drive at work so I'm on a windows 7 box.  Most of the time it's fine, does everything I need, and I'm happy.  I became full of rage for the first time last week trying to properly get virtualenv to play nice with powershell.  (If you code on windows and are in the terminal a lot, switch to powershell, its great and comes with windows 7. There is a download for Windows XP) I'm not going to recap how to set up virtualenv for your project as there is a great walk through on that here.  The issue on windows is around when you want to activate your project.  Powershell has a restricted execution policy turned on by default. The manual way around this is to run powershell as an administrator, and run this: [code] Set-ExecutionPolicy Unrestricted [/code] Works, but that's an extra click.  You can also change this value permanently in the registry at the key listed below, but that didn't seem to stick when opening powershell through launchy [code] HKLM\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell [/code] Enter my hacked up solution. Create a shortcut for powershell with these parameters:

Target: %SystemRoot%\syswow64\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy
Unrestricted Start In: %HOMEDRIVE%%HOMEPATH% 

Then, if your workspace and projects are set up relatively the same, you can create a powershell script (or a cmd script if not using powershell), named workon.ps1 that looks something like this:

$ENV:PYTHONPATH="" 
cd C:\Users\tbroder\workspace\$args\ .\myenv\Scripts\activate

I threw this in my C:\Python26\Scripts folder.  It assumes your project lives in a workspace folder, that your project name is a single word, and that all of your virtualenvs are called myenv.  Example of using it below:

Windows PowerShell Copyright (C) 2009 Microsoft Corporation. All rights reserved. PS
C:\Users\tbroder&gt; workon gsb (myenv) PS C:\Users\tbroder\workspace\gsb&gt;

Some reading from django-nyc

Went to the django-nyc meetup last night, it was in Manhattan and much easier to get to then when it's at huge in bk.  Wrote down a few things to read up on:

select foo, count(*) from bar group by foo in django

Every once in a while you need some old fashion SQL style queries in django. This is a common one for reporting and aggregation.  Its fairly easy to replicate in a queryset.  Say I wanted to get the authors and the number of articles they have written going back to the beginning of 2009 to the present:

from django.db.models import Count
Article.objects
    .filter(created_date__gte=datetime.datetime(2009,1,1))
    .values('author')
    .annotate(Count('author'))

The result:

[{'author__count': 1028, 'author': 17L}, {'author__count': 9, 'author': 9L}, {'author__count': 39, 'author': 12L}, {'author__count': 581, 'author': 10L}, {'author__count': 15, 'author': 7L}, {'author__count': 366, 'author': 13L}, {'author__count': 233, 'author': 5L}, {'author__count': 167, 'author': 15L}, {'author__count': 287, 'author': 14L}, {'author__count': 10, 'author': 6L}, {'author__count': 2101, 'author': 16L}]

Update Google Talk status with Twitter

I've had this idea in my head for a while and just got it to work this morning.

Basically you send a tweet from Twitter and this script runs, picks up your current twitter status, and if need be, updates your gChat status. I'm working on a service version of this where you could use gpowered.net to do all this for you. Stay tuned =) If I change the version of the script that I have running in the service, you will be able to see the source code that I have checked in [here](http://code.google.com/p/gpo wered/source/browse/trunk/gpowered/scripts/twitter2gChat.py)

For those of you that don't know what Twitter is: _
"Twitter is a free social networking and micro-blogging service that allows users to send "updates" (or "tweets"; text-based posts, up to 140 characters long) to the Twitter website, via short message service (e.g. on a cell phone), instant messaging, or a third-party application such as Twitterrific or Facebook. Updates are displayed on the user's profile page and instantly delivered to other users who have signed up to receive them. The sender can restrict delivery to those in his or her circle of friends (delivery to everyone is the default). Users can receive updates via the Twitter website, instant messaging, SMS, RSS, email or through an application. For SMS, four gateway numbers are currently available: short codes for the USA, Canada, and India, as well as a UK number for international use. Several third parties offer posting and receiving updates via email." (Wikipidia)_

Requirements:
xmpppy
python-twitter

import sys, xmpp, os, twitter

class Twitter2gChat:

    twitter_login = os.environ['TWITTER_LOGIN']
    twitter_pass = os.environ['TWITTER_PASS']
    google_login = os.environ['GOOGLE_LOGIN']
    google_pass = os.environ['GOOGLE_PASS']

    twitter_status = None
    updated = False
    catches = 0

    #keep looping and wait for xmpp response
    def GoOn(self,conn):
        while self.StepOn(conn):
            pass

    #keep listening for responses
    def StepOn(self,conn):
        try:
            conn.Process(1)
        except KeyboardInterrupt:
                return 0
        return 1

    #handle responses
    def iqHandler(self, conn,iq_node):
        print 'in iqHandler'
        self.catches = self.catches + 1

        #we have looped enough, die
        if self.catches == 4:
            print 'i think we did it'
            sys.exit(0)

        #print response, don't need to send anything back    
        if self.updated == True:
            print iq_node

        #havn't updated yet, sent status update
        else:
            #we can build of response
            node = iq_node.getChildren()[0]

            #remove what we don't ned
            node.delAttr('status-list-max')
            node.delAttr('status-max')
            node.delAttr('status-list-contents-max')
            iq_node.delAttr('from')
            iq_node.delAttr('type')
            iq_node.delAttr('to')

           #update the current status
            curr_status = node.getChildren()[0]

            #no need to update
            if curr_status.getData() == self.twitter_status:
                print 'status is already tweet'
                sys.exit(0)

            curr_status.setData(self.twitter_status)

            #set response
            iq_node.setType('set')

            print 'sending'
            print iq_node
            self.updated = True
            conn.send(iq_node)
            print 'end of iqHandler\n\n'

    #start talking to the server and update status
    def updateGtalkStatus(self):

        #connect
        jid=xmpp.protocol.JID(self.google_login)
        cl=xmpp.Client(jid.getDomain(),debug=[])
        if not cl.connect(('talk.google.com',5222)):
            print 'Can not connect to server.'
            sys.exit(1)
        if not cl.auth(jid.getNode(),self.google_pass):
            print 'Can not auth with server'
            sys.exit(1)

        #build query to get current status
        iq = xmpp.Iq()
        iq.setType('get')
        iq.setTo('timothy.broder@gmail.com')

        node = xmpp.Node()
        node.setName('query')
        node.setAttr('xmlns', 'google:shared-status')

        iq.addChild(node=node) 
        print iq

        #register with server and send subscribe to status updates
        cl.RegisterHandler('iq',self.iqHandler)
        cl.send(iq)

        self.GoOn(cl)
        cl.disconnect()

    #get current twitter status
    def getTwitterStatus(self):
        api = twitter.Api(username=self.twitter_login, password=self.twitter_pass)
        self.twitter_status = api.GetUserTimeline(self.twitter_login, 1)[0].text

        #don't want to use replies
        if self.twitter_status.find('@') >= 0:
            sys.exit(0)

t = Twitter2gChat()
t.getTwitterStatus()
t.updateGtalkStatus()