1. Josh VanderLinden
  2. twibber

Commits

Josh VanderLinden  committed 5437935

I think I got scheduling to work, but it's hard to say because Twitter is having a big fart right now. I also tried to add some error-catching code so the program doesn't stop working randomly.

  • Participants
  • Parent commits 278a6eb
  • Branches default

Comments (0)

Files changed (1)

File twim.py

View file
 import ConfigParser
 import logging
 import os
-import picke
+import pickle
 import re
 import simplejson
 import sys
 }
 
 class User(object):
-    __slots__ = ('id', 'username', 'password', 'last_tweet_id', 'api', 
+    __slots__ = ('id', 'username', 'password', 'last_tweet_id', '_api', 
                  'last_update')
+    def __init__(self, **kwargs):
+        for kw, val in kwargs.items():
+            setattr(self, kw, val)
+        
+        for sl in self.__slots__:
+            if sl not in kwargs.keys():
+                setattr(self, sl, None)
+    
+    def _get_api(self):
+        """
+        Returns the Twitter API for this user
+        """
+        if not self._api:
+            # connect to Twitter for this user
+            self._api = twitter.Api(username=self.username, 
+                                    password=self.password)
+            self._api.SetSource(APP_TITLE)
+            self._api.SetXTwitterHeaders(APP_TITLE, __homepage__, __version__)
+        return self._api
+
+    api = property(_get_api)
 
 class IMConfig(object):
-    _users = []
+    _users = {}
+    _scheduled_tweets = []
     def __init__(self, configfile):
         # read the configuration and establish default settings
         self.configfile = configfile
         log.debug('Reading config from %s' % configfile)
         self._parser.read(configfile)
         self._default_user = None
-        self._scheduled_tweets = None
 
         for section, options in DEFAULTS.items():
             if not self._parser.has_section(section):
 
         # save the configuration just in case the file has not yet been created
         self.Persist()
+
         self.GetUserList()
+        self.GetScheduledTweets()
 
         if self.send_to_user == self.login_as_user:
             raise ValueError('send_to_user cannot be the same as login_as_user!')
 
             section = OPTIONS[option]
             result = self._parser.set(section, option, str(value))
-            self.Persist()
+            #self.Persist()
             return result
         except:
             pass
                     users[username] = User(
                         id=counter,
                         username=username,
-                        password=password,
-                        last_tweet_id=None
+                        password=password
                     )
 
                 counter += 1
                 log.info('%s has not been saved yet...' % user.username)
                 unsorted.append(user)
             else:
-                un_opt = USERNAME % uid
-                pw_opt = PASSWORD % uid
+                un_opt = USERNAME % user.id
+                pw_opt = PASSWORD % user.id
 
                 # don't save any changes to this user unless the password has
                 # changed since the launch
         """
         Retrieves any scheduled tweets that we have
         """
-        self._scheduled_tweets = pickle.loads(self.scheduled_tweets)
+        try:
+            schedule = pickle.loads(self.scheduled_tweets)
+        except:
+            schedule = []
+        self._scheduled_tweets = schedule
+    
+    def SetScheduledTweets(self):
+        """
+        Pickles the scheduled tweets
+        """
+        self.scheduled_tweets = pickle.dumps(self._scheduled_tweets)
 
     def Persist(self):
         """
         Writes changes to the config to disk.
         """
         self.SaveUserList()
-        self.scheduled_tweets = pickle.dumps(self._scheduled_tweets)
+        if len(self._scheduled_tweets):
+            self.SetScheduledTweets()
         out = open(self.configfile, 'wb')
         self._parser.write(out)
         out.close()
                     'password': password
                 })
         config._users = dict((u.username, u) for u in current_users)
-        config.Persist()
         
         # Persist() is automatically called for each of these
         config.send_to_user = self.page2.username.GetValue()
         config.login_as_user = self.page3.jabber_id.GetValue()
         config.login_as_pass = self.page3.password.GetValue()
+        config.Persist()
 
 ICON = PyEmbeddedImage(
     'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAGIElEQVRIiY2Wa4yUVxnHf+/9n'
             'follow': self.OnFollowCommand,
             'unfollow': self.OnUnfollowCommand,
             'whois': self.OnWhoIsCommand,
-            'remind': None,
             'undo': self.OnUndoCommand,
             'rt': None,
             'search': self.OnSearchCommand,
             'favorite': None,
             'unfavorite': None,
-            'schedule': None
+            'schedule': self.OnScheduleCommand
         }
         if len(self.users) > 1:
             self._commands['as'] = self.OnAsCommand
         for i, pattern in enumerate(patterns):
             match = re.match(pattern, remaining)
             if match:
+                matched = True
                 args = dict(zip(params[i], match.groups()))
                 log.debug('Schedule command args: %s' % args)
