A friendly Discord bot

Discord is a worthy successor of hum radio and IRC. This robust platform offers some unique, useful features which the mainstream social media lack.

Apart from plethora of free and commercial bots available for Discord the Node-powered Discord.js library offers clear API making it easy to create and deploy your own bots. Psst, it is also available in Python.

As it happens I do own a little Minecraft Bedrock server running on a spare MacMini. Mincraft servers cannot exist without Discord these days and a Discord server without bots, therfore Sharon was born.

Sharon is a little, mighty Node app that provides an interface between Discord and Minecraft server.

By typing simple commands in the Discord’s chat users can trigger code that reaches out to Minecraft server’s command line or pulls data from MySQL database. (Since my server is ‘vanilla’ style it doesn’t support add-ones and all what’s available is the command line.)

The task is a bit tricky, as my Minecraft server runs in Docker, but with a little help of expect it proved to be reliable enough for over 11 months uninterrupted operation so far.

Commands

From a simple !hi which triggers a random text response to more advanced commands such as !whitelist or !playtime which receive data either from server’s console or MySQL database, Sharon can do it all in a casual manner that resonates well with young audience. POG!

The !on command happened somehow coincidentally as an unforeseen but welcomed feature of a custom player-tracking system that I’ve developed for the Server earler. It is an extremely rare feature of vanilla Bedrock servers.

!on command returns Gamer Tags of players currently on-line in Mincraft and shows whether they have the Emerald tag.
It also tells some OS stats such as CPU, memory usage and time since reboot. The OS stats image is created on-the-fly with Canvas.js
!playtime pulls data from MySQL database and presents in a digestive form.
The !whitelist command is available only in designated channels with limited access.
Sharon throws a random message to give the user initial feedback as the !whitelist command takes some time to complete.
The bot also informs a user that they have been whitelisted and provides further instructions.

The bot has many more commands and they keep evolving in response to users interaction. Since this is just single-server thing I can update it whenever I please without following any particular release patterns. That gives room for experimentation.

Code

Discord’s API as well written and easy to learn. Below there is an example of simple command’s code.

module.exports =
   name: 'ping'
   description: 'Ping!'
   cooldown: .3
   execute: (client, config, message, args) ->
      message.channel.send 'Pong.'
         .catch (error) -> client.helpers.error_message_send error, {command: @.name}; return
      return
The !on command joins couple of txt files that are created by a lunchd task running on the MC Server’s machine. It does so in a rather crude way at the moment. One could say that a fox is being helped by cat.

Below there is an excerpt from my sought-after !whitelist command. Whitelisting in vanilla Bedrock can only be performed via command-line and can be a pain in the neck for server owners.

# [...]

               send = 'whitelist add "' + gtag + '"'
               expect = '"Player already in whitelist*" "Failed*" "No targets*"' # the array of expected strings sending as space separated quoted strings

            else if subcommand == 'remove'
               send = 'whitelist remove "' + gtag + '"'
               expect = '"Player removed from whitelist*" "Player not in whitelist*"'

            message.channel.send client.helpers.random([ 'okay, lemme see', 'oof, back to work', 'communicating with MC server', 'talking to Minecraft Gods', 'sure, why not']) + " ...⏳"
               .catch (error) -> client.helpers.error_message_send error, err_msg_send_args; return

            client.libs.exec config.abspath + '/mc-connect/mc-connect.exp.sh \'' + send + '\' \'' + expect + '\'', (error, stdout, stderr) ->
               if error
                  console.log 'error: ' + error.message
                  message.channel.send "Wasn't able to do the whitelist thingy this time.\rYou can always try again #{message.author} ..."
                     .catch (error) -> client.helpers.error_message_send error, err_msg_send_args; return
                  return
               if stderr
                  console.log 'stderr: ' + stderr
                  message.channel.send "Wasn't able to do the whitelist thingy this time.\rYou can always try again #{message.author} ..."
                     .catch (error) -> client.helpers.error_message_send error, err_msg_send_args; return
                  return

               # handle responses from the expect script
               # ---- add
               if stdout.includes('Player added to whitelist’)
# [...]

The corresponding except code follows

#!/usr/bin/expect --

log_user 0

# log_file /var/sharon_log.log
set timeout 5
spawn ssh “*********"
expect "*$ "

# set the prompt to a known value
#send "PS1='>'\r"
#expect -re {>$}

send "docker attach --detach-keys=Q emer\r"
expect "DEBU*"

log_user 1
send "[lindex $argv 0]\r"
foreach arg [lindex $argv 1] {
  expect "$arg"
}

log_user 0

send "Q"
# expect ">$"
expect "*$ "


send "exit\r"
expect eof

exit

Conclusion

Developing a Discord bots is a great way of learning Node.js!

Discord.js Guide


If you play Emerland I’m giving you a diamond block for finding this post lol :D

Disclaimer

My bot doesn’t have anything in common with a COVID-19 troll-bot with the same name.