異なるDiscourseサイト間でトピックを同期/クロス投稿する

現在、あるサイトにトピックがあり、それを別のサイトにも表示したいと考えています。ハイパーリンクすることもできますが、どちらのサイトでも編集でき、変更が両方に反映される機能があれば、それが理想です。これにより、一方のトピックが大幅に古くなるのを防ぎ、両方のトピックを常に最新の状態に保つことができます。また、サイトから情報を分散させる方法にもなります。

機能に関するいくつかの考え

  • まず、一方のサイトがトピックのホスト/オーナーとして残り、もう一方のサイトはそれをミラーリングすることになります。さらに進んで、元のトピックが削除された場合に、ミラーリングされたトピックがそれを継承できるかどうかさえ疑問に思います。
  • ミラートピックでは、トピックを非表示にしたり、閉じたりする機能は保持されるべきです。
  • 返信は同期されないはずです。各サイトには異なるユーザーベースがあるため、返信を同期する方法がわかりません。

このような機能の実装は非常に簡単ではないことは理解していますが、これまでに検討されたことがあるのか、また、どのような結果/実験が存在するのか疑問に思っています。

「いいね!」 2

複数のサイトに重複したコンテンツがあるのは、SEOにとって非常に悪いことです。これはサポートされない可能性が高いです。

ユースケースや目的は何ですか? 基本的に、他のサイトをプライマリサイトのバックアップにしたいということですか?

「いいね!」 2

これについてもう少し詳しく教えていただけますか? 私の考えでは、これは重複コンテンツを減らすことになるはずです。

バックアップが目的ではなく、複数のサイトで意味をなすトピックについて、単一の真実の情報源を作成することです。

具体的な例を挙げると、ビザの変更を検討しています。私のプライベートなDiscourseには、やるべきことのチェックリストのようなトピックがあります。しかし、友人もこれに役立つかもしれないので、共有Discourseにも同じトピックを作成します。問題は、単一のトピックを更新するだけで済むのではなく、両方のトピックの情報を個別に同期する必要があることです。これは、しばしばどちらかのトピックに重要な情報が欠けていることを意味します。

APIキーがあれば、他のサイトでも可能かもしれません。エディタのボタン/セクションのようなもので、ターゲットトピックのAPIキーとURLのリストを作成できるのかもしれません。「このトピックのクローンに変更をプッシュする」のようなものをクリックすると、他のインスタンスのトピックに変更がプッシュされるだけです。

「いいね!」 1

全員が見られるように、情報を1か所にまとめましょう。リンクがその方法です。

しかし、あなたはどこか別の場所で利用可能にしたい秘密の情報を持っています。それは別の話です。プラグインを使えば十分に可能です。(例えば、一度しか発生しないタスクを自動化するために何時間も費やすことがよくあります。)

しかし、投稿したユーザーにのみ利用可能になる場所に配置する必要があります。それともサイト全体で利用可能にするのですか?

これもまた、ユーザーごとに設定し、シリアライザーには現在のユーザーのみを含める必要があります(またはユーザープロファイルに保存するかもしれませんか?)。そして、APIキーとさまざまなサイトをマッピングするためのデータ構造が必要になります。これは私が2〜5時間でできると思うようなことですが、実際にはもっとかかるかもしれません。

したがって、このトピックを持つべき他のサイトのURLをどこかに保存する必要があります。その投稿をどのように作成するかについても複雑になる可能性があります。最も簡単な方法は、手動で作成し、ソースサイトのそのトピックのURLを含めることです。おそらく、BBCodeのような形式で生の投稿に保存できます。これにより、ボタンと各トピックへのリンクを作成するコンポーネントを作成でき、Railsコードがジョブをキューに入れて、他のサイトに投稿しようとします。ただし、受信側のサイトにはコードは必要ありません。APIを使用して投稿への編集をプッシュできます。

これは、私が5〜10時間でできると思うようなことですが、実際にはその倍かかるでしょう。それがあなたにとって楽しいなら、クールなプロジェクトになるかもしれません。

「いいね!」 4

これは、ソースサイトでの編集時にWebhookをリッスンし、APIを使用してミラーサイトに投稿する外部の小さなアプリを介しても実行できます。

