Discourse 이메일 메시지가 잘못 스레드 처리됩니다

discuss.python.org에서 현재 Discourse의 이메일 관련 문제를 논의하고 있습니다. 가장 큰 불만은 스레딩(threading) 기능이 없다는 점입니다. 헤더를 좀 더 자세히 살펴본 결과 다음과 같은 상황이었습니다:

  • Message-ID 헤더는 적어도 고유합니다.
  • Reply-To와 References 헤더는 다른 메시지의 Message-ID를 참조하지 않습니다. 심지어 해당 메시지에 대한 답장임에도 불구하고 답장 대상 메시지의 메시지 ID를 참조하지도 않습니다.
  • 대신 토픽 번호를 기반으로 한 가상의 메시지 ID를 참조하고 있습니다.

이로 인해 이메일을 사용하는 사용자는 (a) 완전히 평평하고 스레드 구조가 없는 토론과 (b) 루트 메시지가 실제로 존재하지 않는 것처럼 보이는 현상을 겪게 됩니다. In-Reply-To와 References 헤더가 실제로는 어떤 메시지에도 나타나지 않는 메시지 ID를 참조하기 때문입니다.

이는 잘못된 것이며 RFC 5322를 위반하는 것입니다. 또한 이메일 경험을 훨씬 더 나쁘게 만들고 있습니다.

예를 들어, 그곳에는 첫 번째 메시지에 다음과 같은 헤더가 있는 스레드가 있습니다:

Message-ID: <topic/17208.dc83577b18fc3ecc438ed42a@discuss.python.org>
References: <topic/17208@discuss.python.org>

이것은 첫 번째 메시지입니다. 어디에도 해당 ID를 가진 메시지가 없으므로 References 헤더가 있어서는 안 됩니다.

두 번째 메시지는 다음과 같은 헤더를 가지고 있습니다:

Message-ID: <topic/17208/60568.898edf234f56cf6f3a661c1a@discuss.python.org>
In-Reply-To: <topic/17208@discuss.python.org>
References: <topic/17208@discuss.python.org>

다시 한번, Message-ID는 괜찮지만 In-Reply-To와 References는 전혀 의미가 없습니다.

이것은 수정하기 쉬워야 합니다. 첫 번째 메시지는 In-Reply-To도 References 헤더도 없어야 합니다. 두 번째 메시지는 첫 번째 메시지의 Message-ID를 In-Reply-To와 References 헤더에 포함해야 합니다.

구체적인 사항은 RFC5322 3.6.4절을 참고하십시오:

현재 상태에서는 이메일 사용자가 평평하고 구조화되지 않은 토론을 보게 됩니다. 이러한 수정이 이루어지면, 사용자는 합리적이고 따라가기 쉬운 스레드 형식의 디스플레이를 가질 수 있게 됩니다.

9개의 좋아요

관심 있는 분들을 위해, 카메론이 언급한 토론의 아카이브는 https://mail.python.org/archives/list/python-dev@python.org/message/VHFLDK43DSSLHACT67X4QA3UZU73WYYJ/에서 찾을 수 있습니다.

2개의 좋아요

이것은 회귀(regression) 문제인 것 같습니다. 이 구체적인 주제와 수정 사항을 확인해 주세요.

1개의 좋아요

HEAD와 해당 수정 사항 사이의 diff를 살펴보고 있습니다.

제 생각에는 현재 References 헤더가 antecedant(선행 항목)가 없더라도 항상 설정되는 것 같습니다. topic_canonical_reference_id가 폴백으로 사용되고 있습니다. 여전히 이것이 잘못된 것이라 생각합니다. 해당 id를 가진 이메일 메시지는 존재하지 않으니까요.

In-Reply-To는 post.post_number!=1인 경우에만 설정되므로 조금 더 정확합니다. 하지만 이 역시 topic_canonical_reference_id로 폴백됩니다:

@message.header['In-Reply-To'] = referenced_post_message_ids[0] || topic_canonical_reference_id

제 눈에 이 부분은 두 가지 문제가 있는 것 같습니다:

  • referenced_post_message_ids가 없다면 폴백은 topic_canonical_reference_id가 아니라 1번 게시글의 Message-ID여야 합니다.
  • 회신 이메일 수신(receipt-of-reply-emails) 코드 어딘가에서 회신 메시지의 In-Reply-To 헤더를 제거하고 있는 것 같습니다. 회신 메시지는 referenced_post_message_ids 배열(“list”? 저는 Ruby 초보입니다)을 올바르게 채웠어야 하니까요.
3개의 좋아요

Cameron, 이 주제를 논의하기 위해 열어주셔서 감사합니다. 게시글에 많은 세부 정보를 제공해 주셔서 특히 감사드립니다. 다음 두 커밋에서 비롯된 이 복잡한 문제를 담당하고 있습니다:

Thunderbird와 같은 이메일 클라이언트에서 스레딩(threading)과 관련된 몇 가지 문제를 이미 어느 정도 인지하고 있었습니다. 다만, Discourse의 이메일 스레딩을 사용하는 사용자 수가 많지 않아 그동안 후순위로 미뤄두었습니다. 하지만 이제 문제가 드러나고 있으므로, 이 문제를 다시 검토하고 수정안을 마련하는 데 시간을 할애해야 합니다.

흥미롭게도, Gmail에서 스레딩이 올바르게 작동하도록 하기 위해 당시 처음 보낸 이메일과 이후 모든 이메일에 이 References 헤더를 추가했습니다. 하지만 동의하듯 이는 이상적이지 않으며, 이후 이메일의 In-Reply-To와 References 헤더에서 원래의 Message-ID를 사용하지 않는 것과 함께 스레딩 문제를 일으키는 원인이 될 가능성이 높습니다.

기존 논의 내용과 코드를 살펴보고 이 문제를 해결해 나가는 동안 조금만 기다려 주십시오. 그 사이, 문제가 발생하고 있는 다른 이메일 클라이언트가 있는지 알고 계신가요? 예를 들어 Thunderbird에서 문제가 발생하는 것은 알고 있지만, 다른 클라이언트는 어떨까요? 감사합니다.

7개의 좋아요

긴 답변을 작성했지만 다음과 같은 오류가 발생했습니다:

죄송합니다. 다음 수신자에게 보낸 이메일이 
["incoming+8349bd9eb1f2b582df4f32dbe85c3363@meta.discoursemail.com"] 
(제목: Re: [Discourse Meta] [bug] Discourse email messages are
incorrectly threaded) 처리되지 않았습니다.

이유:
죄송합니다. 신규 사용자는 게시물에 링크를 2개까지만 포함할 수 있습니다.
문제를 수정할 수 있다면 다시 시도해 주세요.

포럼에 게시하여 직접 확인하고 수정할 수 있도록 하겠습니다…

1개의 좋아요

Cameron, 이 주제를 토론용으로 열어 주시고 게시물에 많은 세부 정보를 제공해 주셔서 감사합니다. 저는 다음 두 커밋에서 비롯된 이 복잡한 문제를 담당하고 있습니다:

3b13f1146b2a406238c50d6b45bc9aa721094f46

이것은 괜찮아 보입니다. 이 ID를 DB 레코드와 함께 저장하여 수신된 답글이 이전 포럼 메시지와 연결되도록 하는 것인가요?

또한, RFC5322의 허용된 문자 관점에서 접미사가 구문적으로 합법적인지 확인해 드릴까요?

82cb67e67b83c444f068fd6b3006d8396803454f

이 두 번째 커밋은 우리가 이전에 목격한 또 다른 문제를 다루는 것으로 보입니다: 게시물이 이메일에서 온 경우, 이메일 사용자에게 전송되는 발신 메시지 ID가 작성자의 원본 메시지 ID가 아닙니다. 이로 인해 메일 클라이언트의 관점에서는 두 개의 서로 다른 메시지로 인식되며, 포럼에서 전송된 사본이 아닌 원본에 대한 답글이 깨지는 문제가 발생할 가능성이 큽니다. 예를 들어:

To: the forum
CC: one of the participants

해당 참여자는(잘못될 수 있지만) 포럼에서 복사본을 받고 작성자로부터 직접 복사본을 받게 되며, 메시지 ID가 다르기 때문에 상대방 측에서는 서로 다른 메시지로 나타납니다.

in-reply-to 및 references 헤더 문제를 해결한 후 이 문제에 대해 두 번째 버그 리포트를 작성할 계획이었습니다. 이는 훨씬 더 중요한 문제입니다.

