トピック内の投稿を「既読」としてマークする

サイトをリバースエンジニアリングしようと、コンテンツタイプ、csrfトークン、ユーザーエージェントを指定して「https://{hostUrl}/topics/timings」にPOSTリクエストを送信してみました。

ボディ(json)は次のようになります。

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

ステータスコードは200が返されますが、https://{hostUrl}/u/USERNAME/activity/readで既読履歴が変更されません。

この投稿を調べましたが、あまり役に立ちませんでした。

コードの大部分は次のとおりです。

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

    if r.status_code != 200:
        raise RuntimeError("CSRFの取得に失敗しました")

    data = r.json()

    if "csrf" not in data:
        raise RuntimeError("レスポンスにCSRFが含まれていません")

    return data["csrf"]

def load_topics(session, page):
    print(f"[トピック] ページ {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"[既読] {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

これを再度上げます。まだ回答が必要です。

ありがとうございます

これをフラットな "timings[post_number]": [duration_in_ms] として送信した場合はどうなりますか?動作しますか?

「いいね!」 2

どうやら正確には動作しないようです。

こちらが私が変更したコードスニペットです。

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"[既読] {topic_id} → {r.status_code}")

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

ステータスコードは200のままですが、更新されません。

ありがとうございます。

コードは問題ないように見えますが、どこから問題が発生しているのかわかりません。:confused:
何か明白なことを見落としているだけだと直感的に感じています。:sweat_smile:

これは動作します:

def load_topics(session, page):
    print(f"[トピック] ページ {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 
    }    

    # application/json として送信するために json=payload を使用
    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

本当にありがとうございます!!!

これでついに動作しました。

「いいね!」 1

実は、もう一つあります。

これはかなり興味深いですね!

  1. 投稿の既読履歴は更新されます :white_check_mark:
  2. 投稿の既読数は増加します :white_check_mark:

しかし:

  1. トピックの既読数は増加しません :cross_mark:

私が言っているのはこの2点です!

かなり興味深いですね。

パフォーマンス上の理由から、それらの統計情報は定期的なサイドキックジョブによって更新されても驚きません。

スクリプトでタイミングを更新するユースケースは何ですか?

この発言が真実であると100%の確信を持って言えます!
投稿の閲覧数は数回更新されていますが、トピックの閲覧数は更新されていません。約20時間経過しましたが、投稿の閲覧数は増え続けていますが、トピックの閲覧数は増えていません。

エンドポイントをリバースエンジニアリングしてみたいだけです!クールですね。
少し待ってから戻ってきて、値が変わったかどうかを確認したほうがいいと思います。

どのジョブか分かれば、/sidekiq/scheduler で sidekiq ジョブを手動でトリガーできます :slight_smile:

おそらく Jobs::DirectoryRefreshDaily でしょう。

ユーザーディレクトリの /u?period=daily または週次でも同じものは見えますか?そこでは、上部に数値がいつ更新されたかを確認できます。
image

「今日」の数値は1時間に1回更新され、その他の期間は1日に1回のみ更新されると思います。

「いいね!」 1

@Canapin様、私はウェブサイトの所有者ではありません。一般ユーザーとしてログインしているだけでトリガーできる場合、その方法を教えてください。

@Moin様
私が使用しているサイトではそれが無効になっており、常に「コミュニティメンバーのアクティビティを示すリストがここに表示されます。現在、コミュニティがまだ新しいため、リストは空です!」と表示されます。

このケースではできません。APIをリバースエンジニアリングするには、一般ユーザーであることは理想的ではありません。
もし可能であれば、Discourseはホスト名やSMTPを必要としなくなったため、ローカルの開発インストール、または安価なVPS(3〜4ドルのサーバーで十分)への本番インストールを試してみてください。

「いいね!」 1

@Canapinさん、継続的なご協力ありがとうございます。

それをどうすればよいでしょうか?現在、「22トピック既読」と「260万投稿既読」と表示されています。