Skip to content

Commit e8d5e50

Browse files
author
Philipp Heckel
committed
Replace gotty with ttyd
1 parent 7f776b9 commit e8d5e50

File tree

5 files changed

+35
-25
lines changed

5 files changed

+35
-25
lines changed

README.md

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ I recorded, as well as the [ZIP archive](assets/slack-recording.zip) with the re
121121

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

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

163163
## Installation
164+
Please check out the [releases page](https://github.com/binwiederhier/replbot/releases) for binaries and
165+
deb/rpm packages.
166+
164167
**Requirements**:
165168
- A modern-ish Linux, preferably Ubuntu 18.04+, since that's what I develop on -- though it also runs on other
166169
distros.
167170
- [tmux](https://github.com/tmux/tmux) >= 2.6 is required, which is part of Ubuntu 18.04 (but surprisingly not part of Amazon Linux!)
168171
- [docker](https://docs.docker.com/get-docker/) for almost all scripts REPLbot ships with
169172
- [asciinema](https://asciinema.org/) if you'd like to [record sessions](#recording-sessions)
170-
- [gotty](https://github.com/yudai/gotty) if you'd like to use the [web terminal](#web-terminal) feature
173+
- [ttyd](https://github.com/tsl0922/ttyd) if you'd like to use the [web terminal](#web-terminal) feature
171174

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

206209
**Debian/Ubuntu** (*manual install*)**:**
207210
```bash
208211
sudo apt install tmux
209-
wget https://github.com/binwiederhier/replbot/releases/download/v0.6.0/replbot_0.6.0_amd64.deb
210-
dpkg -i replbot_0.6.0_amd64.deb
212+
wget https://github.com/binwiederhier/replbot/releases/download/v0.6.1/replbot_0.6.1_amd64.deb
213+
dpkg -i replbot_0.6.1_amd64.deb
211214
```
212215

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

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

245248
## Building

bot/bot.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -439,7 +439,7 @@ func (b *Bot) webHandlerInternal(w http.ResponseWriter, r *http.Request) error {
439439
}
440440
b.mu.RUnlock()
441441
if session == nil || webPort == 0 {
442-
return errors.New("session not found")
442+
return fmt.Errorf("session with prefix %s not found", prefix)
443443
}
444444
if len(parts) < 3 { // must be /prefix/, not just /prefix
445445
http.Redirect(w, r, r.URL.String()+"/", http.StatusTemporaryRedirect)

bot/bot_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,11 +212,11 @@ func TestBotBashWebTerminal(t *testing.T) {
212212
if err != nil {
213213
t.Fatal(err)
214214
}
215-
if strings.Contains(string(body), "js/gotty.js") {
215+
if strings.Contains(string(body), "<html ") {
216216
break
217217
}
218218
if i == 5 {
219-
t.Fatal("unexpected response: 'js/gotty.js' not contained in: " + string(body))
219+
t.Fatal("unexpected response: '<html ' not contained in: " + string(body))
220220
}
221221
time.Sleep(time.Second)
222222
}

bot/session.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ func (s *session) Run() error {
281281
return err
282282
}
283283
if err := s.maybeStartWeb(); err != nil {
284-
log.Printf("[%s] Cannot start gotty: %s", s.conf.id, err.Error())
284+
log.Printf("[%s] Cannot start ttyd: %s", s.conf.id, err.Error())
285285
// We just disabled it, so we continue here
286286
}
287287
if err := s.conn.Send(s.conf.control, s.sessionStartedMessage()); err != nil {
@@ -919,7 +919,7 @@ func (s *session) sendWebHelpMessage(enabled, writable bool) error {
919919
return s.conn.Send(s.conf.control, webDisabledMessage+"\n\n"+webHelpMessage)
920920
}
921921

922-
func (s *session) startWeb(permitWrite bool) error {
922+
func (s *session) startWeb(writable bool) error {
923923
s.mu.Lock()
924924
defer s.mu.Unlock()
925925
if s.webCmd != nil && s.webCmd.Process != nil {
@@ -936,18 +936,25 @@ func (s *session) startWeb(permitWrite bool) error {
936936
if s.webPrefix == "" {
937937
s.webPrefix = util.RandomString(10)
938938
}
939-
s.webWritable = permitWrite
940-
args := []string{
941-
"--address", "127.0.0.1",
942-
"--port", strconv.Itoa(s.webPort),
943-
"--reconnect",
944-
"--title-format", "REPLbot session",
945-
"tmux", "attach", "-t", s.tmux.MainID(),
946-
}
939+
s.webWritable = writable
940+
var args []string
947941
if s.webWritable {
948-
args = append([]string{"--permit-write"}, args...)
942+
args = []string{
943+
"--interface", "lo",
944+
"--port", strconv.Itoa(s.webPort),
945+
"--check-origin",
946+
"tmux", "attach", "-t", s.tmux.MainID(),
947+
}
948+
} else {
949+
args = []string{
950+
"--interface", "lo",
951+
"--port", strconv.Itoa(s.webPort),
952+
"--check-origin",
953+
"--readonly", // ttyd is read-only
954+
"tmux", "attach", "-r", "-t", s.tmux.MainID(), // tmux is also read-only
955+
}
949956
}
950-
s.webCmd = exec.Command("gotty", args...)
957+
s.webCmd = exec.Command("ttyd", args...)
951958
if err := s.webCmd.Start(); err != nil {
952959
s.webCmd = nil // Disable web!
953960
return err

cmd/app.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ func execRun(c *cli.Context) error {
9393
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")
9494
} else if maxUserSessions > maxTotalSessions {
9595
return errors.New("max total sessions must be larger or equal to max user sessions")
96-
} else if err := util.Run("gotty", "--version"); webHost != "" && err != nil {
97-
return fmt.Errorf("cannot set --web-host; 'gotty --version' test failed: %s", err.Error())
96+
} else if err := util.Run("ttyd", "--version"); webHost != "" && err != nil {
97+
return fmt.Errorf("cannot set --web-host; 'ttyd --version' test failed: %s", err.Error())
9898
}
9999
cursorRate, err := parseCursorRate(cursor)
100100
if err != nil {

0 commit comments

Comments
 (0)