Thunderbird와 같은 이메일 클라이언트에서 스레딩 관련 문제가 몇 가지 있다는 것을 꽤 오래전부터 인지하고 있었지만, Discourse의 이메일 스레딩 사용자의 상당한 비중을 차지하지는 않아서 미뤄왔습니다. 하지만 이제 이 문제가 드러나고 있으므로, 이 문제를 재검토하고 수정안을 마련하는 데 시간을 할애해야 합니다.

저와 몇몇 다른 사람들은 mutt를 사용합니다. 디버깅을 돕고 코드 리뷰를 하는 데 필요한 모든 것을 기꺼이 수행하겠습니다. 또한 과거에 저는 메일 시스템 관리자로 일한 경력이 있습니다.

[quote=“Cameron Simpson, post:1, topic:233499,
username:cameron-simpson”]
첫 번째 메시지입니다. 해당 ID를 가진 메시지가 어디에도 없으므로 References 헤더가 있어서는 안 됩니다.
[/quote]

흥미롭게도, Gmail에서 스레딩이 올바르게 작동하도록 하기 위해 당시 첫 번째로 전송된 이메일과 그 이후의 모든 이메일에 이 References 헤더를 추가했습니다.

올바른 References 헤더(답글의 in-reply-to처럼 첫 번째 게시물에는 없는)도 작동해야 한다고 생각합니다. 하지만 Gmail은 때때로 메일 표준과 다소 느슨한 관계를 가지고 있습니다. 저는 Gmail 계정이 있으므로 그곳에서도 디버깅을 할 수 있습니다. 그리고 원칙적으로 이 토론 자체를 테스트베드로 사용할 수도 있을 것입니다.

하지만 이것이 이상적이지 않고, 이후 이메일의 In-Reply-To 및 References 헤더에서 원래의 Message-ID를 사용하지 않는 것과 함께 스레딩 문제를 일으키는 원인일 가능성이 높다는 데에는 동의합니다.

과거 토론과 코드를 살펴보고 이 문제를 해결해 나가는 동안 조금만 기다려 주십시오.

걱정 마세요.

그동안, 어떤 다른 이메일 클라이언트들이 사용되고 있으며 문제를 겪고 있는지 알고 계신가요? 예를 들어 Thunderbird에서 이 문제가 발생한다는 것은 알고 있지만, 다른 클라이언트는 어떤가요? 감사합니다.

확실히 mutt입니다. 적어도 mutt의 경우 헤더를 보는 것이 매우 쉽고, 다른 클라이언트에서는 종종 가려지는 답글 트리 체인도 볼 수 있습니다.

메일 스레딩은 Message-ID 및 In-Reply-To 헤더에 의해 완전히 정의됩니다. References 헤더는 USENET의 팔로업(followups)에서 시작되어 (거기서) 여러 메시지 ID를 지원했으며, In-Reply-To는 하나만 지원합니다. References가 현재 RFC5322에도 존재하는 것으로 보이며, 그 의미를 확인해 보겠습니다.

5개의 좋아요

오늘 늦게 이 주제에 대해 긴 글을 쓸 예정이라, 생각을 정리하고 있습니다. 지금까지 추가 정보를 제공해 주셔서 감사합니다!

1개의 좋아요

음, 이건 좀 큰 문제네요. 조금만 참아 주세요. 먼저, 또 이렇게 상세한 답변과 디버깅/리뷰 제안을 해주셔서 정말 감사합니다. :+1: 오늘 아침에 이 문제를 살펴보고 있었는데, 놀랍게도 통합 뷰에서 스레딩이 Thunderbird에서 대부분의 경우 잘 작동하는 것 같습니다. 그리고 References 헤더가 항상 OP(첫 번째 게시글)를 가리키는 것이 그 이유인 것 같습니다. (예를 들어, 이 체인에서 항상 존재하는 Reference 토픽은 <topic/53@discoursehosted.martin-brennan.com>입니다.)

스레딩이 의도대로 작동하지 않는 경우는 다음과 같습니다:

  1. Discourse 내에서 게시글이 생성되고 토픽을 관찰하는 사람들에게 이메일이 발송된 다음
  2. 다른 사람이 그 게시글에 답글을 쓰고, 토픽을 관찰하는 사람들에게 이메일이 발송될 때

두 번째 이메일의 경우, 기존 헤더를 사용하는 대신 이 라인(discourse/lib/email/sender.rb at 98bacbd2c6b9fe57167cd32af5eb4839b4a5d1f6 · discourse/discourse · GitHub)에서 새로운 헤더를 생성하기 때문에 잘못된 In-Reply-To와 References 헤더를 받게 됩니다. 처음 발송된 이메일의 Message-ID를 사용해야 합니다. 스크린샷에서 이 패턴을 따르는 메시지들이 위치해야 할 곳은 다음과 같습니다:

image

답변은 – 경우에 따라 다릅니다. 수신된 이메일로부터 Discourse에서 게시글이 생성된 경우, 예를 들어 이것과 같은 경우, 누군가 그 게시글에 답글을 쓸 때 In-Reply-To와 References 헤더로 해당 게시글의 원래 수신 Message-ID를 사용합니다. 그 근거는 다음과 같습니다:

그렇지 않은 경우, 우리는 단순히 토픽 OP 참조를 사용하고 새로운 참조를 생성하는데, 이것이 당연히 모든 문제를 일으키는 원인이 됩니다. 모든 경우에서 아웃바운드 이메일이 발송될 때마다 새로운 Message-ID를 생성하는데, 이는 타당해 보이며 다른 메일 클라이언트와도 일치합니다.

의도를 이해하는 것 같습니다. 다음과 같은 순서인가요:

  1. cameron이 mutt에서 Discourse로 이메일을 보내고 Message-ID: 74398756983476983@mail.com을 받습니다.
  2. Discourse가 게시글을 생성하고 IncomingEmail 레코드와 함께 게시글에 Message-ID를 저장합니다.
  3. johndoe가 토픽을 관찰하고 있으므로, Discourse에서 Message-ID: topic/222/44@discourse.com을 가진 이메일을 받으며, 원래 Message-ID: 74398756983476983@mail.com에 대한 참조는 없습니다.

이것이 정확한가요? 즉, 이미 고유하므로 자체적으로 생성하는 대신 토픽을 관찰하는 사람들에게 그 Message-ID를 그대로 "전달"해야 하는 것인가요? 그렇다면 cameron이 원래 아웃바운드 메시지에 그에게 CC를 보낸 경우 johndoe의 메일 클라이언트에서는 어떤 일이 일어나나요? 이것은 별도의 이슈처럼 보이므로, 이를 위해 다른 버그 토픽을 여는 것이 좋을 것입니다.

당신도 보고 있는 것을 확인하기 위해 로컬에 mutt 클라이언트를 설정해 보겠습니다. 저는 텍스트 기반 클라이언트에서 이 기능을 테스트해 본 적이 없습니다(오직 Gmail과 Thunderbird만 사용) 따라서 어쨌든 어떻게 보이는지 보고 싶습니다.


오늘 아침 이 문제를 해결하기 위해 제가 생각한 접근 방식은, 이메일에서 Message-ID 헤더를 전송할 때 생성되는 무작위 접미사를 폐기하고, 대신 송신자와 수신자 모두의 user_id를 사용하는 방식으로 변경하는 것이었습니다. 이 방식의 장점은 Message-ID를 어딘가에 저장할 필요가 없다는 것입니다(수신된 이메일이 게시글을 생성할 때 제외) 그리고 References와 In-Reply-To 헤더가 항상 일관될 것입니다. 예를 들어, 다음과 같은 사용자들이 있다고 가정해 보겠습니다:

  • martin - user_id 25
  • cameron - user_id 44
  • sam - user_id 78
  • bob - user_id 999

