You are browsing a read-only backup copy of Wikitech. The live site can be found at wikitech.wikimedia.org

Tool:Bridgebot

From Wikitech-static
Jump to navigation Jump to search
Toolforge tools
Crystal Clear app package utilities.png
Description A bot for bridging IRC and other chat platform channels
Keywords irc, golang
Maintainer(s) BryanDavis (View all)
Source code https://github.com/42wim/matterbridge
License Apache License 2.0
Issues Open tasks · Report a bug
Admin log Tools.bridgebot/SAL

Bridgebot is a deployment of the FLOSS matterbridge software. It can be used to relay messages between multiple different chat systems. The current deployment in Toolforge is bridging various Wikimedia related channels across platforms including Libera.Chat, Telegram, Discord, and Mattermost.

Bot accounts

The bot must be invited/added to chat channels in order to create bridges. Currently the bot has these accounts on various chat platforms:

libera.chat IRC
  • account: wm-bridgebot
  • nicks: wm-bridgebot, wm-bb
Telegram
Discord

Maintenance

The bot runs from Toolforge under the "bridgebot" account as a kubernetes pod.


$ ssh login.toolforge.org
$ become bridgebot

:# Deploying
$ bin/bb.sh start
Starting bridgebot k8s deployment...

:# Quick restart (pod delete)
$ bin/bb.sh restart
Restarting bridgebot pod...

:# "Full" restart (Deployment delete; Deployment create)
$ bin/bb.sh stop
Stopping bridgebot k8s deployment...
$ bin/bb.sh start
Starting bridgebot k8s deployment...

:# Upgrading the golang binary
$ bin/bb.sh download $version
$ vim bb.sh
  update the MATTERBRIDGE=... line to the new version
  :wq
$ bin/bb.sh restart "Upgrading matterbridge to $version"

Joining a new channel

Maintainers of the tool can configure the bot to join a new channel:

$ ssh login.tools.wmflabs.org
$ become bridgebot
$ vim etc/matterbridge.toml
:# Add a new "[[gateway]]" section defining the gateway name and the channels to bridge
$ bin/bb.sh restart

Telegram

If one side of the bridge is Telegram you will need these things to be done:

  1. Have an admin of the Telegram channel invite the "@wmtelegram_bot" user to the channel
  2. Follow the log output of the bot: `bin/bb.sh tail`
  3. Have someone in the Telegram channel talk to the bot
  4. Collect the Telegram "Channel" id number from the DEBUG log output
    [85656] DEBUG telegram:     <= Message is config.Message{Text:"@wmtelegram_bot I need you to see this message so I can finish https://phabricator.wikimedia.org/T258078", Channel:"-1001308089610", Username:"bd808", UserID:"419968011", Avatar:"https://tools-static.wmflabs.org/bridgebot/4a263e14/419968011.png", Account:"telegram.bridgebot", Event:"", Protocol:"", Gateway:"", ParentID:"", Timestamp:time.Time{wall:0x0, ext:0, loc:(*time.Location)(nil)}, ID:"86", Extra:map[string][]interface {}{}}

Discord

If one side of the bridge is Discord you will need these things to be done:

  1. Have an admin of the Discord server invite the bot to their server using the bot's invite link

Technical details

bin/bb.sh

To make operating the bot less complicated for maintainers, we have a bin/bb.sh script to wrap the various Kubernetes commands. This same script is used as the command inside the bot's container to setup and run the matterbridge software when invoked as bin/bb.sh run. The run action copies the matterbridge binary and it's configuration into the container's /tmp directory so that while running there is no need to read or write to the tool's $HOME.

$HOME/bin/bb.sh
#!/usr/bin/env bash
# Management script for bridgebot kubernetes processes

set -e

MATTERBRIDGE=matterbridge-1.23.0-linux-64bit

DEPLOYMENT=bridgebot.bot
POD_NAME=bridgebot.bot

TOOL_DIR=$(cd $(dirname $0)/.. && pwd -P)
BIN_DIR=$TOOL_DIR/bin
CONFIG_DIR=$TOOL_DIR/etc

KUBECTL=/usr/bin/kubectl
_get_pod() {
    $KUBECTL get pods \
        --output=jsonpath={.items..metadata.name} \
        --selector=name=${POD_NAME}
}

case "$1" in
    start)
        echo "Starting bridgebot k8s deployment..."
        $KUBECTL create --validate=true -f ${CONFIG_DIR}/deployment.yaml
        [[ -n $2 ]] && dologmsg "$2"
        ;;
    run)
        # Execute bot inside container
        date +%Y-%m-%dT%H:%M:%S
        echo "Bootstrapping container..."
        cd ${TOOL_DIR}
        cp ${BIN_DIR}/${MATTERBRIDGE} /tmp
        chmod a+x /tmp/${MATTERBRIDGE}
        cp ${CONFIG_DIR}/matterbridge.toml /tmp
        cp ${CONFIG_DIR}/remotenickformat.tengo /tmp
        echo "Starting bridgebot..."
        cd /tmp
        exec /tmp/${MATTERBRIDGE} -debug -conf /tmp/matterbridge.toml
        ;;
    stop)
        echo "Stopping bridgebot k8s deployment..."
        $KUBECTL delete deployment ${DEPLOYMENT}
        [[ -n $2 ]] && dologmsg "$2"
        ;;
    restart)
        echo "Restarting bridgebot pod..."
        $KUBECTL delete pod $(_get_pod)
        [[ -n $2 ]] && dologmsg "$2"
        ;;
    status)
        echo "Active pods:"
        $KUBECTL get pods -l name=${POD_NAME}
        ;;
    tail)
        exec $KUBECTL logs -f $(_get_pod)
        ;;
    attach)
        echo "Attaching to pod..."
        exec $KUBECTL exec -i -t $(_get_pod) -- /bin/bash
        ;;
    version)
        echo "${BIN_DIR}/${MATTERBRIDGE}"
        "${BIN_DIR}/${MATTERBRIDGE}" -version
        ;;
    download)
        version=${2:?No version specified. Expected something like '1.22.1'}
        baseurl="https://github.com/42wim/matterbridge/releases/download"
        new="matterbridge-${version}-linux-64bit"
        if [[ -f "${BIN_DIR}/${new}" ]]; then
            read -p "${new} exists. Overwrite? [y/N]: " -n 1 -r
            if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                exit 1
            fi
            echo
        fi
        echo "Downloading ${new}..."
        wget -O "${BIN_DIR}/${new}" "${baseurl}/v${version}/${new}"
        chmod a+x "${BIN_DIR}/${new}"
        echo "Downloaded $("${BIN_DIR}/${new}" -version)"
        ;;
    *)
        echo "Usage: $0 {start

