Skip to content

Commit

Permalink
Replace gotty with ttyd
Browse files Browse the repository at this point in the history
  • Loading branch information
Philipp Heckel committed Sep 27, 2021
1 parent 7f776b9 commit e8d5e50
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 25 deletions.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ I recorded, as well as the [ZIP archive](assets/slack-recording.zip) with the re

### Web terminal
Entering commands via Slack or Discord can be quite cumbersome, so REPLbot provides a web-based terminal (powered by
the amazingly awesome [gotty](https://github.com/yudai/gotty)). If enabled, a unique link is created for each session,
the amazingly awesome [ttyd](https://github.com/tsl0922/ttyd)). If enabled, a unique link is created for each session,
which provides a read-only or writable web terminal.

![replbot web terminal](assets/slack-web-terminal.png)
Expand Down Expand Up @@ -161,13 +161,16 @@ as `trim` mode can get awkward when the terminal is expanded and the collapsed a
![replbot window mode](assets/discord-window-mode.png)

## Installation
Please check out the [releases page](https://github.com/binwiederhier/replbot/releases) for binaries and
deb/rpm packages.

**Requirements**:
- A modern-ish Linux, preferably Ubuntu 18.04+, since that's what I develop on -- though it also runs on other
distros.
- [tmux](https://github.com/tmux/tmux) >= 2.6 is required, which is part of Ubuntu 18.04 (but surprisingly not part of Amazon Linux!)
- [docker](https://docs.docker.com/get-docker/) for almost all scripts REPLbot ships with
- [asciinema](https://asciinema.org/) if you'd like to [record sessions](#recording-sessions)
- [gotty](https://github.com/yudai/gotty) if you'd like to use the [web terminal](#web-terminal) feature
- [ttyd](https://github.com/tsl0922/ttyd) if you'd like to use the [web terminal](#web-terminal) feature

**Creating a REPLbot Slack app**:
REPLbot requires a Slack "Classic App (bot)", because of its use of the real time messaging (RTM)
Expand Down Expand Up @@ -200,20 +203,20 @@ curl -sSL https://archive.heckel.io/apt/pubkey.txt | sudo apt-key add -
sudo apt install apt-transport-https
sudo sh -c "echo 'deb [arch=amd64] https://archive.heckel.io/apt debian main' > /etc/apt/sources.list.d/archive.heckel.io.list"
sudo apt update
sudo apt install replbot
sudo apt install replbot asciinema
```

**Debian/Ubuntu** (*manual install*)**:**
```bash
sudo apt install tmux
wget https://github.com/binwiederhier/replbot/releases/download/v0.6.0/replbot_0.6.0_amd64.deb
dpkg -i replbot_0.6.0_amd64.deb
wget https://github.com/binwiederhier/replbot/releases/download/v0.6.1/replbot_0.6.1_amd64.deb
dpkg -i replbot_0.6.1_amd64.deb
```

**Fedora/RHEL/CentOS:**
```bash
# Make sure that "tmux" is installed
rpm -ivh https://github.com/binwiederhier/replbot/releases/download/v0.6.0/replbot_0.6.0_amd64.rpm
rpm -ivh https://github.com/binwiederhier/replbot/releases/download/v0.6.1/replbot_0.6.1_amd64.rpm
```

**Docker:**
Expand All @@ -238,8 +241,8 @@ go get -u heckel.io/replbot
**Manual install** (*any x86_64-based Linux*)**:**
```bash
# Make sure that "tmux" is installed
wget https://github.com/binwiederhier/replbot/releases/download/v0.6.0/replbot_0.6.0_linux_x86_64.tar.gz
sudo tar -C /usr/bin -zxf replbot_0.6.0_linux_x86_64.tar.gz replbot
wget https://github.com/binwiederhier/replbot/releases/download/v0.6.1/replbot_0.6.1_linux_x86_64.tar.gz
sudo tar -C /usr/bin -zxf replbot_0.6.1_linux_x86_64.tar.gz replbot
```

## Building
Expand Down
2 changes: 1 addition & 1 deletion bot/bot.go
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,7 @@ func (b *Bot) webHandlerInternal(w http.ResponseWriter, r *http.Request) error {
}
b.mu.RUnlock()
if session == nil || webPort == 0 {
return errors.New("session not found")
return fmt.Errorf("session with prefix %s not found", prefix)
}
if len(parts) < 3 { // must be /prefix/, not just /prefix
http.Redirect(w, r, r.URL.String()+"/", http.StatusTemporaryRedirect)
Expand Down
4 changes: 2 additions & 2 deletions bot/bot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ func TestBotBashWebTerminal(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if strings.Contains(string(body), "js/gotty.js") {
if strings.Contains(string(body), "<html ") {
break
}
if i == 5 {
t.Fatal("unexpected response: 'js/gotty.js' not contained in: " + string(body))
t.Fatal("unexpected response: '<html ' not contained in: " + string(body))
}
time.Sleep(time.Second)
}
Expand Down
31 changes: 19 additions & 12 deletions bot/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ func (s *session) Run() error {
return err
}
if err := s.maybeStartWeb(); err != nil {
log.Printf("[%s] Cannot start gotty: %s", s.conf.id, err.Error())
log.Printf("[%s] Cannot start ttyd: %s", s.conf.id, err.Error())
// We just disabled it, so we continue here
}
if err := s.conn.Send(s.conf.control, s.sessionStartedMessage()); err != nil {
Expand Down Expand Up @@ -919,7 +919,7 @@ func (s *session) sendWebHelpMessage(enabled, writable bool) error {
return s.conn.Send(s.conf.control, webDisabledMessage+"\n\n"+webHelpMessage)
}

func (s *session) startWeb(permitWrite bool) error {
func (s *session) startWeb(writable bool) error {
s.mu.Lock()
defer s.mu.Unlock()
if s.webCmd != nil && s.webCmd.Process != nil {
Expand All @@ -936,18 +936,25 @@ func (s *session) startWeb(permitWrite bool) error {
if s.webPrefix == "" {
s.webPrefix = util.RandomString(10)
}
s.webWritable = permitWrite
args := []string{
"--address", "127.0.0.1",
"--port", strconv.Itoa(s.webPort),
"--reconnect",
"--title-format", "REPLbot session",
"tmux", "attach", "-t", s.tmux.MainID(),
}
s.webWritable = writable
var args []string
if s.webWritable {
args = append([]string{"--permit-write"}, args...)
args = []string{
"--interface", "lo",
"--port", strconv.Itoa(s.webPort),
"--check-origin",
"tmux", "attach", "-t", s.tmux.MainID(),
}
} else {
args = []string{
"--interface", "lo",
"--port", strconv.Itoa(s.webPort),
"--check-origin",
"--readonly", // ttyd is read-only
"tmux", "attach", "-r", "-t", s.tmux.MainID(), // tmux is also read-only
}
}
s.webCmd = exec.Command("gotty", args...)
s.webCmd = exec.Command("ttyd", args...)
if err := s.webCmd.Start(); err != nil {
s.webCmd = nil // Disable web!
return err
Expand Down
4 changes: 2 additions & 2 deletions cmd/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ func execRun(c *cli.Context) error {
return errors.New("share key file must be set and exist if share host is set, check --share-key-file or REPLBOT_SHARE_KEY_FILE")
} else if maxUserSessions > maxTotalSessions {
return errors.New("max total sessions must be larger or equal to max user sessions")
} else if err := util.Run("gotty", "--version"); webHost != "" && err != nil {
return fmt.Errorf("cannot set --web-host; 'gotty --version' test failed: %s", err.Error())
} else if err := util.Run("ttyd", "--version"); webHost != "" && err != nil {
return fmt.Errorf("cannot set --web-host; 'ttyd --version' test failed: %s", err.Error())
}
cursorRate, err := parseCursorRate(cursor)
if err != nil {
Expand Down

0 comments on commit e8d5e50

Please sign in to comment.