(已弃用) 在下拉按钮中显示“Discord Widget”

May 2022
This has has now been converted into a theme component by @keegan :tada::balloon:

Discourse Discord Widget

The Discord Widget does its job, but it’s really cumbersome and generally it’s an eyesore with the style of Discourse.
In the past few months I had tried to implement it everywhere (after-header, body, footer) but every time I found it too big and too customized to be permanently seen.
That’s why, in the end, I decided that a dropdown button would be the best solution. Users can decide if open the widget (maybe only to see the users online) and close it, or click the Connect button to enter the chat.

Desktop View

Display the button for ALL users (logged in and visitors):

Add this script under /admin/customize/themes inside Desktop/Head tab

<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");

api.createWidget('discord-chat-menu', {
  tagName: 'div.discord-panel',

  html() {
    return this.attach('menu-panel', {
      contents: () => h('iframe', {
                        "src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
                        "sandbox": "allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts",
                        "width": "350",
                        "height": "500",
                        "allowtransparency": "true",
                        "frameborder": "0",
                        "id": "chatwidget",
                        "name": "chatwidget",}
                    )

    });
  },

  clickOutside() {
    this.sendWidgetAction('toggleDiscordChat');
  }
});
    
api.decorateWidget('header-icons:before', function(helper) {
  const headerState = helper.widget.parentWidget.state;
  return helper.attach('header-dropdown', {
      title: 'Discord Chat',
      icon: 'fab-discord',
      active: headerState.discordChatVisible,
      action: 'toggleDiscordChat',
    });
});

api.decorateWidget('header-icons:after', function(helper) {
  const headerState = helper.widget.parentWidget.state;
    if (headerState.discordChatVisible) {
        return [helper.attach('discord-chat-menu')];
    }
});

api.attachWidgetAction('header', 'toggleDiscordChat', function() {
  this.state.discordChatVisible = !this.state.discordChatVisible;
});

</script>

Make sure to change the Url here:

  • "src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light', entering your Server ID in place of your-widget-ID. You can find it on Discord in Server Setting > Widget > Server ID.
    E.g. "src": 'https://discordapp.com/widget?id=1234567890123&theme=light',

  • Inside the Url you can also replace the theme style to make it light (&theme=light) or dark (&theme=dark).

  • Change "title": "Discord Chat" as you prefer (e.g. "title": "my wonderful chat")

Add this script under /admin/customize/themes inside Desktop/Body tab to reload and update the iframe every 1.5 mins (made by @Yuun, see here).
Remember to change https://discordapp.com/widget?id=your-widget-ID&theme=light with your Url (e.g. https://discordapp.com/widget?id=1234567890123&theme=light)

<script>
    function reloadIFrame() {
        var disc = document.getElementById("chatwidget");
        if (disc) {
            disc.src="https://discordapp.com/widget?id=your-widget-ID&theme=light";
        }
    }
    window.setInterval("reloadIFrame();", 90000);
</script>

Display the button only to logged in users

<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
const user = require('discourse/models/user').default;
   if (user !== null) {

api.createWidget('discord-chat-menu', {
  tagName: 'div.discord-panel',

  html() {
    return this.attach('menu-panel', {
      contents: () => h('iframe', {
                        "src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
                        "width": "350",
                        "height": "500",
                        "allowtransparency": "true",
                        "frameborder": "0",
                        "id": "chatwidget",
                        "name": "chatwidget",}
                    )

    });
  },

  clickOutside() {
    this.sendWidgetAction('toggleDiscordChat');
  }
});

    
api.decorateWidget('header-icons:before', function(helper) {
  const headerState = helper.widget.parentWidget.state;
  return helper.attach('header-dropdown', {
      title: 'Discord Chat',
      icon: 'fab-discord',
      active: headerState.discordChatVisible,
      action: 'toggleDiscordChat',
    });
});

api.decorateWidget('header-icons:after', function(helper) {
  const headerState = helper.widget.parentWidget.state;
    if (headerState.discordChatVisible) {
        return [helper.attach('discord-chat-menu')];
    }
});

api.attachWidgetAction('header', 'toggleDiscordChat', function() {
  this.state.discordChatVisible = !this.state.discordChatVisible;
})};

</script>

Display the button only for users who belong to a certain trust level (and higher or lower - optional)

Change the script in this way:

