123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 |
- #!/usr/bin/python3
- # inizio
- print("Inizio...\n")
- def exitp():
- # funzione uscita
- print("\nPasso e chiudo...\n")
- exit()
- ################################################################################
- # moduli
- ################################################################################
- from os.path import dirname, realpath, isfile, sep
- import json
- from mastodon import Mastodon
- from mastodon.Mastodon import MastodonAPIError
- from datetime import datetime
- import dateutil.parser
- from lxml import html
- ################################################################################
- # modalità
- ################################################################################
- user_flag = True
- #user_flag = False # togliere cancelletto inizio riga per versione admin
- test_flag = True
- #test_flag = False # togliere cancelletto inizio riga per versione operativa (solo admin)
- if user_flag:
- print("Versione utente! Solo versione di test disponibile...\n")
- test_flag = True
- else:
- print("Versione admin! Maneggiare con cura se non è una versione di test...\n")
- if test_flag:
- print("Versione di TEST!\n")
- else:
- print("Versione operativa!\n")
- checkline = "Sì, so quel che faccio."
- try:
- rawline = input("Continuare?... Scrivere \"" + checkline + "\"\n")
- except EOFError:
- exitp()
- if rawline != checkline:
- print("Non vuoi continuare. Va bene!")
- # esci
- exitp()
- ################################################################################
- # ausiliari
- ################################################################################
- appname = "mannaggiapp"
- baseurl = 'https://mastodon.domain.tld'
- clientcredfname = 'clientcred.secret'
- usercredfname = 'usercred.secret'
- scriptdir = dirname(realpath(__file__))
- usercredfpath = scriptdir + sep + usercredfname
- adminmail = 'admin@mail.tld'
- adminpz = 'passwordveramentebuona'
- default_following_count = 7
- avatar_missing = baseurl + "/avatars/original/missing.png"
- header_missing = baseurl + "/headers/original/missing.png"
- def find_anchor(element):
- if element.text:
- anchor = element.find("a")
- if anchor is not None:
- return True
- return False
- ################################################################################
- # registro app e token
- ################################################################################
- print("Registro app e token, oppure uso token...")
- if not isfile(usercredfpath):
- ############################################################################
- # token non registrato
- ############################################################################
- print(" Creo app e token...")
- Mastodon.create_app(
- appname,
- api_base_url = baseurl,
- to_file = clientcredfname
- )
- mastodon = Mastodon(
- client_id = clientcredfname,
- api_base_url = baseurl
- )
- mastodon.log_in(
- adminmail,
- adminpz,
- to_file = usercredfname
- )
- print("Buone notizie! La app \"{}\" è stata registrata!\nOra preoccupati di ***TOGLIERE*** la password dallo script!\nVa bene cambiare il valore di \'adminpz\' con qualsiasi altro valore valido!\nAnche una stringa vuota: letteralmente \"\"".format(appname))
- print("Rilancia lo script dopo aver tolto la password.\n")
- exitp()
- else:
- ############################################################################
- # token registrato
- ############################################################################
- print(" Uso token...")
- if adminpz:
- print("Avviso! Hai tolto la password dallo script?\nSe no, ***TOGLILA***: va bene cambiare il valore di \'adminpz\' con qualsiasi altro valore valido!\nAnche una stringa vuota: letteralmente \"\"\n")
- mastodon = Mastodon(
- access_token = usercredfname,
- api_base_url = baseurl
- )
- ################################################################################
- # ottenimento degli accounts
- ################################################################################
- if test_flag:
- ############################################################################
- # ottenimento di account di prova
- ############################################################################
- if isfile("accounts-json.txt"):
- ########################################################################
- # ottenimento di account da file json
- ########################################################################
- print("Ottieni accounts salvati su file json...")
- with open("accounts-json.txt", "r") as f:
- lines = f.read()
- accounts = json.loads(lines)
- else:
- ########################################################################
- # ottenimento di account da istanza
- ########################################################################
- print("Ottieni accounts nella directory dei profili pubblica...")
- print(" Seleziona accounts nella directory dei profili (n.b. id salvati su file)")
- with open("directory-profili.txt","r") as f:
- lines = f.read()
- splits = lines.split()
- accounts = []
- print(" Ottieni ogni account tramite la API di Mastodon")
- print(" N.B.: default di 300 richieste ogni 5 minuti... per 12k accounts sono 3+ ore! Usare interfaccia admin.\nLimite di 270 qui per test.")
- nsplits = len(splits)
- fsplits = len(str(nsplits))
- for isplit, split in enumerate(splits):
- fstr = " {:" + str(fsplits) +"d}/{} {}"
- print(fstr.format(isplit+1, nsplits, split))
- try:
- account = mastodon.account(split)
- accounts.append(account)
- except MastodonAPIError as e:
- pass # account non trovato (sospeso oppure altro)
- if isplit == 271:
- break
- with open("accounts-json.txt", "w") as f:
- json_str = json.dumps(accounts, default=str)
- f.write(json_str)
- print(" Accounts scritti in formato JSON")
- else:
- ############################################################################
- # ottenimento di tutti gli account di istanza
- ############################################################################
- print("Ottieni tutti gli accounts di istanza...")
- try:
- accounts = mastodon.admin_accounts() # tutti gli account locali
- except MastodonAPIError:
- print("L'account \"\" non ha l'autorizzazione necessaria per ottenere tutti gli account di istanza")
- exitp()
- print("Accounts ottenuti!\n")
- ################################################################################
- # selezione (+ sospensione se non è un test) accounts
- ################################################################################
- for account in accounts:
- ############################################################################
- # aggiusta interfaccia
- ############################################################################
- # stessa interfaccia admin account dict e user account dict
- if "account" in account:
- # admin account dict that wraps account dict
- # https://mastodonpy.readthedocs.io/en/stable/#admin-account-dicts
- account = account["account"]
- date_ = account["created_at"]
- # controllo su data
- if type(date_) == datetime:
- date = date_
- elif type(date_) == str:
- date = dateutil.parser.parse(date_)
- else:
- raise TypeError(
- "{} è di tipo {} e non è di un tipo appropriato (stringa o datetime)".format(date, type(date))
- )
- ############################################################################
- # condizioni su account
- ############################################################################
- # casi 0: quando non sospendere
- # salta: se l'account è creato dopo fine giugno 2020
- if date > datetime(2020, 6, 30, 23, 59, 59).astimezone(): # luglio o poi
- continue
- # salta: se l'account ha almeno uno status
- if account["statuses_count"] > 0: # almeno uno status
- continue
- # variabili di servizio
- note = html.fromstring(account["note"]) # biografia del profilo
- fields = account["fields"] # metadati del profilo
- avatar = account["avatar"]
- header = account["header"]
- ############################################################################
- # casi 1+: quando sospendere
- # caso 1: account "vuoto"
- # zero metadati, biografia vuota, zero toot, account vecchio
- if note.text is None and len(fields) == 0: # biografia vuota e zero metadati
- if account["following_count"] <= default_following_count:
- continue # salta: si assume no spam tramite seguiti che vanno in federata)
- #TODO: check contro i seguiti di default (n.b. questi cambiano di volta in volta)
- if avatar != avatar_missing or header != header_missing:
- continue # salta: account personalizzato in profilo o intestazione
- #nei casi restanti: sospendi
- print(account["url"] + " caso 1") # da sospendere
- if not test_flag:
- mastodon.admin_account_moderate(account["id"], action="suspend", send_email_notification=False)
- continue # salta loop sempre: altri casi sono esclusi (anche in caso di test)
- ############################################################################
- # caso 2: account spam
- # link nei metadati o biografia, zero toot, account vecchio
- #TODO: check email, numeri di telefono
- # link nella biografia
- if find_anchor(note):
- print(account["url"] + " caso 2a") # da sospendere
- if not test_flag:
- mastodon.admin_account_moderate(account["id"], action="suspend", send_email_notification=False)
- continue # possono esserci altri valori di metadati con ancore (vs. caso 1)
- # link nei metadati
- for field in fields:
- namestr = field["name"]
- if namestr:
- name = html.fromstring(namestr)
- # link
- if find_anchor(name):
- print(account["url"] + " caso 2a1") # da sospendere
- if not test_flag:
- mastodon.admin_account_moderate(account["id"], action="suspend", send_email_notification=False)
- continue # possono esserci altri nomi di metadati con ancore (vs. caso 1)
- valuestr = field["value"]
- if valuestr:
- value = html.fromstring(valuestr)
- if find_anchor(value):
- print(account["url"] + " caso 2a2") # da sospendere
- if not test_flag:
- mastodon.admin_account_moderate(account["id"], action="suspend", send_email_notification=False)
- continue # possono esserci altri valori di metadati con ancore (vs. caso 1)
- ############################################################################
- # fine
- exitp()
|