Add onboard command

This commit is contained in:
Arturo Filastò 2018-06-25 16:31:44 +02:00
parent 38a5637cf5
commit 7951ee6bb5
7 changed files with 257 additions and 35 deletions

View File

@ -8,6 +8,7 @@ import (
_ "github.com/ooni/probe-cli/internal/cli/info"
_ "github.com/ooni/probe-cli/internal/cli/list"
_ "github.com/ooni/probe-cli/internal/cli/nettest"
_ "github.com/ooni/probe-cli/internal/cli/onboard"
_ "github.com/ooni/probe-cli/internal/cli/run"
_ "github.com/ooni/probe-cli/internal/cli/show"
_ "github.com/ooni/probe-cli/internal/cli/upload"

View File

@ -0,0 +1,134 @@
package onboard
import (
"fmt"
"github.com/alecthomas/kingpin"
"github.com/apex/log"
"github.com/ooni/probe-cli/internal/cli/root"
"github.com/ooni/probe-cli/internal/colors"
"github.com/ooni/probe-cli/internal/output"
"gopkg.in/AlecAivazis/survey.v1"
)
func init() {
cmd := root.Command("onboard", "Starts the onboarding process")
cmd.Action(func(_ *kingpin.ParseContext) error {
output.SectionTitle("What is OONI Probe?")
fmt.Println()
output.Paragraph("Your tool for detecting internet censorship!")
fmt.Println()
output.Paragraph("OONI Probe checks whether your provider blocks access to sites and services. Run OONI Probe to collect evidence of internet censorship and to measure your network performance.")
fmt.Println()
output.PressEnterToContinue("Press 'Enter' to continue...")
output.SectionTitle("Heads Up")
fmt.Println()
output.Bullet("Anyone monitoring your internet activity (such as your government or ISP) may be able to see that you are running OONI Probe.")
fmt.Println()
output.Bullet("The network data you will collect will automatically be published (unless you opt-out in the settings).")
fmt.Println()
output.Bullet("You may test objectionable sites.")
fmt.Println()
output.Bullet("Read the documentation to learn more.")
fmt.Println()
output.PressEnterToContinue("Press 'Enter' to continue...")
output.SectionTitle("Pop Quiz!")
output.Paragraph("")
answer := ""
quiz1 := &survey.Select{
Message: "Anyone monitoring my internet activity may be able to see that I am running OONI Probe.",
Options: []string{"true", "false"},
Default: "true",
}
survey.AskOne(quiz1, &answer, nil)
if answer != "true" {
output.Paragraph(colors.Red("Actually..."))
output.Paragraph("OONI Probe is not a privacy tool. Therefore, anyone monitoring your internet activity may be able to see which software you are running.")
} else {
output.Paragraph(colors.Blue("Good job!"))
}
answer = ""
quiz2 := &survey.Select{
Message: "The network data I will collect will automatically be published (unless I opt-out in the settings).",
Options: []string{"true", "false"},
Default: "true",
}
survey.AskOne(quiz2, &answer, nil)
if answer != "true" {
output.Paragraph(colors.Red("Actually..."))
output.Paragraph("The network data you will collect will automatically be published to increase transparency of internet censorship (unless you opt-out in the settings).")
} else {
output.Paragraph(colors.Blue("Well done!"))
}
changeDefaults := false
prompt := &survey.Confirm{
Message: "Do you want to change the default settings?",
Default: false,
}
survey.AskOne(prompt, &changeDefaults, nil)
settings := struct {
IncludeIP bool
IncludeNetwork bool
IncludeCountry bool
UploadResults bool
SendCrashReports bool
}{}
settings.IncludeIP = false
settings.IncludeNetwork = true
settings.IncludeCountry = true
settings.UploadResults = true
settings.SendCrashReports = true
if changeDefaults == true {
var qs = []*survey.Question{
{
Name: "IncludeIP",
Prompt: &survey.Confirm{Message: "Should we include your IP?"},
},
{
Name: "IncludeNetwork",
Prompt: &survey.Confirm{
Message: "Can we include your network name?",
Default: true,
},
},
{
Name: "IncludeCountry",
Prompt: &survey.Confirm{
Message: "Can we include your country name?",
Default: true,
},
},
{
Name: "UploadResults",
Prompt: &survey.Confirm{
Message: "Can we upload your results?",
Default: true,
},
},
{
Name: "SendCrashReports",
Prompt: &survey.Confirm{
Message: "Can we send crash reports to OONI?",
Default: true,
},
},
}
err := survey.Ask(qs, &settings)
if err != nil {
log.WithError(err).Error("there was an error in parsing your responses")
return err
}
}
log.Error("this function is not implemented")
return nil
})
}

View File

@ -13,6 +13,7 @@ import (
"github.com/fatih/color"
colorable "github.com/mattn/go-colorable"
"github.com/ooni/probe-cli/internal/log/handlers/cli/progress"
"github.com/ooni/probe-cli/internal/util"
)
// Default handler outputting to stderr.
@ -68,7 +69,7 @@ func logSectionTitle(w io.Writer, f log.Fields) error {
title := f.Get("title").(string)
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n")
fmt.Fprintf(w, "┃ %s ┃\n", RightPad(title, colWidth))
fmt.Fprintf(w, "┃ %s ┃\n", util.RightPad(title, colWidth))
fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n")
return nil
}
@ -85,7 +86,7 @@ func logTable(w io.Writer, f log.Fields) error {
continue
}
line := fmt.Sprintf("%s: %s", color.Sprint(name), f.Get(name))
lineLength := escapeAwareRuneCountInString(line)
lineLength := util.EscapeAwareRuneCountInString(line)
lines = append(lines, line)
if colWidth < lineLength {
colWidth = lineLength
@ -95,7 +96,7 @@ func logTable(w io.Writer, f log.Fields) error {
fmt.Fprintf(w, "┏"+strings.Repeat("━", colWidth+2)+"┓\n")
for _, line := range lines {
fmt.Fprintf(w, "┃ %s ┃\n",
RightPad(line, colWidth),
util.RightPad(line, colWidth),
)
}
fmt.Fprintf(w, "┗"+strings.Repeat("━", colWidth+2)+"┛\n")

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/apex/log"
"github.com/ooni/probe-cli/internal/util"
)
// XXX Copy-pasta from nettest/groups
@ -118,21 +119,21 @@ func logResultItem(w io.Writer, f log.Fields) error {
fmt.Fprintf(w, "┢"+strings.Repeat("━", colWidth*2+2)+"┪\n")
}
firstRow := RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2)
firstRow := util.RightPad(fmt.Sprintf("#%d - %s", rID, startTime.Format(time.RFC822)), colWidth*2)
fmt.Fprintf(w, "┃ "+firstRow+" ┃\n")
fmt.Fprintf(w, "┡"+strings.Repeat("━", colWidth*2+2)+"┩\n")
summary := makeSummary(name, f.Get("summary").(string))
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
RightPad(name, colWidth),
RightPad(summary[0], colWidth)))
util.RightPad(name, colWidth),
util.RightPad(summary[0], colWidth)))
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
RightPad(networkName, colWidth),
RightPad(summary[1], colWidth)))
util.RightPad(networkName, colWidth),
util.RightPad(summary[1], colWidth)))
fmt.Fprintf(w, fmt.Sprintf("│ %s %s│\n",
RightPad(asn, colWidth),
RightPad(summary[2], colWidth)))
util.RightPad(asn, colWidth),
util.RightPad(summary[2], colWidth)))
if index == totalCount-1 {
fmt.Fprintf(w, "└┬──────────────┬──────────────┬──────────────┬")
@ -156,9 +157,9 @@ func logResultSummary(w io.Writer, f log.Fields) error {
}
// └┬──────────────┬──────────────┬──────────────┬
fmt.Fprintf(w, " │ %s │ %s │ %s │\n",
RightPad(fmt.Sprintf("%d tests", tests), 12),
RightPad(fmt.Sprintf("%d nets", networks), 12),
RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12))
util.RightPad(fmt.Sprintf("%d tests", tests), 12),
util.RightPad(fmt.Sprintf("%d nets", networks), 12),
util.RightPad(fmt.Sprintf("%d ⬆ %d ⬇", dataUp, dataDown), 12))
fmt.Fprintf(w, " └──────────────┴──────────────┴──────────────┘\n")
return nil

