Martin Brennan가 Discourse Meta에서 2022년 7월 22일 06:34에 작성:
음, 이건 꽤 큰 문제인 것 같으니 조금만 참아주세요. 먼저,
상세한 답변과 디버깅/리뷰 제안에 감사드립니다. 정말 도움이 됩니다 
오늘 아침에 이 문제를 살펴보고 있었는데, 놀랍게도 통합 뷰에서 스레딩이
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가지를 수행해야 합니다:
- 안정적인 메시지 ID를 가지고 있으며, 수신 메시지의 메시지 ID를 대체하지 않는 것
In-Reply-To를 올바르게 생성하는 것. 이는 즉각적인 이전 메시지(들)인 즉 이전(들)-Message-ID에서 쉽게 계산할 수 있습니다.
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번 항목에서와 같이 단순한 기계적인 작업입니다.
무엇을 말씀하시는지 이해하는 것 같습니다. 다음과 같은 순서로 진행됩니까:
- cameron이 mutt에서 Discourse로 이메일을 보내
Message-ID: 74398756983476983@mail.com을 얻습니다.
- Discourse가 포스트를 생성하고
IncomingEmail 레코드와 함께 포스트에 Message-ID를 저장합니다.
맞습니다.
- 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}가 될 것입니다.
작업 순서는 다음과 같을 것입니다:
- 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
-
OP에는 References 헤더가 있어서는 안 됩니다. 스레딩에 필요하지 않으며, 효과적으로 존재하지 않는 "0번 게시물"이 있는 것처럼 위장합니다. 이것은 모든 OP가 (a) 답장처럼 보이게 만듭니다(실제로는 답장이 아님) 그리고 (b) 답장인 대상이 리더의 메일함에서 누락된 것처럼 보이게 합니다.
-
이것은 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 포함)를 오도합니다.
- 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여야 합니다.
- 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는 제 메시지를 이메일 수신 구독자들에게 전달하고, “부수적으로” 포럼에 사본을 유지합니다 
참고로, 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. 그들의 이슈 이메일은 재앙이네요 
그러나, 그들의 시나리오에서 PR 자체가 OP입니다. 따라서 풀에 대한 직접 참조는 합리적입니다. “topic” 메시지 ID를 1번 포스트에 사용할 수 있습니다, 단 “topic/1” ID를 동시에 사용하지 않는 한입니다. 하지만 그것은 1번 포스트를 특별 처리하는 추가 노력으로 보이며 - 나는 그냥 "topic/1"을 사용하겠습니다.
복잡성을 더하자면. 제 이해로는, 관리자가 포스트나 토픽을 이동할 수 있습니다. 그것이 “메시지 ID 생성” 방식을 깨뜨리지 않습니까, 특히 포스트만 이동하는 경우? 저는 모든 _포스트_가 _message_id 필드를 가져야 한다고 다소 생각합니다. 수신 메시지(이메일에서)에서 채워지거나 생성된(포스트를 Discourse를 통해 작성) 것입니다. 그러면 그것은 안정적이고 안정적이며 포스트의 이동이나 알고리즘 변경에 강건합니다.
마지막으로, 작은 보안 고려 사항이 있습니다: 기존 포스트의 메시지 ID를 주장하는 경우 수신 이메일의 메시지 ID를 무시해야 합니다(그리고 잠재적으로 메시지를 바운스해야 합니다). 왜냐하면 작성자로서 저는 그 헤더에 원하는 것을 무엇이든 넣을 수 있기 때문입니다
저는 메시지 ID를 단순히 폐기하는 것을 선호합니다 - 포스트는 수용하지만, 그것이 다른 포스트인 것처럼 거짓말하게 두지 마세요 - 당신의 사본에 Discourse가 생성한 ID를 부여하고 정상적으로 진행하세요.