class Notifier: ''' main class ''' program_dir = os.path.dirname(os.path.realpath(__file__)) plugin_man = None plugin_context = None conf = None feed = None do_refresh = False def __init__(self): try: logger.info('Initializing directories...') dirs = self.init_dirs() os.chdir(dirs.user_cache_dir) logger.debug('* Cache: ' + dirs.user_cache_dir) logger.info('* Data: ' + dirs.user_data_dir) plugin_dirs = [] plugin_dirs.append(os.path.join(self.program_dir, 'plugins')) plugin_dirs.append(os.path.join(dirs.user_data_dir, 'plugins')) for d in plugin_dirs: logger.info('* Plugins: ' + d) print('') logger.info('Initializing configuration...') conf_file_path = os.path.join(dirs.user_data_dir, 'fbnotify.conf') logger.info('* File: ' + conf_file_path) self.conf = Config(conf_file_path) print('') logger.info('Initializing feed...') self.feed = Feed(self.conf.feed.url) print('') logger.info('Initializing plugins...') logger.debug('* Blacklisted: ' + str(self.conf.program.plugin_blacklist)) self.plugin_man = PluginManager(plugin_dirs, self.conf.program.plugin_blacklist) self.plugin_man.load_by_role('notify') self.plugin_man.load_by_role('list') self.plugin_man.load_by_role('status') self.plugin_context = self.plugin_man.messaging.register_plugin(self, 'fbnotify') print('') logger.debug('Work dir: ' + os.getcwd()) print except Exception as e: logger.error(traceback.format_exc()) self.bad_stop() def start(self): ''' main loop ''' try: while True: logger.info('Updating...' + ' [' + email.utils.formatdate(time.mktime(time.localtime()), True) + ']') # Update the feed self.plugin_man.messaging.send( 'status', status='updating', description='Updating' ) new_items = None try: new_items = self.feed.get_new_items() except IOError: # Error self.plugin_man.messaging.send( 'status', status='error', description='Unable to load feed URL' ) self.adjust_interval(0) # If new items are loaded if new_items is not None: self.plugin_man.messaging.send( 'status', status='idle', description='Waiting' ) self.notify_items(new_items) self.adjust_interval(len(new_items)) # Wait count = 0 while count < self.conf.feed.check_interval: time.sleep(0.25) count += 0.25 self.plugin_context.receive() if self.do_refresh: break self.do_refresh = False print('') except KeyboardInterrupt: print('') logger.info('Stopped') self.stop() except Exception as e: logger.error(traceback.format_exc()) self.bad_stop() self.bad_stop(); def plugin_receive(self, channel, message): # Receiving a message from the 'fbnotify' channel if 'quit' in message: logger.debug('Quit requested') self.stop() elif 'refresh' in message: logger.debug('Refresh requested') self.do_refresh = True def bad_stop(self): ''' bad things happened, so bad that the application must stop ''' logger.error('Bad Exit!') self.stop() def stop(self): ''' stop the application ''' if self.plugin_man: logger.info('Unloading all plugins...') self.plugin_man.unload_all() logger.info('Exit') quit() def notify_items(self, items): ''' shows notifications about items ''' n = len(items) n_new_notifications = '{0} new notification{1}'.format(n, '' if n == 1 else 's') logger.info(n_new_notifications) if n == 0: return # Update item list self.plugin_man.messaging.send( 'list', items = items ) if n > 1: # Many notifications if self.conf.notification.itemize >= n: # Show individual notifications interval = self.conf.notification.item_interval for item in sorted(items, key=lambda x: x.dt): if item.image_path == None: self.notify(item.text, self.format_time(item.dt), timeout=interval, link=item.link) else: self.notify(item.text, self.format_time(item.dt), icon='file://' + item.image_path, timeout=interval, link=item.link) time.sleep(interval + 1) else: # Declare multiple notifications dt = items[n-1].dt # Earliest notification date self.notify(n_new_notifications, self.format_time(dt), link='www.facebook.com/notifications') else: # Single notification item = items[0] if self.conf.notification.show_content: if item.image_path == None: self.notify(item.text, self.format_time(item.dt), link=item.link) else: self.notify(item.text, self.format_time(item.dt), icon='file://' + item.image_path, link=item.link) else: self.notify(n_new_notifications, self.format_time(item.dt)) def notify(self, title, body, icon=icons.xdg_icon, timeout=10, link=None): ''' requests to show a notification ''' logger.debug('Notify: ' + title + ' ' + body) # This will send a message to any plugin # listening to the 'notify' channel self.plugin_man.messaging.send( 'notify', title = title, body = body, icon = icon, timeout = timeout, link = link ) def adjust_interval(self, new): ''' automatically adjust the feed check interval ''' if self.conf.feed.dynamic_interval: ci = self.conf.feed.check_interval if new: min_interval = 15 # 15 seconds if ci > min_interval: ci = ci * 1/5 if ci < min_interval: ci = min_interval logger.info('Decreased check interval to {0}s'.format(ci)) else: max_interval = 60 * 20 # 20 minutes if ci < max_interval: ci = ci * 8/7 if ci > max_interval: ci = max_interval logger.info('Increased check interval to {0}s'.format(ci)) self.conf.feed.check_interval = ci def init_dirs(self): ''' creates, if not existing, application directories ''' dirs = appdirs.AppDirs('fbnotify', 'Kalabasa') conf_dir = dirs.user_data_dir cache_dir = dirs.user_cache_dir if not os.path.isdir(conf_dir): logger.info('Created configuration directory ' + conf_dir) os.makedirs(conf_dir) if not os.path.isdir(cache_dir): logger.info('Created cache directory ' + cache_dir) os.makedirs(cache_dir) return dirs def format_time(self, then): ''' Formats relative time to the specified time ''' now = datetime.now() if then >= now: text = 'Just now' else: delta = now - then if delta.days >= 1: text = '{0} day{1} ago'.format(delta.days, '' if delta.days == 1 else 's') elif delta.seconds >= 3600: hours = delta.seconds / 3600 text = '{0} hour{1} ago'.format(hours, '' if hours == 1 else 's') elif delta.seconds >= 60: minutes = delta.seconds / 60 text = '{0} minute{1} ago'.format(minutes, '' if minutes == 1 else 's') elif delta.seconds >= 30: text = '{0} second{1} ago'.format(delta.seconds, '' if delta.seconds == 1 else 's') else: text = 'Just now' return text