diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 61c5e8f..8977f31 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,31 +1,32 @@ --- name: Bug report about: Create a report to help us improve -title: 'BUG - ' +title: 'BUG - x happens when y' labels: bug assignees: '' --- -**Describe the bug** -A clear and concise description of what the bug is. +**Description** +A clear description of what the bug is. Provide enough detail so that it is clear what the problem is. Include the exact text of any relevant error messages. + +Please only log one bug per issue. + +**Environment (please complete the following information):** + - OS: [e.g. MacOS 10.15.5 Catalina] + - Python version [e.g. 3.7.7] **To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error +Steps to reproduce the behavior. e.g. run with a particular flag or a particular config value. **Expected behavior** A clear and concise description of what you expected to happen. +**Actual behavior** +A clear and concise description of what actually happened. + **Screenshots** If applicable, add screenshots to help explain your problem. -**Environment (please complete the following information):** - - OS: [e.g. MacOS] - - Python version [e.g. 3.7.7] - **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 11fc491..064cd03 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,18 +1,31 @@ --- -name: Feature request +name: Feature proposal about: Suggest an idea for this project title: '' labels: enhancement assignees: '' --- +**Does your proposal relate to...** + +- [ ] documentation +- [ ] what is displayed when running ephemetoot +- [ ] a new config value +- [ ] a new flag (option) +- [ ] something else **Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] +A clear and concise description of what the problem is. e.g. _I'm always frustrated when [...]_ **Describe the solution you'd like** A clear and concise description of what you want to happen. +**Would like to write the code yourself?** + +- [ ] I would like to write the code myself and then log a pull request +- [ ] I would like someone else to write the code +- [ ] I would like someone to help me write the code + **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..d23341c --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +Descriptive title for this pull request e.g. 'Add --cool_feature flag` + +Changes in this pull request +- +- +- + +Resolves # +Fixes # \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..47b3421 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ephemetoot AT ausglam DOT space. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/README.md b/README.md index bfa6890..f0d7ca6 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -A tool for deleting old toots, written in Python 3. +# ๐Ÿฅณ ==> ๐Ÿงผ ==> ๐Ÿ˜‡ +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) + +**ephemetoot** is a Python command line tool for deleting old toots. # Prior work The initial `ephemetoot` script was based on [this tweet-deleting script](https://gist.github.com/flesueur/bcb2d9185b64c5191915d860ad19f23f) by [@flesueur](https://github.com/flesueur) @@ -8,29 +11,29 @@ The initial `ephemetoot` script was based on [this tweet-deleting script](https: # Usage You can use `ephemetoot` to delete [Mastodon](https://github.com/tootsuite/mastodon) toots that are older than a certain number of days (default is 365). Toots can optionally be saved from deletion if: -* they are pinned; -* they include certain hashtags; +* they are pinned; or +* they include certain hashtags; or * they have certain visibility; or * they are individually listed to be kept -As of version 2, `ephemetoot` can be used for multiple accounts. If you have several 'alts', this can be useful. If you don't have your own server or Mac computer, your friend can now add you to their `ephemetoot` config and it will take care of your old potentially embarrassing toots as well as theirs. However, note [the warning below](#obtain-an-access-token). +As of version 2, `ephemetoot` can be used for multiple accounts. If you have several 'alts', this can be useful. If you don't have your own server or Mac computer, your friend can now add you to their `ephemetoot` config and it will take care of your old toots as well as theirs. However, **note [the warning below](#obtain-an-access-token)**. -# Setup +# Setup & Installation -## Install Python 3 +## Install Python 3 and pip -You need to [install Python 3](https://wiki.python.org/moin/BeginnersGuide/Download) to use `ephemetoot`. Python 2 is now end-of-life, however it continued to be installed as the default Python on MacOS until very recently, and may also be installed on your server. +You need to [install Python 3](https://wiki.python.org/moin/BeginnersGuide/Download) to use `ephemetoot`. Python 2 is now end-of-life, however it continued to be installed as the default Python on MacOS and many Linux distributions until very recently, so you should check. You will also need to check that `pip` is installed and pointing to Python3 (not Python2). On some systems this will mean using the command `pip3`. ## Install ephemetoot -### get code with git +### Option 1 - get code with git If you already have `git` installed on the machine where you're running ephemetoot, you can download the latest release with: ```shell git clone https://github.com/hughrun/ephemetoot.git cd ephemetoot git checkout [tagname] ``` -### get code by downloading zip file -If you don't have `git` or don't want to use it, you can download the zip file by clicking the green `Clone or download` button above and selecting `Download ZIP`. You will then need to unzip the file into a new directory where you want to run it. +### Option 2 - get the code by downloading the zip file +If you don't have `git` or don't want to use it, you can download the zip file by clicking the green button above and selecting `Download ZIP`. You will then need to unzip the file into a new directory where you want to run it. ### install using pip From a command line, move into the main `ephemetoot` directory (i.e. where the README file is) and run: @@ -45,7 +48,7 @@ If you do not have permission to install python modules, you may need to use the ```shell pip install . --user ``` -Note that you will need to run the script with the same user. +Note that you will need to run the script with the same user as ephemetoot will only be installed for that user and not globally. ## Obtain an access token @@ -87,7 +90,7 @@ You can now enter the configuration details for each user: | toots_to_keep | A list of toot ids indicating toots to be kept regardless of other settings. The ID of a toot is the last part of its individual URL. e.g. for [https://ausglam.space/@hugh/101294246770105799](https://ausglam.space/@hugh/101294246770105799) the id is `101294246770105799` | | hashtags_to_keep | A list of hashtags, where any toots with any of these hashtags will be kept regardless of age. Do not include the '#' symbol. Do remember the [rules for hashtags](https://docs.joinmastodon.org/user/posting/#hashtags) | | visibility_to_keep | Toots with any of the visibility settings in this list will be kept regardless of age. Options are: `public`, `unlisted`, `private`, `direct`. | -| archive | The full toot is archived into individual files named by the Toot's `id` in this writeable directory. | +| archive | A string. The full toot is archived into individual files named by the toot's `id` in this writeable directory. | All values other than `access_token`, `username` and `base_url` are optional, however if you include `toots_to_keep`, `hashtags_to_keep`, or `visibility_to_keep` you must make each a list, even if it is empty: @@ -102,9 +105,18 @@ If you want to use `ephemetoot` for multiple accounts, separate the config for e # Running the script -It is **strongly recommended** that you do a [test run](#running-in-test-mode) before using `ephemetoot` live. +It is **strongly recommended** that you do a test run before using `ephemetoot` live. There is no "undo"! -To call the script you enter: +## Running in test mode + +To do a test-run without actually deleting anything, run the script with the `--test` flag: +```shell +ephemetoot --test +``` + +## Running in "live" mode + +To call the script call ephemetoot with no arguments: ```shell ephemetoot ``` @@ -119,12 +131,6 @@ By default ephemetoot expects there to be a config file called `config.yaml` in ephemetoot --config '~/directory/subdirectory/config.yaml' ``` -## Running in test mode - -To do a test-run without actually deleting anything, run the script with the `--test` flag: -```shell -ephemetoot --test -``` ## Slow down deletes to match API limit With the `--pace` flag, delete actions are slowed so that the API limit is never reached, using [`Mastodon.py`'s 'pace' method](https://mastodonpy.readthedocs.io/en/stable/index.html?highlight=pace#mastodon.Mastodon.__init__). This is recommended for your first run, as unless you have tooted fewer than 30 times you are guaranteed to hit the API limit for deletions the first time you run `ephemetoot`. If you do not toot very often on most days, it is probably more efficient to use the default behaviour for daily runs after the first time, but you can use `--pace` every time if you prefer. @@ -161,7 +167,7 @@ Deleting old toots daily is the best approach to keeping your timeline clean and To run automatically every day on a n*x server you could try using crontab: 1. `crontab -e` - 2. enter a new line: `@daily ephemetoot --config /path/to/ephemetoot/config.yaml` + 2. enter a new line: `@daily /path/to/ephemetoot --config /path/to/ephemetoot/config.yaml` 3. exit with `:qw` (Vi/Vim) or `Ctrl + x` (nano) ### MacOS @@ -204,6 +210,8 @@ Prior to Python 3.7, running a Python script on some BSD and Linux systems may t * upgrading your Python version to 3.7 or higher. See [Issue 11](https://github.com/hughrun/ephemetoot/issues/11) for more information. # Upgrading + +## Upgrading with git To upgrade to a new version using git, run the following from inside the `ephemetoot` directory: ```shell @@ -212,7 +220,13 @@ git checkout [tagname] pip install . ``` -Alternatively download and unzip the zip file into your `ephemetoot` directory over the top of your existing installation, and then run `pip install .`. +## Upgrading with a ZIP file +To upgrade without using git: + +* put your config file somewhere safe +* download and unzip the zip file into your `ephemetoot` directory over the top of your existing installation +* move your config file back in to the ephemetoot directory +* run `pip install .` from within the directory # Uninstalling @@ -227,14 +241,10 @@ launchctl unload ~/Library/LaunchAgents/ephemetoot.scheduler.plist rm ~/Library/LaunchAgents/ephemetoot.scheduler.plist ``` -# Bugs and suggestions - -Please check existing [issues](https://github.com/hughrun/ephemetoot/issues) and if your issue is not already listed, create a new one with as much detail as possible (but don't include your access token!). - # Contributing -Contributions are very welcome, but if you want to suggest any changes or improvements, please log an issue or have a chat to [me on Mastodon](https://ausglam.space/@hugh) _before_ making a pull request. +For all bugs, suggestions, pull requests or other contributions, please check the [contributing guide](./contributing.md). # License -GPL 3.0+ +This project is [licensed](./LICENSE) under the GPL 3.0 or future version diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..c3b9a6d --- /dev/null +++ b/contributing.md @@ -0,0 +1,66 @@ +# Introduction + +Thanks for using `ephemetoot`, and for considering contributing to it! Some of the best features have come from suggestions and code contributed by people like you. + +You can contribute in many ways - improving the documentation, reporting bugs, suggesting new features, helping test new code, or even writing some code yourself. Following these guidelines will make the process smoother and easier for you and for maintainers and other contributors. That means everyone is happier and improvements get made faster ๐Ÿ’ซ + +# Expectations + +## Adhere to the Code of Conduct ๐Ÿค— +All contributors must adhere to the [Code of Conduct](./CODE_OF_CONDUCT.md) for this project. If you do not wish to follow this Code of Conduct, feel free to direct your energies towards a different project. + +## Do not log security problems as public issues +If you have identified a security flaw in **ephemetoot**, please email `ephemetoot AT ausglam DOT space` to discuss this confidentially. + +## Check existing issues ๐Ÿง +Your bug or enhancement might already be listed in the [issues](./issues). It's a good idea to check existing issues before you log your own. If you like someone else's enhancement suggestion, please "upvote" it with a ๐Ÿ‘ reaction. If you have also experienced the same bug as someone else, you can add any useful additional context to the existing issue. + +## Always log an issue ๐Ÿ“ +If you would like to contribute code or documentation changes that do not already have an issue listed, you should always [log an issue](./issues) first. Please **do not add pull requests without prior discussion**. Whilst pull requests are very welcome and encouraged, if you don't log an issue for discussion first, you may end up wasting your time if someone else is already working on the same feature, or maintainers decide it isn't a good fit. This also allows for your proposed feature to be scoped before you get too deep in the weeds coding it. + +Regardless of whether is is a bug report, feature request or code proposal, provide as much detail as possible in your issue, and give it a clear name. + +## One issue per bug or suggestion โ˜๏ธ +Each issue should refer to a single bug or enhancement. Don't include multiple suggestions, or a mix of bug report and enhancement proposal, in a single issue. Multiple items in the one issue ticket will make it confusing to know when to close an issue, and means that maintainers will probably have to create new issues so that each task can be tracked properly. It also makes it hard to maintain a clear discussion thread in the issue if there are multiple things being discussed. + +## Issue and commit naming conventions โœ๏ธ + +**Issues** should have clear names that describe the problem or the proposed enhancement. Past examples of good issue titles are: + +- "Ephemetoot may die when encountering utf8 encoded toots" ([bug](https://github.com/hughrun/ephemetoot/issues/11)) +- "Optionally include datetime stamp with every action" ([enhancement](https://github.com/hughrun/ephemetoot/issues/23)) + +**Commit and pull request messages** should start with an [imperative verb](https://www.grammarly.com/blog/imperative-verbs/). Simple commits such as documentation fixes may only need a brief sentence. Something bigger like an enhancement should usually have a heading briefly describing the outcome of the commit, with a longer explanation underneath. Past examples of good commit titles are: + +- "handle IndexError when there are no toots in the timeline" ([bugfix](https://github.com/hughrun/ephemetoot/commit/92643271d53e00089a10bacd1795cfd50e030413)) +- "add support for archiving toots into JSON files" ([new feature](https://github.com/hughrun/ephemetoot/commit/c0d680258ff0fe141fbabcf14a60eee8994e8d18)) + +## Closing issues in pull requests ๐Ÿ + +When your pull request resolves an issue, you can optionally use [one of the magic words](https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) to automatically close the issue. An example of a longer commit messages that does this is [`Add --version flag`](https://github.com/hughrun/ephemetoot/commit/a1db933bbd6c03e633975463801e6c94f7b9e9fa). The pull request template includes wording for this so you just need to add the issue number. + +## Use 'black' code formatting standards ๐Ÿ–ค + +We use [black](https://pypi.org/project/black/) to maintain code formatting consistency. Thanks to [@MarkEEaton](https://github.com/MarkEEaton) for the suggestion. You should generally run `black .` in the main **ephemetoot** directory before making a pull request, or alternatively check that your code is formatted to the `black` standards. Maintainer [@hughrun](https://github.com/hughrun) often forgets to run `black` so logging an issue about code formatting is completely legitimate ๐Ÿ˜€ + +## prefer configuration over flags โš™๏ธ +When adding a new feature, you should probably use a new, _optional_ value in the configuration file, rather than a new command line flag. As a general rule of thumb, use a flag when your change will affect the _output_, and a config value when it will affect the _actions_. + +For example, we use a configuration file boolean value for `keep_pinned` because that affects the _actions_ - if it is set to "true" then pinned toots are not deleted, and if set to "false", pinned toots _are_ deleted. On the other hand we use the `--datestamp` flag to print a datestamp against each action as it is logged. This doesn't change the action, merely the output to the screen or log file. + +There are some exceptions to this general rule (`--test` prevents any real actions, for example), but the exceptions should be rare and reasonably obvious. + +# Your first contribution +First time contributors are warmly encouraged! If you have never contributed to a project on GitHub or another public code repository, the **ephemetoot** maintainers can help you through the process. + +## Terminology ๐Ÿ“™ +You can contribute in many ways - even pointing out where the documentation is unclear will be a real help to future users. Already confused by some of the terms here? Check out [First Timers Only](https://www.firsttimersonly.com) for some tips. + +## Pull Requests ๐Ÿคฏ +"Pull Requests" can be confusing. You can learn how the process works from this free series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github). + +This is a pretty small project so there usually won't be a lot of issues waiting for someone to work on, but keep an eye out for anything tagged `good first issue` - these are especially for you! + +# Help + +You can get in touch with Hugh at [@hugh@ausglam.space](https://ausglam.space/@hugh) if you need help contributing or want to discuss something about **ephemetoot**. \ No newline at end of file diff --git a/example-config.yaml b/example-config.yaml index 36fa56c..99f3723 100644 --- a/example-config.yaml +++ b/example-config.yaml @@ -1,11 +1,11 @@ # access_token : the access token from the app you created in Mastodon at Settings - Development -# username : your username without the '@' or server domain. -# base_url : the base url of your Mastodon server, without the 'https://' +# username : your username without the "@" or server domain. +# base_url : the base url of your Mastodon server, without the "https://" # days_to_keep : number of days to keep toots. Defaults to 365 # keep_pinned : either true or false - if true, any pinned toots will be kept (default false) # toots_to_keep : a list of toot ids indicating toots to be kept regardless of other settings -# hashtags_to_keep : a list of hashtags, where any toots with any of these hashtags will be kept. Do not include the '#' symbol -# visibility_to_keep : any toots with visibility settings in this list will be kept. Options are: 'public', 'unlisted', 'private', 'direct' +# hashtags_to_keep : a list of hashtags, where any toots with any of these hashtags will be kept. Do not include the "#" symbol +# visibility_to_keep : any toots with visibility settings in this list will be kept. Options are: "public", "unlisted", "private", "direct" # archive : path to a writeable directory into which toots are "archived" as JSON files # you can list only one user, or multiple users @@ -27,6 +27,7 @@ visibility_to_keep : - direct - private + archive : ~/toots_archive/ - # aus.social account # values other than access_token, username, and base_url are all optional diff --git a/lib/ephemetoot.py b/lib/ephemetoot.py index 188a76a..b75d394 100644 --- a/lib/ephemetoot.py +++ b/lib/ephemetoot.py @@ -1,27 +1,37 @@ from datetime import date, datetime, timedelta, timezone import json -from mastodon import Mastodon, MastodonError, MastodonAPIError, MastodonNetworkError, MastodonRatelimitError +from mastodon import ( + Mastodon, + MastodonError, + MastodonAPIError, + MastodonNetworkError, + MastodonRatelimitError, +) import os import requests import subprocess import sys import time + def version(vnum): try: - latest = requests.get('https://api.github.com/repos/hughrun/ephemetoot/releases/latest') + latest = requests.get( + "https://api.github.com/repos/hughrun/ephemetoot/releases/latest" + ) res = latest.json() - latest_version = res['name'] - print('\nYou are using ephemetoot Version ' + vnum) - print('The latest release is ' + latest_version + '\n') - + latest_version = res["name"] + print("\nYou are using ephemetoot Version " + vnum) + print("The latest release is " + latest_version + "\n") + except Exception as e: - print('Something went wrong:') + print("Something went wrong:") print(e) + def schedule(options): try: - with open(options.schedule + '/ephemetoot.scheduler.plist', 'r') as file: + with open(options.schedule + "/ephemetoot.scheduler.plist", "r") as file: lines = file.readlines() if options.schedule == ".": @@ -38,45 +48,55 @@ def schedule(options): lines[21] = " " + options.time[0] + "\n" lines[23] = " " + options.time[1] + "\n" - with open('ephemetoot.scheduler.plist', 'w') as file: + with open("ephemetoot.scheduler.plist", "w") as file: file.writelines(lines) - sys.tracebacklimit = 0 # suppress Tracebacks + sys.tracebacklimit = 0 # suppress Tracebacks # save the plist file into ~/Library/LaunchAgents subprocess.run( - ["cp " + options.schedule + "/ephemetoot.scheduler.plist" + " ~/Library/LaunchAgents/"], - shell=True + [ + "cp " + + options.schedule + + "/ephemetoot.scheduler.plist" + + " ~/Library/LaunchAgents/" + ], + shell=True, ) # unload any existing file (i.e. if this is an update to the file) and suppress any errors subprocess.run( - ["launchctl unload ~/Library/LaunchAgents/ephemetoot.scheduler.plist"], - stdout=subprocess.DEVNULL, + ["launchctl unload ~/Library/LaunchAgents/ephemetoot.scheduler.plist"], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, - shell=True + shell=True, ) # load the new file and suppress any errors subprocess.run( ["launchctl load ~/Library/LaunchAgents/ephemetoot.scheduler.plist"], - shell=True + shell=True, ) - print('โฐ Scheduled!') + print("โฐ Scheduled!") except Exception: - print('๐Ÿ™ Scheduling failed.') + print("๐Ÿ™ Scheduling failed.") + def checkToots(config, options, retry_count=0): - keep_pinned = 'keep_pinned' in config and config['keep_pinned'] - toots_to_keep = config['toots_to_keep'] if 'toots_to_keep' in config else [] - visibility_to_keep = config['visibility_to_keep'] if 'visibility_to_keep' in config else [] - hashtags_to_keep = set(config['hashtags_to_keep']) if 'hashtags_to_keep' in config else set() - days_to_keep = config['days_to_keep'] if 'days_to_keep' in config else 365 + keep_pinned = "keep_pinned" in config and config["keep_pinned"] + toots_to_keep = config["toots_to_keep"] if "toots_to_keep" in config else [] + visibility_to_keep = ( + config["visibility_to_keep"] if "visibility_to_keep" in config else [] + ) + hashtags_to_keep = ( + set(config["hashtags_to_keep"]) if "hashtags_to_keep" in config else set() + ) + days_to_keep = config["days_to_keep"] if "days_to_keep" in config else 365 try: print( - "Fetching account details for @" - + config['username'] - + "@" - + config['base_url'] + "Fetching account details for @" + + config["username"] + + "@" + + config["base_url"] ) def jsondefault(obj): @@ -85,8 +105,10 @@ def checkToots(config, options, retry_count=0): def checkBatch(timeline, deleted_count=0): for toot in timeline: - if 'id' in toot and 'archive' in config: - filename = os.path.join(config['archive'], str(toot['id']) + '.json') + if "id" in toot and "archive" in config: + filename = os.path.join( + config["archive"], str(toot["id"]) + ".json" + ) with open(filename, "w") as f: f.write(json.dumps(toot, indent=4, default=jsondefault)) f.close() @@ -97,45 +119,71 @@ def checkToots(config, options, retry_count=0): if keep_pinned and hasattr(toot, "pinned") and toot.pinned: if not options.hide_skipped: if options.datestamp: - print(str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') + print( + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) - print( - "๐Ÿ“Œ skipping pinned toot - " - + str(toot.id) - ) + print("๐Ÿ“Œ skipping pinned toot - " + str(toot.id)) elif toot.id in toots_to_keep: if not options.hide_skipped: if options.datestamp: - print(str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') - - print( - "๐Ÿ’พ skipping saved toot - " - + str(toot.id) - ) + print( + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) + + print("๐Ÿ’พ skipping saved toot - " + str(toot.id)) elif toot.visibility in visibility_to_keep: if not options.hide_skipped: if options.datestamp: - print(str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') - + print( + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) + print( - "๐Ÿ‘€ skipping " - + toot.visibility - + " toot - " + "๐Ÿ‘€ skipping " + + toot.visibility + + " toot - " + str(toot.id) ) elif len(hashtags_to_keep.intersection(toot_tags)) > 0: if not options.hide_skipped: if options.datestamp: - print(str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') - - print( - "#๏ธโƒฃ skipping toot with hashtag - " - + str(toot.id) - ) + print( + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) + + print("#๏ธโƒฃ skipping toot with hashtag - " + str(toot.id)) elif cutoff_date > toot.created_at: if hasattr(toot, "reblog") and toot.reblog: if options.datestamp: - print(str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') + print( + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) print( "๐Ÿ‘Ž unboosting toot " @@ -153,7 +201,14 @@ def checkToots(config, options, retry_count=0): mastodon.status_unreblog(toot.reblog) else: if options.datestamp: - print(str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') + print( + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) print( "โŒ deleting toot " @@ -172,11 +227,15 @@ def checkToots(config, options, retry_count=0): diff = mastodon.ratelimit_reset - now print( - "\nRate limit reached at " + - str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ) + - ' - next reset due in ' + - str(format(diff / 60, '.0f')) + - ' minutes.\n' + "\nRate limit reached at " + + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ) + + " - next reset due in " + + str(format(diff / 60, ".0f")) + + " minutes.\n" ) mastodon.status_delete(toot) @@ -187,21 +246,22 @@ def checkToots(config, options, retry_count=0): diff = mastodon.ratelimit_reset - now print( - "\nRate limit reached at " + - str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ) + - ' - waiting for next reset due in ' + - str(format(diff / 60, '.0f')) + - ' minutes.\n' + "\nRate limit reached at " + + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ) + + " - waiting for next reset due in " + + str(format(diff / 60, ".0f")) + + " minutes.\n" ) - time.sleep(diff + 1) # wait for rate limit to reset + time.sleep(diff + 1) # wait for rate limit to reset except MastodonError as e: print( - "๐Ÿ›‘ ERROR deleting toot - " - + str(toot.id) - + " - " - + str(e.args) + "๐Ÿ›‘ ERROR deleting toot - " + str(toot.id) + " - " + str(e.args) ) print("Waiting 1 minute before re-trying") time.sleep(60) @@ -212,10 +272,7 @@ def checkToots(config, options, retry_count=0): 2 ) # wait 2 secs between deletes to be a bit nicer to the server except Exception as e: - print( - "๐Ÿ›‘ ERROR deleting toot - " - + str(toot.id) - ) + print("๐Ÿ›‘ ERROR deleting toot - " + str(toot.id)) print(e) print("Exiting due to error.") break @@ -224,24 +281,17 @@ def checkToots(config, options, retry_count=0): break except KeyError as e: print( - "โš ๏ธ There is an error in your config.yaml file. Please add a value for " - + str(e) + "โš ๏ธ There is an error in your config.yaml file. Please add a value for " + + str(e) + " and try again." ) break except: e = sys.exc_info() - print( - "๐Ÿ›‘ Unknown ERROR deleting toot - " - + str(toot.id) - ) - - print("ERROR: " - + str(e[0]) - + " - " - + str(e[1]) - ) + print("๐Ÿ›‘ Unknown ERROR deleting toot - " + str(toot.id)) + + print("ERROR: " + str(e[0]) + " - " + str(e[1])) # the account_statuses call is paginated with a 40-toot limit # get the id of the last toot to include as 'max_id' in the next API call. @@ -254,7 +304,15 @@ def checkToots(config, options, retry_count=0): else: if options.test: if options.datestamp: - print('\n\n' + str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') + print( + "\n\n" + + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) print( "Test run completed. This would have removed " @@ -263,34 +321,38 @@ def checkToots(config, options, retry_count=0): ) else: if options.datestamp: - print('\n\n' + str( datetime.now(timezone.utc).strftime('%a %d %b %Y %H:%M:%S %z') ), end=' : ') + print( + "\n\n" + + str( + datetime.now(timezone.utc).strftime( + "%a %d %b %Y %H:%M:%S %z" + ) + ), + end=" : ", + ) - print( - "Removed " - + str(deleted_count) - + " toots." - ) + print("Removed " + str(deleted_count) + " toots.") - print('') - print('---------------------------------------') - print('๐Ÿฅณ ==> ๐Ÿงผ ==> ๐Ÿ˜‡ User cleanup complete!') - print('---------------------------------------\n') + print("") + print("---------------------------------------") + print("๐Ÿฅณ ==> ๐Ÿงผ ==> ๐Ÿ˜‡ User cleanup complete!") + print("---------------------------------------\n") except IndexError: print("No toots found!") if options.pace: mastodon = Mastodon( - access_token=config['access_token'], - api_base_url="https://" + config['base_url'], + access_token=config["access_token"], + api_base_url="https://" + config["base_url"], ratelimit_method="pace", ) else: mastodon = Mastodon( - access_token=config['access_token'], - api_base_url="https://" + config['base_url'], + access_token=config["access_token"], + api_base_url="https://" + config["base_url"], ratelimit_method="wait", ) @@ -299,31 +361,23 @@ def checkToots(config, options, retry_count=0): account = mastodon.account(user_id) timeline = mastodon.account_statuses(user_id, limit=40) - print( - "Checking " - + str(account.statuses_count) - + " toots" - ) + print("Checking " + str(account.statuses_count) + " toots") checkBatch(timeline) except KeyError as val: - print('\nโš ๏ธ error with in your config.yaml file!') - print( - 'Please ensure there is a value for ' - + str(val) - + '\n' - ) + print("\nโš ๏ธ error with in your config.yaml file!") + print("Please ensure there is a value for " + str(val) + "\n") except MastodonAPIError: - print('\n๐Ÿ™… User and/or access token does not exist or has been deleted') + print("\n๐Ÿ™… User and/or access token does not exist or has been deleted") except MastodonNetworkError: - print('\n๐Ÿ“ก ephemetoot cannot connect to the server - are you online?') + print("\n๐Ÿ“ก ephemetoot cannot connect to the server - are you online?") if retry_count < 4: - print('Waiting 1 minute before trying again') + print("Waiting 1 minute before trying again") time.sleep(60) retry_count += 1 - print( 'Attempt ' + str(retry_count + 1) ) + print("Attempt " + str(retry_count + 1)) checkToots(config, options, retry_count) else: - print('Gave up waiting for network') + print("Gave up waiting for network") diff --git a/setup.py b/setup.py index 2e22160..0237dd0 100644 --- a/setup.py +++ b/setup.py @@ -1,19 +1,20 @@ from setuptools import setup, find_packages -setup(name='ephemetoot', - version='2.3.1', - url='https://github.com/hughrun/ephemetoot', - license='GPL-3.0-or-later', - packages=find_packages(), - scripts=['bin/ephemetoot'], - install_requires=['Mastodon.py', 'pyyaml', 'requests'], - zip_safe=False, - author='Hugh Rundle', - author_email='hugh@hughrundle.net', - description='A command line tool for selectively deleting old toots from one or more Mastodon accounts.', - keywords='mastodon, mastodon api', - project_urls={ - 'Source Code': 'https://github.com/hughrun/ephemetoot', - 'Documentation': 'https://github.com/hughrun/ephemetoot' - } -) \ No newline at end of file +setup( + name="ephemetoot", + version="2.3.1", + url="https://github.com/hughrun/ephemetoot", + license="GPL-3.0-or-later", + packages=find_packages(), + scripts=["bin/ephemetoot"], + install_requires=["Mastodon.py", "pyyaml", "requests"], + zip_safe=False, + author="Hugh Rundle", + author_email="hugh@hughrundle.net", + description="A command line tool for selectively deleting old toots from one or more Mastodon accounts.", + keywords="mastodon, mastodon api", + project_urls={ + "Source Code": "https://github.com/hughrun/ephemetoot", + "Documentation": "https://github.com/hughrun/ephemetoot", + }, +)