feed2toot-0.17/0000755000175100017510000000000014027414665014011 5ustar chaicachaica00000000000000feed2toot-0.17/PKG-INFO0000644000175100017510000000145214027414665015110 0ustar chaicachaica00000000000000Metadata-Version: 2.1 Name: feed2toot Version: 0.17 Summary: Parse rss feeds and send new posts to Mastodon Home-page: https://gitlab.com/chaica/feed2toot Author: Carl Chenet Author-email: carl.chenet@ohmytux.com License: GNU GPL v3 Download-URL: https://gitlab.com/chaica/feed2toot Description: Parse rss feeds and send new posts to the Mastodon social network Platform: UNKNOWN Classifier: Intended Audience :: End Users/Desktop Classifier: Environment :: Console Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Provides-Extra: influxdb feed2toot-0.17/README.md0000644000175100017510000000373614027411762015274 0ustar chaicachaica00000000000000### Feed2toot Feed2toot automatically parses rss feeds, identifies new posts and posts them on the [Mastodon](https://mastodon.social) social network. For the full documentation, [read it online](https://feed2toot.readthedocs.io/en/latest/). If you would like, you can [support the development of this project on Liberapay](https://liberapay.com/carlchenet/). Alternatively you can donate cryptocurrencies: - BTC: 1AW12Zw93rx4NzWn5evcG7RNNEM2RSLmAC - XMR: 43GGv8KzVhxehv832FWPTF7FSVuWjuBarFd17QP163uxMaFyoqwmDf1aiRtS5jWgCiRsi73yqedNJJ6V1La2joznKHGAhDi ### Quick Install * Install Feed2toot from PyPI # pip3 install feed2toot * Install Feed2toot from sources *(see the installation guide for full details) [Installation Guide](http://feed2toot.readthedocs.io/en/latest/install.html)* # tar zxvf feed2toot-0.17.tar.gz # cd feed2toot # python3 setup.py install # # or # python3 setup.py install --install-scripts=/usr/bin ### Create the authorization for the Feed2toot app * Just launch the following command:: $ register_feed2toot_app ### Use Feed2toot * Create or modify feed2toot.ini file in order to configure feed2toot: [mastodon] instance_url=https://mastodon.social user_credentials=feed2toot_usercred.txt client_credentials=feed2toot_clientcred.txt ; Default visibility is public, but you can override it: ; toot_visibility=unlisted [cache] cachefile=cache.db [rss] uri=https://www.journalduhacker.net/rss toot={title} {link} [hashtaglist] several_words_hashtags_list=hashtags.txt * Launch Feed2toot $ feed2toot -c /path/to/feed2toot.ini ### Authors * Carl Chenet * Antoine Beaupré * First developed by Todd Eddy ### License This software comes under the terms of the GPLv3+. Previously under MIT license. See the LICENSE file for the complete text of the license. feed2toot-0.17/feed2toot/0000755000175100017510000000000014027414665015704 5ustar chaicachaica00000000000000feed2toot-0.17/feed2toot/__init__.py0000644000175100017510000000135314027412626020012 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 feed2toot-0.17/feed2toot/addtags.py0000644000175100017510000000373014027412522017656 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 # Add as many tags as possible depending on the tweet length '''Add as many tags as possible depending on the tweet length''' # standard library imports from operator import itemgetter class AddTags: '''Add as many tags as possible depending on the tweet length''' def __init__(self, tweet, tags): '''Constructor of AddTags class''' self.tags = tags self.tweet = tweet self.main() def main(self): '''Main of the AddTags class class''' maxlength = 500 tweetlength = len(self.tweet) # sort list of tags, the ones with the greater length first tagswithindices = ({'text':i, 'length': len(i)} for i in self.tags) sortedtagswithindices = sorted(tagswithindices, key=itemgetter('length'), reverse=True) self.tags = (i['text'] for i in sortedtagswithindices) # add tags is space is available for tag in self.tags: taglength = len(tag) if (tweetlength + (taglength + 1)) <= maxlength: self.tweet = ' '.join([self.tweet, tag]) tweetlength += (taglength + 1) @property def finaltweet(self): '''return the final tweet with as many tags as possible''' return self.tweet feed2toot-0.17/feed2toot/cliparse.py0000644000175100017510000001460214027412530020050 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 # CLI parsing '''CLI parsing''' # standard library imports from argparse import ArgumentParser import glob import logging import os import os.path import sys __version__ = '0.17' class CliParse: '''CliParse class''' def __init__(self): '''Constructor for the CliParse class''' self.main() def main(self): '''main of CliParse class''' feed2tootepilog = 'For more information: https://feed2toot.readthedocs.io' feed2tootdescription = 'Take rss feed and send it to Mastodon' parser = ArgumentParser(prog='feed2toot', description=feed2tootdescription, epilog=feed2tootepilog) parser.add_argument('--version', action='version', version=__version__) parser.add_argument('-c', '--config', default=[os.path.join(os.getenv('XDG_CONFIG_HOME', '~/.config'), 'feed2toot.ini')], nargs='+', dest="config", help='Location of config file (default: %(default)s)', metavar='FILE') parser.add_argument('-a', '--all', action='store_true', default=False, dest='all', help='tweet all RSS items, regardless of cache') parser.add_argument('--ignore-ssl', action='store_true', default=False, dest='ignore_ssl', help='ignore ssl errors while fetching rss feeds') parser.add_argument('-l', '--limit', dest='limit', default=10, type=int, help='tweet only LIMIT items (default: %(default)s)') parser.add_argument('-t', '--lock-timeout', dest='locktimeout', default=3600, type=int, help='lock timeout in seconds after which feed2toot can removes the lock itself') parser.add_argument('--cachefile', dest='cachefile', help='location of the cache file (default: %(default)s)') parser.add_argument('--lockfile', dest='lockfile', default=os.path.join(os.getenv('XDG_CONFIG_HOME', '~/.config'), 'feed2toot.lock'), help='location of the lock file (default: %(default)s)') parser.add_argument('-n', '--dry-run', dest='dryrun', action='store_true', default=False, help='Do not actually post tweets') parser.add_argument('-v', '--verbose', '--info', dest='log_level', action='store_const', const='info', default='warning', help='enable informative (verbose) output, work on log level INFO') parser.add_argument('-d', '--debug', dest='log_level', action='store_const', const='debug', default='warning', help='enable debug output, work on log level DEBUG') levels = [i for i in logging._nameToLevel.keys() if (type(i) == str and i != 'NOTSET')] parser.add_argument('--syslog', nargs='?', default=None, type=str.upper, action='store', const='INFO', choices=levels, help="""log to syslog facility, default: no logging, INFO if --syslog is specified without argument""") parser.add_argument('--hashtaglist', dest='hashtaglist', help='a list of hashtags to match') parser.add_argument('-p', '--populate-cache', action='store_true', default=False, dest='populate', help='populate RSS entries in cache without actually posting them to Mastodon') parser.add_argument('-r', '--rss', help='the RSS feed URL to fetch items from', dest='rss_uri', metavar='http://...') parser.add_argument('--rss-sections', action='store_true', default=False, dest='rsssections', help='print the available sections of the rss feed to be used in the tweet template') self.opts = parser.parse_args() # expand the path to the cache file if defined if self.opts.cachefile: self.opts.cachefile = os.path.expanduser(self.opts.cachefile) # verify if the path to cache file is an absolute path # get the different config files, from a directory or from a *.ini style self.opts.config = list(map(os.path.expanduser, self.options.config)) for element in self.opts.config: if element and not os.path.exists(element): sys.exit('You should provide an existing path for the config file: %s' % element) if os.path.isdir(element): self.opts.configs = glob.glob(os.path.join(element, '*.ini')) else: # trying to glob the path self.opts.configs = glob.glob(element) # verify if a configuration file is provided if not self.opts.configs: sys.exit('no configuration file was found at the specified path(s) with the option -c') # verify the path to the hashtaglist if self.opts.hashtaglist: hashtaglist = os.path.expanduser(self.opts.hashtaglist) if not os.path.exists(hashtaglist): sys.exit('the {hashtaglist} file does not seem to exist, please provide a valid path'.format(hashtaglist=hashtaglist)) @property def options(self): '''return the path to the config file''' return self.opts feed2toot-0.17/feed2toot/confparse.py0000644000175100017510000001326614027412542020236 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 # 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 # 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 feed2toot-0.17/feed2toot/confparsers/cache.py0000644000175100017510000000437114027412735021647 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 feed2toot-0.17/feed2toot/confparsers/hashtags/nohashtags.py0000644000175100017510000000226114027413035024533 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 feed2toot-0.17/feed2toot/confparsers/rss/addtags.py0000644000175100017510000000242014027413076023012 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 # 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 ', urifeed) if not matches: sys.exit('This uri to parse is not formatted correctly: {urifeed}'.format(urifeed)) feedname, finaluri = matches.groups() rssuri = finaluri else: rssuri = config.get('rss', 'uri') else: sys.exit('{confoption} parameter in the [{section}] section of the configuration file is mandatory. Exiting.'.format(section=section, confoption=confoption)) else: rssuri = clioption # ignore ssl if asked if ignoressl: if hasattr(ssl, '_create_unverified_context'): ssl._create_default_https_context = ssl._create_unverified_context # get the rss feed for rss parameter of [rss] section feed = feedparser.parse(rssuri) if not feed: sys.exit('Unable to parse the feed at the following url: {rss}'.format(rss=rss)) ######################################### # no_uri_pattern_no_global_pattern option ######################################### currentoption = 'no_uri_pattern_no_global_pattern' # default value if config.has_option(section, currentoption): nopatternurinoglobalpattern = config.getboolean(section, currentoption) return rssuri, feed, feedname, nopatternurinoglobalpattern feed2toot-0.17/feed2toot/confparsers/rss/urilist.py0000644000175100017510000001015514027413162023076 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 ', line) if not matches: sys.exit('This line in the list of uri to parse is not formatted correctly: {line}'.format(line)) feedname, line = matches.groups() confobjects = line.split('|') if len(confobjects) > 3 or len(confobjects) == 2: sys.exit('This line in the list of uri to parse is not formatted correctly: {line}'.format(line)) if len(confobjects) == 3: rss, rssobject, patternstring = line.split('|') if len(confobjects) == 1: rss = confobjects[0] rssobject = '' patternstring = '' # split different searched patterns patterns = [i for i in patternstring.split(stringsep) if i] # ignore ssl if asked if ignoressl: if hasattr(ssl, '_create_unverified_context'): ssl._create_default_https_context = ssl._create_unverified_context # retrieve the content of the rss feed = feedparser.parse(rss) if 'bozo_exception' in feed: bozoexception = True logging.warning(feed['bozo_exception']) if not accept_bozo_exceptions: continue # check if the rss feed and the rss entry are valid ones if 'entries' in feed: if rssobject and rssobject not in feed['entries'][0].keys(): sys.exit('The rss object {rssobject} could not be found in the feed {rss}'.format(rssobject=rssobject, rss=rss)) else: sys.exit('The rss feed {rss} does not seem to be valid'.format(rss=rss)) feeds.append({'feed': feed, 'patterns': patterns, 'rssobject': rssobject, 'feedname': feedname}) # test if all feeds in the list were unsuccessfully retrieved and if so, leave if not feeds and bozoexception: sys.exit('No feed could be retrieved. Leaving.') return feeds feed2toot-0.17/feed2toot/feedcache.py0000644000175100017510000000346714027412555020153 0ustar chaicachaica00000000000000# vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 '''Manage a cache with the ids of the feed entries''' # standard libraires imports from collections import deque import os import os.path class FeedCache: '''FeedCache class''' def __init__(self, options): '''Constructore of the FeedCache class''' self.options = options self.main() def getdeque(self): '''return the deque''' return self.dbfeed def main(self): '''Main of the FeedCache class''' if os.path.exists(self.options['cachefile']): with open(self.options['cachefile']) as dbdsc: dbfromfile = dbdsc.readlines() dblist = [i.strip() for i in dbfromfile] self.dbfeed = deque(dblist, self.options['cache_limit']) else: self.dbfeed = deque([], self.options['cache_limit']) def append(self, rssid): '''Append a rss id to the cache''' self.dbfeed.append(rssid) def close(self): '''Close the cache''' with open(self.options['cachefile'], 'w') as dbdsc: dbdsc.writelines((''.join([i, os.linesep]) for i in self.dbfeed)) feed2toot-0.17/feed2toot/filterentry.py0000644000175100017510000000763314027412600020621 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2015-2021 Carl Chenet # 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 # 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 # 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 # 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 '''Manage a lock file''' # standard libraires imports import codecs def extract_hashtags_from_list(options): '''extract hashtags from the the list''' if 'hashtaglist' in options and options['hashtaglist']: severalwordshashtags = codecs.open(options['hashtaglist'], encoding='utf-8').readlines() severalwordshashtags = [i.rstrip('\n') for i in severalwordshashtags] else: severalwordshashtags = [] return severalwordshashtags def build_hashtags(entry, rss, options, severalwordshashtags): '''build hashtags''' severalwordsinhashtag = False # has the the rss feed hashtag if 'tags' in entry and options['addtags']: hastags = True else: hastags = False if hastags: rss['hashtags'] = [] for i, _ in enumerate(entry['tags']): if 'hashtaglist' in options: prehashtags = entry['tags'][i]['term'] tmphashtags = entry['tags'][i]['term'] for element in severalwordshashtags: if element in prehashtags: severalwordsinhashtag = True tmphashtags = prehashtags.replace(element, ''.join(element.split())) # replace characters stopping a word from being a hashtag if severalwordsinhashtag: # remove ' from hashtag tmphashtags = tmphashtags.replace("'", "") # remove - from hashtag tmphashtags = tmphashtags.replace("-", "") # remove . from hashtag tmphashtags = tmphashtags.replace(".", "") # remove space from hashtag finalhashtags = tmphashtags.replace(" ", "") rss['hashtags'].append('#{}'.format(finalhashtags)) else: nospace = ''.join(entry['tags'][i]['term']) # remove space from hashtag nospace = nospace.replace(" ", "") rss['hashtags'].append('#{}'.format(nospace)) return rss feed2toot-0.17/feed2toot/lock.py0000644000175100017510000000505514027412640017202 0ustar chaicachaica00000000000000# vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 '''Manage a lock file''' # standard libraires imports import datetime import logging import os import os.path import sys class LockFile: '''LockFile object''' def __init__(self, lockfile, locktimeout): '''check the lockfile and the locktimeout''' self.lockfile = lockfile ltimeout = datetime.timedelta(seconds=locktimeout) self.lfdateformat = '%Y-%m-%d_%H-%M-%S' # if a lock file exists if os.path.exists(self.lockfile): if os.path.isfile(self.lockfile): with open(self.lockfile, 'r') as lf: lfcontent = lf.read().rstrip() # lfcontent should be a datetime logging.debug('Check if lock file is older than timeout ({timeout} secs)'.format(timeout=locktimeout)) locktime = datetime.datetime.strptime(lfcontent, self.lfdateformat) if locktime < (datetime.datetime.now() - ltimeout): # remove the lock file logging.debug('Found an expired lock file') self.release() self.create_lock() else: # quit because another feed2toot process is running logging.debug('Found a valid lock file. Exiting immediately.') sys.exit(0) else: # no lock file. Creating one self.create_lock() def create_lock(self): '''Create a lock file''' with open(self.lockfile, 'w') as lf: currentdatestring = datetime.datetime.now().strftime(self.lfdateformat) lf.write(currentdatestring) logging.debug('lockfile {lockfile} created.'.format(lockfile=self.lockfile)) def release(self): '''Release the lockfile''' os.remove(self.lockfile) logging.debug('Removed lock file.') feed2toot-0.17/feed2toot/main.py0000644000175100017510000001327514027412647017210 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 """Checks an RSS feed and posts new entries to Mastodon.""" # standard libraires imports import codecs import importlib import logging import logging.handlers import sys import re # external liraries imports from bs4 import BeautifulSoup # app libraries imports from feed2toot.addtags import AddTags from feed2toot.cliparse import CliParse from feed2toot.confparse import ConfParse from feed2toot.feedcache import FeedCache from feed2toot.filterentry import FilterEntry from feed2toot.hashtags import build_hashtags from feed2toot.hashtags import extract_hashtags_from_list from feed2toot.lock import LockFile from feed2toot.message import build_message from feed2toot.message import send_message_dry_run from feed2toot.message import send_message from feed2toot.plugins import activate_plugins from feed2toot.rss import populate_rss from feed2toot.sortentries import sort_entries class Main: '''Main class of Feed2toot''' def __init__(self): self.main() def setup_logging(self, options): if options.syslog: sl = logging.handlers.SysLogHandler(address='/dev/log') sl.setFormatter(logging.Formatter('feed2toot[%(process)d]: %(message)s')) # convert syslog argument to a numeric value loglevel = getattr(logging, options.syslog.upper(), None) if not isinstance(loglevel, int): raise ValueError('Invalid log level: %s' % loglevel) sl.setLevel(loglevel) logging.getLogger('').addHandler(sl) logging.debug('configured syslog level %s' % loglevel) logging.getLogger('').setLevel(logging.DEBUG) sh = logging.StreamHandler() sh.setLevel(options.log_level.upper()) logging.getLogger('').addHandler(sh) logging.debug('configured stdout level %s' % sh.level) def main(self): '''The main function''' clip = CliParse() clioptions = clip.options self.setup_logging(clioptions) # iterating over the different configuration files cfgp = ConfParse(clioptions) confs = cfgp.confvalues for conf in confs: options = conf[0] config = conf[1] tweetformat = conf[2] feeds = conf[3] plugins = conf[4] # check the logfile and logtimeout lockfile = LockFile(options['lockfile'], options['locktimeout']) # create link to the persistent list cache = FeedCache(options) severalwordshashtags = extract_hashtags_from_list(options) # reverse feed entries because most recent one should be sent as the last one in Mastodon for feed in feeds: # store the patterns by rss if 'patterns' in feed: patterns = feed['patterns'] entries = feed['feed']['entries'][0:clioptions.limit] entries.reverse() # --rss-sections option: print rss sections and exit if clioptions.rsssections: if entries: print('The following sections are available in this RSS feed: {}'.format([j for j in entries[0]])) else: print('Could not parse the section of the rss feed') # release the lock file lockfile.release() sys.exit(0) # sort entries and check if they were not previously sent totweet = sort_entries(clioptions.all, cache, entries) for entry in totweet: # populate rss with new entry to send rss = populate_rss(entry) rss = build_hashtags(entry, rss, options, severalwordshashtags) # parse tweetfomat to elements elements = re.findall(r"\{(.*?)\}",tweetformat) # strip : from elements to allow string formating, eg. {title:.20} for i,s in enumerate(elements): if s.find(':'): elements[i] = s.split(':')[0] fe = FilterEntry(elements, entry, options, feed['patterns'], feed['rssobject'], feed['feedname']) entrytosend = fe.finalentry if entrytosend: finaltweet = build_message(entrytosend, tweetformat, rss, options['tootmaxlen'], options['notagsintoot']) if clioptions.dryrun: send_message_dry_run(config, entrytosend, finaltweet) else: send_message(config, clioptions, options, entrytosend, finaltweet, cache, rss) # plugins if plugins and entrytosend: activate_plugins(plugins, finaltweet) # do not forget to close cache (shelf object) cache.close() # release the lock file lockfile.release() feed2toot-0.17/feed2toot/message.py0000644000175100017510000000624214027412657017705 0ustar chaicachaica00000000000000# vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 '''Build the message''' # standard libraires imports import logging # external liraries imports from bs4 import BeautifulSoup # app libraries imports from feed2toot.addtags import AddTags from feed2toot.removeduplicates import RemoveDuplicates from feed2toot.tootpost import TootPost def build_message(entrytosend, tweetformat, rss, tootmaxlen, notagsintoot): '''populate the rss dict with the new entry''' tweetwithnotag = tweetformat.format(**entrytosend) # replace line breaks tootwithlinebreaks = tweetwithnotag.replace('\\n', '\n') # remove duplicates from the final tweet dedup = RemoveDuplicates(tootwithlinebreaks) # only add tags if user wants to if not notagsintoot: # only append hashtags if they exist # remove last tags if tweet too long if 'hashtags' in rss: addtag = AddTags(dedup.finaltweet, rss['hashtags']) finaltweet = addtag.finaltweet else: finaltweet = dedup.finaltweet else: finaltweet = dedup.finaltweet # strip html tags finaltweet = BeautifulSoup(finaltweet, 'html.parser').get_text() # truncate toot to user-defined value whatever the content is if len(finaltweet) > tootmaxlen: finaltweet = finaltweet[0:tootmaxlen-1] return ''.join([finaltweet[0:-3], '...']) else: return finaltweet def send_message_dry_run(config, entrytosend, finaltweet): '''simulate sending message using dry run mode''' if entrytosend: logging.warning('Would toot with visibility "{visibility}": {toot}'.format( toot=finaltweet, visibility=config.get( 'mastodon', 'toot_visibility', fallback='public'))) else: logging.debug('This rss entry did not meet pattern criteria. Should have not been sent') def send_message(config, clioptions, options, entrytosend, finaltweet, cache, rss): '''send message''' storeit = True if entrytosend and not clioptions.populate: logging.debug('Tooting with visibility "{visibility}": {toot}'.format( toot=finaltweet, visibility=config.get( 'mastodon', 'toot_visibility', fallback='public'))) twp = TootPost(config, options, finaltweet) storeit = twp.storeit() else: logging.debug('populating RSS entry {}'.format(rss['id'])) # in both cas we store the id of the sent tweet if storeit: cache.append(rss['id']) feed2toot-0.17/feed2toot/plugins/0000755000175100017510000000000014027414665017365 5ustar chaicachaica00000000000000feed2toot-0.17/feed2toot/plugins/__init__.py0000644000175100017510000000253313531260741021472 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2017 Carl Chenet # 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 # 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 # standard libraires imports import importlib def activate_plugins(plugins, finaltweet): '''activate plugins''' for plugin in plugins: capitalizedplugin = plugin.title() pluginclassname = '{plugin}Plugin'.format(plugin=capitalizedplugin) pluginmodulename = 'feed2toot.plugins.{pluginmodule}'.format(pluginmodule=pluginclassname.lower()) try: pluginmodule = importlib.import_module(pluginmodulename) pluginclass = getattr(pluginmodule, pluginclassname) pluginclass(plugins[plugin], finaltweet) except ImportError as err: print(err) feed2toot-0.17/feed2toot/plugins/influxdbplugin.py0000644000175100017510000000307713140113423022757 0ustar chaicachaica00000000000000# -*- coding: utf-8 -*- # Copyright © 2017 Carl Chenet # 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 # 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 # 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 # 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 # Remove duplicates from the final string before sending the tweet '''Remove duplicates from the final string before sending the tweet''' class RemoveDuplicates: '''Remove duplicates from the final string before sending the tweet''' def __init__(self, tweet): '''Constructor of RemoveDuplicates class''' self.tweet = tweet self.main() def main(self): '''Main of the RemoveDuplicates class''' # identify duplicate links links = [] for element in self.tweet.split(): if element != ' ' and (element.startswith('http://') or element.startswith('https://')): newlink = True # if we already found this link, increment the counter for i, _ in enumerate(links): if links[i]['link'] == element: newlink = False links[i]['count'] += 1 if newlink: links.append({'link': element, 'count': 1}) # remove duplicates validatedlinks = [] for i in range(len(links)): if links[i]['count'] >= 2: validatedlinks.append(links[i]) wildcard = 'FEED2TOOTWILDCARD' for element in validatedlinks: for i in range(element['count']): # needed for not inversing the order of links if it is a duplicate # and the second link is not one if i == 0: self.tweet = self.tweet.replace(element['link'], wildcard, 1) else: self.tweet = self.tweet.replace(element['link'], '', 1) # finally self.tweet = self.tweet.replace(wildcard, element['link'], 1) # remove all 2xspaces self.tweet = self.tweet.replace(' ', ' ') @property def finaltweet(self): '''return the final tweet after duplicates were removed''' return self.tweet feed2toot-0.17/feed2toot/rss.py0000644000175100017510000000261014027412700017050 0ustar chaicachaica00000000000000# vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 '''Manage a lock file''' # standard libraires imports import datetime import logging import os import os.path import sys def populate_rss(entry): '''populate the rss dict with the new entry''' if 'id' in entry: logging.debug('found feed entry {entryid}'.format(entryid=entry['id'])) rss = { 'id': entry['id'], } elif 'guid' in entry: logging.debug('found feed entry {entryid}'.format(entryid=entry['guid'])) rss = { 'id': entry['guid'], } else: logging.debug('found feed entry {entryid}'.format(entryid=entry['link'])) rss = { 'id': entry['link'], } return rss feed2toot-0.17/feed2toot/sortentries.py0000644000175100017510000000265414027412707020641 0ustar chaicachaica00000000000000# vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 '''Manage a lock file''' # standard libraires imports import datetime import logging import os import os.path import sys def sort_entries(is_all, cache, entries): '''sort entries before sending''' totweet = [] if not is_all: for i in entries: if 'id' in i: if i['id'] not in cache.getdeque(): totweet.append(i) elif 'guid' in i: if i['guid'] not in cache.getdeque(): totweet.append(i) else: # if id or guid not in the entry, use link if i['link'] not in cache.getdeque(): totweet.append(i) else: totweet = entries return totweet feed2toot-0.17/feed2toot/tootpost.py0000644000175100017510000000361714027412717020154 0ustar chaicachaica00000000000000# vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 """Checks an RSS feed and posts new entries to Mastodon.""" # 3rd party libraries imports from mastodon import Mastodon class TootPost: '''TootPost class''' def __init__(self, config, options, toot): '''Constructore of the TootPost class''' self.config = config self.options = options self.store = True self.toot = toot self.main() def main(self): '''Main of the TweetPost class''' mastodon = Mastodon( client_id=self.config.get('mastodon', 'client_credentials'), access_token=self.config.get('mastodon', 'user_credentials'), api_base_url=self.config.get('mastodon', 'instance_url') ) toot_visibility = self.config.get('mastodon', 'toot_visibility', fallback='public') if 'custom' in self.options['media']: mediaid = mastodon.media_post(self.config['media']['custom']) mastodon.status_post(self.toot, media_ids=[mediaid], visibility=toot_visibility) else: mastodon.status_post(self.toot, visibility=toot_visibility) def storeit(self): '''Indicate if the tweet should be stored or not''' return self.store feed2toot-0.17/feed2toot.egg-info/0000755000175100017510000000000014027414665017376 5ustar chaicachaica00000000000000feed2toot-0.17/feed2toot.egg-info/PKG-INFO0000644000175100017510000000145214027414665020475 0ustar chaicachaica00000000000000Metadata-Version: 2.1 Name: feed2toot Version: 0.17 Summary: Parse rss feeds and send new posts to Mastodon Home-page: https://gitlab.com/chaica/feed2toot Author: Carl Chenet Author-email: carl.chenet@ohmytux.com License: GNU GPL v3 Download-URL: https://gitlab.com/chaica/feed2toot Description: Parse rss feeds and send new posts to the Mastodon social network Platform: UNKNOWN Classifier: Intended Audience :: End Users/Desktop Classifier: Environment :: Console Classifier: License :: OSI Approved :: GNU General Public License (GPL) Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Provides-Extra: influxdb feed2toot-0.17/feed2toot.egg-info/SOURCES.txt0000644000175100017510000000233114027414665021261 0ustar chaicachaica00000000000000README.md setup.py feed2toot/__init__.py feed2toot/addtags.py feed2toot/cliparse.py feed2toot/confparse.py feed2toot/feedcache.py feed2toot/filterentry.py feed2toot/hashtags.py feed2toot/lock.py feed2toot/main.py feed2toot/message.py feed2toot/removeduplicates.py feed2toot/rss.py feed2toot/sortentries.py feed2toot/tootpost.py feed2toot.egg-info/PKG-INFO feed2toot.egg-info/SOURCES.txt feed2toot.egg-info/dependency_links.txt feed2toot.egg-info/requires.txt feed2toot.egg-info/top_level.txt feed2toot/confparsers/__init__.py feed2toot/confparsers/cache.py feed2toot/confparsers/feedparser.py feed2toot/confparsers/hashtaglist.py feed2toot/confparsers/lock.py feed2toot/confparsers/media.py feed2toot/confparsers/plugins.py feed2toot/confparsers/hashtags/__init__.py feed2toot/confparsers/hashtags/nohashtags.py feed2toot/confparsers/rss/__init__.py feed2toot/confparsers/rss/addtags.py feed2toot/confparsers/rss/ignoressl.py feed2toot/confparsers/rss/pattern.py feed2toot/confparsers/rss/toot.py feed2toot/confparsers/rss/tootmaxlen.py feed2toot/confparsers/rss/uri.py feed2toot/confparsers/rss/urilist.py feed2toot/plugins/__init__.py feed2toot/plugins/influxdbplugin.py scripts/__init__.py scripts/feed2toot scripts/register_feed2toot_appfeed2toot-0.17/feed2toot.egg-info/dependency_links.txt0000644000175100017510000000000114027414665023444 0ustar chaicachaica00000000000000 feed2toot-0.17/feed2toot.egg-info/requires.txt0000644000175100017510000000007314027414665021776 0ustar chaicachaica00000000000000Mastodon.py beautifulsoup4 feedparser [influxdb] influxdb feed2toot-0.17/feed2toot.egg-info/top_level.txt0000644000175100017510000000002214027414665022122 0ustar chaicachaica00000000000000feed2toot scripts feed2toot-0.17/scripts/0000755000175100017510000000000014027414665015500 5ustar chaicachaica00000000000000feed2toot-0.17/scripts/__init__.py0000644000175100017510000000135314027412356017606 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 feed2toot-0.17/scripts/feed2toot0000755000175100017510000000157314027412455017322 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 # Feed2toot startup '''Feed2toot startup''' import sys from feed2toot.main import Main if __name__ == '__main__': Main() sys.exit(0) feed2toot-0.17/scripts/register_feed2toot_app0000755000175100017510000000773614027412463022074 0ustar chaicachaica00000000000000#!/usr/bin/env python3 # vim:ts=4:sw=4:ft=python:fileencoding=utf-8 # Copyright © 2015-2021 Carl Chenet # 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 # 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 from argparse import ArgumentParser from getpass import getpass from os import getcwd from os import linesep from os import sep from mastodon import Mastodon from mastodon.Mastodon import MastodonIllegalArgumentError import sys __version__ = '0.2' epilog = 'For more information: https://feed2toot.readthedocs.io' description = 'Create a Mastodon app for Feed2toot' parser = ArgumentParser(prog='register_feed2toot_app', description=description, epilog=epilog) parser.add_argument('--version', action='version', version=__version__) parser.add_argument('--client-credentials-file', dest='clientcredfile', help='the name of the client credentials for the Mastodon app', default='feed2toot_clientcred.txt') parser.add_argument('--user-credentials-file', dest='usercredfile', help='the name of the user credentials for the Mastodon app', default='feed2toot_usercred.txt') parser.add_argument('--name', help='the name of the Mastodon app', default='feed2toot') opts = parser.parse_args() clientcredfile=opts.clientcredfile usercredfile=opts.usercredfile headline = '{linesep}This script generates the Mastodon application credentials for Feed2toot.{linesep}{clientcredfile} and {usercredfile} will be written{linesep}in the current directory: {cwd}.{linesep}WARNING: previous files with the same names will be overwritten.{linesep}{linesep}A connection is also initiated to create the application.{linesep}Your password is *not* stored.{linesep}'.format(linesep=linesep, clientcredfile=clientcredfile, usercredfile=usercredfile, cwd=getcwd()) print(headline) # get the instance instance = input('Mastodon instance URL (defaults to https://mastodon.social): ') if not instance: instance = 'https://mastodon.social' elif not instance.startswith('http'): instance = ''.join(['https://', instance]) # get the username userok = False while not userok: user = input('Mastodon login: ') if not user: print('Your Mastodon username can not be empty.') userok = False elif '@' not in user or '.' not in user: print('Your Mastodon username should be an email.') userok = False else: userok = True # get the password password = getpass(prompt='Mastodon password: ') Mastodon.create_app( opts.name, api_base_url=instance, to_file = '{cwd}{sep}{clientcredfile}'.format(cwd=getcwd(), sep=sep, clientcredfile=clientcredfile) ) mastodon = Mastodon(client_id = '{cwd}{sep}{clientcredfile}'.format(cwd=getcwd(), sep=sep, clientcredfile=clientcredfile), api_base_url=instance) try: mastodon.log_in( user, password, to_file = '{cwd}{sep}{usercredfile}'.format(cwd=getcwd(), sep=sep, usercredfile=usercredfile) ) except MastodonIllegalArgumentError as err: print(err) sys.exit('{linesep}I guess you entered a bad login or password.{linesep}'.format(linesep=linesep)) summary = '{linesep}The app {appname} was added to your Preferences=>Accounts=>Authorized apps page.{linesep}The file {clientcredfile} and {usercredfile} were created in the current directory.{linesep}'.format(appname=opts.name, linesep=linesep, clientcredfile=clientcredfile, usercredfile=usercredfile) print(summary) sys.exit(0) feed2toot-0.17/setup.cfg0000644000175100017510000000004614027414665015632 0ustar chaicachaica00000000000000[egg_info] tag_build = tag_date = 0 feed2toot-0.17/setup.py0000755000175100017510000000347014027414161015521 0ustar chaicachaica00000000000000# Copyright 2015-2021 Carl Chenet # 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 # 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 #!/usr/bin/env python3 # Setup for Feed2toot '''Setup for Feed2toot''' from setuptools import setup, find_packages CLASSIFIERS = [ 'Intended Audience :: End Users/Desktop', 'Environment :: Console', 'License :: OSI Approved :: GNU General Public License (GPL)', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7' ] setup( name='feed2toot', version='0.17', license='GNU GPL v3', description='Parse rss feeds and send new posts to Mastodon', long_description='Parse rss feeds and send new posts to the Mastodon social network', author = 'Carl Chenet', author_email = 'carl.chenet@ohmytux.com', url = 'https://gitlab.com/chaica/feed2toot', classifiers=CLASSIFIERS, download_url='https://gitlab.com/chaica/feed2toot', packages=find_packages(), scripts=['scripts/feed2toot', 'scripts/register_feed2toot_app'], install_requires=['beautifulsoup4', 'feedparser', 'Mastodon.py'], extras_require={ 'influxdb': ["influxdb"] } )