"Discord Bot": run one on your Discourse server & keep things in sync

Purpose of this plugin

This plugin does two things:

  1. Helps you sync membership of your Discord server with your Discourse instance.

  2. Fundamentally, allows you to power a Discord bot using your Discourse server to do stuff to link the two systems.

    image
    these aren’t the two sync commands, just an illustration of bot interaction that is possible. the second command in the screenshot was deprecated during development :wink:

It is necessarily complex to set up, but great for fans of both systems who are keen to put the time in to understand how each system works, at least on a functional and conceptual level. You need to understand some of the fundamentals of how Discourse and Discord user management works to get the best out of this plugin.

Bot Commands

There are three commands:

  1. Ping!
  2. !disckick
  3. !discsync

Ping!

To check if the bot is responding, just type “Ping!” and the bot should respond with “:robot: : Pong!” - marvellous, eh? :smiley: (this is a bit of a meme btw!)

!disckick <min_trust_level: default 2>

Typing this will kick any user that exists on your Discourse but at a Trust Level below your supplied value. The default if you don’t supply a value is 2.

e.g.:

image

!discsync <clean up: default false, min_visibility: default 0, include automated groups: default false>

This command will attempt to copy over all groups into Roles on Discord which match provided criteria (or defaults if not provided). It will then attempt to populate the roles as per the Group membership on Discourse. It’s a one way sync only. No Discourse data is ever changed.

State of this plugin

This is a complex plugin to set-up. That was pretty unavoidable.

However, it’s child’s-play to use once it’s all up and running.

Consider the code as currently in Beta. It’s been tested, even in Production, but likely to contain bugs. I recommend testing this first on a fresh Discord server before applying it to your “Production” main Discord server, unless you are just starting out.

Any bugs are much more likely to upset your Discord instance than your Discourse instance, because there are no changes made to Discourse. However, I’ve written it in a way that means most things are recoverable and you can merely repeat something to fix an issue usually. Discourse is used as a master for membership and group information and this data is used to update membership and Roles on your Discord server.

Setup Instructions

Prerequisites

  1. a Discord server on which you are Admin
  2. a Discord App (see below)
  3. a Discord Bot (see below)
  4. ssh root access to your Discourse server
  5. updates to app.yml to
  • install libsodium, a dependency (see below)
  • install the plugin
  1. All users of your Discourse will need to login using Discord OAuth login that will appear on your login page once the plugin is installed.

Discord App

Go here and create an app:

Hit “New Application”

You will need to create a Bot and it will look something like this (leave it not ‘public’):

image

You will need to authorise the bot in the browser, see Discord Developer Portal

Once the bot is set up, copy the Token. You will need to enter that into Discourse later.

app.yml changes

plugin

You need just one plugin for this now that Discord Social Login is now native to Discourse core (wooo :tada: )

libsodium

This library is required for the discordrb gem, installed by plugin.rb, to work.

hooks:
    before_code:
       - exec:
              cmd:
                - apt-get update
                - apt-get install -y libsodium-dev

This goes just above where you specify your plugins. The hooks statement should pre-exist. You need to add the plugins to the plugin section directly beneath this.

Then at the prompt ./launcher rebuild app as usual.

This should work, np.

You may get some console and log errors indicating a failure of the bot on first build as you’ve not yet entered the bot’s credentials in the front end of Discourse’s settings yet. (more gracefully handling this is a todo, I’ll delete this bit when that gets done). It will do no harm though and Discourse itself will work normally.

Once in your Discourse admin area, go to Plugin Settings and fill these:

image

You will get the IDs from Discord interface. You will need to activate Developer Mode to allow you to copy these IDs. In Discord, go to your Settings - > Appearance - > ADVANCED and enable Developer Mode:

image

You can then get ID’s on from the interface, e.g.:

image

You will also need to fill in the Discord OAuth Settings. The Client ID and Secret are from your very same Discord App.

image

Once you’ve set these, go back to your root prompt in linux and type:

./launcher restart app

If you’ve set up your Discord and Discourse servers correctly, you should see the bot join the server.

image

