示例#1
0
def main():
    """ The main function.
    Check given arguments get feed and run given command. """
    argv = docopt.docopt(__doc__, version='informant v{}'.format(__version__))
    InformantConfig().set_argv(argv)
    InformantConfig().debug_print = ui.debug_print
    InformantConfig().readlist = fs.read_datfile()
    config = InformantConfig().get_config()
    ui.debug_print('cli args: {}'.format(argv))

    if 'feeds' in config:
        feed = []
        for config_feed in config['feeds']:
            feed += Feed(config_feed).entries
    else:
        feed = Feed().entries

    if not feed:
        ui.warn_print('no news feed items, informant is performing no action')
        sys.exit()

    feed = sorted(feed, key=lambda k: k.timestamp, reverse=True)

    if argv.get(CHECK_CMD):
        check_cmd(feed)
    elif argv.get(LIST_CMD):
        list_cmd(feed)
    elif argv.get(READ_CMD):
        read_cmd(feed)
    sys.exit()
示例#2
0
def format_list_item(entry, index):
    """ Returns a formatted string with the entry's index number, title, and
    right-aligned timestamp. Unread items are bolded"""
    bold = InformantConfig().colors['BOLD']
    clear = InformantConfig().colors['CLEAR']
    terminal_width = shutil.get_terminal_size().columns
    timestamp = str(entry.pretty_date)
    wrap_width = terminal_width - len(timestamp) - 1
    heading = str(index) + ': ' + entry.title
    wrapped_heading = textwrap.wrap(heading, wrap_width)
    padding = terminal_width - len(wrapped_heading[0] + timestamp)
    if entry.has_been_read():
        return (
            wrapped_heading[0] +
            ' ' * (padding) +
            timestamp +
            '\n'.join(wrapped_heading[1:])
                )
    else:
        return (
            bold +
            wrapped_heading[0] +
            clear +
            ' ' * (padding) +
            timestamp +
            bold +
            '\n'.join(wrapped_heading[1:]) +
            clear
        )
示例#3
0
    def fetch(self):
        feed = None
        if InformantConfig().get_argv_use_cache():
            cachefile = InformantConfig().get_cachefile()
            os.umask(
                0o0002
            )  # unrestrict umask so we can cache with proper permissions
            try:
                session = CacheControl(requests.Session(),
                                       cache=FileCache(cachefile,
                                                       filemode=0o0664,
                                                       dirmode=0o0775))
                feed = feedparser.parse(session.get(self.url).content)
            except Exception as e:
                ui.err_print('Unable to read cache information: {}'.format(e))
                feed = feedparser.parse(self.url)
        else:
            feed = feedparser.parse(self.url)

        if feed.bozo:
            ui.err_print('Encountered feed error: {}'.format(
                feed.bozo_exception))
            sys.exit(255)
        else:
            return feed
示例#4
0
def main():
    argv = docopt.docopt(__doc__, version='informant v{}'.format(__version__))
    InformantConfig().set_argv(argv)
    InformantConfig().debug_print = ui.debug_print
    InformantConfig().readlist = fs.read_datfile()
    run()
    sys.exit()
示例#5
0
def read_cmd(feed):
    """ Run the read command. Print news items and mark them as read. """
    argv = InformantConfig().get_argv()
    if argv.get(READALL_OPT):
        for entry in feed:
            entry.mark_as_read()
    else:
        if argv[ITEM_ARG]:
            try:
                item = int(argv[ITEM_ARG])
                entry = feed[item]
            except ValueError:
                for entry in feed:
                    if entry.title == item:
                        break
                #NOTE: this will read the oldest unread item if no matches are found
            ui.pretty_print_item(entry)
            entry.mark_as_read()
        else:
            unread_entries = list()
            for entry in feed:
                if not entry.has_been_read():
                    unread_entries.insert(0, entry)
            for entry in unread_entries:
                ui.pretty_print_item(entry)
                entry.mark_as_read()
                if entry is not unread_entries[-1]:
                    read_next = ui.prompt_yes_no('Read next item?', 'yes')
                    if read_next in ('n', 'no'):
                        break
                else:
                    print('No more unread items')
    fs.save_datfile()
示例#6
0
 def mark_as_read(self):
     """ Save this entry to mark it as read. """
     readlist = InformantConfig().readlist
     if self.has_been_read():
         return
     title = self.title
     date = self.timestamp
     readlist.append(str(date.timestamp()) + '|' + title)