「いいね!」 4

またこれについて考えていました。

プラグインで、プライマリドキュメントのソースURL用のカスタムトピックフィールドを追加できます。(プライマリドキュメントを非表示にする場合、リモートユーザー名とAPIキーのフィールドも必要になると思いますが、それは後回しでも構いません。あるいは、それらはユーザーのカスタムフィールドに配置することもできます。APIキーに読み取り専用権限があるかどうかは、キーを生成した人が確認することになります。)

トピックを作成する際に、「remote: https://meta.discourse.org/t/synchronising-crossposting-topics-across-different-discourse-sites/263269」のようなものを入力すると、トピックが作成されたときに、Discourseはリモートトピックの生のテキストを取得し、それをrawに編集として挿入し、リモートURLでtopic_custom_fieldをインスタンス化します。おそらく、先頭に「copied from url」を追加します。

これで、リモートトピックをローカルにコピーし、その記録を持つことができます。

その後、「ソースを確認」ボタンを設けることで、リモートトピックを取得し、リモートトピックのupdated_at、さらには他のカスタムフィールドにrawを保存できます(ジョブで定期的に行うこともでき、UXを節約できます)。その後、更新ボタンで、既存のrawをリモートのものに編集として置き換えることができます。

プライマリサイトが公開されている場合、この部分は非常に簡単です。プライベートサイトから取得するためにAPIキーを追加すると複雑になり、複数のサイトにわたるAPIキーのセットを管理するとさらに複雑になります。元のソースを置き換える必要がある場合は、remap rakeタスクでそれを行うか、必要に応じてリモートURLでカスタムフィールドを編集する機能を追加できるかもしれません。

この部分は、このソリューションではセカンダリサイトがプライマリサイトからデータを取得するため、無料で提供されます。

はい。ソースサイトへのリンクを設置することもでき、人々はコメントを見るためにソースサイトにアクセスしたり、Embed comments from Discourse in your single page app を介してコメントを埋め込んだりできるかもしれません。

これに予算がある場合は、お気軽にご連絡ください。

「いいね!」 1

ジェイさん、

以前から考えていたのですが、最近になってようやく進展がありました。残念ながら、DiscourseからDiscourseへの同期は私にとって価値がなくなりました(トピックはほんの一握りでした)が、一般的に他のプラットフォームからマークダウンファイルを同期したいという願望が高まりました。