If ever the bot falls over (it will go offline) you will need to do the same thing again to bring it back. Improved independence of the bot from the Discourse core server is a to do. That said, I’ve run a bot in Production for a long while and it’s yet to fall over.

Future commands?

If you have an idea for another command that leverages the link between Discourse and Discord that you believe would be useful to the wider community let me know in the replies and we can look at the potential to implement that. NB this bot is not intended to perform tasks outside the scope of Discourse.

Limitations

The main goal of this plugin was to allow the user to create a bot on their Discord that would be powered by their Discourse server and be able to carry out some basic membership management functions. It also provides a foundation for any additional bot-like interactions between the two systems for the future. Part of the motivation for using bots is that they are just plain fun.

Apologies, only English language is presently supported and have yet to add hooks to support translation, partly because there’s quite a lot of language. Let me know if that’s a huge issue.

Bot commands are ad hoc by their very nature.

I have also been working on another plugin which avoids the use of bot commands and instead uses more vanilla REST calls from Discourse to manage things. The advantage of that approach is that you can create a batch process to sync things periodically without having to initiate the process every time. I’ll see if I have time to complete that given competing priorities …

Known Issues

The Robot is a bit chatty during ‘online’ Discourse upgrades as keeps getting reactivated during the upgrade process. This comes back to working out a good way to running the bot in a separate, managed process instead of a thread branched off the webserver. I believe this is all harmless though, and if the bots output is only to your admin channel on Discord, do you really care?

Thanks

There are several backers to thank for this plugin which took me a big while to get to this stage inc. @Wedgebert, @FoohonPie. Thanks to Jeff for his generous contribution. Thanks to @angus for all the encouragement and dealing with the financial support.

The plugin was inspired by the work @Watercolor_Games did at an earlier stage and relies on the discord OAuth plugin built by @featheredtoast

The plugin relies on the semi-official Discord-backed discordrb Ruby library and the fantastic work Discord team has done to make their system accessible. Thanks to @Falco for helping me with a dependency in an extremely responsive manner.

Additionally, of course, this would not be possible without the amazing plugin ecosystem of Discourse (woo!)

State of these instructions

They will be improved in time and I welcome feedback. There are areas that are bound to be unclear.

22 Likes

Hey I’m super interested in this.

Would the opposite be possible? I’d love a way to be able to create a Discourse setup for my Discord server. I’m not sure if there’s a Discord API event for roles being given but you could probably check when someone logs in with Discord or with CRON.

Yes, absolutely.

You could either build that as a bot command for ad hoc application, but with a repeat feature that reran the command after a period (nice but not very transparent and no way to individually manage these recurrences)

OR

As I alluded to above, have it scheduled as a job in Discourse as a sidekiq job. That might be a separate plugin (I actually started on the skeleton of that approach before moving to a bot only solution mainly because the ‘bot’ approach was kind of the Discord ‘thing’, fun and offered a unique approach and result), but if I can get Discourse to fire off bot commands then that’s moot (not yet had the chance to test that), and then we can write stuff once and have it work either way (nice!).

Both these solutions would rely on the same OAuth login though.

Whilst this is an excellent idea, it can’t be a priority for me at present because I have a lot of other client work at the moment I need to deliver, but if you wish to support such an extension, we can discuss offline on a PM and schedule it in.

2 Likes

It might be interesting to play around with, but I have nowhere near the skill level required to volunteer to develop/maintain an addon like this. I might make a messy fork at some time but I certainly don’t want my name on anything official. :slight_smile:

1 Like

Absolutely get messy. It’s the only way to learn. :).

Minor bug fix deployed:

Got this during rebuild:

Bot is still offline. Triple checked everything and it’s all set up correctly. Not sure how to proceed now… Maybe I need to open a port on my server instance?

1 Like

Thanks for testing this out and so soon after the update. Let me try to reproduce and I will revert. I did not change anything wrt to the port config.

1 Like

No issues rebuilding here, Bot comes up fine … have you changed anything in your server config in between?

Your 400 Bad Request sounds like your server sent a corrupt or bad request to the Discord server and this was the response. This suggests it received it fine and was not blocked.

