手搓了一个简陋但有意思的 Discourse 脚本 随机帖子金句

没发现有类似的插件或者组件,自己捣鼓的话坑太深,干脆召唤 AI 写 JS 实现了,异常简陋,但感觉效果还行,记录分享下

在论坛帖子列表顶部插入一个随机帖子金句,点击可以进入帖子

金句和对应帖子标题链接是手动写入 JS 的……

1 Like

emmmm 感觉好像没什么意义 或许应该弄一个签到的按钮 点击后谈一个悬浮UI里面写这个感觉会不错

挺有趣,可以放到discourse meta上,我去装一下

1 Like

不会也没有时间写成插件或者组件……

只是一段简单的 JS 代码

分享在 github

1 Like

Git 的这个链接貌似打不开。

是不是仓库的权限没有设置好?

github 小号被标记了 :rofl:(可能是上次提 issue 连续回复然后解决问题以后 issue 被整个删了导致的……)
解封需要国外手机号验证,用 sms-activate 第一个号没收到短信,第二个就一直 「You’ve reached your request limit, please try again later.」……

直接发这好了,暂时也不会迭代

Discourse 随机帖子金句脚本

简介

一个简陋但能用的 Discourse 脚本,在帖子列表顶部插入一个随机帖子金句,并提供跳转到对应帖子的链接。
该脚本由 AI 生成,仅供参考,如需优化请自行调整。

功能

  • 在论坛帖子列表顶部随机显示一条帖子金句。
  • 点击金句可以跳转到对应帖子。
  • 通过手动更新 JS 文件,维护需要展示的金句。

安装方法

  1. 编辑 Discourse 主题或组件

    • 进入 管理 > 自定义 > 主题
    • 选择一个主题或创建一个新组件。
    • 自定义 CSS/HTML 选项卡中,找到 通用 > head
    • head 部分粘贴完整的脚本代码。
  2. 修改金句数据

    • 在脚本内找到 _postsData 变量。

    • 按照格式添加或修改金句:

      // 初始化原始帖子数据(只定义一次)
      if (!window._postsData) {
        window._postsData = [
          {
            postId: "demoid1",
            title: "demotitle1",
            quotes: [
              "demoquote11",
              "demoquote12",
            ]
          },
          {
            postId: "demoid2",
            title: "demotitle2",
            quotes: [
              "demoquote21",
              "demoquote22",
            ]
          },
        ];
      }
      
    • postId:对应帖子 ID(用于跳转)。

    • title:帖子标题(用于显示和链接)。

    • quotes:该帖子的多个可选金句。

  3. 保存并应用

    • 点击 保存
    • 刷新页面,随机帖子金句会出现在帖子列表顶部。
