Browse Source

Add bot code

master v0.0.1-pre
Sam Hofius 5 years ago
parent
commit
44cfbb49b9
  1. 10
      go.mod
  2. 20
      go.sum
  3. 281
      main.go

10
go.mod

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
module git.hugfreevikings.wtf/bots/macro
go 1.15
require (
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
github.com/kf5grd/keybasebot v1.7.0
github.com/urfave/cli/v2 v2.3.0
samhofi.us/x/keybase/v2 v2.1.1
)

20
go.sum

@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/kf5grd/keybasebot v1.7.0 h1:8Jl6RSYEIyzniZ7UFyH/fjTlsM0tZHTCZ5F8bq8JTDA=
github.com/kf5grd/keybasebot v1.7.0/go.mod h1:8T07cWZZrl2G6hTRsL9x2SBwaH8gEZocF9NRknSU3dY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
samhofi.us/x/keybase/v2 v2.0.8/go.mod h1:lJivwhzMSV+WUg+XUbatszStjjFVcuLGl+xcQpqQ5GQ=
samhofi.us/x/keybase/v2 v2.1.1 h1:XPWrmdbJCrNcsW3sRuR6WuALYOZt7O+av0My6YoehqE=
samhofi.us/x/keybase/v2 v2.1.1/go.mod h1:lJivwhzMSV+WUg+XUbatszStjjFVcuLGl+xcQpqQ5GQ=

281
main.go