<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
   var level = User.currentProp("trust_level");
   if (level >=2) {  //Change the trust level here

api.createWidget('discord-chat-menu', {
  tagName: 'div.discord-panel',

  html() {
    return this.attach('menu-panel', {
      contents: () => h('iframe', {
                        "src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
                        "width": "350",
                        "height": "500",
                        "allowtransparency": "true",
                        "frameborder": "0",
                        "id": "chatwidget",
                        "name": "chatwidget",}
                    )

    });
  },

  clickOutside() {
    this.sendWidgetAction('toggleDiscordChat');
  }
});

    
api.decorateWidget('header-icons:before', function(helper) {
  const headerState = helper.widget.parentWidget.state;
  return helper.attach('header-dropdown', {
      title: 'Discord Chat',
      icon: 'fab-discord',
      active: headerState.discordChatVisible,
      action: 'toggleDiscordChat',
    });
});

api.decorateWidget('header-icons:after', function(helper) {
  const headerState = helper.widget.parentWidget.state;
    if (headerState.discordChatVisible) {
        return [helper.attach('discord-chat-menu')];
    }
});

api.attachWidgetAction('header', 'toggleDiscordChat', function() {
  this.state.discordChatVisible = !this.state.discordChatVisible;
})};

</script>

In this case, the button is displayed to all users that belong to trust level 2 or higher (>=2). Use only =2 to display it only for users at trust level 2 (no higher), or (<=2) for users that belong to trust level 2 or lower. You can change the trust level (from 0 to 4) as needed. For more information about trust levels read Understanding Discourse Trust Levels

Display the button only for users who belong to staff (admins + mods) [@Neuferkar]

<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
   const user = User.currentProp(); 
    if (user !== null && user.staff) { 

api.createWidget('discord-chat-menu', {
  tagName: 'div.discord-panel',

  html() {
    return this.attach('menu-panel', {
      contents: () => h('iframe', {
                        "src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
                        "width": "350",
                        "height": "500",
                        "allowtransparency": "true",
                        "frameborder": "0",
                        "id": "chatwidget",
                        "name": "chatwidget",}
                    )

    });
  },

  clickOutside() {
    this.sendWidgetAction('toggleDiscordChat');
  }
});

    
api.decorateWidget('header-icons:before', function(helper) {
  const headerState = helper.widget.parentWidget.state;
  return helper.attach('header-dropdown', {
      title: 'Discord Chat',
      icon: 'fab-discord',
      active: headerState.discordChatVisible,
      action: 'toggleDiscordChat',
    });
});

api.decorateWidget('header-icons:after', function(helper) {
  const headerState = helper.widget.parentWidget.state;
    if (headerState.discordChatVisible) {
        return [helper.attach('discord-chat-menu')];
    }
});

api.attachWidgetAction('header', 'toggleDiscordChat', function() {
  this.state.discordChatVisible = !this.state.discordChatVisible;
})};

</script>

Change this line if (user !== null && user.staff) as you wish to display the button only to admins or only to mods:

  • if (user !== null && user.administrator)
  • if (user !== null && user.moderator)

Display the button only to members of a primary group (e.g. “footeam”) [@Neuferkar and @cpradio]

Be sure to target the primary_group_name correctly (see here)

<script type="text/discourse-plugin" version="0.8">
const { h } = require('virtual-dom');
const { iconNode } = require("discourse-common/lib/icon-library");
const user = User.currentProp(); 
if (user !== null && user.primary_group_name === "footeam") { 

api.createWidget('discord-chat-menu', {
  tagName: 'div.discord-panel',

  html() {
    return this.attach('menu-panel', {
      contents: () => h('iframe', {
                        "src": 'https://discordapp.com/widget?id=your-widget-ID&theme=light',
                        "width": "350",
                        "height": "500",
                        "allowtransparency": "true",
                        "frameborder": "0",
                        "id": "chatwidget",
                        "name": "chatwidget",}
                    )

    });
  },

  clickOutside() {
    this.sendWidgetAction('toggleDiscordChat');
  }
});

    
api.decorateWidget('header-icons:before', function(helper) {
  const headerState = helper.widget.parentWidget.state;
  return helper.attach('header-dropdown', {
      title: 'Discord Chat',
      icon: 'fab-discord',
      active: headerState.discordChatVisible,
      action: 'toggleDiscordChat',
    });
});

api.decorateWidget('header-icons:after', function(helper) {
  const headerState = helper.widget.parentWidget.state;
    if (headerState.discordChatVisible) {
        return [helper.attach('discord-chat-menu')];
    }
});

api.attachWidgetAction('header', 'toggleDiscordChat', function() {
  this.state.discordChatVisible = !this.state.discordChatVisible;
})};

</script>

Mobile View

For mobile I use an icon with an invite Url to my server since on phones there is a cool Discord App to install.

