(Obsoleto) Mostrar um "Widget Discord" em um botão suspenso

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 curtidas

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 curtidas

Done.

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

5 curtidas

Updated initial post to fix broken widget

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

1 curtida

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

5 curtidas

It could be a TC I reckon.

2 curtidas

Olá, obrigado por desenvolver este widget! Acabei de adicioná-lo ao nosso fórum seguindo suas instruções e, embora o widget apareça corretamente, ele não parece se conectar (tenta carregar infinitamente… veja a captura de tela abaixo). Verifiquei três vezes se o código copiado estava 100% correto, mas ele ainda parece não carregar adequadamente. Também adicionei o ID do servidor Discord corretamente em todas as seções referenciadas.

Qualquer ajuda seria muito apreciada!

1 curtida

Copiar e colar todo esse código é, sem dúvida, um pouco confuso e propenso a erros? Alguém com tempo suficiente e interesse próprio deveria considerar publicar isso como um Componente de Tema e aplicar correções conforme apropriado (e postar um Tópico para isso em Theme)

1 curtida

Certifique-se de que você habilitou o "Server Widget" no Discord em Configurações do Servidor > Widget

1 curtida

Olá pessoal, peguei este guia incrível e o empacotei em um componente de tema facilmente instalável:

4 curtidas

Obrigado por este guia. Acabei modificando-o ligeiramente e incorporando o WidgetBot. Eu esperava usar o crate WidgetBot em vez de um iframe, tive problemas para carregar javascript externo, então isso serve por enquanto.

Também notei alguns problemas. O método de único usuário logado não funcionou e o ícone estava aparecendo mesmo quando deslogado. Então usei o método de nível de confiança com >= 1. O outro problema é que a largura não parece ultrapassar 350 (não acho que seja um problema de cache, pois tentei no modo privado e em navegadores diferentes). Além disso, a altura parece estar se ajustando corretamente.

<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");

// ensure user is logged in and of trust level 1 or higher
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>

Isso está obsoleto em favor de

User.currentProp

Então você deve ser capaz de usar >=0, já que usuários com nível de confiança 0 também são usuários registrados.

Se me lembro corretamente, 350 foi codificado no código do iframe do Discord? :thinking:

Boa observação. Eu ainda sou novo no Discourse e estou em modo “bootstrap”, onde todos estão no nível 1. Vou ajustar isso.

Certo, mas eu mudei isso…

                    "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",
                    }

O WidgetBot certamente não tem limite de tamanho de iframe… imagem do meu site.