View File

@ -1,22 +0,0 @@
package cli
import (
"regexp"
"strings"
"unicode/utf8"
)
// Finds the control character sequences (like colors)
var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d")
func escapeAwareRuneCountInString(s string) int {
n := utf8.RuneCountInString(s)
for _, sm := range ctrlFinder.FindAllString(s, -1) {
n -= utf8.RuneCountInString(sm)
}
return n
}
func RightPad(str string, length int) string {
return str + strings.Repeat(" ", length-escapeAwareRuneCountInString(str))
}

View File

@ -1,9 +1,13 @@
package output
import (
"bufio"
"fmt"
"os"
"time"
"github.com/apex/log"
"github.com/ooni/probe-cli/internal/util"
)
// Progress logs a progress type event
@ -76,3 +80,18 @@ func SectionTitle(text string) {
"title": text,
}).Info(text)
}
func Paragraph(text string) {
const width = 80
fmt.Println(util.WrapString(text, width))
}
func Bullet(text string) {
const width = 80
fmt.Printf("• %s\n", util.WrapString(text, width))
}
func PressEnterToContinue(text string) {
fmt.Print(text)
bufio.NewReader(os.Stdin).ReadBytes('\n')
}

View File

@ -1,8 +1,13 @@
package util
import (
"bytes"
"fmt"
"os"
"regexp"
"strings"
"unicode"
"unicode/utf8"
"github.com/ooni/probe-cli/internal/colors"
)
@ -17,3 +22,86 @@ func Fatal(err error) {
fmt.Fprintf(os.Stderr, "\n %s %s\n\n", colors.Red("Error:"), err)
os.Exit(1)
}
// Finds the control character sequences (like colors)
var ctrlFinder = regexp.MustCompile("\x1b\x5b[0-9]+\x6d")
func EscapeAwareRuneCountInString(s string) int {
n := utf8.RuneCountInString(s)
for _, sm := range ctrlFinder.FindAllString(s, -1) {
n -= utf8.RuneCountInString(sm)
}
return n
}
func RightPad(str string, length int) string {
return str + strings.Repeat(" ", length-EscapeAwareRuneCountInString(str))
}
// WrapString wraps the given string within lim width in characters.
//
// Wrapping is currently naive and only happens at white-space. A future
// version of the library will implement smarter wrapping. This means that
// pathological cases can dramatically reach past the limit, such as a very
// long word.
// This is taken from: https://github.com/mitchellh/go-wordwrap/tree/f253961a26562056904822f2a52d4692347db1bd
func WrapString(s string, lim uint) string {
// Initialize a buffer with a slightly larger size to account for breaks
init := make([]byte, 0, len(s))
buf := bytes.NewBuffer(init)
var current uint
var wordBuf, spaceBuf bytes.Buffer
for _, char := range s {
if char == '\n' {
if wordBuf.Len() == 0 {
if current+uint(spaceBuf.Len()) > lim {
current = 0
} else {
current += uint(spaceBuf.Len())
spaceBuf.WriteTo(buf)
}
spaceBuf.Reset()
} else {
current += uint(spaceBuf.Len() + wordBuf.Len())
spaceBuf.WriteTo(buf)
spaceBuf.Reset()
wordBuf.WriteTo(buf)
wordBuf.Reset()
}
buf.WriteRune(char)
current = 0
} else if unicode.IsSpace(char) {
if spaceBuf.Len() == 0 || wordBuf.Len() > 0 {
current += uint(spaceBuf.Len() + wordBuf.Len())
spaceBuf.WriteTo(buf)
spaceBuf.Reset()
wordBuf.WriteTo(buf)
wordBuf.Reset()
}
spaceBuf.WriteRune(char)
} else {
wordBuf.WriteRune(char)
if current+uint(spaceBuf.Len()+wordBuf.Len()) > lim && uint(wordBuf.Len()) < lim {
buf.WriteRune('\n')
current = 0
spaceBuf.Reset()
}
}
}
if wordBuf.Len() == 0 {
if current+uint(spaceBuf.Len()) <= lim {
spaceBuf.WriteTo(buf)
}
} else {
spaceBuf.WriteTo(buf)
wordBuf.WriteTo(buf)
}
return buf.String()
}