그리고 topic_id 233499인 이 토픽이 있으며, 게시글은 OP인 post_id 100부터 시작합니다. 형식은 topic/#{topic_id}/#{post_id}.s#{sender_user_id}r#{receiver_user_id}가 됩니다. 작업 순서는 다음과 같습니다:

  1. martin이 OP를 생성합니다.
  • cameron에게 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499.s25r44@meta.discourse.org
    • References: topic/233499@meta.discourse.org
  • sam에게 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499.s25r78@meta.discourse.org
    • References: topic/233499@meta.discourse.org
  1. cameron이 이메일로 답글을 씁니다.
  • discourse에게 mutt에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: 43585349859734@test.com
    • References: topic/233499@meta.discourse.org topic/233499.s25r44@meta.discourse.org
    • In-Reply-To: topic/233499.s25r44@meta.discourse.org
  1. discourse (위 이메일로부터 cameron으로)가 게시글 101을 생성합니다.
  • sam에게 discourse에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499/101.s44r78@meta.discourse.org
    • References: 43585349859734@test.com topic/233499@meta.discourse.org
    • In-Reply-To: 43585349859734@test.com
  1. sam이 cameron에게 이메일로 답글을 씁니다.
  • discourse에게 gmail에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: 5346564746574@gmail.com
    • References: topic/233499/101.s44r78@meta.discourse.org topic/233499@meta.discourse.org
    • In-Reply-To: topic/233499/101.s44r78@meta.discourse.org
  1. discourse (위 이메일로부터 sam으로)가 게시글 102를 생성합니다.
  • cameron에게 discourse에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499/102.s78r44@meta.discourse.org
    • References: 5346564746574@gmail.com topic/233499@meta.discourse.org
    • In-Reply-To: 5346564746574@gmail.com
  1. bob이 토픽에서 게시글 103을 생성하며, 누구에게도 답글이 아닙니다(참고: 여기의 References에는 OP 이메일에 두 사용자 모두에게 전송된 Message-ID가 포함되어 있습니다).
  • cameron에게 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499/103.s999r44@meta.discourse.org
    • References: topic/233500@meta.discourse.org topic/23499.s25r44@meta.discourse.org
  • sam에게 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499/103.s999r78@meta.discourse.org
    • References: topic/233499@meta.discourse.org topic/23499.s25r78@meta.discourse.org
  1. cameron이 이메일로 답글을 씁니다.
  • discourse에게 mutt에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: 6759850728742572@test.com
    • References: topic/233499@meta.discourse.org topic/233499/103.s999r44@meta.discourse.org
    • In-Reply-To: topic/233499/103.s999r44@meta.discourse.org

cameron의 받은편지함

  • martin - 토픽 OP
    • 전송 → 대상: discourse, 제목: RE: 토픽 OP
      • sam - 두 번째 게시글에 대한 답글
    • bob - 특정 게시글이 아닌 토픽 내 답글
      • 전송 → 대상: discourse, 제목: RE: bob의 게시글

sam의 받은편지함

  • martin - 토픽 OP
    • cameron - 두 번째 게시글
      • 전송 → 대상: discourse, 제목: RE: 두 번째 게시글
    • bob - 특정 게시글이 아닌 토픽 내 답글

이것이 정확하다고 생각합니다. 제가 작성한 헤더를 검토해 주시고, 이 시나리오에서 기대하는 것이 맞는지 확인해 주시겠어요? 제가 조금 불확실한 유일한 부분은 모든 References를 커버했는지 여부이며, 물론 배포하기 전에 dev 브랜치에서 라이브 이메일 세트로 이를 테스트할 것입니다. 저는 아직 mutt에서 아무것도 테스트하지 않았습니다.


부연 설명으로, GitHub가 알림 이메일에서 어떻게 처리하는지도 살펴봤는데, 그들은 유사한 방식을 취하고 있음을 알게 되었습니다. 즉, 해당 “토픽”(이 경우 GitHub 풀 리퀘스트)과 관련된 모든 이메일에서 사용되는 항상 존재하는 Reference(discourse/discourse/pull/252@github.com)를 가지고 있습니다:

References: <discourse/discourse/pull/252@github.com> <discourse/discourse/pull/252/issue_event/7042100517@github.com>
In-Reply-To: <discourse/discourse/pull/252/issue_event/7042100517@github.com>
6개의 좋아요

Martin Brennan가 Discourse Meta에서 2022년 7월 22일 06:34에 작성:

음, 이건 꽤 큰 문제인 것 같으니 조금만 참아주세요. 먼저,
상세한 답변과 디버깅/리뷰 제안에 감사드립니다. 정말 도움이 됩니다 :+1:
오늘 아침에 이 문제를 살펴보고 있었는데, 놀랍게도 통합 뷰에서 스레딩이
Thunderbird에서 대부분의 경우에 잘 작동하는 것 같습니다. 그리고
References 헤더가 항상 원문(OP)을 가리키는 것이 그 데 도움이 되는 것
같습니다(예를 들어, 이 체인에서 항상 존재하는 Reference
토픽은
<topic/53@discoursehosted.martin-brennan.com>입니다).

방금 RFC5322의 3.6.4 섹션을 다시 꼼꼼히 읽었습니다. 이전 버전들(822와
2822)에서 발전하여, 이메일의 In-Reply-To 헤더, USENET의 References
헤더, 그리고 현대적인 여러 이전 메시지 인용 방식을 통합했습니다.

간단한 요약:

  • Message-ID는 메시지에 대한 단일하고 영구적인 식별자입니다.
  • In-Reply-To에는 이 메시지가 직접 답장으로 보낸 모든 메시지 ID가
    포함됩니다. 즉, 두 메시지에 답장을 보낸 경우 이 두 메시지 ID가 포함됩니다.
  • References는 OP에서 직전 메시지에 이르기까지의 이전 메시지 ID의
    답장 체인입니다. 따라서 실제로는 항상 OP의 메시지 ID로 시작해야
    합니다.

따라서 이러한 토론의 경우, 라벨이 메시지 ID라고 가정하면:

OP
  -> reply1
    -> reply2 ---+
  -> reply3      |
    -> reply4    |
      -> reply5 <+

reply5에는 다음과 같은 정보가 포함됩니다:

  • message-id=reply5
  • in-reply-to=“reply2 reply4”
  • references=“OP reply3 reply4”

references에 “reply1 reply2”(reply5에 이르는 다른 체인)를 포함하는 것도
합법적이지만, RFC에서는 일부 클라이언트가 references가 단일 선형 답장
체인이어야 한다고 기대하기 때문에 이를 권장하지 않습니다(평탄화된
방향성 비순환 그래프가 아닌).

따라서 references를 구성할 때의 제 추천은 “주요” 이전 메시지의
references를 사용하고, 주요 이전 메시지의 메시지 ID를 뒤에 붙이는
것입니다. 이렇게 하면 항상 올바른 순서의 선형 체인을 얻을 수 있습니다.

흥미롭게도 여기에는 일부 스레딩이 있는 것 같습니다.

하지만 주목하세요: 맨 위 게시물에는 작은 “답장임” 화살표가 있습니다.
그것은 1번 게시물인데도요. 저는 이것이 “topic” references 항목 때문인
것으로 예상합니다. 이는 TB(Thunderbird)가 더 이전의 메시지가 있었다고
생각하게 만듭니다(물론 실제로는 없었지만).

mutt에서는 거의 스레딩이 보이지 않습니다:

23Jul2022 06:24 Olha via Discus - ┌>[Py] [Users] I need an advise  discuss-users 5.7K
22Jul2022 17:12 Paul Jurczak vi - ├>[Py] [Users] I need an advise  discuss-users 5.5K
22Jul2022 13:21 Rob via Discuss - ├>[Py] [Users] I need an advise  discuss-users 6.8K
22Jul2022 12:53 vasi-h via Disc - ├>[Py] [Users] I need an advise  discuss-users 5.5K
22Jul2022 11:38 Cameron Simpson - ├>[Py] [Users] I need an advise  discuss-users  14K
22Jul2022 10:27 Rob via Discuss - ├>[Py] [Users] I need an advise  discuss-users 6.6K
22Jul2022 06:14 vasi-h via Disc r ┴>[Py] [Users] I need an advise  discuss-users 6.5K

이는 모든 메시지의 In-Reply-To가 가상의 “topic” 메시지 ID를 직접
가리키기 때문입니다. Mutt는 메일 리더이기 때문에 References를 무시할
가능성이 높습니다. References는 USENET 뉴스에서 유래했으니까요.
아마 Thunderbird는 references를 사용하거나 references 정보를 사용하여
in-reply-to를 보완하고 있는 것 같습니다.

스레딩을 수행하려면 In-Reply-To 또는 References 중 하나만 참조하면
됩니다. 전자는 이메일에서, 후자는 USENET에서 유래했습니다.
양쪽 모두를 지원하고 계신 것(정말 좋습니다!)이므로 일관성을 유지해야
합니다.

(참고: USENET 미러링에 대한 논의도 있습니다. 여러 Python 개발자들이
USENET 인터페이스를 통해 리스트를 소비하기 때문입니다. 다시 말하지만,
별개의 주제입니다.)

[…]

[quote=“Cameron Simpson, post:8, topic:233499,
username:cameron-simpson”]
이것은 괜찮아 보입니다. 이 ID를 DB 레코드와 함께 저장하여 수신 답장을
이전 포럼 메시지에 연결할 수 있습니까?
[/quote]