You can get this for:

  • malformed requests
  • lack of authorisation
  • exceeding rate limits

Unfortunately the error message doesn’t appear to help us determine which of these was the particular issue.

Presumably your Discourse is up? Check all the plugin settings are populated correctly.

Just to rule out a temporary gremlin run ./launcher restart app when you get chance … this will take your site down for a few seconds only (sorry about that!).

I see it runs the bot in the after_initialize block, so it will prevent migrations (and rebuilds) if the bot is misconfigured, or if Discord is down.

Maybe try to handle this exception and just log it?

2 Likes

It runs on a separate thread? So I expect that not to be an issue?

However this callback seems to run several times in a rebuild, so the main issue will be that a misconfigured bot could actually breach rate limits if authorisation fails too many times in quick succession. If that happens the account, in worse case, could be blocked for some time.

In any case, I’ll do a quick patch to do that with the current implementation to lessen the impact. Thanks for the suggestion.

UPDATE: @falco, this is done, e.g.:

image

1 Like

First I want to say thank you for making this, once I can get it working its going to be a huge help for us managing our discourse and discord for a volunteer community.
I am running into a 400 error when trying to run this bot. Prior to this plugin being added we have been successfully using the official discord auth plugin and webhooks via the chat integration plugin. I’ve validated that those are both still working appropriately.

I created the bot within the same discord app I’d previously created authorized the bot within the discord server, it shows up in the member list as offline. Your documentation didn’t say what permission value to apply, but given most bots I’ve encountered for discord asked for it I went ahead and set it up with a permissions value of 8 for Administrator.

Within the discord server I then copied the ID of the Role it created, which was named after my app, and the id of the channel for admin text.I added the app Role to that channel with full rights, just to be sure, then added the IDs to discourse and restarted.

Unfortunately no matter what I try I’m getting a 400, I even tried a rebuild just to be sure.

2 Likes

Thank you, kind words. It was also enabled by the generosity of funders.

Yes, that’s great. It should have Admin.

This is the second report of this issue.

If you intentially remove the last char of the token in settings (remember what this is) do you get a 401 instead?

Let’s move this to PM because it might get messy :wink: (we can always post the solution here).

2 Likes

Just to post an update in case anyone else sees this, we’re actively looking into the issue but haven’t yet narrowed down the cause. I’d say if anyone else is running into this issue please say so would help to have others testing.

1 Like

This is now fixed. It was a weird one to track down.

Thanks to @ransim for raising and working with me to get to the bottom of it.

Huge thanks to the #ruby_discordrb gang on Discord API for their patient and instant help!

@neemiasvf

5 Likes

@merefield no problem,Your plugin is great.But I have encountered some problems now.
such this:

Discourse Sync:  Starting.  Please be patient, I'm rate limited to respect Discord services.
Discourse Sync:  Checking if there are any eligible groups for sync ...
Discourse Sync: 1 eligible group(s) were found
Discourse Sync:  Preparing list of users who also have a registered account on Discord ...
Discourse Sync:  Preparing list of groups that users who have a registered account on Discord belong to on Discourse ...
Discourse Sync: 0 eligible group(s) were found with Discord users
Discourse Sync:  No users were found in elibigle groups for sync using provided or default criteria!

my commond is : !discsync 4

and commond changed: !discsync false 5 false

Discourse Sync:  Starting.  Please be patient, I'm rate limited to respect Discord services.
Discourse Sync:  Checking if there are any eligible groups for sync ...
Discourse Sync: 10 eligible group(s) were found
Discourse Sync:  Preparing list of users who also have a registered account on Discord ...
Discourse Sync:  Preparing list of groups that users who have a registered account on Discord belong to on Discourse ...
1 Like

Hey @p0nda, sorry for slow reply.

If you include any parameters, you need to include all of them. It might be interpreting things strangely.

3 Likes

Unable to get the extended bot settings to show up? I have the changes to the app.yml and plugins installed. I have the OAuth setup working, but not the bot settings. Any ideas?

1 Like

What do you mean by ‘extended’?

Can you name an example setting you can’t see?

1 Like

The OAuth setting are there but no bot settings

1 Like