@ -0,0 +1,281 @@ @@ -0,0 +1,281 @@
// This is a very simple bot that has 2 commands: set, and get. The set command sets a
// string variable named "message" in the Meta store, and the get command retrieves that
// variable and sends it to the user in a chat message.
package main
import (
"fmt"
"os"
"strings"
"github.com/google/shlex"
"github.com/urfave/cli/v2"
bot "github.com/kf5grd/keybasebot"
"github.com/kf5grd/keybasebot/pkg/util"
"samhofi.us/x/keybase/v2"
"samhofi.us/x/keybase/v2/types/chat1"
)
const (
back = "`"
backs = "```"
)
type Command struct {
Name string
Args []string
Response string
}
// Current version
var version string
// Exit code on failure
const exitFail = 1
func main() {
app := cli.App{
Name: "funcy",
Usage: "Go interpreter for Keybase",
Version: version,
Writer: os.Stdout,
EnableBashCompletion: true,
Action: run,
Flags: []cli.Flag{
&cli.PathFlag{
Name: "home",
Aliases: []string{"H"},
Usage: "Keybase Home Folder",
EnvVars: []string{"MACROBOT_HOME"},
},
&cli.StringFlag{
Name: "bot-owner",
Usage: "Username of the bot owner",
EnvVars: []string{"MACROBOT_OWNER"},
},
&cli.StringFlag{
Name: "log-conv",
Usage: "Conversation ID to send logs to",
EnvVars: []string{"MACROBOT_LOGCONV"},
},
&cli.BoolFlag{
Name: "debug",
Aliases: []string{"d"},
Usage: "Enable extra log output",
EnvVars: []string{"MACROBOT_DEBUG"},
},
&cli.BoolFlag{
Name: "json",
Aliases: []string{"j"},
Usage: "Output log in JSON format",
EnvVars: []string{"MACROBOT_JSON"},
},
},
}
if err := app.Run(os.Args); err != nil {
os.Exit(exitFail)
}
}
func run(c *cli.Context) error {
// setup bot
b := bot.New("", keybase.SetHomePath(c.Path("home")))
b.Debug = c.Bool("debug")
b.JSON = c.Bool("json")
b.LogConv = chat1.ConvIDStr(c.String("log-conv"))
b.LogWriter = os.Stdout
// register the bot's commands
b.Commands = append(b.Commands,
bot.BotCommand{
Name: "CreateCommand",
Ad: adCreateCommand(),
Run: bot.Adapt(cmdCreateCommand,
bot.MessageType("text"),
bot.CommandPrefix("!create"),
),
},
)
// run bot
b.Run()
return nil
}
func adCreateCommand() *chat1.UserBotCommandInput {
var createDesc = fmt.Sprintf(`Commands should be in the following format, and should be followed by the response:
%s
<command>(<arg name 1>, <arg name 2>, ... <arg name n>)
%s
You can reference the arguments in the response by prepending the argument name with a %s$%s
Here's an example command that can be used to tell the bot to say "Hello" to a specific person:
%s
!create sayhi(user) Hello, @$user!
%s
`, backs, backs, back, back, backs, backs)
return &chat1.UserBotCommandInput{
Name: "create",
Usage: "<command> <response>",
Description: "Create a new command",
ExtendedDescription: &chat1.UserBotExtendedDescription{
Title: "Create A New Command",
DesktopBody: createDesc,
MobileBody: createDesc,
},
}
}
func cmdCreateCommand(m chat1.MsgSummary, b *bot.Bot) (bool, error) {
body := m.Content.Text.Body
fields := strings.Fields(body)
if len(fields) < 3 {
return true, fmt.Errorf("Must provide both command and response")
}
command, err := parseCommandDef(strings.Replace(body, "!create", "", 1))
if err != nil {
return true, err
}
metaIndex := fmt.Sprintf("%s-%s", m.ConvID, command.Name)
if _, ok := b.Meta[metaIndex]; ok {
return true, fmt.Errorf("That command already exists")
}
responsePrefix := strings.Fields(command.Response)[0]
if strings.HasPrefix("/", responsePrefix) && !util.StringInSlice(responsePrefix, []string{"/giphy", "/flip", "/me", "/shrug"}) {
return true, fmt.Errorf("The only `/` commands allowed are `/giphy`, `/flip`, `/me`, and `/shrug`. Nice try though!")
}
for _, c := range b.Commands {
if command.Name == strings.ToLower(c.Ad.Name) && c.AdType != "conv" {
return true, fmt.Errorf("You cannot override an existing public command")
}
}
var usageString string
if len(command.Args) > 0 {
for _, arg := range command.Args {
usageString = fmt.Sprintf("%s <%s>", usageString, arg)
}
usageString = strings.TrimSpace(usageString)
}
b.Meta[metaIndex] = command.Response
b.Commands = append(b.Commands, bot.BotCommand{
Name: fmt.Sprintf("Custom [%s]", metaIndex),
AdType: "conv",
AdConv: m.ConvID,
Ad: &chat1.UserBotCommandInput{
Name: command.Name,
Usage: usageString,
Description: command.Response,
},
Run: bot.Adapt(cmdCommand(command),
bot.MessageType("text"),
bot.CommandPrefix("!"+command.Name),
),
})
b.AdvertiseCommands()
b.KB.ReactByConvID(m.ConvID, m.Id, ":heavy_check_mark:")
return true, nil
}
func cmdCommand(cmd Command) bot.BotAction {
return func(m chat1.MsgSummary, b *bot.Bot) (bool, error) {
argText := strings.Replace(m.Content.Text.Body, "!", "", 1)
argText = strings.TrimSpace(argText)
a, _ := shlex.Split(argText)
var receivedArgs []string
command := a[0]
if len(a) > 1 {
receivedArgs = a[1:]
}
if cmd.Name != command {
return false, nil
}
metaIndex := fmt.Sprintf("%s-%s", m.ConvID, cmd.Name)
response, ok := b.Meta[metaIndex]
if !ok {
return false, nil
}
if len(receivedArgs) != len(cmd.Args) {
return true, fmt.Errorf("Incorrect number of arguments supplied")
}
b.Logger.Debug("cmd.Name: %s, cmd.Args: %s, argText: %s, receivedArgs: %s", cmd.Name, cmd.Args, argText, receivedArgs)
cleanResponse := response.(string)
cleanResponse = strings.ReplaceAll(cleanResponse, "%", "%%")
cleanResponse = strings.ReplaceAll(cleanResponse, "$USER", m.Sender.Username)
for argIndex, arg := range cmd.Args {
cleanResponse = strings.ReplaceAll(cleanResponse, "$"+strings.TrimSpace(arg), receivedArgs[argIndex])
}
b.KB.SendMessageByConvID(m.ConvID, cleanResponse)
return true, nil
}
}
func parseCommandDef(def string) (Command, error) {
// def: funcName(arg1, arg2) response goes here
def = strings.TrimSpace(def)
argStart := strings.Index(def, "(") + 1
argEnd := strings.Index(def, ")")
if argStart == -1 || argEnd == -1 {
return Command{}, fmt.Errorf("Invalid command definition")
}
name := strings.ToLower(strings.TrimSpace(def[0 : argStart-1]))
args := strings.Split(def[argStart:argEnd], ",")
args = trimArgs(args)
response := strings.TrimSpace(def[argEnd+1:])
if len(args) > 0 {
missing := make([]string, 0)
for _, arg := range args {
if !strings.Contains(response, "$"+arg) {
missing = append(missing, arg)
}
}
if len(missing) > 0 {
return Command{}, fmt.Errorf("The following variable(s) were set in the command, but not referenced in the response: %s", strings.Join(missing, ", "))
}
}
ret := Command{
Name: name,
Args: args,
Response: response,
}
return ret, nil
}
func trimArgs(args []string) []string {
newArgs := make([]string, 0)
for _, arg := range args {
trimmed := strings.TrimSpace(arg)
if trimmed != "" {
newArgs = append(newArgs, trimmed)
}
}
return newArgs
}
Loading…
Cancel
Save