我针对 multiple-reactions-per-post 提出了一个新的请求 https://meta.discourse.org/t/support-multiple-reactions-per-post-retort-style/235064,因为另一个链接仅用于为 Discourse Reactions 允许的唯一反应提供更多反应选择。
Retort 插件并非 #官方 插件,因此我们无法决定何时停止由社区积极开发和维护。
我们所能做的就是尽可能地告知大家,以便他们有时间找到替代方案(或者至少提前告知他们的成员,以尽量减轻失望)。
希望这两个 Feature 请求能在某个时候被 reactions 插件采纳,但目前它们仍处于“不错的想法”阶段。但我会为此祈祷。![]()
我理解你的想法。现实情况是,我和 James 一段时间以来都没有时间来妥善支持 Retort。你会注意到,存储库中最后一次提交是我在 2021 年 7 月 21 日提交的,距今已有一年多了。该插件能够在此期间一直正常工作,这真是太棒了,也证明了 James 在构建它时付出的工作的质量。
当我说明我没有时间时,请相信我,我希望我有!每次我不得不做出这样的决定时(就像登陆页面插件一样),我都会感到难过。我不是 Retort 的创建者,但我已经投入了时间。当你决定淘汰某样东西时,就像接受你创造或喜爱的东西,并为此花费了许多小时、天和周的生命,它必须消亡。我知道当 James 觉得他必须继续做其他事情时,做出这个决定对他来说很艰难。
相比之下,Reactions 插件正在被 Discourse.org 维护,这是一个拥有 60 多人的组织,并且正在积极维护。它已被 Discourse.org 的客户使用的多个服务器所采用。是的,它还没有像 Retort 那样的功能,但我建议你将该功能开发作为一种途径。也许你可以说服像我这样的人,或者Pavilion 的另一位成员,为该插件提交一个缺失的功能。这将是实现你在此处长期目标的一个明智途径。
如果你想付钱让开发者暂时让它复活,Marketplace 随时为你服务。但你可能需要多次这样做,或者同意支付维护费。
我猜答案是否定的?我想迁移到反应,并尝试找到流行的表情符号……
我想这应该是可能的,因为它们会存储在数据库的某个地方。不幸的是,我的测试站点没有安装这个插件,无法检查具体细节。是否有 discourse-retort-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('|')
这将为您提供:
- 首先是使用的唯一回复表情符号的数量
- 然后是回复,您可以将其粘贴到“反应”配置中,也许在修剪后,如果它太长而无法用于“反应”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
如果您想将现有回复的列表复制到 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
这太长了,无法在此粘贴!
我不知道如何将所有这些回复——或其中一些——迁移到反应。反应插件中没有提到回复,所以它不会自动执行。我有 927 个反应,包含 116 个唯一的表情符号,我想将它们迁移到反应。
我预计我最终会处理这个问题,最好是在回复停止工作之前;如果我实施迁移,我计划在此处记录。但至少知道您拥有什么可能对您有所帮助。
在编写一些将 Retort 迁移到 Reactions 的实验性代码时,我发现当用户名更改时,Retorts 不会被更新。
我认为这对于 Reactions 将不再是问题,因为 PostActions 连接到实际的用户记录,而不是在 PostDetail 的 JSON blob 中记录用户名。
总的来说,如果有人决定采用并维护 Retort,他们应该考虑从 PostDetail 迁移到 PostActions,遵循 Reactions 的做法。
同样,它也无法识别帖子何时被删除。
我的 Script framework to rearrange topics and categories 有了一个新功能,它不仅仅是重新排列主题和分类!
我通常会提醒大家,我不会 Ruby 或 Ruby on Rails,所以我的代码是特有的,而不是惯用的。但到目前为止,在我的测试中它似乎是有效的!
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? # 用户名已更改或已删除用户会留下孤立的 Retorts
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 的功能,包括一个管理员界面。
为了使其能够投入生产使用,Reactions 的维护者需要对 Reactions 插件中的代码抽象进行一些小改动以进行改进。
支持两个插件之间这种生产级别的迁移需要大量的质量保证,否则很容易出现此类问题。
抱歉!我错过了那些帖子,只看了主分支。这个帖子很长……
我同意。我完全忽略了这一点。但它不仅仅是 ReactionManager.toggle!——它真的需要传递 created_at,不是吗?
迁移到 Reactions 真正改变了“喜欢”的语义,因为当有人编辑他们的帖子时,你无法撤销它。我不会做出同样的选择。![]()
无论如何,我想通过脚本来驱动它,并且我对通过 UI 来驱动它毫无兴趣。我不是 UI 的目标受众,所以也许我的 hack 可用并没有坏处。
当然,我无意阻止您出于自身目的编写它,但我不会建议其他网站使用它,除非它们熟悉代码和数据结构。
最重要的是,Reactions 插件目前的编写方式不利于稳定迁移,并且无法在各种环境中可靠运行。
如果有人希望从 Retort 迁移到 Reactions,Pavilion 可以按合同手动处理(发送电子邮件至 contact@pavilion.tech 或给我发私信)。如果 Reactions 插件更新为允许通用迁移,我们将完成迁移工作,使其免费提供。
啊。这回答了我的一些疑问。要理解 7 年来 450 篇帖子很难。
那么,我是否理解“需要”(欢迎任何人提供自己的“需要”定义)做的是,以某种方式扩展 Reactions,使其能够更干净地处理迁移数据,并提供它所缺少的功能?
这大概需要多少小时或多少美元的工作量?
这在很大程度上仍然是准确的。