<script type="text/discourse-plugin" version="0.8">
  // 全局定义 shuffle 函数,确保后续调用时可用
  function shuffle(array) {
    for (var i = array.length - 1; i > 0; i--) {
      var j = Math.floor(Math.random() * (i + 1));
      var temp = array[i];
      array[i] = array[j];
      array[j] = temp;
    }
    return array;
  }

  function insertRandomProverb() {
    console.log("insertRandomProverb 被调用");
    var panel = document.querySelector('.topic-list-body');
    if (!panel) {
      console.log("未找到 .topic-list-body 元素,延时重试");
      setTimeout(insertRandomProverb, 300);
      return;
    }
    console.log("找到了 panel:", panel);

    // 移除已存在的金句,避免重复插入
    var existing = panel.querySelector('.random-proverb');
    if (existing) {
      existing.remove();
      console.log("已移除旧的金句");
    }
    
    // 初始化原始帖子数据(只定义一次)
    if (!window._postsData) {
      window._postsData = [
        {
          postId: "demoid1",
          title: "demotitle1",
          quotes: [
            "demoquote11",
            "demoquote12",
          ]
        },
        {
          postId: "demoid2",
          title: "demotitle2",
          quotes: [
            "demoquote21",
            "demoquote22",
          ]
        },
      ];
    }
    
    // 如果洗牌数组不存在或为空,则生成洗牌后的金句数组
    if (!window._shuffledQuotes || window._shuffledQuotes.length === 0) {
      var combined = [];
      window._postsData.forEach(function(post) {
        post.quotes.forEach(function(q) {
          combined.push({
            postId: post.postId,
            title: post.title,
            quote: q
          });
        });
      });
      window._shuffledQuotes = shuffle(combined.slice());
      console.log("已生成洗牌金句数组:", window._shuffledQuotes);
    }
    
    function getNextQuote() {
      var data = window._shuffledQuotes.shift();
      if (!data) {
        var combined = [];
        window._postsData.forEach(function(post) {
          post.quotes.forEach(function(q) {
            combined.push({
              postId: post.postId,
              title: post.title,
              quote: q
            });
          });
        });
        window._shuffledQuotes = shuffle(combined.slice());
        data = window._shuffledQuotes.shift();
      }
      return data;
    }
    
    var quoteData = getNextQuote();
    var isMobile = window.innerWidth <= 1024;
    
    // 构造公共部分(链接部分)
    var commonLink = '<a href="/t/' + quoteData.postId + '" class="title raw-link raw-topic-link">' +
                       quoteData.quote + '……<br><small>✨ ' + quoteData.title + '</small>' +
                     '</a>';
    var commonCell = '<td class="main-link clearfix topic-list-data" colspan="1">' +
                       '<span class="link-top-line" role="heading" aria-level="2">' +
                         '<span class="topic-statuses"></span>' +
                         commonLink +
                       '</span>' +
                     '</td>';
    
    // 如果是 PC 版,则附加额外的单元格;移动版则只用公共单元格
    var extraCells = "";
    if (!isMobile) {
      extraCells =
        '<td class="posters topic-list-data theme-avatar-small">' +
          '<a href="/u/system" data-user-card="system" class="latest single">' +
            '<img alt="" width="24" height="24" src="/" class="avatar latest single" title="system - 原始发帖人、最新发帖人">' +
          '</a>' +
        '</td>' +
        '<td class="num posts-map posts topic-list-data">' +
          '<a href="/t/' + quoteData.postId + '" class="badge-posts"><span class="number">0</span></a>' +
        '</td>' +
        '<td class="num views topic-list-data"><span class="number">0</span></td>' +
        '<td class="activity num topic-list-data age">' +
          '<a href="/t/' + quoteData.postId + '" class="post-activity"><span class="relative-date" data-time="1738932033887" data-format="tiny">0m</span></a>' +
        '</td>';
    }
    
    var rowHTML = '<tr class="random-proverb topic-list-item ' + (isMobile ? '' : 'pc-template') + '">' +
                    commonCell + extraCells +
                  '</tr>';
    
    panel.insertAdjacentHTML('afterbegin', rowHTML);
    console.log("金句行已插入");
  }

  // 初次加载时执行
  if (document.readyState === 'complete' || document.readyState === 'interactive') {
    insertRandomProverb();
  } else {
    document.addEventListener('DOMContentLoaded', insertRandomProverb);
  }

  // 尝试使用 MutationObserver 监听较稳定的容器
  // 这里优先尝试 data-test-id="topic-list",若找不到则退回 document.body
  var stableContainer = document.querySelector('[data-test-id="topic-list"]');
  if (!stableContainer) {
    console.log("未找到稳定容器 [data-test-id='topic-list'],使用 document.body 作为备用");
    stableContainer = document.body;
  }
  var observer = new MutationObserver(function(mutationsList, observer) {
    // 检查 .topic-list-body 是否存在且包含金句
    var panel = document.querySelector('.topic-list-body');
    if (panel && !panel.querySelector('.random-proverb')) {
      console.log("MutationObserver 检测到金句缺失,重新插入");
      insertRandomProverb();
    }
  });
  observer.observe(stableContainer, { childList: true, subtree: true });
  console.log("MutationObserver 已启动,在稳定容器:", stableContainer);
</script>

<style>
  .random-proverb {
    opacity: 1;
    text-align: left;
    margin-top: 10px;
    padding: 5px;
  }
  .random-proverb br {
    line-height: 1px !important;
  }
  .random-proverb .title {
    opacity: 0.5;
  }
  .random-proverb small {
    margin-top: 0.5em;
    opacity: 0.7;
  }
  /* 移动版:调整单元格内边距 */
  @media screen and (max-width: 1024px) {
    .random-proverb > td {
      padding: 20px 0 !important;
    }
    .random-proverb small {
      float: right;
    }
  }
  /* PC版:取消 <small> 的浮动,保持帖子标题左对齐 */
  .pc-template small {
    float: none !important;
  }
</style>

好吧。

Github 还有小号的呀。

我就一个号,一直就没有变过,反而是换个公司换个号,其实也挺麻烦的。

github repo discourse-random-quote 已经能访问了
原来是因为个人资料信息不像个人账户而是组织/项目信息的原因被限制的,修改以后就解封了
另外接码方面,开始随手选的印尼的号码一直提示「You’ve reached your request limit, please try again later」,换了个美国号码搞定

@hex 代码盲,所谓的大号也只是放了个 blog ……

貌似还是访问权限错误。

建议换一个没有登录过的浏览器试试能不能正常访问。

1 Like

汗,这次是真忘了私有转公开了
现在好了……

1 Like