답변은 – 상황에 따라 다릅니다. 포스트가 수신 이메일(예를 들어, 이것과 같은 당신의 것)에서 Discourse에 생성된 경우, 누군가가 그것에 답장할 때 In-Reply-To 및 References 헤더로 해당 포스트의 원래 수신 Message-ID를 사용하며, 이는 다음과 같습니다:

discourse/lib/email/sender.rb at 98bacbd2c6b9fe57167cd32af5eb4839b4a5d1f6 · discourse/discourse · GitHub

그렇지 않은 경우, 우리는 단순히 토픽 OP 참조를 사용하고 새로운 참조를 생성할 뿐이며, 이는 분명히 모든 문제를 일으키는 원인입니다. 모든 경우에서 수신 이메일이 전송될 때마다 새로운 Message-ID를 생성하며, 이는 다른 메일 클라이언트와 동일하게 보이며 올바른 것 같습니다.

아쉽게도, 정확하지는 않습니다. 메시지의 원본(즉, Discourse에서 작성)인 경우, 메시지 ID 생성은 괜찮습니다. 메시지 ID가 없는 경우(불법적) 하나를 생성하는 것은 표준 관행입니다(보통 MTA가 수행). 하지만 메시지를 전달하는 경우(이메일에서 작성됨), 기존 메시지 ID를 보존해야 합니다.

제 생각에는 3가지를 수행해야 합니다:

  1. 안정적인 메시지 ID를 가지고 있으며, 수신 메시지의 메시지 ID를 대체하지 않는 것
  2. In-Reply-To를 올바르게 생성하는 것. 이는 즉각적인 이전 메시지(들)인 즉 이전(들)-Message-ID에서 쉽게 계산할 수 있습니다.
  3. References를 올바르게 생성하는 것. 이는 쉽게 이전-References + 이전-Message-ID로 계산할 수 있습니다.

1번 항목에 대해, 인용한 코드를 보면 이메일 메시지 ID는 다음과 같아야 합니다(Python 스타일 문법, 죄송합니다):

def message_id(post):
    return post.incoming_email.message_id or discourse_message_id(post)

즉, 이메일에서 유래한 경우 포스트의 이메일 메시지 ID이고, 그렇지 않은 경우 이 메시지에서 나중에 설명하는 알고리즘과 유사한 것으로 Discourse 메시지 ID를 사용하는 것입니다: (a) 안정적이고 (b) 구문적으로 유효한 어떤 것이든.

그런 다음 In-Reply-To 및 References 필드를 계산하는 것은 2번과 3번 항목에서와 같이 단순한 기계적인 작업입니다.

무엇을 말씀하시는지 이해하는 것 같습니다. 다음과 같은 순서로 진행됩니까:

  1. cameron이 mutt에서 Discourse로 이메일을 보내 Message-ID: 74398756983476983@mail.com을 얻습니다.
  2. Discourse가 포스트를 생성하고 IncomingEmail 레코드와 함께 포스트에 Message-ID를 저장합니다.

맞습니다.

  1. johndoe가 토픽을 관찰하고 있으므로, Discourse에서 johndoe에게 Message-ID: topic/222/44@discourse.com과 원래 Message-ID: 74398756983476983@mail.com에 대한 참조가 없는 이메일이 전송됩니다.

아니요. johndoe에게 전송되는 이메일의 Message-ID로 IncomingEmail.message_id를 그대로 전달하는 것이 실제로 필요합니다. 그것은 동일한 메시지입니다.

그것이 올바른지, 즉 고유성이 이미 보장되어 있으므로 자체적으로 생성하는 대신 토픽을 관찰하는 사람들에게 그 Message-ID를 단순히 "전달"해야 하는지, 그리고 cameron이 원래 송신 메시지에 그를 CC한 경우 johndoe의 메일 클라이언트에서 무슨 일이 일어나는지 궁금합니다. 이것은 별개의 문제처럼 보이므로 이를 위한 별도의 버그 토픽을 여는 것이 좋을 것 같습니다.

전달함으로써, 원래 메시지(cameron->cc:johndoe)와 Discourse가 전달한 메시지(cameron->Discourse->johndoe)는 동일한 메시지 ID와 동일한 메시지 내용을 갖습니다. 수신 메일 시스템은 둘 다 저장합니다. 메일 리더는 둘 다 보고, 둘 다 표시하거나 하나만 유지합니다(이는 메일 리더의 정책 결정이며 - 하나만 유지하는 것이 일반적입니다). 동일한 메시지이므로 일반적으로 어느 것이 유지되는지는 중요하지 않습니다.

Discourse를 무시하고, 리스트를 통한 사본과 직접 이메일을 통한 사본인 메시지를 고려해 보겠습니다. 그것들은 동일한 메시지이며, 동일한 메시지 ID를 갖습니다.

당신도 보고 있는 것을 확인하기 위해 로컬에 mutt 클라이언트를 설정할 것입니다. 저는 텍스트 기반 클라이언트에서 이 기능을 테스트해 본 적이 없습니다(Gmail과 Thunderbird만 사용했으므로) 어쨌든 어떻게 보이는지 보고 싶습니다.

설정 관련해서 기꺼이 도움을 드리겠습니다. 스레드 뷰를 위해 정렬을 스레드로 설정해야 합니다. Mutt는 매우 설정 가능합니다.

오늘 아침에 이 문제를 해결하기 위해 제 생각의 흐름은, 이메일에서 Message-ID 헤더를 전송할 때 생성되는 무작위로 생성된 접미사를 폐기하고, 대신 송신자와 수신자 양쪽의 user_id를 사용하는 방식으로 변경하는 것이었습니다. 이 방식의 장점은 Message-ID를 어디에도 저장할 필요가 없다는 것입니다(수신 이메일이 포스트를 생성할 때를 제외하고) 그리고 References 및 In-Reply-To 헤더가 항상 일관될 것입니다.

네, 그것이 훨씬 낫습니다. 수신 이메일의 메시지 ID가 송신 이메일의 Discourse 파생 메시지 ID를 대체해야 한다는 점을 참고하십시오.

(대부분의 메일 시스템은 Discourse 토픽 메시지 구조와 같은 주변 컨텍스트가 없기 때문에 무작위 문자열을 사용합니다 - 메시지는 단독으로 고려됩니다; 하지만 유일한 실제 요구 사항은 영구적인 고유성입니다.)

예를 들어 보겠습니다. 다음 사용자들이 있다고 가정합시다:

  • martin - user_id 25
  • cameron - user_id 44
  • sam - user_id 78
  • bob - user_id 999

그리고 topic_id 233499의 이 토픽이 있고, post_id 100부터 OP로 시작하는 포스트들이 있다고 합시다. 형식은 topic/#{topic_id}/#{post_id}.s#{sender_user_id}r#{receiver_user_id}가 될 것입니다.

작업 순서는 다음과 같을 것입니다:

  1. martin이 OP를 생성합니다.
  • cameron에게 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499.s25r44@meta.discourse.org
    • References: topic/233499@meta.discourse.org
  • sam에게 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499.s25r78@meta.discourse.org
    • References: topic/233499@meta.discourse.org
  1. OP에는 References 헤더가 있어서는 안 됩니다. 스레딩에 필요하지 않으며, 효과적으로 존재하지 않는 "0번 게시물"이 있는 것처럼 위장합니다. 이것은 모든 OP가 (a) 답장처럼 보이게 만듭니다(실제로는 답장이 아님) 그리고 (b) 답장인 대상이 리더의 메일함에서 누락된 것처럼 보이게 합니다.

  2. 이것은 OP의 각 송신 사본에 대해 다른 메시지 ID를 만듭니다.
    그것은 나쁩니다. 그것들은 동일해야 합니다. sam이 답장에서 cameron에게 직접 CC한다고 가정해 보겠습니다. In-Reply-To는 cameron이 받은 적 없는 메시지 ID를 인용하게 됩니다.

메시지 ID 필드에서 sender_user_id와 receiver_user_id를 단순히 제거하여 모든 수신자가 보는 단일 고유 ID를 얻을 수 있습니다.

고유성 제약은 개별 이메일 수준의 “메시지” 객체가 아니라 포스트 자체입니다.

References에 대해, OP에는 하나를 가져서는 안 됩니다. TB와 기타 모든 것이 문제없을 것입니다. In-Reply-To 대신 References를 사용하여 스레딩하는 경우, 답장 메시지의 References가 충분합니다.

Mutt에서 메일링 리스트 토론 스레드의 시작 부분입니다:

16Jul2022 01:09 Rob Boehne      - │├>[Python-Dev] Re: [SPAM] Re: Swit python-dev 9.2K
16Jul2022 01:33 Peter Wang      - │├>                                 python-dev 3.0K
16Jul2022 00:24 Skip Montanaro  - ├>[Python-Dev] Re: Switching to Dis python-dev 4.2K
16Jul2022 04:49 Erlend Egeberg  - ├>[Python-Dev] Re: Switching to Dis python-dev  10K
16Jul2022 04:20 Mariatta        - ├>[Python-Dev] Re: Switching to Dis python-dev  10K
15Jul2022 21:18 Petr Viktorin   - [Python-Dev] Switching to Discourse python-dev 4.2K

최신순으로 이메일을 정렬하고 있다는 점을 무시해 주세요. 초기 게시물(아래에 있음)에 화살표가 없는 것을 확인하세요. 그 메시지는 References도 In-Reply-To도 없습니다. 다른 모든 메시지는 In-Reply-To를 갖습니다(그리고 References도 있을 수 있지만, 이것은 이메일 메일링 리스트이므로 반드시 그런 것은 아닙니다; 전에 언급했듯이 그것들은 보완적인 것입니다.)

앞서 제 Discourse 예를 반복하면:

23Jul2022 06:24 Olha via Discus - ┌>[Py] [Users] I need an advise  discuss-users 5.7K
22Jul2022 17:12 Paul Jurczak vi - ├>[Py] [Users] I need an advise  discuss-users 5.5K
22Jul2022 13:21 Rob via Discuss - ├>[Py] [Users] I need an advise  discuss-users 6.8K
22Jul2022 12:53 vasi-h via Disc - ├>[Py] [Users] I need an advise  discuss-users 5.5K
22Jul2022 11:38 Cameron Simpson - ├>[Py] [Users] I need an advise  discuss-users  14K
22Jul2022 10:27 Rob via Discuss - ├>[Py] [Users] I need an advise  discuss-users 6.6K
22Jul2022 06:14 vasi-h via Disc r ┴>[Py] [Users] I need an advise  discuss-users 6.5K

그것들이 모두 시작 부분에 화살표를 가지고 있는 것을 보세요? 그것은 메일 클라이언트가 그것들이 모두 공통의(그리고 누락된) 루트 메시지에 대한 답장이라고 믿기 때문이며, 이는 References 헤더의 “topic” 메시지 ID 때문입니다.
반면 1번 포스트는 실제로는 위에 표시된 가장 아래 메시지입니다.

요약:

  • 송신자와 수신자를 메시지 ID에서 제거하는 조건으로, 당신의 계획은 좋습니다 - 그것들은 불필요하며 사실 수신자는 문제를 일으킬 것입니다(송신자는 단순히 중복됩니다).
  • References에서 “topic” 유사 메시지 ID를 제거하세요 - 그것은 이메일 클라이언트(시각적으로 드러나지 않더라도 TB 포함)를 오도합니다.
  1. cameron이 이메일로 답장합니다.
  • discourse에게 mutt에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: 43585349859734@test.com
    • References: topic/233499@meta.discourse.org topic/233499.s25r44@meta.discourse.org
    • In-Reply-To: topic/233499.s25r44@meta.discourse.org

네, 다시 말하지만 “topic” 참조가 있어서는 안 된다는 단서와 함께.
예상대로, OP 메시지 ID에 대한 참조가 있습니다. 그러나 sam이 OP에서 보는 것과 동일한 메시지 ID여야 합니다.

  1. discourse(위의 이메일에서 cameron으로)가 101번 포스트를 생성합니다.
  • sam에게 discourse에서 다음 헤더를 가진 이메일이 전송됩니다:
    • Message-ID: topic/233499/101.s44r78@meta.discourse.org
    • References: 43585349859734@test.com topic/233499@meta.discourse.org
    • In-Reply-To: 43585349859734@test.com

여기서 오류가 발생합니다. Message-ID는 .incoming_post.message_id 필드에서 43585349859734@test.com이어야 합니다.
(음, 제 생각에는 이것이 post.message_id()이며, 이메일 생성 포스트의 경우 post.incoming_post.message_id를 반환하고, 그렇지 않은 경우 Discourse가 생성한 것을 반환합니다).

고려해 보세요: 저는 메시지 ID 43585349859734@test.com으로 답장을 작성하고 전송합니다. 연속성을 위해 저는 로컬 폴더에 사본을 유지하는데,在那里 그것은 OP에 대한 답장으로 표시됩니다. 이상적으로는 Discourse도 제 포스트 사본을 저에게 전송해야 합니다(이는 많은 메일링 리스트에서 정책 설정입니다), 따라서 저는 Discourse의 버전도 받게 됩니다. 그것은 동일한 메시지 ID를 가져야 합니다. 왜냐하면 그것은 동일한 메시지이며, 단지 다른 경로를 통해 온 것이기 때문입니다.

Discourse의 메시지는 제 메시지에 대한 "답장"이 아닙니다. 그것은 제 메시지 _자체_이며, 단순히 전달된 것입니다.

이 효과는 당신의 후속 예시들로 퍼져나갑니다. 실제 프로세스는 당신이 만든 것보다 더 단순해야 합니다.

이렇게 생각해 보세요. 이메일에서 포스트에 답장하면, 그것은 사실상 제가 Discourse를 통해 sam(그리고 다른 사람들)에게 이메일을 보내는 것과 같습니다. Discourse는 제 메시지를 이메일 수신 구독자들에게 전달하고, “부수적으로” 포럼에 사본을 유지합니다 :slight_smile:

참고로, GitHub의 알림 이메일이 어떻게 처리되는지도 살펴봤는데, 유사한 것을 하고 있음을 발견했습니다. 그들은 항상 존재하는 Reference(discourse/discourse/pull/252@github.com)를 가지고 있으며, 이것은 이 경우 GitHub 풀 요청인 해당 "토픽"과 관련된 모든 이메일에서 사용됩니다:

References: <discourse/discourse/pull/252@github.com> <discourse/discourse/pull/252/issue_event/7042100517@github.com>
In-Reply-To: <discourse/discourse/pull/252/issue_event/7042100517@github.com>

호, github. 그들의 이슈 이메일은 재앙이네요 :slight_smile:

그러나, 그들의 시나리오에서 PR 자체가 OP입니다. 따라서 풀에 대한 직접 참조는 합리적입니다. “topic” 메시지 ID를 1번 포스트에 사용할 수 있습니다, 단 “topic/1” ID를 동시에 사용하지 않는 한입니다. 하지만 그것은 1번 포스트를 특별 처리하는 추가 노력으로 보이며 - 나는 그냥 "topic/1"을 사용하겠습니다.

복잡성을 더하자면. 제 이해로는, 관리자가 포스트나 토픽을 이동할 수 있습니다. 그것이 “메시지 ID 생성” 방식을 깨뜨리지 않습니까, 특히 포스트만 이동하는 경우? 저는 모든 _포스트_가 _message_id 필드를 가져야 한다고 다소 생각합니다. 수신 메시지(이메일에서)에서 채워지거나 생성된(포스트를 Discourse를 통해 작성) 것입니다. 그러면 그것은 안정적이고 안정적이며 포스트의 이동이나 알고리즘 변경에 강건합니다.

마지막으로, 작은 보안 고려 사항이 있습니다: 기존 포스트의 메시지 ID를 주장하는 경우 수신 이메일의 메시지 ID를 무시해야 합니다(그리고 잠재적으로 메시지를 바운스해야 합니다). 왜냐하면 작성자로서 저는 그 헤더에 원하는 것을 무엇이든 넣을 수 있기 때문입니다 :slight_smile: 저는 메시지 ID를 단순히 폐기하는 것을 선호합니다 - 포스트는 수용하지만, 그것이 다른 포스트인 것처럼 거짓말하게 두지 마세요 - 당신의 사본에 Discourse가 생성한 ID를 부여하고 정상적으로 진행하세요.

7개의 좋아요

정말 놀랍습니다. 이렇게 심도 있는 답변을 주셔서 다시 한번 감사드립니다. 이 내용을 소화하고 실행 가능한 항목으로 정리하는 데 다소 시간이 걸릴 것 같습니다. 또한 현재 저에게는 우선순위가 높은 다른 내부 프로젝트들도 진행 중이므로, 양해 부탁드립니다. 이 정보를 바탕으로 우리 스레딩 시스템을 훨씬 더 견고하고 사양에 맞게 개선할 수 있을 것 같습니다. 게시글을 읽는 과정에서 추가 질문이 생길 수 있으니,到时候 다시 한번 감사드립니다, 캐머런.

2개의 좋아요