如果 PR 的机会确实存在,我可能会为了好酒而去做。毕竟今天是星期五晚上。
但更严肃地说,我们在这里讨论的是 Reactions 插件 ReactionManager 的一次小重构。这类工作通常不通过 PR 接受。需要得到 Reactions 维护者的认可。
我想你也想确保点赞是silent的,并且created_at能被处理,否则用户会收到迁移通知的垃圾信息。(我在我的测试站点登录时看到了这个。)
不知何故,即使created_by被处理了,我仍然触发了out of love的最大点赞数,我没有进一步调查,因为我摆脱了所有其他通知。
@joffreyjaffeux 有什么理由不公开干净迁移所需的功能吗?
我刚迁移到 Reactions(因为我想这是官方的……),但不想丢失所有之前的 retort 数据。
抱歉,但由于上述原因,目前无法提供稳定的迁移。
好了,现在发生了,我因为 retort 插件无法更新 discourse。
这是我收到的数据库迁移错误:
无法创建唯一索引“index_post_details_on_post_id_and_key_ccnew_ccnew” DETAIL:键(post_id, key)=(30297, +1|retort) 重复。
我使用这个脚本作为我自己的迁移代码的基础。这是我所做的。
- 为了让 discourse 再次正常工作,我不得不覆盖 .yml 文件模板中的“version”为 discourse 仓库中大约两周前的提交。
- 使用 reactions 插件重建以恢复站点。
- 我将 reactions 插件配置为与 retort 相同的反应集。我不使用任何可能被解释为“喜欢”的反应。
- 我修改了 @mcdanlj 的脚本,并执行了以下步骤(因为我想迁移所有 retort,并且我已经在一对一地映射 retort 和反应):
- 运行
./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 与删除不一致
$stderr.puts sprintf("找不到帖子 %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? # 用户名已更改或已删除用户留下孤立的 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("将用户 %s 在 %s 中的 Retort %s 转换为 Reaction %s", user, p.url, emoji, e)
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 插件来重建我的站点。
总而言之,迁移并不那么困难,但它相当吓人,并且如前所述,这会将同一用户在帖子上的点赞(likes)与反应(reactions)覆盖掉。
同意!我的社区需要支持多种表情符号,并且能够从所有表情符号中进行选择。人们已经习惯了 Discord 聊天服务器世界的这种功能,因此在我的社区中恢复此功能是不可行的。幸运的是,这个插件对我来说还没有损坏,但我已经接受了这样一个现实:我正在倒计时。我希望在接下来的六个月内,能从第三方社区或 Discourse 官方那里得到期望的解决方案。否则,如果这个插件最终破坏了后续的更新,我将被迫无限期地将我的论坛保留在旧版本上。