deployment.apps/bridgebot.bot

The bot's Kubernetes container is managed by a custom deployment defined in $HOME/etc/deployment.yaml and applied by bin/bb.sh.

$HOME/etc/deployment.yaml
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: bridgebot.bot
  namespace: tool-bridgebot
  labels:
    name: bridgebot.bot
    toolforge: tool
spec:
  replicas: 1
  selector:
    matchLabels:
      name: bridgebot.bot
      toolforge: tool
  template:
    metadata:
      labels:
        name: bridgebot.bot
        toolforge: tool
    spec:
      containers:
        - name: bot
          image: docker-registry.tools.wmflabs.org/toolforge-buster-sssd:latest
          resources:
            limits:
              cpu: 1
              memory: 1Gi
            requests:
              cpu: 1
              memory: 1Gi
          command:
            - "/data/project/bridgebot/bin/bb.sh"
            - "run"
          workingDir: /data/project/bridgebot
          env:
            - name: HOME
              value: /data/project/bridgebot
          imagePullPolicy: Always

deployment.apps/bridgebot.bnc

After many issues with matterbridge's IRC client, we decided to place a bouncer (BNC) between matterbridge and libera.chat. This bouncer is running as a pod inside the tool's namespace and connected to a Kubernetes Service which allows the bot's pod to connect to it.

The specific BNC software currently used is ZNC. ZNC was compiled from inside a webservice ruby27 shell (ruby27 just happened to have all the -dev packages needed) and installed to $HOME/.local/bin/znc. ZNC's runtime configuration is stored in $HOME/.znc.

Configuration for ZNC can be managed by connecting to it with an IRC client running inside the tool's namespace. kirc has been compiled and installed to $HOME/.local/bin/kirc for this purpose. The password used to connect to the bouncer can be found in the $HOME/etc/matterbridge.toml configuration file.


$ webservice shell
$ .local/bin/kirc -s bnc -p 6667 -n wm-bb -u wm-bridgebot/libera -k $password
?> @*status help
                   *status In the following list all occurrences of <#chan>
                           support wildcards (* and ?) except ListNicks
                   *status Version: Print which version of ZNC this is
                   *status ListMods: List all loaded modules
...
                   *status Shutdown [message]: Shut down ZNC completely
                   *status Restart [message]: Restart ZNC
?>
$HOME/etc/bnc-deployment.yaml
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: bridgebot.bnc
  namespace: tool-bridgebot
  labels:
    name: bridgebot.bnc
    toolforge: tool
spec:
  replicas: 1
  selector:
    matchLabels:
      name: bridgebot.bnc
      toolforge: tool
  template:
    metadata:
      labels:
        name: bridgebot.bnc
        toolforge: tool
    spec:
      containers:
        - name: bnc
          image: docker-registry.tools.wmflabs.org/toolforge-bullseye-sssd:latest
          resources:
            limits:
              cpu: 250m
              memory: 256Mi
            requests:
              cpu: 100m
              memory: 100Mi
          command:
            - /data/project/bridgebot/.local/bin/znc
            - --foreground
            - --datadir
            - /data/project/bridgebot/.znc
          workingDir: /data/project/bridgebot
          env:
            - name: HOME
              value: /data/project/bridgebot
          imagePullPolicy: Always
---
kind: Service
apiVersion: v1
metadata:
  name: bnc
spec:
  selector:
    name: bridgebot.bnc
  ports:
    - protocol: TCP
      port: 6667
      targetPort: 6667

remotenickformat.tengo

A tengo script is used to do per-channel customization of nick formats used when cross-posting messages between services.

Note: Any syntax errors in the script will stop messages from passing across all bridges until they are corrected.

$HOME/etc/remotenickformat.tengo
/**
 * The script will have the following global variables:
 * to modify: result
 * to read: channel, bridge, gateway, protocol, nick
 */

ret := "[" + protocol + "] <" + nick + "> "

if gateway == "ukwiki-discord-telegram" {
  protos := immutable({
    discord: "дс",
    telegram: "тг"
  })
  proto := protos[protocol]
  ret = "[" + (proto == undefined ? protocol : proto) + "] <" + nick + "> "

} else if gateway == "wikipedia-abstract-irc-telegram" {
  ret = "<" + nick + "> "

}

result = ret