示例#7
0
def warn_print(*args, **kwargs):
    """ Same as builtin print but output to stderr with yellow color and
    "WARNING" preamble.
    """
    yellow = InformantConfig().colors['YELLOW']
    clear = InformantConfig().colors['CLEAR']
    msg = yellow + 'WARN: ' + clear
    for arg in args:
        msg += arg
    print(msg, file=sys.stderr, **kwargs)
示例#8
0
def err_print(*args, **kwargs):
    """ Same as builtin print but output to stderr with red color and "ERROR"
    preamble.
    """
    red = InformantConfig().colors['RED']
    clear = InformantConfig().colors['CLEAR']
    msg = red + 'ERROR: ' + clear
    for arg in args:
        msg += arg
    print(msg, file=sys.stderr, **kwargs)
示例#9
0
def pacman_msg(*args, **kwargs):
    """ Same as print but include yellow color and "informant" preamble so the
    message is clear in pacman.
    """
    yellow = InformantConfig().colors['YELLOW']
    clear = InformantConfig().colors['CLEAR']
    msg = yellow + ':: informant: ' + clear
    for arg in args:
        msg += arg
    print(msg, **kwargs)
示例#10
0
 def has_been_read(self):
     """ Check if this entry has been read and return True or False. """
     debug = InformantConfig().get_argv_debug()
     readlist = InformantConfig().readlist
     if debug:
         ui.debug_print(readlist)
     title = self.title
     date = self.timestamp
     if str(date.timestamp()) + '|' + title in readlist:
         return True
     return False
示例#11
0
def list_cmd(feed):
    """ Run the list command. Print out a list of recent news item titles. """
    argv = InformantConfig().get_argv()
    if argv.get(REV_OPT):
        feed_list = reversed(feed)
    else:
        feed_list = feed
    index = 0
    for entry in feed_list:
        if not argv.get(UNREAD_OPT) \
        or (argv.get(UNREAD_OPT) and not entry.has_been_read()):
            print(ui.format_list_item(entry, index))
            index += 1
示例#12
0
    def fetch(self):
        feed = None
        if InformantConfig().get_argv_clear_cache():
            ui.debug_print('Clearing cache')
            fs.clear_cachefile()
        if InformantConfig().get_argv_use_cache():
            ui.debug_print('Checking cache in {}'.format(
                InformantConfig().get_cachefile()))
            cachefile = InformantConfig().get_cachefile()
            os.umask(
                0o0002
            )  # unrestrict umask so we can cache with proper permissions
            try:
                session = CacheControl(requests.Session(),
                                       cache=FileCache(cachefile,
                                                       filemode=0o0664,
                                                       dirmode=0o0775))
                feed = feedparser.parse(session.get(self.url).content)
            except Exception as e:
                ui.err_print('Unable to read cache information: {}'.format(e))
                ui.debug_print('Falling back to fetching feed')
                feed = feedparser.parse(self.url)
        else:
            feed = feedparser.parse(self.url)

        if feed.bozo:
            e = feed.bozo_exception
            if isinstance(e, URLError):
                # most likely this is an internet issue (no connection)
                ui.warn_print('News could not be fetched for {}'.format(
                    self.name if self.name is not None else self.url))
                ui.debug_print('URLError: {}'.format(e.reason))
            else:
                # I think this is most likely to be a malformed feed
                ui.err_print('Encountered feed error: {}'.format(
                    feed.bozo_exception))
                ui.debug_print('bozo message: {}'.format(
                    feed.bozo_exception.getMessage()))
            # In either of these error cases we probably shouldn't return error
            # so the pacman hook won't hold up an operation.
            # Here return an empty set of entries in case only one of multiple
            # feeds failed to fetch
            try:
                feed = feedparser.util.FeedParserDict()
                feed.update({'entries': []})
            except Exception as e:
                ui.err_print('Unexpected error: {}'.format(e))
                sys.exit()

        return feed
示例#13
0
def save_datfile():
    """ Save the readlist to the datfile """
    debug = InformantConfig().get_argv_debug()
    readlist = InformantConfig().readlist
    if debug:
        return
    filename = InformantConfig().get_savefile()
    try:
        # then open as write to save updated list
        with open(filename, 'wb') as pickle_file:
            pickle.dump(readlist, pickle_file)
            pickle_file.close()
    except PermissionError:
        ui.err_print('Unable to save read information, please re-run with \
correct permissions to access "{}".'.format(filename))
        sys.exit(255)
