Segna i post nel topic come "letti"

Ho provato a fare l’ingegneria inversa del sito inviando richieste POST a “https://{hostUrl}/topics/timings” con content-type, token csrf e user-agent.

Ecco come appare il corpo (json):

payload = {
  "topic_id": topic_id,
  "topic_time": post_count * 60000,
  "timings": timings
}

Restituisce un codice di stato 200 ma la cronologia di lettura non cambia su https://{hostUrl}/u/USERNAME/activity/read

Ho provato a guardare questo post ma non è stato di grande aiuto:

Ecco una buona parte del codice:

def get_csrf(session):
    r = session.get(f"https://{hostUrl}/session/csrf.json")

    if r.status_code != 200:
        raise RuntimeError("Impossibile ottenere CSRF")

    data = r.json()

    if "csrf" not in data:
        raise RuntimeError("Nessun CSRF nella risposta")

    return data["csrf"]

def load_topics(session, page):
    print(f"[Argomenti] Pagina {page}")

    r = session.get(
        f"https://{hostUrl}/latest.json?page={page}"
    )

    if r.status_code != 200:
        return []

    data = r.json()

    return [
        {
            "id": t["id"],
            "posts_count": t["posts_count"]
        }
        for t in data["topic_list"]["topics"]
    ]

def mark_post_as_read(session, topic_id, post_count):
    url = f"https://{hostUrl}/topics/timings"

    timings = {
        str(i): 60000
        for i in range(1, post_count + 1)
    }

    payload = {
        "topic_id": topic_id,
        "topic_time": post_count * 60000,
        "timings": timings
    }

    csrf = get_csrf(session)

    r = session.post(
        url,
        json=payload,
        headers={
            "X-CSRF-Token": csrf,
            "User-Agent": "Mozilla/5.0",
            "Content-Type": "application/json"
        }
    )

    print(f"[Letto] {topic_id} → {r.status_code}")

    if r.status_code != 200:
        print(r.text[:300])

def tab_worker(session):
    page = 1

    while True:
        topics = load_topics(session, page)

        if not topics:
            break

        for t in topics:
            mark_post_as_read(
                session,
                t["id"],
                t["posts_count"]
            )

            time.sleep(0.4)

        page += 1

Sto facendo un “bump” a questo perché ho ancora bisogno della risposta.

Grazie

E se lo invii come un semplice \"timings[post_number]\": [duration_in_ms]? Funziona?

2 Mi Piace

Non sembra funzionare esattamente.

Ecco lo snippet di codice che ho modificato.

def mark_post_as_read(session, topic_id, post_count):
    url = f"https://{hostUrl}/topics/timings"

    payload = {
        "topic_id": topic_id,
        "topic_time": post_count * 60000
    }

    for i in range(1, post_count):
        payload[f"timings[{i}]"] = 60000

    csrf = get_csrf(session)

    r = session.post(
        url,
        json=payload,
        headers={
            "X-CSRF-Token": csrf,
            "User-Agent": "Mozilla/5.0",
            "Content-Type": "application/json"
        }
    )

    print(f"[Read] {topic_id} → {r.status_code}")

    if r.status_code != 200:
        print(r.text[:300])

Restituisce ancora 200 ma non viene aggiornato.

Grazie

Il codice sembra a posto, non sono sicuro da dove provenga il problema. :confused:
Il mio istinto mi dice che ci manca solo qualcosa di ovvio da qualche parte :sweat_smile:

Questo funziona:

def load_topics(session, page):
    print(f"[Topics] Caricamento pagina {page}")
    r = session.get(f"https://{hostUrl}/latest.json?page={page}")
    if r.status_code != 200:
        return []
    return [{"id": t["id"], "posts_count": t["posts_count"]} for t in r.json()["topic_list"]["topics"]]
    timings = {
        str(i): 60000
        for i in range(1, post_count + 1)
    }
    payload = {
        "topic_id": topic_id,
        "topic_time": post_count * 60000,
        "timings": timings 
    }    

    # Usa json=payload per inviare come application/json
    r = session.post(url, json=payload, headers = {
        "X-CSRF-Token": csrf,
        "User-Agent": "Mozilla/5.0",
        "X-Requested-With": "XMLHttpRequest",
        "Content-Type": "application/json"
      }
    )
1 Mi Piace

Grazie MILLE!!!

Finalmente funziona.

1 Mi Piace

In effetti, un’altra cosa.

Questo è piuttosto interessante!

  1. Aggiorna la cronologia di lettura dei post :white_check_mark:
  2. Il conteggio dei post letti aumenta :white_check_mark:

MA:

  1. Il conteggio dei topic letti non aumenta :cross_mark:

Questi sono i 2 di cui sto parlando!

Abbastanza interessante.

Non mi sorprenderebbe se quelle statistiche venissero aggiornate da un normale lavoro pianificato (sidekick job), per motivi di prestazioni.

Qual è il tuo caso d’uso per aggiornare i tempi con uno script?

Posso affermare con il 100% di certezza che questa affermazione è vera!
La lettura dei post è stata aggiornata diverse volte, ma la lettura degli argomenti no. Sono a intervalli diversi? Sono passate circa 20 ore e il conteggio delle letture dei post continua ad aumentare, ma quello degli argomenti no.

Voglio solo provare a fare l’ingegneria inversa degli endpoint! È interessante.

Penso che dovrei aspettare un po’ prima di tornare a vedere se i valori sono cambiati.

Puoi attivare manualmente il job di sidekiq in /sidekiq/scheduler se scopri quale sia :slight_smile:

Forse è Jobs::DirectoryRefreshDaily.

Vedi la stessa cosa nella directory utente a /u?period=daily o settimanale? Lì puoi vedere quando i numeri sono stati aggiornati in alto.
image

Penso che i numeri per “oggi” vengano aggiornati una volta all’ora, mentre gli altri intervalli di tempo vengono aggiornati solo una volta al giorno.

1 Mi Piace

@Canapin, non sono il proprietario del sito web. Se riesco ancora ad attivarlo semplicemente essendo loggato come utente normale, fammi sapere il metodo per farlo.

@Moin
Il sito che sto usando ha questa opzione disabilitata e restituirà sempre “Qui verrà mostrato un elenco dei membri della community con la loro attività. Per ora l’elenco è vuoto perché la tua community è ancora nuovissima!”

Nel suo caso non puoi. Essere un utente normale non è l’ideale per fare il reverse-engineering dell’API.
Se puoi, prova un’installazione di sviluppo locale o un’installazione di produzione su un VPS economico (un server da 3-4$ va bene), dato che Discourse non richiede più un nome host o SMTP.

1 Mi Piace

Ciao @Canapin, grazie per il continuo aiuto.

Come potrei farlo? Attualmente dice 22 argomenti letti e 2,6 milioni di post letti