"""
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)