237 lines
8.8 KiB
Python
Executable File
237 lines
8.8 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# Atomstrom is a small but flexible feed aggregator.
|
|
# Copyright (C) 2013 Ronald Schaten <ronald@schatenseite.de>
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free Software
|
|
# Foundation, either version 3 of the License, or (at your option) any later
|
|
# version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along with
|
|
# this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from models import Base, Feed, Feedinfo, Entry
|
|
|
|
from sqlalchemy import create_engine, desc, func
|
|
from sqlalchemy.orm import sessionmaker
|
|
from datetime import datetime
|
|
from ddate import ddate
|
|
import sys
|
|
import codecs
|
|
import ConfigParser
|
|
from argparse import ArgumentParser
|
|
from email.header import Header
|
|
import smtplib
|
|
import textwrap
|
|
|
|
def send_mail(sender, receiver, subject, body):
|
|
print 'sending to %s: %s' % (receiver[0], subject)
|
|
message = 'From: "%s" <%s>\n' % (Header(sender[0], 'utf-8'), sender[1])
|
|
message += 'To: "%s" <%s>\n' % (Header(receiver[0], 'utf-8'), receiver[1])
|
|
message += 'Subject: %s\n' % Header(subject, 'utf-8')
|
|
message += 'Content-Type: text/plain; charset="utf-8"\n\n'
|
|
message += body.encode('utf-8')
|
|
server = smtplib.SMTP('localhost')
|
|
server.sendmail(sender[1], [receiver[1]], message)
|
|
server.close()
|
|
|
|
def truncate_text(content, length=100, suffix='...'):
|
|
content = " ".join(content.split())
|
|
if len(content) <= length:
|
|
return content
|
|
else:
|
|
return content[:length].rsplit(' ', 1)[0]+suffix
|
|
|
|
def mail_daily_digest(session, sender, receiver, prefix):
|
|
print 'mailing daily digest...'
|
|
entries = session.query(Feed, Feedinfo, Entry).\
|
|
filter(Feed.id == Feedinfo.feed_id).\
|
|
filter(Feed.id == Entry.feed_id).\
|
|
filter(Feed.enabled == 1).\
|
|
filter(Feed.daily == 1).\
|
|
filter(Entry.sent == None).\
|
|
order_by(desc(Entry.firstfetched), Feedinfo.title, Entry.title).\
|
|
all()
|
|
body = ''
|
|
count = 0
|
|
for feed, feedinfo, entry in entries:
|
|
count = count + 1
|
|
link = entry.link
|
|
if entry.resolvedlink:
|
|
link = entry.resolvedlink
|
|
text = truncate_text(entry.get_text(enclosures=False), 250)
|
|
text = textwrap.fill(text, width=78)
|
|
try:
|
|
body += '=> %s - %s\n' % (entry.firstfetched.strftime('%Y-%m-%dT%H:%M'), feedinfo.title)
|
|
body += '> %s\n' % entry.title
|
|
body += '%s\n' % text
|
|
body += '%s\n\n' % link
|
|
except:
|
|
print 'ERROR processing entry %s' % entry.id;
|
|
print sys.exc_info()
|
|
print 'not sending mail'
|
|
return
|
|
if count > 0:
|
|
today = datetime.now()
|
|
subject = '%s (%s) - %d entries' % (today.strftime('%Y-%m-%d'), today.strftime('%A'), count)
|
|
body = '%s\n\n%s\n\n%s' % (subject, ddate(), body)
|
|
if prefix != '':
|
|
subject = '%s %s' % (prefix, subject)
|
|
send_mail(sender, receiver, subject, body)
|
|
for feed, feedinfo, entry in entries:
|
|
entry.sent = datetime.now()
|
|
else:
|
|
print 'no unmailed digest-entries found... not sending mail.'
|
|
|
|
def mail_single_entry(feed, feedinfo, entry, sender, receiver, prefix):
|
|
subject = '%s' % (entry.title)
|
|
if prefix != '':
|
|
subject = '%s %s' % (prefix, subject)
|
|
link = entry.link
|
|
if entry.resolvedlink:
|
|
link = entry.resolvedlink
|
|
body = entry.get_text()
|
|
#body = '\n'.join(textwrap.wrap(body, width=78, break_long_words=False, replace_whitespace=False, break_on_hyphens=False))
|
|
body += '\n\n%s\n%s\n' % (feedinfo.link, link)
|
|
sender[0] = feedinfo.title
|
|
send_mail(sender, receiver, subject, body)
|
|
entry.sent = datetime.now()
|
|
|
|
def mail_single_entries(session, sender, receiver, prefix):
|
|
print 'mailing single entries...'
|
|
count = 0
|
|
entries = session.query(Feed, Feedinfo, Entry).\
|
|
filter(Feed.id == Feedinfo.feed_id).\
|
|
filter(Feed.id == Entry.feed_id).\
|
|
filter(Feed.enabled == 1).\
|
|
filter(Feed.daily == 0 or Feed.daily == None).\
|
|
filter(Entry.sent == None).\
|
|
all()
|
|
for feed, feedinfo, entry in entries:
|
|
mail_single_entry(feed, feedinfo, entry, sender, receiver, prefix)
|
|
count = count + 1
|
|
if count > 0:
|
|
print 'sent %d mails' % count
|
|
else:
|
|
print 'no unmailed single entries found... not sending mail.'
|
|
|
|
def list_all_feeds(session):
|
|
allfeeds = session.query(Feed).\
|
|
order_by(Feed.id)
|
|
totalfeeds = 0
|
|
totalentries = 0
|
|
for feed in allfeeds:
|
|
print feed
|
|
totalfeeds += 1
|
|
totalentries += len(feed.entries)
|
|
print 'TOTAL: %d entries in %d feeds.' % (totalentries, totalfeeds)
|
|
|
|
def fetch_all_feeds(session):
|
|
print 'fetching all feeds...'
|
|
allfeeds = session.query(Feed).\
|
|
filter_by(enabled=1).\
|
|
order_by(Feed.id)
|
|
for feed in allfeeds:
|
|
feed.fetch(session)
|
|
print
|
|
|
|
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
|
|
while True:
|
|
ok = raw_input(prompt)
|
|
if ok in ('y', 'ye', 'yes'):
|
|
return True
|
|
if ok in ('n', 'no'):
|
|
return False
|
|
retries = retries - 1
|
|
if retries < 0:
|
|
return False
|
|
print complaint
|
|
|
|
def feed_ask_delete(session, feed_id):
|
|
feed = session.query(Feed).\
|
|
filter(Feed.id == feed_id).\
|
|
first()
|
|
print feed
|
|
if ask_ok('> Do you really want to delete feed %d? ' % feed.id):
|
|
print 'deleting...'
|
|
session.delete(feed)
|
|
print '... done.'
|
|
|
|
def feed_ask_reset(session, feed_id):
|
|
feed = session.query(Feed).\
|
|
filter(Feed.id == feed_id).\
|
|
first()
|
|
print feed
|
|
if ask_ok('> Do you really want to reset feed %d? ' % feed.id):
|
|
print 'resetting...'
|
|
feed.reset()
|
|
print '... done.'
|
|
|
|
|
|
def main():
|
|
if (sys.stdout.encoding is None):
|
|
streamWriter = codecs.lookup('utf-8')[-1]
|
|
sys.stdout = streamWriter(sys.stdout)
|
|
|
|
config = ConfigParser.ConfigParser()
|
|
config.read('atomstrom.conf')
|
|
|
|
dbconnectstring = '%s://%s:%s@%s/%s?charset=utf8' % (
|
|
config.get('database', 'engine'),
|
|
config.get('database', 'user'),
|
|
config.get('database', 'password'),
|
|
config.get('database', 'hostname'),
|
|
config.get('database', 'database'),
|
|
)
|
|
engine = create_engine(dbconnectstring)
|
|
Base.metadata.create_all(engine)
|
|
|
|
Session = sessionmaker(bind=engine)
|
|
session = Session()
|
|
|
|
#session.add(Feed('http://www.heise.de/newsticker/heise-atom.xml', 1, 0, 0, 1, 1))
|
|
#session.add(Feed('http://blog.schatenseite.de/feed/', 1, 0, 0, 1, 1))
|
|
|
|
parser = ArgumentParser(description='Fetch RSS- and Atom-feeds and send mails.')
|
|
parser.add_argument('-f', '--fetch', action='store_true', help='fetch all feeds')
|
|
parser.add_argument('-s', '--single', action='store_true', help='send single mails')
|
|
parser.add_argument('-d', '--daily', action='store_true', help='send daily digest')
|
|
parser.add_argument('-l', '--list', action='store_true', help='list all configured feeds')
|
|
parser.add_argument('-e', '--delete', action='store', type=int, metavar='ID', help='delete feed <ID> from configuration')
|
|
parser.add_argument('-r', '--reset', action='store', type=int, metavar='ID', help='reset data for feed <ID>')
|
|
args = parser.parse_args()
|
|
|
|
if args.fetch:
|
|
fetch_all_feeds(session)
|
|
if args.single:
|
|
sender = [config.get('email', 'sender_name'), config.get('email', 'sender_address')]
|
|
receiver = [config.get('email', 'receiver_name'), config.get('email', 'receiver_address')]
|
|
prefix = config.get('email', 'prefix_single')
|
|
mail_single_entries(session, sender, receiver, prefix)
|
|
if args.daily:
|
|
sender = [config.get('email', 'sender_name'), config.get('email', 'sender_address')]
|
|
receiver = [config.get('email', 'receiver_name'), config.get('email', 'receiver_address')]
|
|
prefix = config.get('email', 'prefix_digest')
|
|
mail_daily_digest(session, sender, receiver, prefix)
|
|
if args.list:
|
|
list_all_feeds(session)
|
|
if args.delete:
|
|
feed_ask_delete(session, args.delete)
|
|
if args.reset:
|
|
feed_ask_reset(session, args.reset)
|
|
if not (args.fetch or args.single or args.daily or args.list or args.delete or args.reset):
|
|
parser.print_help()
|
|
|
|
session.commit()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|
|
|
|
# -*- coding: utf-8 -*-
|