This commit is contained in:
Ed Summers
2018-06-09 12:53:44 -04:00
commit bdc72c289f
5 changed files with 210 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
feediverse.egg-info
Pipfile*
__pycache__
.config.yml.swp

22
LICENSE Normal file
View File

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2018 Ed Summers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

58
README.md Normal file
View File

@ -0,0 +1,58 @@
*feediverse* will read RSS/Atom feeds and send the messages as Mastodon posts.
It's kind of the same thing as [feed2toot] but just one module that works with
Python 3.
## Install
pip install feediverse
## Run
The first time you run *feediverse* you'll need to tell it your Mastodon
instance and get an access token which it will save in a configuration file. If
you don't specify a config file it will use `~/.feediverse`:
feediverse
Once *feediverse* is configured you can add it to your crontab:
*/15 * * * * /usr/local/bin/feediverse
## Post Format
You can customize the post format by opening the configuration file (default is
~/.feediverse) and updating the *template* property of your feed. The default
format is:
{title} {url}
But you can use the `{summary}` as well, and also add text like so:
Bookmark: {title} {url} {summary}
## Multiple Feeds
Since *feeds* is a list you can add additional feeds to watch if you want.
...
feeds:
- url: https://example.com/feed/
template: "dot com: {title} {url}"
- url: https://example.org/feed/
template: "dot org: {title} {url}"
## Why?
I created *feediverse* because I wanted to send my Pinboard bookmarks to Mastodon.
I've got an IFTTT recipe that does this for Twitter, but IFTTT doesn't appear to
work with Mastodon yet.
That being said *feediverse* should work with any RSS or Atom feed (thanks to
[feedparser]). But please be responsible. Don't fill up Mastodon with tons of
junk just because you can. That kind of toxic behavior is why a lot of people
are leaving other social media platforms and trying to start over in Mastodon.
[feed2toot]: https://gitlab.com/chaica/feed2toot/
[feedparser]: http://feedparser.org/

108
feediverse.py Executable file
View File

@ -0,0 +1,108 @@
#!/usr/bin/env python3
import os
import sys
import yaml
import dateutil
import feedparser
from mastodon import Mastodon
from datetime import datetime, timezone
def main():
config_file = get_config_file()
if not os.path.isfile(config_file):
setup(config_file)
config = read_config(config_file)
masto = Mastodon(
api_base_url=config['url'],
client_id=config['client_id'],
client_secret=config['client_secret'],
access_token=config['access_token']
)
for feed in config['feeds']:
for entry in get_feed(feed['url'], config['updated']):
print(feed['template'].format(**entry))
#masto.status_post(get_text(entry))
save_config(config, config_file)
def save_config(config, config_file):
copy = dict(config)
copy['updated'] = datetime.now(tz=timezone.utc).isoformat()
with open(config_file, 'w') as fh:
fh.write(yaml.dump(copy, default_flow_style=False))
def read_config(config_file):
config = {}
with open(config_file) as fh:
config = yaml.load(fh)
if 'updated' in config:
config['updated'] = dateutil.parser.parse(config['updated'])
else:
config['updated'] = datetime.now(tz=timezone.utc)
return config
def get_feed(feed_url, last_update):
new_entries = 0
feed = feedparser.parse(feed_url)
for entry in feed.entries:
e = get_entry(entry)
if last_update is None or e['updated'] > last_update:
new_entries += 1
yield e
return new_entries
def get_entry(entry):
return {
'url': entry.id,
'title': entry.title,
'summary': entry.get('summary', ''),
'updated': dateutil.parser.parse(entry['updated']),
}
def setup(config_file):
url = input('What is your Mastodon Instance URL? ')
have_app = input('Do you have your app credentials already? [y/n] ')
if have_app.lower() == 'y':
name = 'feediverse'
client_id = input('What is your app\'s client id: ')
client_secret = input('What is your client secret: ')
access_token = input('access_token: ')
else:
print("Ok, I'll need a few things in order to get your access token")
name = input('app name (e.g. feediverse): ')
client_id, client_secret = Mastodon.create_app(name, scopes=['read', 'write'], website='https://github.com/edsu/feediverse')
username = input('mastodon username (email): ')
password = input('mastodon password (not stored): ')
access_token = m.log_in(username, password)
m = Mastodon(client_id, client_secret)
feed_url = input('RSS/Atom feed URL to watch: ')
config = {
'name': name,
'url': url,
'client_id': client_id,
'client_secret': client_secret,
'access_token': access_token,
'feeds': [
{'url': feed_url, 'template': '{title} {url}'}
]
}
save_config(config, config_file)
print("Your feediverse configuration has been saved to {}".format(config_file))
print("Add a line line this to your crontab to check every 15 minutes:")
print("*/15 * * * * /usr/local/bin/feediverse")
def get_config_file():
if __name__ == "__main__" and len(sys.argv) > 1:
config_file = sys.argv[1]
else:
config_file = os.path.join(os.path.expanduser("~"), ".feediverse")
return config_file
if __name__ == "__main__":
main()

18
setup.py Normal file
View File

@ -0,0 +1,18 @@
from setuptools import setup
with open("README.md") as f:
long_description = f.read()
setup(
name='feediverse',
version='0.0.1',
url='https://github.com/edsu/feediverse',
author='Ed Summers',
author_email='ehs@pobox.com',
py_modules=['feediverse', ],
description='Connect an RSS Feed to Mastodon',
long_description=long_description,
long_description_content_type="text/markdown",
install_requires=['feedparser', 'mastodon.py', 'python-dateutil', 'pyyaml'],
entry_points={'console_scripts': ['feediverse = feediverse:main']}
)