initial commit
parent
2b477b2528
commit
7c7cc1611c
|
@ -1,4 +1,3 @@
|
||||||
# ---> Python
|
|
||||||
# Byte-compiled / optimized / DLL files
|
# Byte-compiled / optimized / DLL files
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
|
@ -21,6 +20,7 @@ parts/
|
||||||
sdist/
|
sdist/
|
||||||
var/
|
var/
|
||||||
wheels/
|
wheels/
|
||||||
|
pip-wheel-metadata/
|
||||||
share/python-wheels/
|
share/python-wheels/
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
.installed.cfg
|
.installed.cfg
|
||||||
|
@ -50,7 +50,6 @@ coverage.xml
|
||||||
*.py,cover
|
*.py,cover
|
||||||
.hypothesis/
|
.hypothesis/
|
||||||
.pytest_cache/
|
.pytest_cache/
|
||||||
cover/
|
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
*.mo
|
*.mo
|
||||||
|
@ -73,7 +72,6 @@ instance/
|
||||||
docs/_build/
|
docs/_build/
|
||||||
|
|
||||||
# PyBuilder
|
# PyBuilder
|
||||||
.pybuilder/
|
|
||||||
target/
|
target/
|
||||||
|
|
||||||
# Jupyter Notebook
|
# Jupyter Notebook
|
||||||
|
@ -84,9 +82,7 @@ profile_default/
|
||||||
ipython_config.py
|
ipython_config.py
|
||||||
|
|
||||||
# pyenv
|
# pyenv
|
||||||
# For a library or package, you might want to ignore these files since the code is
|
.python-version
|
||||||
# intended to run in multiple environments; otherwise, check them in:
|
|
||||||
# .python-version
|
|
||||||
|
|
||||||
# pipenv
|
# pipenv
|
||||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
# 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.
|
# install all needed dependencies.
|
||||||
#Pipfile.lock
|
#Pipfile.lock
|
||||||
|
|
||||||
# poetry
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
# 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
|
|
||||||
__pypackages__/
|
__pypackages__/
|
||||||
|
|
||||||
# Celery stuff
|
# Celery stuff
|
||||||
|
@ -122,6 +103,7 @@ celerybeat.pid
|
||||||
|
|
||||||
# Environments
|
# Environments
|
||||||
.env
|
.env
|
||||||
|
*.env
|
||||||
.venv
|
.venv
|
||||||
env/
|
env/
|
||||||
venv/
|
venv/
|
||||||
|
@ -146,17 +128,3 @@ dmypy.json
|
||||||
|
|
||||||
# Pyre type checker
|
# Pyre type checker
|
||||||
.pyre/
|
.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