initial commit
parent
2b477b2528
commit
7c7cc1611c
|
@ -1,4 +1,3 @@
|
|||
# ---> Python
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
@ -21,6 +20,7 @@ parts/
|
|||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
|
@ -50,7 +50,6 @@ coverage.xml
|
|||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
|
@ -73,7 +72,6 @@ instance/
|
|||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
|
@ -84,9 +82,7 @@ profile_default/
|
|||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
|
@ -95,22 +91,7 @@ ipython_config.py
|
|||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
|
@ -122,6 +103,7 @@ celerybeat.pid
|
|||
|
||||
# Environments
|
||||
.env
|
||||
*.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
|
@ -146,17 +128,3 @@ dmypy.json
|
|||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# User name (email address) and password for mastodon server
|
||||
MD_USER="toota-palooza@example.com"
|
||||
MD_PASS="my-super-good-password"
|
||||
MD_HOST="https://always.grumpy.world"
|
|
@ -0,0 +1,24 @@
|
|||
[project]
|
||||
name = "toota-palooza"
|
||||
version = "1.0"
|
||||
authors = [{ name="Paco Hope", email="toota-palooza@filter.paco.to" } ]
|
||||
description = "Fill a mastodon public timeline with public toots from bots who post gutenberg open texts."
|
||||
readme = "README.md"
|
||||
license = { file="LICENSE" }
|
||||
requires-python = ">=3.9"
|
||||
classifiers = [
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
"Homepage" = "https://git.paco.to/nick/toota-palooza"
|
||||
"Bug Tracker" = "https://git.paco.to/nick/toota-palooza/issues"
|
||||
|
||||
[build-system]
|
||||
requires = ["setuptools>=61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project.scripts]
|
||||
toota-palooza = "toota-palooza.cli:main"
|
|
@ -0,0 +1,6 @@
|
|||
build
|
||||
Mastodon.py
|
||||
python-dotenv
|
||||
wheel
|
||||
pip
|
||||
python-daemon
|
|
@ -0,0 +1,6 @@
|
|||
[metadata]
|
||||
name = toota-palooza
|
||||
version = 1.0
|
||||
|
||||
[options]
|
||||
packages = toota-palooza
|
|
@ -0,0 +1,11 @@
|
|||
from mastodon import Mastodon
|
||||
|
||||
'''
|
||||
Mastodon.create_app(
|
||||
'toota-palooza',
|
||||
api_base_url = 'https://infosec.exchange',
|
||||
to_file = '../.toota-palooza.env'
|
||||
)
|
||||
'''
|
||||
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
'''
|
||||
Command line module for calling toota-palooza to do its work
|
||||
'''
|
||||
|
||||
__all__ = ['toota-palooza']
|
||||
__author__ = 'Paco Hope <toota-palooza@filter.paco.to>'
|
||||
__date__ = '25 November 2022'
|
||||
__version__ = '1.0'
|
||||
__copyright__ = 'Copyright © 2022 Paco Hope. See LICENSE for details.'
|
||||
|
||||
from mastodon import Mastodon
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
import time
|
||||
import argparse
|
||||
import sys
|
||||
from pprint import pprint
|
||||
# import toota-palooza
|
||||
|
||||
|
||||
def mastodonInit():
|
||||
"""Connect to the Mastodon instance based on .env files"""
|
||||
|
||||
server = Mastodon(
|
||||
client_id = '.toota-palooza.env',
|
||||
api_base_url = os.getenv('MD_HOST')
|
||||
)
|
||||
|
||||
load_dotenv()
|
||||
server.log_in(
|
||||
os.getenv('MD_USER'),
|
||||
os.getenv('MD_PASS'),
|
||||
to_file = '.toota-palooza-usercred.env'
|
||||
)
|
||||
return(server)
|
||||
|
||||
|
||||
def checkPublicTimeline( server):
|
||||
"""Do one run. Connect to the database, connect to the server, get the public timeline.
|
||||
Look at users and check to see if any are potential impersonators. Then exit."""
|
||||
|
||||
# Here's the idea: pick a chunkSize. Ask the server for that many toots off the public
|
||||
# timeline. As long as the server gives us as many as we asked for, keep trying.
|
||||
# as soon as we get less than we asked for, quit.
|
||||
#
|
||||
# XXX Not sure about rate-limiting
|
||||
#
|
||||
chunkSize = 20
|
||||
maxPosts = 1000
|
||||
timelineList = []
|
||||
useridList = {}
|
||||
total = 0
|
||||
calls = 0
|
||||
while( total < maxPosts ):
|
||||
timelineList = server.timeline(timeline='public', since_id=latestId, limit=chunkSize)
|
||||
calls = calls + 1
|
||||
for post in timelineList:
|
||||
userid = post.account.acct
|
||||
username=userid.split('@')[0]
|
||||
|
||||
try:
|
||||
domain=userid.split('@')[1]
|
||||
except IndexError:
|
||||
# if there is no domain, then it's a local account
|
||||
domain=os.getenv('MD_HOST').split('/')[2]
|
||||
useridList[userid] = (username,domain,post.account.display_name,post.account.bot,post.url)
|
||||
latestId=post.id
|
||||
|
||||
if( len(timelineList) < 1):
|
||||
# We got fewer than we asked for. Drop out of the loop.
|
||||
total = total + len(timelineList)
|
||||
break
|
||||
else:
|
||||
# record how many we did, and go again.
|
||||
total = total + len(timelineList)
|
||||
timelineList = []
|
||||
|
||||
def daemon_main():
|
||||
"""Run from a command line."""
|
||||
server = mastodonInit()
|
||||
|
||||
while(True):
|
||||
# do a thing
|
||||
time.sleep(600)
|
||||
|
||||
def once():
|
||||
"""Run from a command line."""
|
||||
server = mastodonInit()
|
||||
return 0
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Check for suspicious impersonators.'
|
||||
)
|
||||
parser.add_argument( '-d', '--debug', action='store_true',
|
||||
help='Enable debugging messages.')
|
||||
parser.add_argument( '-o', '--once', action='store_true',
|
||||
help='Run once and exit. Default is to run as a daemon.')
|
||||
args = parser.parse_args()
|
||||
|
||||
if( args.once ):
|
||||
exit(once())
|
||||
else:
|
||||
daemon_main()
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(once())
|
Loading…
Reference in New Issue