Just add to your site the Custom Header Links (icons) theme component and enter in the theme settings:

  • Header links: Mobile-only link,fab-discord,https://discord.gg/INVITE_ID,vmo,blank
  • Svg icons: fab-discord

Remember to generate an invite of infinite duration, so it will always be valid and you will not have to change it again and make sure to update the Url from https://discord.gg/INVITE_ID to your real invite ID:

image

49 个赞

In

helper.attach('header-dropdown', {
      title: 'Discord Chat',
      icon: 'discord',
      active: headerState.discordChatVisible,
      action: 'toggleDiscordChat',
    });

, icon: 'discord' should be changed to icon: 'fab-discord'

2 个赞

Done.

The guide is a wiki, feel free to edit and improve it!

5 个赞

Updated initial post to fix broken widget

"sandbox": "allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts",

1 个赞

Are there plans to make this into an official plugin? It will be easier to maintain it that way. Thanks for sharing this!

5 个赞

It could be a TC I reckon.

2 个赞

您好,感谢您开发此小部件!我已按照您的说明将其添加到我们的论坛,虽然小部件显示正常,但似乎并未连接(一直在尝试加载……请参见下面的屏幕截图)。我已仔细检查并确认复制的代码 100% 准确,但它似乎仍未正确加载。我也已在所有引用的部分正确添加了 Discord 服务器 ID。

非常感谢您的帮助!

1 个赞

复制代码并粘贴所有这些代码,可以说有点混乱且容易出错?有足够时间和自身利益的人应该考虑将其发布为一个主题组件,并酌情应用修复程序(并在 Theme 中发布一个主题)。

1 个赞

请确保您已在 Discord 的服务器设置 > 小部件下启用了“服务器小部件”。

1 个赞

各位,我已将这份很棒的指南打包成一个易于安装的主题组件:

4 个赞

感谢这篇指南。我对其进行了少量修改并嵌入了 WidgetBot。我曾希望使用 WidgetBot crate 而不是 iframe,但在加载外部 JavaScript 时遇到了一些问题,所以目前就这样吧。

我还注意到了一些问题。仅登录用户方法不起作用,并且在用户未登录时图标也会显示。所以我使用了信任级别方法,将条件设置为 \u003e= 1。另一个问题是宽度似乎无法超过 350(我认为这不是缓存问题,因为我在隐私模式和不同浏览器中都试过了)。另外,高度似乎没有正确调整。

<script type="text/discourse-plugin" version="0.8">
const { h } = require("virtual-dom");
const { iconNode } = require("discourse-common/lib/icon-library");
var level = Discourse.User.currentProp("trust_level");

// 确保用户已登录且信任级别为 1 或更高
if (level >= 1) {

    api.createWidget("discord-chat-menu", {
        tagName: "div.discord-panel",

        html() {
            return this.attach("menu-panel", {
                contents: () =>
                    h("iframe", {
                        src: "https://e.widgetbot.io/channels/<server_id>/<channel_id>",
                        sandbox: "allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts",
                        width: "400",
                        height: "500",
                        allowtransparency: "false",
                        frameborder: "0",
                        id: "widgetbot",
                        name: "widgetbot",
                    }),
            });
        },

        clickOutside() {
            this.sendWidgetAction("toggleDiscordChat");
        },
    });

    api.decorateWidget("header-icons:before", function (helper) {
        const headerState = helper.widget.parentWidget.state;
        return helper.attach("header-dropdown", {
            title: "Discord Chat",
            icon: "fab-discord",
            active: headerState.discordChatVisible,
            action: "toggleDiscordChat",
        });
    });

    api.decorateWidget("header-icons:after", function (helper) {
        const headerState = helper.widget.parentWidget.state;
        if (headerState.discordChatVisible) {
            return [helper.attach("discord-chat-menu")];
        }
    });

    api.attachWidgetAction("header", "toggleDiscordChat", function () {
        this.state.discordChatVisible = !this.state.discordChatVisible;
    });
}
</script>

This is deprecated in favor of

User.currentProp

Then you should be able to use >=0 since also trust level 0 users are registered users.

If I remember correctly 350 was hardcoded in the Discord iframe code? :thinking:

说得对。我对 Discourse 还不熟悉,目前处于“引导”模式,所有人都处于 1 级。我会进行调整。

是的,但我已经更改了……

                    "iframe", {
                        src: "https://e.widgetbot.io/channels/<server_id>/<channel_id>",
                        sandbox: "allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts",
                        width: "400",
                        height: "500",
                        allowtransparency: "false",
                        frameborder: "0",
                        id: "widgetbot",
                        name: "widgetbot",
                    }

WidgetBot 在 iframe 大小上肯定没有限制……我网站上的图片。