{"id":13366756,"url":"https://github.com/oklahomer/Go-sarah","last_synced_at":"2025-03-12T18:31:23.799Z","repository":{"id":12856951,"uuid":"72982532","full_name":"oklahomer/go-sarah","owner":"oklahomer","description":"Simple yet customizable bot framework written in Go.","archived":false,"fork":false,"pushed_at":"2024-12-01T06:01:28.000Z","size":4997,"stargazers_count":260,"open_issues_count":0,"forks_count":16,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-12T01:11:13.439Z","etag":null,"topics":["bot","bot-framework","gitter","gitter-bot","go","slack","slack-bot","slackbot","xmpp","xmpp-client"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/oklahomer/go-sarah/v4","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oklahomer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-06T10:04:43.000Z","updated_at":"2025-01-31T11:00:05.000Z","dependencies_parsed_at":"2024-01-08T14:30:48.353Z","dependency_job_id":"0f013d27-1526-431a-9cbb-d2593b258b4a","html_url":"https://github.com/oklahomer/go-sarah","commit_stats":{"total_commits":433,"total_committers":4,"mean_commits":108.25,"dds":0.006928406466512715,"last_synced_commit":"c74b30a90135172226da07a206d8b9472bcc407c"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oklahomer%2Fgo-sarah","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oklahomer%2Fgo-sarah/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oklahomer%2Fgo-sarah/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oklahomer%2Fgo-sarah/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oklahomer","download_url":"https://codeload.github.com/oklahomer/go-sarah/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243271290,"owners_count":20264431,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","bot-framework","gitter","gitter-bot","go","slack","slack-bot","slackbot","xmpp","xmpp-client"],"created_at":"2024-07-30T00:01:31.115Z","updated_at":"2025-03-12T18:31:23.758Z","avatar_url":"https://github.com/oklahomer.png","language":"Go","funding_links":[],"categories":["杂项","雜項"],"sub_categories":["高级控制台界面","高級控制台界面"],"readme":"[![Go Reference](https://pkg.go.dev/badge/github.com/oklahomer/go-sarah/v4.svg)](https://pkg.go.dev/github.com/oklahomer/go-sarah/v4)\n[![Go Report Card](https://goreportcard.com/badge/github.com/oklahomer/go-sarah/v4)](https://goreportcard.com/report/github.com/oklahomer/go-sarah/v4)\n![CI Status](https://github.com/oklahomer/go-sarah/actions/workflows/ci.yml/badge.svg?branch=master)\n[![Coverage Status](https://coveralls.io/repos/github/oklahomer/go-sarah/badge.svg?branch=master)](https://coveralls.io/github/oklahomer/go-sarah?branch=master)\n[![Maintainability](https://api.codeclimate.com/v1/badges/a2f0df359bec1552b28f/maintainability)](https://codeclimate.com/github/oklahomer/go-sarah/maintainability)\n[![Join the chat at https://gitter.im/go-sarah-dev/community](https://badges.gitter.im/go-sarah-dev/community.svg)](https://gitter.im/go-sarah-dev/community?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n# Introduction\nSarah is a general-purpose bot framework named after the author's firstborn daughter.\n\nThis comes with a unique feature called \"stateful command\" as well as some basic features such as commands and scheduled tasks.\nIn addition to those fundamental features, this project provides rich life cycle management including _**[live configuration update](https://github.com/oklahomer/go-sarah/wiki/Live-Configuration-Update)**_, _**[customizable alerting mechanism](https://github.com/oklahomer/go-sarah/wiki/Alerter)**_, _**automated [command](https://github.com/oklahomer/go-sarah/wiki/CommandPropsBuilder)\\/[task](https://github.com/oklahomer/go-sarah/wiki/ScheduledTaskPropsBuilder) (re)building**_, and _**[panic-proofed concurrent command/task execution](https://github.com/oklahomer/go-sarah/wiki/Worker)**_.\n\nSuch features are achieved with a composition of fine-grained components.\nEach component has its own interface and a default implementation, so developers are free to customize their bot experience by replacing the default implementation for a particular component with their own implementation.\nThanks to such segmentalized lifecycle management architecture, the [adapter component](https://github.com/oklahomer/go-sarah/wiki/Default-Bot-and-Adapter) to interact with each chat service has fewer responsibilities compared to other bot frameworks;\nAn adapter developer may focus on implementing the protocol to interact with the corresponding chat service.\nTo take a look at those components and their relations, see [Components](https://github.com/oklahomer/go-sarah/wiki/Components).\n\n# IMPORTANT NOTICE\n## v4 Release\nThis is the fourth major version of `go-sarah`, which involves some architectural changes:\n- `sarah.NewBot` now returns a single value: `sarah.Bot`\n- Utility packages including logger, retry, and worker are now hosted by `github.com/oklahomer/go-kasumi`\n\n## v3 Release\nThis is the third major version of `go-sarah`, which introduces the Slack adapter's improvement to support both RTM and Events API.\nBreaking interface changes for the Slack adapter was inevitable and that is the sole reason for this major version up.\nOther than that, this does not include any breaking change.\nSee [Migrating from v2.x to v3.x](https://github.com/oklahomer/go-sarah/wiki/Migrating-from-v2.x-to-v3.x) for details.\n\n## v2 Release\nThe second major version introduced some breaking changes to `go-sarah`.\nThis version still supports and maintains all functionalities, while better interfaces for easier integration are introduced.\nSee [Migrating from v1.x to v2.x](https://github.com/oklahomer/go-sarah/wiki/Migrating-from-v1.x-to-v2.x) to migrate from the older version.\n\n# Supported Chat Services/Protocols\nAlthough a developer may implement `sarah.Adapter` to integrate with the desired chat service,\nsome adapters are provided as reference implementations:\n- [Slack](https://github.com/oklahomer/go-sarah/tree/master/slack)\n- [Gitter](https://github.com/oklahomer/go-sarah/tree/master/gitter)\n- [XMPP](https://github.com/oklahomer/go-sarah-xmpp)\n- [LINE](https://github.com/oklahomer/go-sarah-line)\n\n# At a Glance\n## General Command Execution\n![hello world](/doc/img/hello.png)\n\nAbove is a general use of `go-sarah`.\nRegistered commands are checked against the user input and the matching one is executed;\nwhen a user inputs \".hello,\" hello command is executed and a message \"Hello, 世界\" is returned.\n\n## Stateful Command Execution\nThe below image depicts how a command with a user's **conversational context** works.\nThe idea and implementation of \"user's conversational context\" is `go-sarah`'s signature feature that makes a bot command \"**state-aware**.\"\n\n![](/doc/img/todo_captioned.png)\n\nThe above example is a good way to let a user input a series of arguments in a conversational manner.\nBelow is another example that uses a stateful command to entertain the user.\n\n![](/doc/img/guess_captioned.png)\n\n## Example Code\nFollowing is the minimal code that implements such general command and stateful command introduced above.\nIn this example, two ways to implement [`sarah.Command`](https://github.com/oklahomer/go-sarah/wiki/Command) are shown.\nOne simply implements `sarah.Command` interface; while another uses `sarah.CommandPropsBuilder` for lazy construction.\nDetailed benefits of using `sarah.CommandPropsBuilder` and `sarah.CommandProps` are described at its wiki page, [CommandPropsBuilder](https://github.com/oklahomer/go-sarah/wiki/CommandPropsBuilder).\n\nFor more practical examples, see [./examples](https://github.com/oklahomer/go-sarah/tree/master/examples).\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/oklahomer/go-sarah/v4\"\n\t\"github.com/oklahomer/go-sarah/v4/slack\"\n\t\n\t\"os\"\n\t\"os/signal\"\n\t\"syscall\"\n\t\n\t// Below packages register commands in their init().\n\t// Importing with a blank identifier will do the magic.\n\t_ \"guess\"\n\t_ \"hello\"\n)\n\nfunc main() {\n\t// Set up Slack adapter\n\tsetupSlack()\n\t\n\t// Prepare Sarah's core context.\n\tctx, cancel := context.WithCancel(context.Background())\n\n\t// Run\n\tconfig := sarah.NewConfig()\n\terr := sarah.Run(ctx, config)\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"failed to run: %s\", err.Error()))\n\t}\n\t\n\t// Stop when a signal is sent.\n\tc := make(chan os.Signal, 1)\n   \tsignal.Notify(c, syscall.SIGTERM)\n   \tselect {\n   \tcase \u003c-c:\n   \t\tcancel()\n   \n   \t}\n}\n\nfunc setupSlack() {\n\t// Set up slack adapter.\n\tslackConfig := slack.NewConfig()\n\tslackConfig.Token = \"REPLACE THIS\"\n\tadapter, err := slack.NewAdapter(slackConfig, slack.WithRTMPayloadHandler(slack.DefaultRTMPayloadHandler))\n\tif err != nil {\n\t\tpanic(fmt.Errorf(\"faileld to setup Slack Adapter: %s\", err.Error()))\n\t}\n\n\t// Set up an optional storage so conversational contexts can be stored.\n\tcacheConfig := sarah.NewCacheConfig()\n\tstorage := sarah.NewUserContextStorage(cacheConfig)\n\n\t// Set up a bot with Slack adapter and a default storage.\n\tbot := sarah.NewBot(adapter, sarah.BotWithStorage(storage))\n\t\n\tsarah.RegisterBot(bot)\n}\n```\n\n---\n\n```go\npackage guess\n\nimport (\n\t\"context\"\n\t\"github.com/oklahomer/go-sarah/v4\"\n\t\"github.com/oklahomer/go-sarah/v4/slack\"\n\t\"math/rand\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nfunc init() {\n\tsarah.RegisterCommandProps(props)\n}\n\nvar props = sarah.NewCommandPropsBuilder().\n\tBotType(slack.SLACK).\n\tIdentifier(\"guess\").\n\tInstruction(\"Input .guess to start a game.\").\n\tMatchFunc(func(input sarah.Input) bool {\n\t\treturn strings.HasPrefix(strings.TrimSpace(input.Message()), \".guess\")\n\t}).\n\tFunc(func(ctx context.Context, input sarah.Input) (*sarah.CommandResponse, error) {\n\t\t// Generate an answer value at the very beginning.\n\t\trand.Seed(time.Now().UnixNano())\n\t\tanswer := rand.Intn(10)\n\n\t\t// Let a user guess the right answer.\n\t\treturn slack.NewResponse(input, \"Input number.\", slack.RespWithNext(func(c context.Context, i sarah.Input) (*sarah.CommandResponse, error){\n\t\t\treturn guessFunc(c, i, answer)\n\t\t}))\n\t}).\n\tMustBuild()\n\nfunc guessFunc(_ context.Context, input sarah.Input, answer int) (*sarah.CommandResponse, error) {\n\t// For handiness, create a function that recursively calls guessFunc until the user input the right answer.\n\tretry := func(c context.Context, i sarah.Input) (*sarah.CommandResponse, error) {\n\t\treturn guessFunc(c, i, answer)\n\t}\n\n\t// See if the user inputs a valid number.\n\tguess, err := strconv.Atoi(strings.TrimSpace(input.Message()))\n\tif err != nil {\n\t\treturn slack.NewResponse(input, \"Invalid input format.\", slack.RespWithNext(retry))\n\t}\n\n\t// If the guess is right, tell the user and finish the current user context.\n\t// Otherwise, let the user input the next guess with bit of a hint.\n\tif guess == answer {\n\t\treturn slack.NewResponse(input, \"Correct!\")\n\t} else if guess \u003e answer {\n\t\treturn slack.NewResponse(input, \"Smaller!\", slack.RespWithNext(retry))\n\t} else {\n\t\treturn slack.NewResponse(input, \"Bigger!\", slack.RespWithNext(retry))\n\t}\n}\n```\n\n---\n\n```go\npackage hello\n\nimport (\n\t\"context\"\n\t\"github.com/oklahomer/go-sarah/v4\"\n\t\"github.com/oklahomer/go-sarah/v4/slack\"\n\t\"strings\"\n)\n\nfunc init() {\n    sarah.RegisterCommand(slack.SLACK, \u0026command{})\t\n}\n\ntype command struct {\n}\n\nvar _ sarah.Command = (*command)(nil)\n\nfunc (hello *command) Identifier() string {\n\treturn \"hello\"\n}\n\nfunc (hello *command) Execute(_ context.Context, i sarah.Input) (*sarah.CommandResponse, error) {\n\treturn slack.NewResponse(i, \"Hello!\")\n}\n\nfunc (hello *command) Instruction(input *sarah.HelpInput) string {\n\tif 12 \u003c input.SentAt().Hour() {\n\t\t// This command is only active in the morning.\n\t\t// Do not show instruction in the afternoon.\n\t\treturn \"\"\n\t}\n\treturn \"Input .hello to greet\"\n}\n\nfunc (hello *command) Match(input sarah.Input) bool {\n\treturn strings.TrimSpace(input.Message()) == \".hello\"\n}\n\n```\n\n# Supported Golang Versions\nOfficial [Release Policy](https://golang.org/doc/devel/release.html#policy) says \"each major Go release is supported\nuntil there are two newer major releases.\" Following this policy would help this project enjoy the improvements\nintroduced in the later versions. However, not all projects can immediately switch to a newer environment. \nMigration could especially be difficult when this project cuts off the older version's support right after a new major Go\nrelease.\n\nAs a transition period, this project includes support for one older version than the Go project does.\nSuch a version is guaranteed to be listed in [.travis.ci](https://github.com/oklahomer/go-sarah/blob/master/.travis.yml).\nIn other words, new features/interfaces introduced in 1.10 can be used in this project only after 1.12 is out.\n\n# Further Readings\n- [Project wiki](https://github.com/oklahomer/go-sarah/wiki)\n- [GoDoc](https://pkg.go.dev/github.com/oklahomer/go-sarah/v4)\n- [Example codes](https://github.com/oklahomer/go-sarah/tree/master/examples)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foklahomer%2FGo-sarah","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foklahomer%2FGo-sarah","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foklahomer%2FGo-sarah/lists"}