Martin Brennan 님이 Discourse Meta를 통해 2022년 7월 25일 00:28에 작성:

정말 다시 한번 이렇게 깊이 있는 답변을 주셔서 감사합니다. 이 내용을 소화하고 실행 가능한 항목으로 정리하는 데 조금 시간이 걸릴 것 같으니 양해 부탁드립니다 (또한 현재 진행 중인 다른 내부 프로젝트들도 우선순위가 높아 바쁜 상태입니다). 이 정보를 바탕으로 저희의 스레딩 시스템을 훨씬 더 견고하고 규격에 맞게 개선할 수 있을 것 같습니다. 게시글을 읽어나가면서 추가 질문이 생길 수도 있으니, Cameron 님 감사합니다.

물론입니다. 감사합니다, Cameron Simpson

1개의 좋아요

참고로, 작성자님의 이 후속 게시글에는 다음과 같은 헤더가 포함되어 있습니다:

Message-ID: <topic/233499/1137586.d14eea2849d76c355ec214fb@meta.discourse.org>
In-Reply-To: <YttEVzlTh/ymDSPT@cskk.homeip.net>
References: <topic/233499@meta.discourse.org>
      <YttEVzlTh/ymDSPT@cskk.homeip.net>

즉, 제 원래 이메일 메시지 ID가 보존되어 있습니다. 따라서 In-Reply-To는 정확하며, References에는 제 이메일 메시지 ID가 포함되어 있습니다.

이것은 discuss.python.org에서 우리가 관찰한 내용과는 달랐습니다.

감사합니다,
Cameron Simpson

1개의 좋아요

오, 정말 흥미로운 관찰이네요. 저는 그 작은 화살표를 눈치채지 못했었네요.

이것도 매우 흥미롭네요. (소스를 확인하지는 않았지만) Thunderbird가 그렇게 한다고 믿고 있으며, Gmail UI도 같은 동작을 하므로 아마도 그렇게 할 것입니다.

우리는 이것을 하고 있는 것 같지만 일관성이 없는 것 같습니다? 기본적으로 다음 사항을 확인해야 합니다:

  • TODO #1 - 게시글에 관련 IncomingEmail 레코드가 있는 경우, 이메일을 보낼 때 항상 해당 Message-ID를 사용한다.
  • TODO #2 - 토픽의 OP와 관련된 이메일을 보낼 때 References를 사용하지 않는다. @cameron-simpson 그런데 한 가지 질문이 있습니다 – OP가 수신된 이메일을 통해 생성된 경우, OP의 References에 그 Message-ID를 사용할까요, 아니면 여전히 제외할까요?

흥미롭네요. 이메일의 모든 수신자에게 고유한 Message-ID가 있어야 한다고 생각했거든요. 사실 내부 토픽을 되돌아보면, 스팸 동작을 피하기 위해 각 수신자의 Message-ID에 고유성을 추가하는 방향으로 간 이유라고 믿습니다. 아마도 올해早些에 이메일 테스트를 많이 했던 인프라 팀의 @supermathie 도 여기에 의견을 더할 수 있을까요?

말씀하시는 것은, 모든 수신자에게 단일 Message-ID를 결정하는 것이 게시글이어야 한다는 것이죠. 그렇다면 이메일이 생성되는 각 게시글에 대해 하나를 생성하면 될 것 같습니다. 그러면 IncomingEmail.message_id를 여기로 이동시킬 수도 있습니다. 잠정적으로, 우리가 해야 할 변경 사항은 다음과 같습니다:

  • TODO #3 - Post 테이블에 outbound_message_id를 추가한다. 게시글과 관련하여 이메일이 처음 보내질 때 한 번 생성한다. 이후의 References 및 In-Reply-To 헤더에 사용한다. IncomingEmail에서 게시글이 생성될 때 그 값을 설정한다. 형식은 topic/:topic_id/:post_id/:random_alphanumeric_string@host여야 합니다. 예: topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org

이 변경 사항 이후 제 첫 번째 예시는 다음과 같아질 것입니다:

  1. martin이 OP를 생성합니다
  • cameron에게 다음 헤더가 포함된 이메일이 전송됩니다:
    • Message-ID: topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org
  • sam에게 다음 헤더가 포함된 이메일이 전송됩니다:
    • Message-ID: topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org

OP가 특별 처리를 받지 않는다는 점을 고려하면, 더 이상 topic/:topic_id@hostname 형식이 아닐 것입니다.

  • TODO #4 - PostReply 레코드와 Post 테이블의 새로운 outbound_message_id 컬럼을 기반으로 정확한 In-Reply-To 및 References 헤더가 생성되도록 보장한다

이 부분에 대해 우리가 어떤 고려 사항을 가지고 있을 것 같지만, 다시 확인해 보겠습니다.

그렇게 보입니다 :sweat_smile:


Cameron, 여기의 TODO 항목들이 합리적인지 확인해 주실 수 있나요? 이제 보니 정말 많지 않은 것 같습니다. 또한, 이 작업을 진행할 때 WIP 변경 사항이 배포된 테스트용 Discourse 인스턴스에 저와 함께 가입해서 이메일을 주고받으며 모든 것이 올바르게 작동하는지 테스트해 보실 의향이 있으신가요? 물론 저는 당신을 참여시키기 전에 저만의 테스트를 수행하겠습니다.

그렇지 않다면, 괜찮습니다 – 저는 Thunderbird를 가지고 있고 mutt를 설정하여 거기서 모두 테스트해 볼 수 있을 테니까요 :slight_smile:

1개의 좋아요

@cameron-simpson 여기서 한 가지 명확히 하고 싶은 것은 "message_id"의 스코핑입니다.

이번 일련의 논의가 시작된 계기는 @supermathie가 우리의 고유하지 않은 message_id들이 문제를 일으키고 있다는 강한 의심 때문이었습니다.

Discourse는 보낸 모든 이메일에 대해 사용자별로 고유한 이메일을 생성합니다. 예를 들어 이 토픽을 2명의 사용자가 관찰하고 있다고 가정해 보겠습니다:

  • 사용자 1은 사용자 1에게 지정된 고유한 구독 취소 링크가 포함된 페이로드 1을 받습니다.
  • 사용자 2는 사용자 2에게 지정된 고유한 구독 취소 링크가 포함된 페이로드 2를 받습니다.

만약 두 경우 모두에서 메시지 ID가 discourse_topic_100/23(topic_id/post_number)이라면, 우리는 외부 MTA들에게 discourse_topic_100/23이 두 가지 서로 다른 페이로드일 수 있다고 알리는 셈이 됩니다. 가설에 따르면, MTA들은 이를 스팸 신호로 취급할 수 있습니다.

“Discourse, 당신은 방금 discourse_topic_100/23이라는 이름의 이메일을 두 통이나 보냈군요. 무슨 일이에요?”

Discourse는 모든 이메일 전송을 직접 제어하고, 전통적인 메일링 리스트처럼 BCC 목록이나 CC 목록에 이메일이 추가되지 않으므로, 사용자별 고유한 구독 취소 링크를 사용하는 것이 가능합니다.

이 부분에 대해 어떻게 생각하시나요? 예를 들어 discourse_topic_100/23/7333(topic_id, post_number, user_id)과 같이 사용자별 이메일 생성 시 쉽게 참조할 수 있는 고유한 페이로드를 메일의 고유 식별자로 사용하는 단순한 변경은 어떨까요?

1개의 좋아요

Martin Brennan이 Discourse Meta를 통해 2022년 7월 26일 00:27에 작성:

[quote=“Cameron Simpson, post:11, topic:233499,
username:cameron-simpson”]
Mutt는 References가 메일 리더이기 때문에 무시할 가능성이 높으며, References는 USENET 뉴스에서 유래했습니다.
Thunderbird가 참조를 사용하거나 참조 정보로 in-reply-to를 보완하고 있을 수 있습니다.

스레딩을 위해 In-Reply-To 또는 References 중 하나만 참조하면 됩니다. 전자는 이메일에서, 후자는 USENET에서 유래했습니다.
양쪽 모두를 지원하고 있으므로(훌륭합니다!) 일관성을 유지해야 합니다.
[/quote]

이것도 매우 흥미롭습니다. (소스를 검토하지 않았지만) Thunderbird가 그렇게 하는 것 같고, Gmail UI도 같은 일을 하므로 아마도 그럴 것입니다.

Mutt는 둘 다 사용할 것으로 생각되지만, 존재한다면 아마도 In-Reply-To만 사용하고, 그렇지 않으면 References로 _하위 대체(falling back)_할 것입니다. 소스를 확인해야 합니다.

