Skip to content

dcsan/xbot

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Jan 13, 2024
f3fb34e ยท Jan 13, 2024
Jul 21, 2020
Jul 27, 2022
Nov 15, 2020
Jul 25, 2020
Jul 16, 2022
Jan 13, 2024
Jan 13, 2024
Aug 5, 2020
Jan 13, 2024
Jan 13, 2024

Repository files navigation

TEN / BoPO Discord Bot Game system

This is an engine to run text adventure games inside Discord. It includes a full parser for a custom game scripting language.

Released as Open Source, so please do what you want! If you need help leave an issue here in the repo or ask me.

I wrote up a bit more about the project genesis here:

https://dc.rik.ai/projects/ten

Initial setup

Clone with submodules for story content

git clone --recurse-submodules git@github.com:dcsan/xbot.git

or if you already cloned it, get the submodules

git submodule update --init --recursive

Try the game!

You can play the game by joining this server https://discord.gg/Qgup6qU

screenshot

or install and play on your own server:

https://discord.com/oauth2/authorize?client_id=759874591694389279&scope=bot

Adding stories

edit scripts in the cdn/storyData directory

Example story scripts are in another repo here https://github.com/dcsan/storydata

Short example:

rooms:
  - name: office

    states:
      - name: default
        long: A boring looking tech company's office. Seems like you've been here before, but it just looks the same as any other one.
        short: A large empty room with a corner `desk`
        imageUrl: office/rooms/office-chest-closed.jpg

    actions:
      - match: smell
        if:
          all:
            - matches.has = yes
        # if true then do this
        then:
          # reply and set property on the 'lamp' object
          reply: You light the lamp with the matches
          setProps:
            - lamp.state = lit
        # otherwise if NOT matches.has say this:
        else:
          reply: you don't have any matches```

Engine functions

Example Commands:

The script language is documented here with example stories: https://github.com/dcsan/storydata

command description
status player status and all items in the room
look view the room
look <item> inspect an item
get take an item from the room
inv show inventory items you're carrying
use <item> use an item
use <item> on <item> use an item with another thing
debug turn event debugging on/off
log show whole chat history
cheat see room actions

Full Command list: https://github.com/exiteer/xbot/blob/master/server/src/mup/parser/CommandList.ts

Parser

You can see each command has it's own little regex and a route to the function to handle it. eg look

  {
    cname: 'lookRoom',  // unique name in lookup table
    rex: /^(look|lookroom|๐Ÿ‘€)$/i,  // synonyms
    event: RouterService.lookRoom,  // handler function to call
    type: 'postCommand' // this happens *after* other events
  },

Game events

There is a hierachy of models that accept events

- Game
  - Story
    - Room
      - Player
      - Item(s)

so then in RouterService

the look command results get returned back to the user based on the current game/story/room instance.

  lookRoom: async (evt: SceneEvent) => {
    return await evt.game?.story.room.lookRoom(evt)
  },

in the Room

  async lookRoom(evt: SceneEvent) {
    logger.log('lookRoom', this.roomObj.doc.name)
    await this.roomObj.describeThing(evt) // the room
  }

this.roomObj is an instance of the room model, which is got from a little up the hierarchy. In this case it's a bit redundant as we are already in a room instance, but this allows events to be called on the room or actors in the room, or the game, without worrying at which level.

  // this method gets room of an item or just itself if running on the room
  get roomObj(): Room {
    // @ts-ignore
    if (this.klass === 'room') return <Room>this
    // @ts-ignore
    return this.room
  }

So we now have a room instance and we call describeThing on it.

This is a more abstract method that is called by the lookRoom that can be called on a room or a thing in the room.

So we pass it the highest level item the event from the user's interaction.

It's implemented in GameObject which is a more abstract class that Room and Item extend eg class Room extends GameObject {

So in GameObject#describeThing

  // may work for rooms and things
  async describeThing(evt: SceneEvent) {
    const stateInfo: StateBlock = this.getStateBlock()
    const palBlocks = this.renderBlocks(stateInfo, evt.pal)
    await evt.pal.sendBlocks(palBlocks)
    return palBlocks
  }

pal is the 'Platform Abstraction Layer' that handles formatting output for Discord/Slack/Telegram whatever output channel. So we use the pal to render the response and then return it all the way up the hierarchy. "blocks" are a term since we can return a list of chat blocks to send to the user.

So in this case we're returning the state of the room as a 'block'.

Again this is implemented in GameObject so that rooms, items, players can all describe their state back to the chat in terms of 'blocks'.

  getStateBlock() {
    const state = this.state
    let block: StateBlock = this.doc.states.find(one => one.name === state)
    // logger.logObj(`get state [${state}] block`, { state, block })

    if (!block) {
      logger.warn('cant find block for state', { name: this.name, state })
      block = this.doc.states[0]
      if (!block) {
        // should never happen since states are required
        logger.assertDefined(block, 'cannot find block for state', { state: this.state, states: this.doc.states })
      }
    }
    return block
  }

DevOps

### Deploying / Nginx / Proxy

Makefile has various deploy scripts

These are specific to my domain, so you will need to change eg cbg.rik.ai to your domain

server/build is where the built files the client go before deploying

start server

make sure you have a .env.production file with the correct settings then start the server with

NODE_ENV=production pm2 --name=cbg start dist/index.js

ssh cert

if you update/deploy the nginx script, you might need to run certbot on the server again check the certificate is in the correct place

certbot certonly -n -d cbg.rik.ai --nginx

Static files

some web game features are in /client but you just need to host those files. check nginx config for the correct path, eg

/mnt/ext250/web-apps/cbg.rik.ai/cdn/

sometimes the discord CDN will cache failed requests so be patient when updating files

Notes

Proxy for local dev

http://xbot-d9fc7b57.localhost.run/api/webhooks/slack

live app https://cbg.rik.ai/api/webhooks/slack

API calls

Everything handled by the server is under /api namespace.

More info about the company TEN / BoPO Discord Bot

About

exiteer game bot

Resources

Stars

Watchers

Forks

Packages

No packages published