当社での主なユースケースは、GitlabプロジェクトのreadmesやwikiをDiscourse内で利用できるようにすること(フィードバックや検索のため)でしたが、Gitlabのファイルが単一の真実の情報源として残っていました。Rubyの知識不足からPythonスクリプトを作成しましたが、実装としては明らかに過剰ですが、機能的には満足のいくものです。最初のまともなバージョンは、あなたが概説したことの一部も行います。いくつかの機能:

  • 元のソース(Gitlabのファイル)へのリンクが含まれています
  • 特定のリビジョン(Gitlabコミット#)へのリンクが含まれています
  • 画像と画像URLを処理します
    • Gitlabリポジトリから画像をダウンロードします
    • それらをDiscourseにアップロードします
    • 元の画像URLをアップロードのショートURLに置き換えます
  • すべてを簡単に見つけられるように、「synced_with_gitlab」タグを追加します

Githubでもほぼ同じように機能しました。どちらもほぼ同じ種類のマークダウンなので、かなりスムーズでした。

これをオープンソースにしたいのですが、法務部の意見を見る必要があります。さらに、まだハッキーなPythonの混乱状態です。将来的にはRubyプラグインに変換するつもりですが、必要な時間を見つけることができるかどうかを確認する必要があります。

「いいね!」 3

本当ですね。このプロジェクトに再挑戦しており、現在、以下のようなデータベースの構築を検討しています。

  1. プラットフォームごとのテーブル(Discourseテーブル、Gitlabテーブルなど)を設けて、考えられるニュアンスに対応する
  2. 各プラットフォームテーブルで、WebhookとAPIキーによるポーリングの両方をサポートする
  3. DBまたはAPIキーの暗号化 – 現在は、DB全体を暗号化し、スクリプトとパスフレーズを介してインターフェースするのが最善だと考えています。

Discourseテーブルは以下のようになります。

タイプ 間隔(分) 最終実行(分) ソースドメイン ソース投稿ID ソースユーザー ソースキー ターゲットドメイン ターゲット投稿ID ターゲットユーザー ターゲットキー
Webhook - 120 meta.discourse.com 1280952 - - discourse.mysite.com 120 Tris xyz12345
Polling 60 40 meta.discourse.com 1280953 Tris20 12345xyz discourse.mysite.com 121 Tris xyz12345
Polling 60 35 meta.discourse.com 1750968 Tris20 12345xyz discourse.mysite.com 221 Tris xyz12345
Polling 60 40 meta.discourse.com 1123292 Tris20 12345xyz discourse.mysite.com 131 Tris xyz12345
Webhook - 4800 meta.discourse.com 1283678 - - discourse.mysite.com 129 Tris xyz12345

これはクレイジーで過剰設計に聞こえますか?一部には、両方の可能性(Webhookとポーリング)を考慮した適切なソリューションを構築したいと考えています。

また、DBの内容を安全に保つための提案があれば、非常に感謝します。現在の考えは、スクリプト起動時に引数として与えなければならないパスフレーズでDBを暗号化することです。例えば、

discourse-sync run password123

ちょっとしたインスピレーションとして、Webhookは非常に高速に動作します。


これは、2つの異なるインスタンス上の2つのトピックでした。左のトピックはソーストピック、右のトピックはターゲットです。

単一のフィールドを暗号化するRailsの方法があります。それがダッシュボードで行ったことです。

Active Record Encryption — Ruby on Rails Guides を参照してください。

ポーリングとWebhookの両方を持つことは冗長に思えます。どちらかのアプローチを選択すると思います。

「いいね!」 1

ActivityPubプラグインは、近い将来、DiscourseからDiscourseへのフェデレーションも可能になると思いますか?

「いいね!」 3

参考までに、このアイデアは私も何度も思いつきました。ここに専用のトピックがあるかどうかわかりませんが、もしなければ作成すべきです!

「いいね!」 1

理論的には同意しますが、残念ながら企業の世界は物事を複雑にします。

  1. ITポリシーのため、社内ではWebhookは利用できません(CDCKによって社外でホストされており、重いプロセスなしでは外部へのポートフォワーディングを許可できません) - そのため、APIバージョンは必須です。
  2. Webhookは、他のすべての人にとって迅速で、素晴らしく、完全に合理的であるため、それらにも対応するのが理にかなっています :slight_smile:

それで、しばらく前にこの機能のラフバージョンを作成しました。うまく機能しますが、拡張性は高くありません。私の設計が悪かったのです。Markdownテーブルをインプットとして使用するトピックを検討していました。30以上のエントリがあると素晴らしいのですが、その場合は混乱します。

DiscourseからDiscourseへのユースケースの一部として私が考えているのは、ドキュメント(Meta)の単一の真実のソースが、他のインスタンスのそれぞれの投稿に同期されることです。これは、チームがCoreを変更し、ユーザー ドキュメントMetaを更新した場合、ネイティブに私のインスタンスに最新バージョンのドキュメントがあり、すべてのユーザーが見つけられることを意味します。

V2では、上記のようなSQLiteデータベースを入力として使用し、今回はPythonではなくRustで記述する予定です。

以下にラフスケッチを示します。

graph TB
    A[terminal] -- ~\u003ediscourse-sync run $PASSWORD --\u003e B[Rust Script]
    B -- SQLCipher:Decrypt DB using Password --\u003e C[( sqlite: sources-and-targets')]
    C -- 'Discourse' Table Data --\u003e B
    B -.-\u003e D{Decision on Type}
    D -- Webhook --\u003e E[Listen for Webhook Info]
    D -- Polling --\u003e F[Polling API]
    E --\u003e G[Receive New Information]
    F --\u003e G
    G --\u003e H[Parse and Process Data]
    H --\u003e I[POST\n tgt_domain, tgt_usr, tgt_key, post_id]
    I --'raw' and images--\u003e J[ Target Post ]

    subgraph Rust Script Operations
    B
    D
E
F
G
H
I
    end

フィードバックや提案をいただけると大変嬉しいです :slightly_smiling_face:

「いいね!」 1

@angus おそらくあなたにも関係があるでしょう。ActivityPubプラグインでこれの多くをすでに解決したと思います。

「いいね!」 6

はい、これは現在ActivityPubプラグインでサポートされています。実際、来週の私のToDoリストにあります。

「いいね!」 7

これは両方のインスタンスに適用されますか?たとえば、ソースは公開されていますが、ターゲットは非公開の場合、これはまだ機能しますか?

ActivityPubプラグインは非常に良いように見えますが、プライベートインスタンスには対応していないのではないかと心配しています。

APプラグインの現在のバージョンでは、プライベートインスタンスにも適用されます。プライベートインスタンスは、パブリックDiscourseインスタンスのカテゴリをフォローできるため、それらのインスタンスから公開されたアクティビティを受信できます。(ただし、プライベートインスタンスのコンテンツは公開されないため、同期はパブリックからプライベートへの一方向のみです。)

「いいね!」 3

したがって、ActivityPubは次のように機能できるようです。

パブリック → パブリック :white_check_mark:
パブリック → プライベート :white_check_mark:

プライベート → パブリック :x:
プライベート → プライベート :x:

これは確かに多くのケースで役立ちます。たとえば、Metaからドキュメントをブロードキャストする場合などです。残念ながら、これはまだ私のユースケースの1つを満たしていません。それは、プライベートインスタンスから別のプライベートインスタンスに投稿することです。調査した結果、ActivityPubプラグインは将来これらのユースケースをサポートする可能性は低いと考えてよいでしょうか?ActivityPubは、パブリックからパブリックを念頭に置いて設計されたように見えます。

「いいね!」 2

@Tris20 興味深いですね!この件について、ご意見と詳細を共有していただきありがとうございます。

あなたが説明していることは、ActivityPubが解決するために構築された(一つの)問題です。あなたの意欲をそぎたくはありませんが、正直に言うと、あなたが説明している方法でこれを達成しようとすると、幅広い課題に直面することになるでしょう。克服しなければならないすべての課題を網羅的に説明するつもりはありませんが、感覚をつかむために言うと、ActivityPubプラグインにはすでに700近くのrspecテストがあり、1年の開発を経て、ようやくトピックからトピックへの完全な同期をサポートするようになったところです。

ActivityPubを介したプライベートからパブリック、およびプライベートからプライベートへの公開をサポートする本質的な制限は、私には見当たりません。問題は、プライベートインスタンスを扱う際に、アクセスとセキュリティの懸念が満たされていることを確認することです。

提案させてください。おそらく、ActivityPubプラグイン(および標準としてのActivityPub)がこの点で既に行っている作業を基盤として構築する方法を考えてみてはどうでしょうか。実際、DiscourseからDiscourseへのコンテンツ同期の問題に対する解決策は現在も機能しています。まだあなたのユースケースをカバーしていませんが、あなたのニーズを満たすために解決する必要がある問題の大部分を解決します。

おそらく、プラグイン内でプライベートからプライベートへの同期がどのように機能するか(つまり、アクセスとセキュリティの問題がどのように処理されるか)を考えてみてはどうでしょうか?そうすれば、あなたと私が協力して、それを機能として追加するためのPRを作成することもできるかもしれません。おそらく、プラグイン(または標準としてのActivityPub)のコンテキストであなたが望むことを達成することが不可能だと感じる段階に達するかもしれませんが、その段階に達するために行った作業は、独立したソリューションのために必要となる作業と実質的に同じなので、無駄にはならないでしょう。

ActivityPubの世界には多くの賢い人々がおり、この種の問題(つまり、プライベート公開)が以前に深く検討されたことがあるとしても、私は驚かないでしょう。それに関する先行事例を見つけることができる場所の一つは、ActivityPubの主要なコミュニティフォーラムであるSocialHub(もちろんDiscourse)です。これは現在、Discourse ActivityPubプラグインを使用しています :slight_smile:

「いいね!」 7