References를 사용하면 OP(첫 번째 게시물)까지의 전체 체인을 알 수 있습니다. In-Reply-To의 경우, 주변 선행 메시지들이 있어야 조각들을 이어 붙일 수 있습니다. 메일링 리스트의 경우, 보통 스레드가 끝날 때까지 전체 스레드를 로컬에 보관합니다. 이것이 일반적인 관행일 것입니다.

이것을 수행하고 있는 것 같지만 일관성이 없는 것 같습니다? 기본적으로 다음 사항을 확인해야 합니다:

  • TODO #1 - 게시물에 관련 IncomingEmail 레코드가 있는 경우, 이메일을 보낼 때 항상 해당 Message-ID를 사용해야 합니다.

네. 그래서 메시지 ID를 위한 명시적인 필드를 하나 만들어 한 번만 채워 넣고, 이후에는 코드에서 메시지 ID가 생성되는 프로세스가 어떻게 변경되든 항상 그 값을 사용하도록 하는 것이 가장 합리적이라고 생각했습니다.

  • TODO #2 - 토픽의 OP와 관련된 이메일을 보낼 때 References를 사용하지 마십시오.

네. OP에는 선행 메시지가 없으므로 References나 In-Reply-To가 없습니다.

@cameron-simpson 한 가지 질문이 있습니다 – OP가 수신 이메일을 통해 생성된 경우, OP의 References에 그 Message-ID를 사용할까요, 아니면 여전히 제외할까요?

여전히 제외해야 합니다. 하지만 그것을 OP의 영속적인 메시지 ID로 사용하십시오.

따라서 이메일로 작성된 메시지(OP 또는 답글)는 이메일에서 메시지 ID를 가져옵니다. 웹에서 작성된 메시지는 사용자가 제출을 누를 때 Discourse에 의해 생성된 ID를 받습니다. 그 이후에는 생성 방식과 관계없이 그것이 메시지 ID가 됩니다.

[quote=“Cameron Simpson, post:11, topic:233499,
username:cameron-simpson”]
메시지 ID 필드에서 sender_user_id와 receiver_user_id를 제거하면 모든 수신자가 보는 단일 고유 ID를 얻을 수 있습니다.

고유성 제약은 개별 이메일 수준의 “메시지” 객체가 아니라 게시물 자체입니다.
[/quote]

흥미롭네요. 이메일의 모든 수신자에게 고유한 Message-ID가 있어야 한다고 생각했습니다?

아니요. 메시지 ID는 "메시지"를 식별합니다. 개별 사본을 식별하는 것이 아닙니다. 포럼에 게시하고 누군가를 CC할 수 있습니다. 만약 그 사람이 제게 직접 사본을 받고 포럼을 통해서도 받으면, 동일한 메시지 ID를 가져야 합니다.

실제로 우리가 각 수신자의 Message-ID에 고유성을 추가하는 방향으로 간 이유는 스팸 행동을 피하기 위해서라고 생각합니다. 내부 토픽을 되돌아보며. 아마도 @supermathie (인프라 팀 소속이며 올해早些 이메일 테스트를 많이 수행했던 사람)도 여기에 의견을 제시할 수 있을까요?

아마도. 하지만 표면적으로 볼 때, 스레딩은 실제로 깨져 있습니다. 확실히 동일한 메시지를 여러 사람에게 보내는 경우 동일한 메시지 ID를 가져야 하며, 일반적으로 포워더(이메일->디스코스->이메일 수신자)로서 Discourse는 메시지 ID를 수정해서는 안 됩니다.

말씀하시는 것은 게시물이 모든 수신자에게 단일 Message-ID를 결정하는 것이어야 한다는 뜻이네요. 그래서 이메일을 생성하는 각 게시물에 대해 하나를 생성하는 것이 좋을까요?

모든 게시물에는 이메일 측면에서 사용될 안정적이고 고유한 메시지 ID가 하나 있어야 합니다. 게시물이 이메일에서 유래했다면, 그 원래 메시지 ID를 사용해야 합니다. 그렇지 않은 경우(웹 인터페이스를 통해) Discourse는 메시지 ID를 생성하고 게시물과 함께 저장해야 합니다.

그러면 IncomingEmail.message_id를 여기로 이동시킬 수도 있습니다.

물론입니다. 이메일 측 상태를 포함하는 별도의 필드 집합(메시지 ID 정도면 충분해 보입니다)을 갖추면 될 것입니다.

잠정적으로, 우리가 해야 할 변경 사항은 다음과 같습니다:

  • TODO #3 - **Post 테이블에 outbound_message_id를 추가합니다. 게시물을 기반으로 이메일이 처음 전송될 때 한 번 생성합니다.

게시물을 이메일에서 가져왔다면, 새로운 것을 생성하는 대신 그것을 사용해야 합니다.

이후의 References 및 In-Reply-To 헤더에 사용하십시오. IncomingEmail에서 게시물이 생성될 때 그 값을 설정하십시오.

네. 이메일의 메시지 ID에.

형식은
topic/:topic_id/:post_id/:random_alphanumeric_string@host 여야 합니다. 예:
topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org**

직접 생성하는 것에 대해서는 저에게도 좋습니다.

이 변경 이후 제 첫 번째 예시는 다음과 같이 됩니다:

  1. martin이 OP를 생성합니다
  • cameron에게 다음 헤더가 포함된 이메일이 전송됩니다:
  • Message-ID: topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org
  • sam에게 다음 헤더가 포함된 이메일이 전송됩니다:
  • Message-ID: topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org

네.

하지만 주의하세요: 메시지 ID는 안정적이고 고유할 필요가 있을 뿐입니다. topic/:topid_id/:post_id@host가 안정적이고 다시 생성되지 않을 것이라면 그것으로 충분합니다. 하지만 그것이 걱정된다면(예: DB 복원이나 마이그레이션 또는 가져오기로 인해 동일한 번호가 가져오는 경우) 랜덤 문자열은 충돌에 대해 강건하게 만들어 줍니다.

메시지 ID의 왼쪽 부분은 dot-atom-text이며, 여기에서 정의되어 있습니다:

이는 알파벳과 숫자 및 제한된 구두점 문자 집합(=“/” 포함)으로 구성됩니다.

음, 당신의 헤더입니다. 다음과 같아야 합니다:

Message-ID: <topic/233499/33545/gvy8475y7c45y87554c@meta.discourse.org>

각각의 괄호를 주목하십시오. 메시지 ID는 공식적으로 각 괄호 사이의 부분이며, 각 괄호는 필수적입니다. 여기 문법:

OP가 더 이상 특별한 처리를 받지 않으므로, topic/:topic_id@hostname 형식이 더 이상 적용되지 않는다는 고려 사항도 있습니다.

좋아 보입니다.

  • TODO #4 - PostReply 레코드와 Post 테이블의 새로운 outbound_message_id 컬럼을 기반으로 올바른 In-Reply-To 및 References 헤더가 생성되도록 보장하십시오

감사합니다.

이것에 대한 어떤 고려 사항이 있을 것 같지만, 재확인하겠습니다.

+1

확실히 그렇게 보입니다 :sweat_smile:

Cameron, 여기에 나열된 TODO들이 합리적으로 들리는지 확인해 주시겠어요?

저에게는 올바르게 보입니다.

이제 보니 그렇게 많지 않아 보입니다. 또한 이 작업을 수행할 때, WIP 변경 사항이 배포된 테스트용 Discourse 인스턴스에 함께 참여하여 이메일을 주고받으며 기능이 올바르게 작동하는지 테스트할 의향이 있으신가요? 물론 저는 당신을 포함시키기 전에 제 테스트를 먼저 수행할 것입니다.

물론입니다. 어떤 방식으로든 도움을 줄 수 있습니다.

그렇지 않다면, 괜찮습니다 – 저는 Thunderbird를 가지고 있고 mutt를 설정할 것이며, 거기서 모두 테스트할 수 있습니다 :slight_smile:

원하시면 mutt에 대해서도 도와드릴 수 있습니다.

3개의 좋아요

같은 메시지 ID를 가지고도 이렇게 사소한 차이가 있어도 서로 다른 메시지를 보낼 수 있다고 생각합니다.

일반적인 메일링 리스트는 정도의 차이는 있지만 이 일을 계속하고 있습니다. 최소한 헤더의 조작은 항상 일어나며, 메시지 본문이 수정되는 경우도 있습니다. 희귀한 예로 python-list는 텍스트가 아닌 첨부 파일을 삭제합니다. 하지만 메시지는 여전히 같은 메시지 ID를 유지한 채로 전달됩니다. 그리고 거의 모든 리스트는 하단에 리스트 관리 페이지 링크나 구독 취소 링크 같은 추가 문구를 붙입니다. 이런 문구는 메시지가 도착했을 때 원래에는 없었을 것입니다.

