기존 링크는 Discourse Reactions가 허용하는 단 하나의 반응에 대해 더 많은 반응 옵션을 제공하는 용도만 해당되므로, 게시글당 여러 반응(multiple-reactions-per-post)에 대해 새로운 요청을 Support multiple-reactions per post (Retort style) 에 열었습니다.
Retort 플러그인은 #공식 플러그인이 아니므로, 커뮤니티에서 더 이상 능동적으로 개발 및 유지 관리하는 시점에 대해 우리는 의견을 낼 수 있는 권한이 없습니다.
우리가 할 수 있는 최선은 가능한 한 사람들에게 정보를 제공하여 대안을 찾거나 (적어도 회원들에게 미리 알려 실망을 완화할 수 있도록) 시간을 확보할 수 있도록 하는 것입니다.
결국 그 두 개의 Contribute > Feature 요청이 reactions 플러그인에 채택되기를 희망하지만, 아직까지는 ‘좋은 아이디어’ 단계에 머물러 있습니다. 하지만 두 손 모아 기도하고 있습니다. ![]()
그분의 마음을 이해합니다. 현실적으로 저나 제임스 중 누구도 상당한 시간 동안 Retort를 제대로 지원할 시간이 없었습니다. 저장소의 마지막 커밋이 1년 이상(2021년 7월 21일, 저에 의해) 전에 이루어졌음을 알 수 있습니다. 플러그인이 이토록 오랫동안 계속 작동한 것은 다행스러운 일이며, 이는 제임스가 이 플러그인을 구축하는 데 투입한 작업의 품질을 입증하는 것입니다.
시간이 없다고 말할 때, 믿어주십시오, 저도 시간이 있었으면 좋겠습니다! 이런 결정(예: Landing Pages Plugin의 경우처럼)을 내려야 할 때마다 슬픔을 느낍니다. 저는 Retort를 만들지 않았지만, 이 프로젝트에 시간을 투자해 왔습니다. 무언가를 폐기하기로 결정하는 것은, 자신이 만들거나 사랑하며 수 시간, 수일, 수주간 자신의 삶을 바쳐온 것이 죽음을 맞이해야 한다는 것을 받아들이는 것과 같습니다. 제임스가 다른 일로 넘어가야 한다고 느꼈을 때, 그에게도 어려운 결정이었을 것이라는 점을 잘 알고 있습니다.
반면에, Reactions Plugin은 60명 이상의 조직인 Discourse.org에서 적극적으로 유지보수되고 있습니다. Discourse.org 클라이언트가 사용하는 여러 서버에서 사용되고 있습니다. 네, 아직 Retort와 동일한 기능을 갖추지는 못했지만, 그 기능 개발을 추구하는 것이 좋은 방법이라고 권고합니다. 저나 Pavilion의 다른 멤버를 설득하여 플러그인에 없는 기능을 PR로 제안하도록 할 수도 있습니다. 여기에서 장기적인 목표를 달성하기 위한 현명한 방법입니다.
그동안 개발자에게 비용을 지불하여 기능을 복원하도록 원하신다면 항상 #marketplace를 이용할 수 있습니다. 다만 이 과정을 여러 번 반복해야 하거나 유지보수 계약(리테이너)을 체결해야 할 수도 있습니다.
답은 불가능한 것 같네요? 리액션으로 전환하려고 하는데, 인기 있는 이모지들을 찾아보고 싶습니다…
어딘가의 데이터베이스에 저장될 것이므로 가능할 것 같다고 추측합니다. 불행히도 제 테스트 사이트에는 이 플러그인이 설치되어 있지 않아 구체적인 사항을 확인할 수 없습니다. discourse-retort-retorts 테이블이나 이와 유사한 테이블이 있나요?
현재 사용 중인 리포트(retorts)를 |로 구분된 문자열로 가져오는 방법은 다음과 같습니다:
# ./launcher enter app
# rails c
retorts = {}
PostDetail.where(extra: 'retort').each do |p|
retort = p.key.split('|').first
(retorts[retort] ||= []) << p
end
retorts.length
retorts.keys.join('|')
이를 통해 다음을 얻을 수 있습니다:
- 먼저 사용된 고유 리포트 이모지 수
- 그리고 리포트 목록을 Reaction 설정에 붙여넣을 수 있는 형태로 반환합니다. Reactions UI에서 너무 길어 작동하지 않을 경우 잘라낼 수 있습니다.
제 경우, 이 문자열을 얻었습니다:
tada|rage|money_with_wings|face_vomiting|crossed_fingers|grin|vulcan_salute|worried|slightly_smiling_face|dart|+1|relaxed|star_struck|upside_down_face|sweat_drops|astonished|frowning_face|champagne|heavy_plus_sign|bulb|joy|fireworks|zap|smile|fast_forward|grinning|clap|sandwich|heart_eyes|rofl|smiley|wave|ice_cream|sob|mortar_board|open_mouth|pray|grimacing|roll_eyes|arrow_right_hook|brain|wink|cry|nerd_face|slight_smile|confused|ok|thinking|it|heart|smirk|sleepy|eyes|disappointed|question|laughing|man_shrugging|drum|shushing_face|herb|man_facepalming|ear|scream|ok_hand|mantelpiece_clock|smiling_face_with_three_hearts|confetti_ball|sunglasses|nose|pirate_flag|neutral_face|sweat_smile|gift|pensive|dark_sunglasses|exclamation|call_me_hand|green_heart|face_with_monocle|blush|boom|hugs|stuck_out_tongue|zipper_mouth_face|slightly_frowning_face|face_with_raised_eyebrow|exploding_head|information_source|sailboat|fire|gun|carousel_horse|sparkles|hearts|pizza|frowning|drooling_face|-1|100|metal|partying_face|four_leaf_clover|grinning_face_with_smiling_eyes|scream_cat|person_shrugging|deciduous_tree|sunflower|see_no_evil|hear_no_evil|speak_no_evil|微笑|top|face_with_peeking_eye|face_with_hand_over_mouth|stethoscope|money_mouth_face"
Reactions로 마이그레이션할 때 무엇을 유지할지 논의하기 위해 기존 리포트 목록을 discourse 게시물에 복사하여 붙여넣고 싶다면, 대신 다음을 사용할 수 있습니다:
":" + retorts.keys.join(': :') + ':'
제 경우, 현재 이 집합을 얻습니다:
“
:微笑:
”
각 이모지의 인스턴스 수와 함께 이모지 목록을 볼릿 리스트로 얻으려면:
retorts.keys.sort.each do |k|
puts "* :#{k}: #{retorts[k].length}"
end
전체 이모지 볼릿 리스트를 붙여넣지는 않겠지만, 다음과 같이 시작합니다:
161
1
1
1
9
2
2
23
3
각 이모지에 해당하는 모든 게시물을 보려면:
retorts.keys.sort.each do |k|
puts "* :#{k}: #{retorts[k].length}"
retorts[k].each do |r|
p = Post.find_by(id: r.post_id)
next if p.nil?
puts " * #{p.full_url}"
end
end
여기에 붙여넣기에는 너무 깁니다!
모르겠는 것은 모든 리포트 — 또는 일부 — 를 reactions로 마이그레이션 하는 방법입니다. reactions 플러그인에는 retort에 대한 언급이 없으므로 자동으로 수행되지 않습니다. reactions로 마이그레이션하고 싶은 116개의 고유 이모지를 가진 927개의 reactions가 있습니다.
Retort가 더 이상 작동하지 않게 되기 전에, 이상적으로는 그 전에 이 문제를 처리해야 할 것으로 예상됩니다. 마이그레이션을 구현하게 되면 여기에 문서화할 계획입니다. 하지만 최소한 무엇이 있는지 아는 것이 도움이 될 수 있습니다.
Retort를 Reactions로 마이그레이션하기 위해 실험 코드를 작성하던 중, 사용자 이름이 변경될 때 Retort가 업데이트되지 않는다는 사실을 발견했습니다.
Reactions의 경우 PostActions가 실제 사용자 레코드에 조인되므로, PostDetail의 JSON 블롭에 사용자 이름을 기록하는 방식과 달리 이러한 문제가 발생하지 않을 것으로 생각합니다.
일반적으로, 누군가가 Retort를 채택하고 유지보수하기로 결정한다면, Reactions에서 수행한 방식을 따라 PostDetail에서 PostActions로 마이그레이션하는 것을 고려해야 합니다.
마찬가지로, 게시물이 삭제된 경우를 인식하지도 못합니다.
제가 만든 Script framework to rearrange topics and categories 프레임워크에 토픽과 카테고리 재배열을 약간 벗어나는 새로운 기능이 추가되었습니다!
보통 저는 Ruby나 Ruby on Rails를 잘 알지 못한다고 경고하곤 하는데, 그래서 제 코드는 관용적(idiomatic)이라기보다는 특이(idiosyncratic)한 편입니다. 하지만 지금까지 테스트해 본 결과 잘 작동하고 있습니다!
def migrateRetortToReactions(allowed:, likes: nil, emojimap: nil)
# 기존 좋아요를 덮어쓰지 않고 가능한 한 마이그레이션 수행
# 이는 필연적으로 손실이 있는 변환이며, PostDetail의 순서에 의해만 일관성이 유지됨
# 한 PostDetail 레코드를 다른 레코드보다 우선시하려는 시도는 하지 않음
emojimap = {} if emojimap.nil?
allowed.each do |a|
emojimap[a] = a
end
retort = "retort".freeze
emojiType = "emoji".freeze
usermap = Hash.new { |hash, username| hash[username] = User.find_by_username(username) }
postmap = Hash.new { |hash, post_id| hash[post_id] = Post.find(post_id) }
likeType = PostActionType.where(name_key: "like").pluck(:id).first
PostDetail.where(extra: retort).each do |pd|
begin
p = postmap[pd.post_id]
rescue
# PostDetail이 삭제 관련해서 일관성이 없음
$stderr.puts sprintf("Could not find post for %d: %s / %s", pd.post_id, pd.key, pd.value)
next
end
emoji = pd.key.split('|').first
users = JSON.parse(pd.value)
users.each do |user|
u = usermap[user]
next if u.nil? # 사용자 이름 변경 또는 삭제된 사용자는 고아 Retort를 남김
if likes.include?(emoji)
pa = PostAction.where(post_id: p.id, user_id: u.id, post_action_type_id: likeType).first
next unless pa.nil?
$stderr.puts sprintf("Adding like for Retort %s for user %s in %s", emoji, user, p.url)
PostActionCreator.create(u, p, :like, created_at: pd.created_at, silent: true)
elsif emojimap.has_key?(emoji)
e = emojimap[emoji]
r = DiscourseReactions::Reaction.where(post_id: p.id, reaction_type: emojiType, reaction_value: e).first_or_create
ru = DiscourseReactions::ReactionUser.where(user_id: u.id, post_id: p.id).first
next unless ru.nil?
$stderr.puts sprintf("Converting Retort %s to Reaction %s for user %s in %s", emoji, e, user, p.url)
DiscourseReactions::ReactionUser.create(reaction_id: r.id, user_id: u.id, post_id: p.id, created_at: pd.created_at)
else
$stderr.puts sprintf("Ignoring unmapped Retort %s for user %s in %s", emoji, user, p.url)
end
end
end
end
제가 만든 프레임워크를 사용하여 다음과 같은 yaml 설정을 제공합니다:
- migrateRetortToReactions:
allowed:
- rofl
- astonished
- crossed_fingers
- sob
- thinking
- grimacing
- frowning_face
- drum
likes:
- dart
- +1
- joy
- "100"
- brain
- heart
- heart_eyes
- hearts
emojimap:
rage: sob
four_leaf_clover: crossed_fingers
cry: sob
open_mouth: astonished
scream: frowning_face
그러나 이러한 인수를 리터럴 Ruby 코드로 만들어 Ruby 스크립트로 감싸고, script/ 디렉터리에 배치하여 실행하는 것도 가능합니다.
여러분, 위 토픽에서 논의했듯이, 저는 이미 Retort에서 Reactions로의 마이그레이션 기능을 관리자 UI와 함께 작성해 두었습니다.
이 기능이 프로덕션 환경에서 사용 가능하려면, Reactions 플러그인 유지보수 담당자가 Reactions 플러그인 내 코드 추상화를 개선하기 위해 소규모 수정을 해야 합니다.
이러한 두 플러그인 간의 프로덕션 레벨 마이그레이션을 지원하는 데에는 상당한 품질 보증이 필요하며, 그렇지 않으면 이와 같은 문제가 쉽게 발생할 수 있습니다.
죄송합니다! 그 게시물을 놓치고 메인 브랜치만 확인하고 있었습니다. 긴 스레드라서요…
동의합니다. 저는 그 부분을 완전히 무시하고 있었습니다. 하지만 ReactionManager.toggle!만의 문제는 아닙니다 — created_at을 전달해야 하지 않나요?
Reactions로의 전환은 "좋아요"의 의미를 실제로 바꿉니다. 사용자가 게시물을 수정하면 그걸 취소할 수 없으니까요. 저는 같은 구현 선택을 하지 않았을 겁니다. ![]()
어쨌든, 저는 스크립트로 이 기능을 구동하려는 것이므로 UI를 통해 구동하는 데는 전혀 관심이 없습니다. 저는 UI의 대상 사용층이 아니므로, 제 해킹 방식이 공개되어 있어도 해가 되지 않을 수도 있습니다.
물론, 본인이 직접 사용할 목적으로 작성하는 것을 막으려는 의도는 아니지만, 해당 코드와 데이터 구조에 익숙하지 않은 다른 사이트에서는 사용을 권장하지 않습니다.
결론적으로, 현재 Reactions 플러그인은 환경 간에 안정적으로 작동하는 안정적인 마이그레이션을 지원하도록 작성되어 있지 않습니다.
Retort에서 Reactions로 마이그레이션을 원하시는 경우, Pavilion이 계약 기반으로 수동으로 처리하고 있습니다 (contact@pavilion.tech로 이메일을 보내거나 저에게 DM을 주세요). Reactions 플러그인이 일반화된 마이그레이션을 허용하도록 업데이트되면, 이를 무료로 공개하기 위해 마이그레이션 작업을 완료할 것입니다.
아하. 이 글로 그동안 궁금했던 몇 가지 질문이 해결되었습니다. 7년에 걸쳐 쌓인 450개의 게시글을 이해하기란 쉽지 않네요.
즉, “필요한” 작업(누구나 "필요"에 대한 자기만의 정의를 제시할 수 있습니다)은 리액션을 확장하여 데이터를 더 깔끔하게 마이그레이션할 수 있도록 하고, 또한 현재 부족한 기능을 제공하는 것임을 의미하는 건가요?
그 작업의 규모는 시간이나 비용 기준으로 대략 얼마나 될까요?
이 내용은 대체로 여전히 정확합니다.