示例#14
0
 def has_been_read(self):
     """ Check if this entry has been read and return True or False. """
     readlist = InformantConfig().readlist
     #ui.debug_print('readlist: {}'.format(readlist))
     title = self.title
     date = self.timestamp
     if str(date.timestamp()) + '|' + title in readlist:
         return True
     return False
示例#15
0
def running_from_pacman():
    """ Return True if the parent process is pacman """
    debug = InformantConfig().get_argv_debug()
    ppid = os.getppid()
    p_name = subprocess.check_output(['ps', '-p', str(ppid), '-o', 'comm='])
    p_name = p_name.decode().rstrip()
    if debug:
        debug_print('informant: running from: {}'.format(p_name))
    return p_name == 'pacman'
示例#16
0
def save_datfile():
    """ Save the readlist to the datfile """
    debug = InformantConfig().get_argv_debug()
    readlist = InformantConfig().readlist
    if debug:
        ui.debug_print('running in debug mode, will not update readlist')
        return
    filename = InformantConfig().get_savefile()
    try:
        # then open as write to save updated list
        with open(filename, 'wb') as pickle_file:
            pickle.dump(readlist, pickle_file)
            pickle_file.close()
    except PermissionError:
        ui.err_print('Unable to save read information, please re-run with \
correct permissions to access "{}".'.format(filename))
        sys.exit(
            255
        )  # this should never block pacman because the hook should run with root/sudo
示例#17
0
def clear_cachefile():
    """ Empty the cachefile directory """
    cache_dir = InformantConfig().get_cachefile()
    pattern = os.path.join(cache_dir, '*')
    ui.debug_print('Removing based on pattern: {}'.format(pattern))
    for filename in glob.glob(pattern):
        if os.path.isdir(filename):
            shutil.rmtree(filename)
        else:
            os.remove(filename)
示例#18
0
def read_datfile():
    """ Return the saved readlist from the datfile """
    debug = InformantConfig().get_argv_debug()
    filename = InformantConfig().get_savefile()
    if debug:
        ui.debug_print('Getting datfile from "{}"'.format(filename))

    try:
        with open(filename, 'rb') as pickle_file:
            try:
                readlist = pickle.load(pickle_file)
                pickle_file.close()
                if isinstance(readlist, tuple):
                    # backwards compatibility with informant < 0.4.0 save data
                    readlist = readlist[1]
            except (EOFError, ValueError):
                readlist = []
    except (FileNotFoundError, PermissionError):
        readlist = []
    return readlist
示例#19
0
def pretty_print_item(entry):
    """ Print out the given entry, replacing some markup to make it look nicer.
    If the '--raw' option has been provided then the markup will not be
    replaced. """
    argv = InformantConfig().get_argv()
    title = entry.title
    body = entry.body
    bold = InformantConfig().colors['BOLD']
    clear = InformantConfig().colors['CLEAR']
    timestamp = str(entry.pretty_date)
    if not argv.get(RAW_OPT):
        #if not using raw also bold title
        title = bold + title + clear
        h2t = html2text.HTML2Text()
        h2t.inline_links = False
        h2t.body_width = 85
        body = h2t.handle(body)
    if entry.feed_name is not None:
        feed_name = '({})'.format(entry.feed_name)
        print('{}\n{}\n{}\n\n{}'.format(title, feed_name, timestamp, body))
    else:
        print('{}\n{}\n\n{}'.format(title, timestamp, body))
示例#20
0
def run():
    """ The main function.
    Check given arguments get feed and run given command. """
    argv = InformantConfig().get_argv()
    config = InformantConfig().get_config()
    if argv.get(DEBUG_OPT):
        ui.debug_print('cli args: {}'.format(argv))

    if 'feeds' in config:
        feed = []
        for config_feed in config['feeds']:
            feed += Feed(config_feed).entries
    else:
        feed = Feed().entries

    feed = sorted(feed, key=lambda k: k.timestamp, reverse=True)

    if argv.get(CHECK_CMD):
        check_cmd(feed)
    elif argv.get(LIST_CMD):
        list_cmd(feed)
    elif argv.get(READ_CMD):
        read_cmd(feed)
示例#21
0
def debug_print(*args, **kwargs):
    """ Same as builtin print but output to stderr and only when the debug
    option is provided.
    """
    if InformantConfig().get_argv_debug():
        print(*args, file=sys.stderr, **kwargs)