또한 콘텐츠 서명에 관한 오랜 논의가 있었는데, 서명이 무엇을 포함해야 하는지를 둘러싼 것이었습니다.

따라서 수신자별로 고유한 구독 취소 링크를 추가하되 원래 메시지 ID를 유지하는 방식에 대해 전혀 문제가 없다고 생각합니다. 각 메시지 _사본_에 개별 메시지 ID를 부여하는 경우의 스레딩(스레드) 손실과 비교하면, 그 이점이 훨씬 훨씬 더 큽니다.

다시 한번 이메일 사용자의 관점에서 생각해 보세요. 저는 discourse 메시지에 답장을 보내면서 관심 있는 외부 인물에게 CC를 _추가_할 수 있습니다. 상대방이 discourse에서 사본을 받을 수도 있고, 아닐 수도 있습니다. 하지만 만약 받았다면, 추가된 문구에도 불구하고 원래의 메시지 ID가 포함되어 있어야 합니다. 그렇지 않으면 상대방은 제 메시지의 사본 두 통을 받게 되지만, 메일 시스템은 그것이 동일한 메시지의 사본임을 알지 못하게 됩니다. 이로 인해 문제가 발생합니다.

요약하자면: 아주 사소한 추가 구독 취소 텍스트 때문에 개별 메시지 ID를 사용하는 것은 정당화되지 않는다고 생각합니다. 메시지 ID는 하나만 유지하세요.

4개의 좋아요

죄송합니다, 이제야 내용을 확인하고 있습니다. 몇 가지 생각이 있어서 공유합니다. 일부는 이미 다루어진 내용이기도 하고요…

여기서 어려운 점은 Discourse에서 외부로 전송되는 것이 수신된 메시지와는 별개의 메시지라는 것입니다. 여기에는 다른 메타데이터(이 목적을 위해 To/From/Reply-to/Unsubscribe 등)와 다른 본문(사용자별로 커스터마이징된 것으로 보입니다? 메일링 리스트 모드에서는 이것이 발생하지 않나요?)이 포함됩니다.

정확히 메시지는 무엇일까요? RFC 5322를 성서처럼 대한다면:

메시지는 헤더 필드로 구성되며, 선택적으로 메시지 본문이 따라올 수 있습니다.

“Message-ID:” 필드는 특정 메시지의 특정 버전을 가리키는 고유 메시지 식별자를 제공합니다.

[강조는 저의 것입니다]

바로 이 “특정 버전” 때문에, 다른 Message-ID를 사용하여 수신된 메시지를 다시 보내는 것은 적절하지 않다고 생각합니다. 다만, Discourse를 "포럼 소프트웨어"로 보는 관점에서 "메일링 리스트 소프트웨어"로 보는 관점으로 전환하면 그렇게 하는 것이 어느 정도 합리적으로 보일 수 있으므로, 왜 그렇게 생각하는지 이해는 합니다. 5322에는 또한 다음과 같이 나와 있습니다:

메시지가 "변경"되는 경우가 많지만, 그러한 변경이 해당 메시지의 새로운 인스턴스로 간주되지 않는 경우가 있습니다. 따라서 메시지는 새로운 메시지 식별자를 받지 않습니다. 예를 들어, 메시지가 전송 시스템에 도입될 때 추적 필드(3.6.7절에서 설명됨)와 재전송 필드(3.6.6절에서 설명됨)와 같은 추가 헤더 필드가 앞에 붙는 경우가 많습니다. 이러한 헤더 필드의 추가는 메시지의 정체성을 변경하지 않으므로 원래 “Message-ID:” 필드가 유지됩니다. 모든 경우에서, 메시지의 발신자가 전달하고자 하는 의미(즉, 이것이 동일한 메시지인지 다른 메시지인지)가 “Message-ID:” 필드가 변경되는지 여부를 결정하며, 메시지에 나타나거나(또는 나타나지 않거나)하는 특정 구문적 차이는 아닙니다.

결국, Discourse가 메시지를 외부로 전송할 때 메시지의 발신자가 변경되는지에 달려 있다고 볼 수 있습니다.

아마 Resent-Message-ID와 같은 필드를 사용해야 할까요?

이것은 822로 거슬러 올라가 항상 존재해 왔습니다. 하지만 나중에 말씀하신 대로, 업데이트는 되었습니다.

5322는 또한 Discourse와 Github가 이를 사용하는 방식에 대해 직접적으로 언급하고 있습니다:

“In-Reply-To:” 필드는 새로운 메시지가 답장으로 보내지는 메시지(또는 메시지들)를 식별하는 데 사용할 수 있으며, “References:” 필드는 대화의 "스레드"를 식별하는 데 사용할 수 있습니다.

아마도 약간 부적절하게, 아마도 적합한 “Thread Identifier” 헤더가 부족하기 때문일 것입니다. 하지만 이 해석이 RFC 작성자가 의도한 것이 아닐 수도 있습니다… "References"는 있지만 "In-Reply-To"는 없는 메시지에 대해서는 다루지 않고 있습니다.

이 부분의 까다로운 점은 하나의 이메일을 보내는 것이 아니라, N개의 이메일을(수신자당 하나씩) 보내는 것이므로, 각자의 개별 메타데이터(Unsubscribe 등)가 정확할 수 있다는 것입니다.

그리고 네, 테스트 중에 스팸 판정이 Message-ID와 연관되어 있다는 강력한 징후를 보았습니다. 나중에 다시 확인되면(같은 사용자 또는 다른 사용자) 스팸으로 표시될 가능성이 훨씬 더 높아졌습니다.

솔직히 말해서, 여기의 이득은 전적으로 특정 메일 클라이언트에서 이메일을 올바르게 스레딩하는 것에 관한 것이며, 그 대가로 전달률(deliverability)이 희생됩니다.

현재의 topic/#{topic_id}/#{post_id}.s#{sender_user_id}r#{receiver_user_id}는 적어도 사용자의 메일박스 내에서 일관성을 유지합니다. 그 가정은

제 가장 큰 우려는 전달률입니다 - 주요 제공자로부터 가시성이 전혀 없는 상황에서 이메일을 전달하는 것은 이미 어렵습니다.

하지만 메일링 리스트 모드에서 Discourse가 메일링 리스트 소프트웨어처럼 더 많이 작동하도록 만드는 것에 대해 강력한 논리를 봅니다. @martin 메일링 리스트 모드에서 메시지 본문을 커스터마이징하지 않는다고 믿습니다. 메일링 리스트 모드에서 Message-ID를 보존하고 재사용하는 것에 대해 더 엄격한 접근을 취하는 것이 합리적이라고 생각하시나요?

5개의 좋아요

완벽함이 '충분히 좋은 것’의 적이 되는 상황을 원하지 않습니다.

현재 메시지에는 "무작위 접미사"를 사용하고 있으며, 이로 인해 분명히 많은 문제가 발생하고 있습니다.

현재 논의되고 있는 옵션은 3가지입니다:

  1. 역참조가 불가능한 무작위 메시지 ID
  2. 주제/게시물/사용자별로 안정적인 메시지 ID
  3. 주제/게시물 쌍별로 안정적인 메시지 ID

현재는 (1) 상태이며, 이로 인해 큰 혼란을 초래하고 있습니다.

(2)와 (3) 사이에서 결정이 지연될까 걱정됩니다.

아마도 Discourse에서 이메일에 추가 CC를 보낼 때 예상치 못한 동작이 발생할 수 있다는 점을 인정하고 (2)부터 시작하여, 적어도 대부분의 문제를 해결하는 것이 좋겠습니다.

4개의 좋아요

아! 이미 topic/#{topic_id}/#{post_id}.s#{sender_user_id}r#{receiver_user_id} 형식을 사용하고 있는 줄 알았네요.

이메일 고유성 및 도달률에 대한 우려와 메일링 리스트 모드에 대한 우려 사이의 균형을 맞추기 위해, 메일링 리스트 모드가 비활성인 경우 (2)번, 활성인 경우 (3)번을 적용하는 방향으로 진행하는 것이 좋다고 생각합니다.

마찬가지로 References 헤더의 경우에도, 주제(topic)의 첫 번째 게시물(#1)에는 이 헤더를 생략하고, 그 외에는 해당 주제(topic/#{topic_id})와 답장하는 대상 게시물(있다면)을 참조하도록 하는 것이 좋겠습니다.

3개의 좋아요