PR(풀 리퀘스트) 기회가 현실적으로 있다면, 좋은 레드 와인 한 병을 얻는다면 그냥 제가 할 것 같습니다. 어쨌든 금요일 밤이니까요.
하지만 좀 더 진지하게 말하자면, 여기서 논의하고 있는 것은 Reactions 플러그인의 ReactionManager에 대한 작은 리팩터링입니다. 그런 종류의 작업은 일반적으로 PR을 통해 수락되지 않습니다. Reactions의 유지보수자들의 동의가 필요합니다.
좋아요가 silent 상태인지, 그리고 좋아요와 반응에 대해 created_at이 처리되는지도 확인해야 할 것 같습니다. 그렇지 않으면 마이그레이션으로 인해 사용자에게 알림이 대량으로 발송될 수 있습니다. (제 로그인 관련 테스트 사이트에서 이 문제를 확인했습니다.)
어떤 이유에서인지 created_by를 처리해도 여전히 out of love 최대 좋아요 제한이 트리거되었습니다. 하지만 다른 모든 알림을 제거했기 때문에 더 이상 조사하지 않았습니다.
@joffreyjaffeux 마이그레이션을 깔끔하게 수행하기 위해 필요한 기능을 공개하지 않는 이유가 있을까요?
저는 방금 Reactions로 옮겼어요 (공식적인 기능이라고 해서요…) 하지만 이전의 모든 반박(retort) 데이터를 잃는 건 정말 싫어요.
죄송합니다. 위에서 언급한 이유로 인해 현재 안정적인 마이그레이션을 제공할 수 없습니다.
음, 결국 일이 벌어졌네요. retort 플러그인 때문에 discourse를 업데이트할 수 없었습니다.
다음과 같은 데이터베이스 마이그레이션 오류가 발생했습니다:
could not create unique index "index_post_details_on_post_id_and_key_ccnew_ccnew" DETAIL: Key (post_id, key)=(30297, +1|retort) is duplicated.
저는 이 스크립트를 기반으로 자체 마이그레이션 코드를 작성했습니다. 제가 수행한 작업은 다음과 같습니다.
- discourse를 다시 작동시키기 위해 템플릿 .yml 파일의 "version"을 discourse 저장소에서 약 2주 전 커밋으로 덮어써야 했습니다.
- reactions 플러그인을 추가하여 사이트를 다시 빌드하여 복구했습니다.
- reactions 플러그인을 retort와 동일한 리액션 세트로 구성했습니다. '좋아요’로 해석될 수 있는 리액션은 사용하지 않았습니다.
- @mcdanlj의 스크립트를 약간 수정하여 다음 단계들을 따랐습니다 (모든 retort를 마이그레이션하고 싶었으며, retort와 리액션 사이에 이미 1:1 매핑이 있었기 때문):
./launcher enter app실행rails c실행- 아래 코드를 붙여넣으세요 (rails 콘솔이 잘못된 줄바꿈으로 코드를 다시 출력하는 것 같습니다. 이중 줄바꿈을 추가했지만 출력에는 큰 차이가 없었습니다. 만약 아래 코드에서 구문 오류가 발생하면 각 줄 뒤에 한 줄을 더 추가하세요):
def migrateRetortToReactions()
retort = "retort".freeze
emojiType = "emoji".freeze
usermap = Hash.new { |hash, username| hash[username] = User.find_by_username(username) }
postmap = Hash.new { |hash, post_id| hash[post_id] = Post.find(post_id) }
likeType = PostActionType.where(name_key: "like").pluck(:id).first
PostDetail.where(extra: retort).each do |pd|
begin
p = postmap[pd.post_id]
rescue
# PostDetail not consistent WRT delete
$stderr.puts sprintf("Could not find post for %d: %s / %s", pd.post_id, pd.key, pd.value)
next
end
emoji = pd.key.split('|').first
users = JSON.parse(pd.value)
users.each do |user|
u = usermap[user]
next if u.nil? # changed user name or deleted user leaves orphaned Retorts
e = emoji
r = DiscourseReactions::Reaction.where(post_id: p.id, reaction_type: emojiType, reaction_value: e).first_or_create
ru = DiscourseReactions::ReactionUser.where(user_id: u.id, post_id: p.id).first
next unless ru.nil?
$stderr.puts sprintf("Converting Retort %s to Reaction %s for user %s in %s", emoji, e, user, p.url)
DiscourseReactions::ReactionUser.create(reaction_id: r.id, user_id: u.id, post_id: p.id, created_at: pd.created_at)
end
end
end
- 이 시점에서 혹시 모르도록 사이트 백업을 만들었습니다.
- 그런 다음
migrateRetortToReactions를 실행하세요. 시간이 좀 걸릴 수 있습니다. 제 경우엔 문제를 보거나 겪지 않았습니다. 실행 후 콘솔에 변경된 모든 객체가 표시되는 것 같으니q를 눌러 종료하세요. - 이제 사이트에서 데이터가 올바르게 마이그레이션되었어야 합니다.
- 마지막 단계로
PostDetail.where(extra: "retort").destroy_all을 실행하여 retort 데이터를 삭제해야 합니다. - 이제 최신 discourse 버전으로 retort 플러그인 없이 사이트를 다시 빌드할 수 있었습니다.
결국 마이그레이션은 그렇게 어렵지 않았지만 꽤 무서웠습니다. 그리고 이전에 논의했듯이, 같은 사용자가 좋아요와 retort를 모두 달았던 게시물에서는 리액션이 좋아요를 덮어쓰게 됩니다.
동의합니다! 여러 가지 이모지를 사용할 수 있고, 모든 이모지 중에서 원하는 것을 선택할 수 있는 기능은 제 커뮤니티에 필수적입니다. 사람들은 디스코드 채팅 서버 세계에서 이를 당연하게 여기고 있으므로, 제 커뮤니티에서 이 기능을 되돌리는 것은 절대 받아들일 수 없습니다. 다행히 이 플러그인은 아직 제 환경에서 문제가 되지 않았지만, 이 플러그인이 향후 업데이트를 방해하게 된다면 제 포럼을 오래된 빌드 버전에서 영구적으로 유지해야 할지도 모른다는 현실에 직면해 있습니다. 향후 6개월 내에 서드파티 커뮤니티나 디스코urses 공식에서 원하는 해결책이 나오기를 바랍니다. 그렇지 않다면, 이 플러그인이 업데이트를 막는 문제가 발생하게 될 경우, 제 포럼을 오래된 버전에서 계속 유지할 수밖에 없을 것입니다.
Ember 5의 새로운 변경 사항으로 인해 Retort는 더 이상 사용할 수 없게 되었습니다. 그 기능을 유지할 수 있는 방법을 모색하고 있습니다.