Report abuse

"""
This is a Twitter bot that lets you play Zork.

Props to @jenesaisdiq for inspiration and @redroselet for moral support.

To play, just twitter @zorker with a Zork move. For example:

'@zorker attack troll with sword'

Be cautioned, though, this bot takes it's time playing, and if everyone tries
to move at once it'll all fall apart.
"""

__author__ = 'Scott Torborg (scotttorborg.com)'

from twitter import Twitter
from pprint import pprint
import pexpect, re, time

username = 'zorker'
password = 'REDACTED'

twitter = Twitter(username, password)

class dfrotz(object):
    def __init__(self):
        self.dfrotz = pexpect.spawn('dfrotz zork1.z5')
        self.re = re.compile('\n((?P<location>.*)Score\:\ '
                            '(?P<score>\d+)\s*Moves\:\ '
                            '(?P<moves>\d+))?\s*(?P<text>.+)\>',
                            re.DOTALL)

    def tell(self, line):
        self.dfrotz.sendline(line.strip())

    def listen(self):
        resp = self._response()
        raw = self.re.search(resp).groupdict()
        for k in ['score', 'location', 'moves']:
            if k in raw and raw[k]:
                setattr(self, k, raw[k].strip())
        return raw['text'].strip().replace('\n', ' ')

    def _response(self):
        res = []
        c = None
        while c != '>':
            try:
                c = self.dfrotz.read_nonblocking(1, 0.1)
                res.append(c)
            except Exception, msg:
                break
        return ''.join(res).replace('\r\n', '\n')


def parse_twitter(text):
    r = re.match('^(\@\S+\s)*(?P<msg>.+)$', text)
    if r:
        return r.group('msg')
    else:
        return None

def twitter_short(s):
    print "twittering '%s'" % s
    twitter.statuses.update(status=s)

def twitter_long(s):
    msg = ''
    for word in s.strip().split(' '):
        if len(msg) + len(word) > 139:
            twitter_short(msg)
            msg = word
        else:
            if msg == '':
                msg = word
            else:
                msg += ' ' + word
    twitter_short(msg)
    # wait 3 seconds between posting twitters
    time.sleep(3)

# when we first launch, get all @replies to our username and store the
# most recent id, so we ignore any @replies that are before this one.
replies = twitter.statuses.replies()
if len(replies) > 0:
    last_id = replies[0]['id']
else:
    last_id = None

df = dfrotz()

intro = df.listen()
df._response()
twitter_long('West of House You are standing in an open field west of a '
             'white house, with a boarded front door. There is a small '
             'mailbox here.')

count = 0
while True:
    # get moves from twitter
    print "getting moves from twitter"
    if last_id:
        moves = twitter.statuses.replies(since_id=last_id)
    else:
        moves = twitter.statuses.replies()
    for move in moves:
        last_id = move['id']
        reply = parse_twitter(move['text'])
        print "move is [%s]" % reply
        if reply and reply != '':
            # send move to frotz
            df.tell(reply)
            # twitter response from frotz
            twitter_long(df.listen())
            # every 15 moves, twitter with the current loc, score, moves.
            if (count % 15) == 0:
                tweet = ('Location: %s, Score: %s, Moves: %s'
                                % (df.location, df.score, df.moves))
                twitter_long(tweet)
            count += 1
        else:
            print "ignoring empty reply"

    time.sleep(120)