-                matched = True
+                
+                tweet = args.pop('tweet')
+                now = datetime.now()
+                
+                if 'delta' in args.keys():
+                    unit = args['unit']
+                    value = int(args['delta'])
+                    delta = timedelta(**{unit: value})
+                    schedule_at = now + delta
+                else:
+                    for key, val in args.items():
+                        args[key] = int(val)
+                    
+                    required = ('year', 'month', 'day')
+                    for arg in required:
+                        if arg not in args.keys():
+                            args[arg] = getattr(now, arg)
+                    
+                    schedule_at = datetime(**args)
+                
+                log.info('Scheduled to say "%s" at %s' % (tweet, schedule_at))
+                config._scheduled_tweets.append((schedule_at, 
+                                                 user.username, 
+                                                 tweet))
+                config.Persist()
+                self.TellUser('%s is now scheduled to tweet "%s" at %s' % (
+                    APP_TITLE,
+                    tweet,
+                    schedule_at.strftime('%H:%M on %b %d, %Y')
+                ))
         
         if not matched:
-            self.TellUser(
-                "Sorry, but I don't understand your scheduling format!")
+            if len(remaining):
+                self.TellUser(
+                    "Sorry, but I don't understand your scheduling format!")
+            else:
+                schedule = [at.strftime('On %Y-%m-%d (%H:%M): ') + t for at,u,t in config._scheduled_tweets]
+                self.TellUser('Scheduled Tweets:\n' + '\n'.join(schedule))
 
     def OnMessage(self, con, evt):
         """
         sender = evt.getFrom().getStripped()
         body = evt.getBody()
         
+        if not body: return
         log.info('Received message from %s: %s' % (sender, body))
 
         # don't accept messages from anyone but the user we sent tweets to
                 self.client.send(xmpp.Presence(to=config.send_to_user, typ=typ))
             return
 
-        if not body: return
-
         as_user = config.GetDefaultUser()
         body = body.strip()
         if body.startswith('./'):
                     return
 
                 body = body.strip()
+        
+        self.PostUpdate(as_user, body)
+    
+    def PostUpdate(self, user, body):
+        """
+        Sends an update to Twitter
+        """
 
-        # post the update
-        posted = as_user.api.PostUpdates(body)
+        posted = user.api.PostUpdates(body)
         if posted:
-            log.info('Posted message as %s' % as_user.username)
+            log.info('Posted message as %s' % user.username)
             self._history.extend(posted)
             text = 'Successfully posted %i message%s as %s' % (
                 len(posted),
                 len(posted) != 1 and 's' or '',
-                as_user.username
+                user.username
             )
             self.TellUser(text)
 
         For the love of threads!
         """
         while not event.isSet():
-            event.wait(interval)
-            action()
+            try:
+                event.wait(interval)
+                action()
+            except Exception, ex:
+                log.error(ex)
+                self.TellUser('We are experiencing problems... please be patient.')
 
     def CheckForUpdates(self):
         """
         updates are found, they will be sent to the Jabber user specified in
         the configuration file
         """
-        now = datetime.now()
-        for user in self.users.values():
-            last_update = user.last_update or now - self.interval
-            next_run = last_update + self.interval
-            if next_run <= now:
-                self.GetUpdatesFor(user)
+        try:
+            now = datetime.now()
+    
+            # run through any scheduled tweets to see if it's time to post
+            to_remove = []
+            for info in config._scheduled_tweets:
+                at, un, tx = info
+                if at <= now:
+                    log.info('Time to tweet "%s"' % tx)
+                    self.PostUpdate(self.users[un], tx)
+                    to_remove.append(info)
+            
+            # remove any tweets that have been posted
+            for info in to_remove:
+                config._scheduled_tweets.remove(info)
+            if len(to_remove):
+                config.SetScheduledTweets()
+        
+            for user in self.users.values():
+                last_update = user.last_update or now - self.interval
+                next_run = last_update + self.interval
+                if next_run <= now:
+                    self.GetUpdatesFor(user)
+        except urllib2.HTTPError, ex:
+            log.error(ex)
+            self._client = None
+            self.GetClient()
+            time.sleep(5)
 
     def CheckForPosts(self):
         """
         are found, they're IM'ed out.
         """
         api = user.api
-        if not api:
-            # connect to Twitter for this user
-            api = twitter.Api(username=user.username, password=user.password)
-            api.SetSource('Twitter IM')
-            api.SetXTwitterHeaders(APP_TITLE, __homepage__, __version__)
-            user.api = api
 
         # get some updates
         do_not_update = len(updates) > 0
         
         if not do_not_update:
             user.last_update = datetime.now()
+        
+        config.Persist()
     
     def HTMLizeTweet(self, name, tweet):
         """