├── .gitignore
├── env_discord.sample
├── pkg
├── game
│ ├── game_test.go
│ └── game.go
├── glide.lock
└── moves
│ └── moves.go
├── img
└── example.png
├── docs
├── img
│ ├── register_final.png
│ ├── register_part_1.png
│ ├── register_part_2.png
│ ├── register_part_3.png
│ ├── register_part_4.png
│ └── register_authorize.png
├── game-playbook.md
├── register.md
└── deploying.md
├── bin
├── docker_quickstart
└── docker_custom
├── vendor
└── github.com
│ ├── matryer
│ └── moq
│ │ ├── moq-logo.png
│ │ ├── preview.png
│ │ ├── pkg
│ │ └── moq
│ │ │ ├── testpackages
│ │ │ ├── imports
│ │ │ │ ├── one
│ │ │ │ │ └── one.go
│ │ │ │ └── two
│ │ │ │ │ └── two.go
│ │ │ ├── emptyinterface
│ │ │ │ └── empty.go
│ │ │ ├── variadic
│ │ │ │ └── greeter.go
│ │ │ ├── vendoring
│ │ │ │ ├── user
│ │ │ │ │ └── user.go
│ │ │ │ └── vendor
│ │ │ │ │ └── github.com
│ │ │ │ │ └── matryer
│ │ │ │ │ └── somerepo
│ │ │ │ │ └── code.go
│ │ │ ├── channels
│ │ │ │ └── example.go
│ │ │ ├── dotimport
│ │ │ │ └── service.go
│ │ │ └── example
│ │ │ │ └── example.go
│ │ │ ├── template.go
│ │ │ ├── importer.go
│ │ │ ├── moq.go
│ │ │ └── moq_test.go
│ │ ├── moq-logo-small.png
│ │ ├── .travis.yml
│ │ ├── generate
│ │ └── generate.go
│ │ ├── .gitignore
│ │ ├── example
│ │ ├── example.go
│ │ └── mockpersonstore_test.go
│ │ ├── LICENSE
│ │ ├── main.go
│ │ └── README.md
│ └── bwmarrin
│ └── discordgo
│ ├── docs
│ ├── img
│ │ └── discordgo.png
│ ├── index.md
│ └── GettingStarted.md
│ ├── examples
│ ├── airhorn
│ │ ├── airhorn.dca
│ │ ├── README.md
│ │ └── main.go
│ ├── README.md
│ ├── mytoken
│ │ ├── main.go
│ │ └── README.md
│ ├── pingpong
│ │ ├── README.md
│ │ └── main.go
│ ├── avatar
│ │ ├── README.md
│ │ └── main.go
│ └── appmaker
│ │ ├── README.md
│ │ └── main.go
│ ├── user_test.go
│ ├── .travis.yml
│ ├── mkdocs.yml
│ ├── types_test.go
│ ├── message_test.go
│ ├── user.go
│ ├── LICENSE
│ ├── oauth2_test.go
│ ├── types.go
│ ├── logging.go
│ ├── ratelimit_test.go
│ ├── tools
│ └── cmd
│ │ └── eventhandlers
│ │ └── main.go
│ ├── oauth2.go
│ ├── discord.go
│ ├── ratelimit.go
│ ├── README.md
│ ├── restapi_test.go
│ ├── events.go
│ ├── event.go
│ ├── discord_test.go
│ ├── message.go
│ └── endpoints.go
├── data
├── game
│ ├── fellowship
│ │ ├── game.json
│ │ └── basic.json
│ └── apocalypse_world
│ │ ├── gunlugger.json
│ │ ├── angel.json
│ │ ├── game.json
│ │ ├── chopper.json
│ │ ├── driver.json
│ │ ├── battlebabe.json
│ │ ├── brainer.json
│ │ └── basic.json
└── example
│ └── game
│ └── example_game
│ ├── basic.json
│ ├── game.json
│ └── simple_playbook.json
├── go.mod
├── CONTRIBUTING.md
├── Dockerfile
├── playbooks
├── gunlugger.json
├── hardholder.json
├── chopper.json
├── driver.json
├── battlebabe.json
├── maestrod.json
├── hocus.json
├── brainer.json
├── savvyhead.json
└── angel.json
├── go.sum
├── README.md
├── static
└── css
│ └── main.css
└── main.go
/.gitignore:
--------------------------------------------------------------------------------
1 | .env*
2 | tags
3 |
--------------------------------------------------------------------------------
/env_discord.sample:
--------------------------------------------------------------------------------
1 | export DISCORD_TOKEN=""
2 | export CLIENT_ID=""
3 |
--------------------------------------------------------------------------------
/pkg/game/game_test.go:
--------------------------------------------------------------------------------
1 | package game
2 |
3 | import (
4 | "testing"
5 | )
6 |
--------------------------------------------------------------------------------
/img/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/img/example.png
--------------------------------------------------------------------------------
/docs/img/register_final.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/docs/img/register_final.png
--------------------------------------------------------------------------------
/docs/img/register_part_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/docs/img/register_part_1.png
--------------------------------------------------------------------------------
/docs/img/register_part_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/docs/img/register_part_2.png
--------------------------------------------------------------------------------
/docs/img/register_part_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/docs/img/register_part_3.png
--------------------------------------------------------------------------------
/docs/img/register_part_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/docs/img/register_part_4.png
--------------------------------------------------------------------------------
/docs/img/register_authorize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/docs/img/register_authorize.png
--------------------------------------------------------------------------------
/bin/docker_quickstart:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker run --name apocalyptica --rm --env-file -p8080:8080 ../.env -d gamefiend/apocalyptica
3 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/moq-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/vendor/github.com/matryer/moq/moq-logo.png
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/vendor/github.com/matryer/moq/preview.png
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/imports/one/one.go:
--------------------------------------------------------------------------------
1 | package one
2 |
3 | // Thing is just a thing.
4 | type Thing struct{}
5 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/moq-logo-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/vendor/github.com/matryer/moq/moq-logo-small.png
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/emptyinterface/empty.go:
--------------------------------------------------------------------------------
1 | package emptyinterface
2 |
3 | // Empty is an empty interface
4 | type Empty interface{}
5 |
--------------------------------------------------------------------------------
/bin/docker_custom:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t apocalyptica-custom \
3 | && docker run --name apocalyptica --rm --env-file -p8080:8080 ../.env -d apocalyptica-cu
4 | stom
5 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/docs/img/discordgo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/vendor/github.com/bwmarrin/discordgo/docs/img/discordgo.png
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/airhorn/airhorn.dca:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamefiend/apocalyptica/HEAD/vendor/github.com/bwmarrin/discordgo/examples/airhorn/airhorn.dca
--------------------------------------------------------------------------------
/data/game/fellowship/game.json:
--------------------------------------------------------------------------------
1 | {
2 | "Game": {
3 | "Name": "Fellowship",
4 | "Author": "Jacob Randolph",
5 | "Description": "Narrative fantasy adventure",
6 | "Playbooks": [
7 | ]
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/variadic/greeter.go:
--------------------------------------------------------------------------------
1 | package variadic
2 |
3 | import "context"
4 |
5 | // Greeter greets people.
6 | type Greeter interface {
7 | Greet(ctx context.Context, names ...string) string
8 | }
9 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/vendoring/user/user.go:
--------------------------------------------------------------------------------
1 | package user
2 |
3 | import "github.com/matryer/somerepo"
4 |
5 | // Service does something good with computers.
6 | type Service interface {
7 | DoSomething(somerepo.SomeType) error
8 | }
9 |
--------------------------------------------------------------------------------
/data/example/game/example_game/basic.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!doit",
4 | "Full": "Do it!",
5 | "Miss": "You missed!",
6 | "Hit": "You get to do most of what you want",
7 | "Crit": "You do everything you want and then some."
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/gamefiend/apocalyptica
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/bwmarrin/discordgo v0.16.0
7 | github.com/gorilla/websocket v1.2.1-0.20170708072655-462d5c5828f6 // indirect
8 | golang.org/x/crypto v0.0.0-20170706152725-a48ac81e47fd // indirect
9 | )
10 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/channels/example.go:
--------------------------------------------------------------------------------
1 | package channels
2 |
3 | // Queue is a type to be sent down a channel.
4 | type Queue []string
5 |
6 | // Queuer provides a channel example.
7 | type Queuer interface {
8 | Sub(topic string) (<-chan Queue, error)
9 | }
10 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
3 | sudo: false
4 |
5 | go:
6 | - 1.7.x
7 | - 1.8.x
8 | - tip
9 |
10 | before_install:
11 | - go get github.com/golang/lint/golint
12 |
13 | before_script:
14 | - go vet ./...
15 | - golint ./...
16 |
17 | script:
18 | - go test -v ./...
19 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/imports/two/two.go:
--------------------------------------------------------------------------------
1 | package two
2 |
3 | import (
4 | "github.com/matryer/moq/pkg/moq/testpackages/imports/one"
5 | )
6 |
7 | // DoSomething does something.
8 | type DoSomething interface {
9 | Do(thing one.Thing) error
10 | Another(thing one.Thing) error
11 | }
12 |
--------------------------------------------------------------------------------
/pkg/glide.lock:
--------------------------------------------------------------------------------
1 | hash: 3c058d1701f5af9b832c624e8490e19fcc8009c3fcd11902a0034d69f70abb51
2 | updated: 2017-10-03T21:34:45.501143-04:00
3 | imports:
4 | - name: github.com/bwmarrin/discordgo
5 | version: 2fda7ce223a66a5b70b66987c22c3c94d022ee66
6 | - name: github.com/matryer/moq
7 | version: cc6b1a47faa71a50960a3b75e409d5ffaa5d6df4
8 | testImports: []
9 |
--------------------------------------------------------------------------------
/data/example/game/example_game/game.json:
--------------------------------------------------------------------------------
1 | {
2 | "Game":
3 | {
4 | "Name": "Example Game",
5 | "Author": "Example Person",
6 | "Description": "A game to serve as an example.",
7 | "Playbooks": [
8 | {
9 | "Name": "Simple Character",
10 | "Moves": "simple_playbook.json"
11 | }
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | Contributors welcome!
2 | ---
3 |
4 | If you want to contribute to the project, that's great!
5 |
6 | Right now, we aren't going to add any additional moves to basic.json (going to speak to AW's creator about that first), so please correct text but don't add moves!
7 |
8 | Please make an issue with proposals for change/fix before submitting a PR.
9 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/user_test.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import "testing"
4 |
5 | func TestUser(t *testing.T) {
6 | t.Parallel()
7 |
8 | user := &User{
9 | Username: "bob",
10 | Discriminator: "8192",
11 | }
12 |
13 | if user.String() != "bob#8192" {
14 | t.Errorf("user.String() == %v", user.String())
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM golang:1.8
2 | STOPSIGNAL SIGTERM
3 | LABEL maintainer="Quinn Murphy"
4 |
5 | WORKDIR /go/src/github.com/gamefiend/apocalyptica
6 |
7 | COPY . .
8 |
9 | RUN curl https://glide.sh/get | sh \
10 | && glide install \
11 | && glide up \
12 | && CGO_ENABLED=0 go-wrapper install
13 | EXPOSE 8080
14 | ENTRYPOINT ["go-wrapper","run"]
15 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/generate/generate.go:
--------------------------------------------------------------------------------
1 | package generate
2 |
3 | // In a terminal, run `go generate` in this directory to have
4 | // it generates the generated.go file.
5 |
6 | //go:generate moq -out generated.go . MyInterface
7 |
8 | // MyInterface is a test interface.
9 | type MyInterface interface {
10 | One() bool
11 | Two() int
12 | Three() string
13 | }
14 |
--------------------------------------------------------------------------------
/data/example/game/example_game/simple_playbook.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!simple",
4 | "Full": "Do Something Simple.",
5 | "Miss": "Your move is too simple and flops.",
6 | "Hit": "Your move is just simple enough to get things going.",
7 | "Crit": "Simplicity is just what we needed! You cut through the fog."
8 | }
9 | ]
10 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 | go:
3 | - 1.6
4 | - 1.7
5 | - 1.8
6 | install:
7 | - go get github.com/bwmarrin/discordgo
8 | - go get -v .
9 | - go get -v github.com/golang/lint/golint
10 | script:
11 | - diff <(gofmt -d .) <(echo -n)
12 | - go vet -x ./...
13 | - golint ./...
14 | - go test -v -race ./...
15 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/vendoring/vendor/github.com/matryer/somerepo/code.go:
--------------------------------------------------------------------------------
1 | // Package somerepo is a vendored package to test how moq deals with
2 | // packages in the vendor package.
3 | package somerepo
4 |
5 | // SomeType is just some old type.
6 | type SomeType struct {
7 | // Truth indicates whether true is true or not. Computers.
8 | Truth bool
9 | }
10 |
--------------------------------------------------------------------------------
/playbooks/gunlugger.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!fts",
4 | "Full": "Fuck This Shit",
5 | "Miss": "You're caught half in, and half out. Stuck in the breeze.",
6 | "Hit": "You can go or stay, but if you go it's gonna cost: leave something behind, or take something with you. The MC tells you what.",
7 | "Crit": "This shit, has been fucked. You're gone."
8 | },
9 | ]
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # DiscordGo Examples
4 |
5 | These examples demonstrate how to utilize DiscordGo.
6 |
7 | Please explore the individual folders and give them a try!
8 |
9 | **Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
10 | Discord chat channel for support.**
11 |
12 |
--------------------------------------------------------------------------------
/data/game/apocalypse_world/gunlugger.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!fts",
4 | "Full": "Fuck This Shit",
5 | "Miss": "You're caught half in, and half out. Stuck in the breeze.",
6 | "Hit": "You can go or stay, but if you go it's gonna cost: leave something behind, or take something with you. The MC tells you what.",
7 | "Crit": "This shit, has been fucked. You're gone."
8 | },
9 | ]
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | .vscode
26 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/dotimport/service.go:
--------------------------------------------------------------------------------
1 | // Package dotimport addresses issue 21.
2 | package dotimport
3 |
4 | //go:generate moq -out service_moq_test.go -pkg dotimport_test . Service
5 |
6 | // Service is the interface which should be mocked by moq
7 | type Service interface {
8 | User(ID string) (User, error)
9 | }
10 |
11 | // User is just a struct for testing
12 | type User struct {
13 | Name string
14 | }
15 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: DiscordGo
2 | site_author: Bruce Marriner
3 | site_url: http://bwmarrin.github.io/discordgo/
4 | repo_url: https://github.com/bwmarrin/discordgo
5 |
6 | dev_addr: 0.0.0.0:8000
7 | theme: yeti
8 |
9 | markdown_extensions:
10 | - smarty
11 | - toc:
12 | permalink: True
13 | - sane_lists
14 |
15 | pages:
16 | - 'Home': 'index.md'
17 | - 'Getting Started': 'GettingStarted.md'
18 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/testpackages/example/example.go:
--------------------------------------------------------------------------------
1 | package example
2 |
3 | import "context"
4 |
5 | // Person is a person.
6 | type Person struct {
7 | ID string
8 | Name string
9 | Company string
10 | Website string
11 | }
12 |
13 | // PersonStore stores people.
14 | type PersonStore interface {
15 | Get(ctx context.Context, id string) (*Person, error)
16 | Create(ctx context.Context, person *Person, confirm bool) error
17 | ClearCache(id string)
18 | }
19 |
--------------------------------------------------------------------------------
/data/game/apocalypse_world/angel.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Playbook": "Angel"
4 | },
5 | {
6 | "Name": "!healingtouch",
7 | "Full": "Healing Touch",
8 | "Miss": "You heal nothing. Both you and the person you lied to about healing have to openbrain except you've already missed the roll",
9 | "Hit": "You put some guts back, heal 1 segment but you gotta openbrain now and hope the Pyschic Maelstrom plays nice.",
10 | "Crit": "You put lots of guts back. heal 1 segment."
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/example/example.go:
--------------------------------------------------------------------------------
1 | package example
2 |
3 | import "context"
4 |
5 | //go:generate moq -out mockpersonstore_test.go . PersonStore
6 |
7 | // Person represents a real person.
8 | type Person struct {
9 | ID string
10 | Name string
11 | Company string
12 | Website string
13 | }
14 |
15 | // PersonStore provides access to Person objects.
16 | type PersonStore interface {
17 | Get(ctx context.Context, id string) (*Person, error)
18 | Create(ctx context.Context, person *Person, confirm bool) error
19 | }
20 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/types_test.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "testing"
5 | "time"
6 | )
7 |
8 | func TestTimestampParse(t *testing.T) {
9 | ts, err := Timestamp("2016-03-24T23:15:59.605000+00:00").Parse()
10 | if err != nil {
11 | t.Fatal(err)
12 | }
13 | if ts.Year() != 2016 || ts.Month() != time.March || ts.Day() != 24 {
14 | t.Error("Incorrect date")
15 | }
16 | if ts.Hour() != 23 || ts.Minute() != 15 || ts.Second() != 59 {
17 | t.Error("Incorrect time")
18 | }
19 |
20 | _, offset := ts.Zone()
21 | if offset != 0 {
22 | t.Error("Incorrect timezone")
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/bwmarrin/discordgo v0.16.0 h1:/HhaLf7VXwJe/zcN+i/tKIbhKa1Y9Xy0uFXHyiDm7TU=
2 | github.com/bwmarrin/discordgo v0.16.0/go.mod h1:5NIvFv5Z7HddYuXbuQegZ684DleQaCFqChP2iuBivJ8=
3 | github.com/gorilla/websocket v1.2.1-0.20170708072655-462d5c5828f6 h1:QuyU2q1n/YHzmBCRpKpnzhGsn3rX8iCggRXfHhaM2uY=
4 | github.com/gorilla/websocket v1.2.1-0.20170708072655-462d5c5828f6/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
5 | golang.org/x/crypto v0.0.0-20170706152725-a48ac81e47fd h1:JtQxzQbegepfaE8AMqkRsccfcZjW0A8qrg/0R2lc8Us=
6 | golang.org/x/crypto v0.0.0-20170706152725-a48ac81e47fd/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
7 |
--------------------------------------------------------------------------------
/playbooks/hardholder.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!leadership",
4 | "Full": "Leadership",
5 | "Miss": "They're going to do it, but you're going to hear about it later. ",
6 | "Hit": "They do what you command.",
7 | "Crit": "Battlefield voice makes them snap to; they do what you want and take +1forward doing it ma'amsirma'am."
8 | },
9 |
10 | {
11 | "Name": "!wealth",
12 | "Full": "Wealth",
13 | "Miss": "You don't have enough. What you want depends on your holding.",
14 | "Hit": "You have a surplus in one area, but a want in another",
15 | "Crit": "You have surplus at hand and available for needs of the session. They build a monument in your honor (not really, no)."
16 | },
17 | ]
--------------------------------------------------------------------------------
/data/game/apocalypse_world/game.json:
--------------------------------------------------------------------------------
1 | {
2 | "Game":
3 | {
4 | "Name": "Apocalypse World",
5 | "Description": "This is what we've got, yes. What are you going to make of it?",
6 | "Playbooks": [
7 | {
8 | "Name": "The Angel",
9 | "Moves": "angel.json"
10 | },
11 | {
12 | "Name": "The Battlebabe",
13 | "Moves": "battlebabe.json"
14 | },
15 | {
16 | "Name": "The Brainer",
17 | "Moves": "brainer.json"
18 | },
19 | { "Name": "The Chopper",
20 | "Moves": "chopper.json"
21 | },
22 | {
23 | "Name": "The Driver",
24 | "Moves": "driver.json"
25 | },
26 | {
27 | "Name": "The Gunlugger",
28 | "Moves": "gunlugger.json"
29 | }
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/mytoken/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 |
8 | "github.com/bwmarrin/discordgo"
9 | )
10 |
11 | // Variables used for command line parameters
12 | var (
13 | Email string
14 | Password string
15 | )
16 |
17 | func init() {
18 |
19 | flag.StringVar(&Email, "e", "", "Account Email")
20 | flag.StringVar(&Password, "p", "", "Account Password")
21 | flag.Parse()
22 |
23 | if Email == "" || Password == "" {
24 | flag.Usage()
25 | os.Exit(1)
26 | }
27 | }
28 |
29 | func main() {
30 |
31 | // Create a new Discord session using the provided login information.
32 | dg, err := discordgo.New(Email, Password)
33 | if err != nil {
34 | fmt.Println("error creating Discord session,", err)
35 | return
36 | }
37 |
38 | // Print out your token.
39 | fmt.Printf("Your Authentication Token is:\n\n%s\n", dg.Token)
40 | }
41 |
--------------------------------------------------------------------------------
/playbooks/chopper.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!packalpha",
4 | "Full": "Pack Alpha",
5 | "Miss": "Someone in your gang makes a bid, idle or serious, to replace you for alpha. Are you going to take that?",
6 | "Hit": "Choose 1. They do what you want, they don't fight back over it, you don't have to make an example of one of them.",
7 | "Crit": "Take everything. They do what you want, you don't get any lip, and you dn't have to make an example out of anyone."
8 | },
9 |
10 | {
11 | "Name": "!fuckingthieves",
12 | "Full": "Fucking Thieves",
13 | "Miss": "You know, you had that thing. Too bad some asswipe stole it from you.",
14 | "Hit": "One of you happens to have something pretty close, unless it's hi-tech then no dice.",
15 | "Crit": "One of you have the thing, or close enough that it doesn't matter."
16 | },
17 | ]
18 |
--------------------------------------------------------------------------------
/playbooks/driver.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!eyeonthedoor",
4 | "Full": "Eye On the Door",
5 | "Miss": "You're caught vulnerable, half in and half out",
6 | "Hit": "You can go, or stay, but if you go it will cost you: leave something behind or take something with you, the MC will tell you what.",
7 | "Crit": "You're gone. Puff of smoke, like real magic shit."
8 | },
9 |
10 | {
11 | "Name": "!reputation",
12 | "Full": "Reputation",
13 | "Miss": "They've heard of you all right. The MC will tell you what they heard. Probably nothing good cho'.",
14 | "Hit": "They've heard of you, and you say what they've heard; the MC will make them respond accordingly.",
15 | "Crit": "They've heard of you, and you say what they've heard; the MC will make them respond accordingly, and you get +1forward for dealing with them."
16 | },
17 | ]
18 |
--------------------------------------------------------------------------------
/data/game/apocalypse_world/chopper.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!packalpha",
4 | "Full": "Pack Alpha",
5 | "Miss": "Someone in your gang makes a bid, idle or serious, to replace you for alpha. Are you going to take that?",
6 | "Hit": "Choose 1. They do what you want, they don't fight back over it, you don't have to make an example of one of them.",
7 | "Crit": "Take everything. They do what you want, you don't get any lip, and you dn't have to make an example out of anyone."
8 | },
9 |
10 | {
11 | "Name": "!fuckingthieves",
12 | "Full": "Fucking Thieves",
13 | "Miss": "You know, you had that thing. Too bad some asswipe stole it from you.",
14 | "Hit": "One of you happens to have soemthing pretty close, unless it's hi-tech then no dice.",
15 | "Crit": "One of you have the thing, or close enough that it doesn't matter."
16 | },
17 | ]
--------------------------------------------------------------------------------
/playbooks/battlebabe.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!dangeroussexy",
4 | "Full": "Dangerous & Sexy",
5 | "Miss": "Your enemies identify you immediately as their foremost threat!",
6 | "Hit": "Hold 1. Spend 1 hold to: \n* make eye contact with an NPC present, who freezes or flinches and can't take action until you break it off.",
7 | "Crit": "Hold 2. Spend 1 hold to: \n* make eye contact with an NPC present, who freezes or flinches and can't take action until you break it off."
8 | },
9 |
10 | {
11 | "Name": "!visionsofdeath",
12 | "Full": "Visions of Death",
13 | "Miss": "You forsee your own death, and take -1 throughout the battle",
14 | "Hit": "Name one NPC who'll die OR one NPC who'll live. The MC will make your visions come true, if it's even remotely possible",
15 | "Crit": "Name one person who'll die OR one person who'll live"
16 | },
17 | ]
18 |
--------------------------------------------------------------------------------
/data/game/apocalypse_world/driver.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!eyeonthedoor",
4 | "Full": "Eye On the Door",
5 | "Miss": "You're caught vulnerable, half in and half out",
6 | "Hit": "You can go, or stay, but if you go it will cost you: leave something behind or take something with you, the MC will tell you what.",
7 | "Crit": "Your gone. Puff of smoke, like real magic shit."
8 | },
9 |
10 | {
11 | "Name": "!reputation",
12 | "Full": "Reputation",
13 | "Miss": "They've heard of you all right. The MC will tell you what they heard. Probably nothing good cho'.",
14 | "Hit": "They've heard of you, and you say what they've heard; the MC will make them respond accordingly.",
15 | "Crit": "They've heard of you, and you say what they've heard; the MC will make them respond accordingly, and you get +1forward for dealing with them."
16 | },
17 | ]
--------------------------------------------------------------------------------
/data/game/apocalypse_world/battlebabe.json:
--------------------------------------------------------------------------------
1 | [
2 | "Playbook": "The BattleBabe",
3 | {
4 | "Name": "!dangeroussexy",
5 | "Full": "Dangerous & Sexy",
6 | "Miss": "Your enemies identify you immediately as their foremost threat!",
7 | "Hit": "Hold 1. Spend 1 hold to: \n* make eye contact with an NPC present, who freezes or flinches and can't take action until you break it off.",
8 | "Crit": "Hold 2. Spend 1 hold to: \n* make eye contact with an NPC present, who freezes or flinches and can't take action until you break it off."
9 | },
10 |
11 | {
12 | "Name": "!visionsofdeath",
13 | "Full": "Visions of Death",
14 | "Miss": "You forsee your own death, and take -1 throughout the battle",
15 | "Hit": "Name one NPC who'll die OR one NPC who'll live. The MC will make your visions come true, if it's even remotely possible",
16 | "Crit": "Name one person who'll die OR one person who'll live"
17 | },
18 | ]
19 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Apocalyptica
2 | ---
3 |
4 | ## An Apocalypse World 2e Discord Bot.
5 |
6 | [Apocalypse World](http://apocalypse-world.com) is a great game by Vincent Baker. This bot makes playing the game online easier by providing a dice-roller that also outputs the details of a hit or miss:
7 |
8 | 
9 |
10 | ### Usage
11 | ```
12 | !moves - display supported moves (only the basic moves are included)
13 | !!help - display usage
14 |
15 | ! - rolls and displays move results.
16 | ```
17 |
18 |
19 | ### Use it Now
20 |
21 | Want to try an instance of Apocalyptica on your server? [Try it now](https://apocalyptica.social-fiction.net).
22 |
23 | ### Roll Your Own
24 |
25 | If you want to use your own server, you will first need to [register your app](docs/register.md), then look at the [different ways you can deploy](docs/deploying.md) to Apocalyptica.
26 |
27 | #### Adding your own moves.
28 |
29 | Add your own moves by adding [your own games and playbooks](docs/game-playbook.md)
30 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/message_test.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestContentWithMoreMentionsReplaced(t *testing.T) {
8 | s := &Session{StateEnabled: true, State: NewState()}
9 |
10 | user := &User{
11 | ID: "user",
12 | Username: "User Name",
13 | }
14 |
15 | s.StateEnabled = true
16 | s.State.GuildAdd(&Guild{ID: "guild"})
17 | s.State.RoleAdd("guild", &Role{
18 | ID: "role",
19 | Name: "Role Name",
20 | Mentionable: true,
21 | })
22 | s.State.MemberAdd(&Member{
23 | User: user,
24 | Nick: "User Nick",
25 | GuildID: "guild",
26 | })
27 | s.State.ChannelAdd(&Channel{
28 | Name: "Channel Name",
29 | GuildID: "guild",
30 | ID: "channel",
31 | })
32 | m := &Message{
33 | Content: "<&role> <@!user> <@user> <#channel>",
34 | ChannelID: "channel",
35 | MentionRoles: []string{"role"},
36 | Mentions: []*User{user},
37 | }
38 | if result, _ := m.ContentWithMoreMentionsReplaced(s); result != "@Role Name @User Nick @User Name #Channel Name" {
39 | t.Error(result)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/docs/game-playbook.md:
--------------------------------------------------------------------------------
1 | # Games and Playbook Format
2 |
3 | You can add your own *Powered By the Apocalypse* game to Apocalyptica's list.
4 |
5 | Apocalyptica reads its list of games out of `data/game`.
6 |
7 | Each game has its own directory. containing files:
8 |
9 | - `game.json`: contains game metadata like Name, Author, and a list of playbooks.
10 | - `basic.json`: Basic moves for the game are always in this file.
11 | - `.json`: 1 or more files, each with moves specific to playbooks for the game.
12 |
13 | a skeleton structure that can be copied and applied is in `data/example/game/example_game`. Copy that directory into `data/game`, rename and modify as you need.
14 |
15 | # Contributing a Game
16 |
17 | We would love to support many PbtA games out of the box! More importantly, though, we want to honor the wishes of creators. Please file an issue with "[GAME] My Game" in the title **before** submitting a PR and tell us:
18 |
19 | - info about the game (Name, publisher, short description)
20 | - if there is permission from the creator of the game to use it, with some proof.
21 |
22 | Thanks in advance!
23 |
24 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Mat Ryer and David Hernandez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/pingpong/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## DiscordGo Ping Pong Example
4 |
5 | This example demonstrates how to utilize DiscordGo to create a Ping Pong Bot.
6 |
7 | This Bot will respond to "ping" with "Pong!" and "pong" with "Ping!".
8 |
9 | **Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
10 | Discord chat channel for support.**
11 |
12 | ### Build
13 |
14 | This assumes you already have a working Go environment setup and that
15 | DiscordGo is correctly installed on your system.
16 |
17 |
18 | From within the pingpong example folder, run the below command to compile the
19 | example.
20 |
21 | ```sh
22 | go build
23 | ```
24 |
25 | ### Usage
26 |
27 | This example uses bot tokens for authentication only. While user/password is
28 | supported by DiscordGo, it is not recommended for bots.
29 |
30 | ```
31 | ./pingpong --help
32 | Usage of ./pingpong:
33 | -t string
34 | Bot Token
35 | ```
36 |
37 | The below example shows how to start the bot
38 |
39 | ```sh
40 | ./pingpong -t YOUR_BOT_TOKEN
41 | Bot is now running. Press CTRL-C to exit.
42 | ```
43 |
--------------------------------------------------------------------------------
/playbooks/maestrod.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!everybodyeats",
4 | "Full": "Everbody eats, even that guy",
5 | "Miss": "Ask the MC 1 question about someone, but that person knows you're looking in on them",
6 | "Hit": "Ask the MC 1 question.\n* How are they doing? what's up with them?\n* What or who do they love best\n* Who do they know, like and/or trust?\n* When next should I expect to see them?\n* How could I get to them, physically or emotionally?",
7 | "Crit": "Ask the MC 3 questions.\n* How are they doing? what's up with them?\n* What or who do they love best\n* Who do they know, like and/or trust?\n* When next should I expect to see them?\n* How could I get to them, physically or emotionally?"
8 | },
9 |
10 | {
11 | "Name": "!motive",
12 | "Full": "Just give me a motive",
13 | "Miss": "Not very discriminating are you? The MC chooses several people (maybe your target, maybe not) and they get it. It being 3-harm (ap)",
14 | "Hit": "Someone's going to be feeling bad. Your target suffers 2-harm (ap) in the next 24 hours.",
15 | "Crit": "You really don't like them. Your target's insides are slowly turning to mush. They take 4-harm (ap) in the next 24 hours."
16 | },
17 | ]
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "flag"
7 | "fmt"
8 | "io"
9 | "io/ioutil"
10 | "os"
11 |
12 | "github.com/matryer/moq/pkg/moq"
13 | )
14 |
15 | func main() {
16 | var err error
17 | defer func() {
18 | if err != nil {
19 | fmt.Fprintln(os.Stderr, err)
20 | flag.Usage()
21 | os.Exit(1)
22 |
23 | }
24 | }()
25 | var (
26 | outFile = flag.String("out", "", "output file (default stdout)")
27 | pkgName = flag.String("pkg", "", "package name (default will infer)")
28 | )
29 | flag.Usage = func() {
30 | fmt.Println(`moq [flags] destination interface [interface2 [interface3 [...]]]`)
31 | flag.PrintDefaults()
32 | }
33 | flag.Parse()
34 | args := flag.Args()
35 | if len(args) < 2 {
36 | err = errors.New("not enough arguments")
37 | return
38 | }
39 | destination := args[0]
40 | args = args[1:]
41 | var buf bytes.Buffer
42 | var out io.Writer
43 | out = os.Stdout
44 | if len(*outFile) > 0 {
45 | out = &buf
46 | }
47 | m, err := moq.New(destination, *pkgName)
48 | if err != nil {
49 | return
50 | }
51 | err = m.Mock(out, args...)
52 | if err != nil {
53 | return
54 | }
55 | // create the file
56 | if len(*outFile) > 0 {
57 | err = ioutil.WriteFile(*outFile, buf.Bytes(), 0777)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/user.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // A User stores all data for an individual Discord user.
9 | type User struct {
10 | ID string `json:"id"`
11 | Email string `json:"email"`
12 | Username string `json:"username"`
13 | Avatar string `json:"avatar"`
14 | Discriminator string `json:"discriminator"`
15 | Token string `json:"token"`
16 | Verified bool `json:"verified"`
17 | MFAEnabled bool `json:"mfa_enabled"`
18 | Bot bool `json:"bot"`
19 | }
20 |
21 | // String returns a unique identifier of the form username#discriminator
22 | func (u *User) String() string {
23 | return fmt.Sprintf("%s#%s", u.Username, u.Discriminator)
24 | }
25 |
26 | // Mention return a string which mentions the user
27 | func (u *User) Mention() string {
28 | return fmt.Sprintf("<@%s>", u.ID)
29 | }
30 |
31 | // AvatarURL returns a URL to the user's avatar.
32 | // size: The size of the user's avatar as a power of two
33 | func (u *User) AvatarURL(size string) string {
34 | var URL string
35 | if strings.HasPrefix(u.Avatar, "a_") {
36 | URL = EndpointUserAvatarAnimated(u.ID, u.Avatar)
37 | } else {
38 | URL = EndpointUserAvatar(u.ID, u.Avatar)
39 | }
40 |
41 | return URL + "?size=" + size
42 | }
43 |
--------------------------------------------------------------------------------
/playbooks/hocus.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!fortunes",
4 | "Full": "Fortunes",
5 | "Miss": "Your followers are in want. They look to you to aid to provide succor.",
6 | "Hit": "Your followers have a surplus! Yay! They also have a want. Boo.",
7 | "Crit": "Your followers have a surplus! Everyone is happy-ish! If it's barter, then that's your share as the leader and soothsayer to your followers as is only right."
8 | },
9 |
10 | {
11 | "Name": "!frenzy",
12 | "Full": "Frenzy",
13 | "Miss": "They look amongst themselves, and then turn on you.",
14 | "Hit": "Hold 1. Spend a hold 1 for 1 to make the mob:\n* Bring people forward and deliver them\n* Bring forward all their precious things\n* Unite and fight for you as a gang (2-harm 0-armor size appropriate)\n* Fall into an orgy of uninhibited emotion: fucking, lamenting, fighting, sharing, celebrating, as you choose\n* Go quietly back to their lives",
15 | "Crit": "Hold 3. Spend a hold 1 for 1 to make the mob:\n* Bring people forward and deliver them\n* Bring forward all their precious things\n* Unite and fight for you as a gang (2-harm 0-armor size appropriate)\n* Fall into an orgy of uninhibited emotion: fucking, lamenting, fighting, sharing, celebrating, as you choose\n* Go quietly back to their lives"
16 | },
17 | ]
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/avatar/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## DiscordGo Avatar Example
4 |
5 | This example demonstrates how to utilize DiscordGo to change the avatar for
6 | a Discord account. This example works both with a local file or the URL of
7 | an image.
8 |
9 | **Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
10 | Discord chat channel for support.**
11 |
12 | ### Build
13 |
14 | This assumes you already have a working Go environment setup and that
15 | DiscordGo is correctly installed on your system.
16 |
17 | From within the avatar example folder, run the below command to compile the
18 | example.
19 |
20 | ```sh
21 | go build
22 | ```
23 |
24 | ### Usage
25 |
26 | This example uses bot tokens for authentication only. While email/password is
27 | supported by DiscordGo, it is not recommended to use them.
28 |
29 | ```
30 | ./avatar --help
31 | Usage of ./avatar:
32 | -f string
33 | Avatar File Name
34 | -t string
35 | Bot Token
36 | -u string
37 | URL to the avatar image
38 | ```
39 |
40 | The below example shows how to set your Avatar from a local file.
41 |
42 | ```sh
43 | ./avatar -t TOKEN -f avatar.png
44 | ```
45 | The below example shows how to set your Avatar from a URL.
46 |
47 | ```sh
48 | ./avatar -t TOKEN -u http://bwmarrin.github.io/discordgo/img/discordgo.png
49 | ```
50 |
--------------------------------------------------------------------------------
/playbooks/brainer.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!deepbrain",
4 | "Full": "Deep Brain Scan",
5 | "Miss": "All you do is hurt people, they inflict 1-harm (ap) upon your subject.",
6 | "Hit": "**Hold 1, spend your hold 1 for 1 to ask a question**: \n* What was your character's lowest moment?\n* For what does your character crave forgivness, and of whom?\n* What are your character's secret pains?\n* In what ways are your character's mind and soul vulnerable?",
7 | "Crit": "**Hold 3, spend your hold 1 for 1 to ask a question**: \n* What was your character's lowest moment?\n* For what does your character crave forgivness, and of whom?\n* What are your character's secret pains?\n* In what ways are your character's mind and soul vulnerable?"
8 | },
9 |
10 | {
11 | "Name": "!puppetstrings",
12 | "Full": "In-brain puppet strings",
13 | "Miss": "All you do is hurt people, they inflict 1-harm (ap) upon your subject.",
14 | "Hit": "**Hold 1. If they fullfill the command, all your holds are spent. Until then, At your will, no matter the circumstances, you can spend your hold 1 for 1**: \n* Inflict 1-harm (ap)\n* They take -1 right now",
15 | "Crit": "**Hold 3. If they fullfill the command, all your holds are spent. Until then, At your will, no matter the circumstances, you can spend your hold 1 for 1**: \n* Inflict 1-harm (ap)\n* They take -1 right now"
16 | },
17 | ]
18 |
--------------------------------------------------------------------------------
/data/game/apocalypse_world/brainer.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!deepbrain",
4 | "Full": "Deep Brain Scan",
5 | "Miss": "All you do is hurt people, they inflice 1-harm (ap) upon your subject.",
6 | "Hit": "**Hold 1, spend your hold 1 for 1 to ask a question**: \n* What was your character's lowest moment?\n* For what does your character crave forgivness, and of whom?\n* What are your character's secret pains?\n* In what ways are your character's mind and soul vulnerable?",
7 | "Crit": "**Hold 3, spend your hold 1 for 1 to ask a question**: \n* What was your character's lowest moment?\n* For what does your character crave forgivness, and of whom?\n* What are your character's secret pains?\n* In what ways are your character's mind and soul vulnerable?"
8 | },
9 |
10 | {
11 | "Name": "!puppetstrings",
12 | "Full": "In-brain puppet strings",
13 | "Miss": "All you do is hurt people, they inflice 1-harm (ap) upon your subject.",
14 | "Hit": "**Hold 1. If they fullfill the command, all your holds are spent. Until then, At your will, no matter the circumstances, you can spend your hold 1 for 1**: \n* Inflict 1-harm (ap)\n* They take -1 right now",
15 | "Crit": "**Hold 3. If they fullfill the command, all your holds are spent. Until then, At your will, no matter the circumstances, you can spend your hold 1 for 1**: \n* Inflict 1-harm (ap)\n* They take -1 right now"
16 | },
17 | ]
--------------------------------------------------------------------------------
/playbooks/savvyhead.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!thingsspeak",
4 | "Full": "Things Speak",
5 | "Miss": "This is what happens when you play with the Psychich Maelstrom. It plays with your head. Open your brain, but you've already missed the roll",
6 | "Hit": "Ask the MC 1 question.\n* Who handled this last befor me?\n* Who made this?\n* What strong emotions have been most recently nearby this?\n* What words have been said most recently nearby this?\n* What has been done most recently with this, or to this?\n* What's wrong with this, and how might I fix it?",
7 | "Crit": "Ask the MC 3 questions.\n* Who handled this last befor me?\n* Who made this?\n* What strong emotions have been most recently nearby this?\n* What words have been said most recently nearby this?\n* What has been done most recently with this, or to this?\n* What's wrong with this, and how might I fix it?"
8 | },
9 |
10 | {
11 | "Name": "!bonefeel",
12 | "Full": "Bonefeel",
13 | "Miss": "The MC holds 1 and can spend it to have you be there already, but somehow pinned, caught or trapped.",
14 | "Hit": "Hold 1. You or the MC can spend that to have you already be where you need to be with proper tools and knowledge without any clear explanation why. Take +1forward now",
15 | "Crit": "Hold 1. You or the MC can spend that to have you already be where you need to be with proper tools and knowledge without any clear explanation why. Take +1forward now."
16 | },
17 | ]
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/mytoken/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## DiscordGo MyToken Example
4 |
5 | This example demonstrates how to utilize DiscordGo to login with an email and
6 | password then to print out the Authentication Token for that user's account.
7 |
8 | Everytime this application is run a new authentication token is generated
9 | for your account. Logging you in via email and password then creating a new
10 | token is a cpu/mem expensive task for Discord. Because of that, it is highly
11 | recommended to avoid doing this very often. Please only use this once to get a
12 | token for your use and then always just your token.
13 |
14 | **Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
15 | Discord chat channel for support.**
16 |
17 | ### Build
18 |
19 | This assumes you already have a working Go environment setup and that
20 | DiscordGo is correctly installed on your system.
21 |
22 | From within the mytoken example folder, run the below command to compile the
23 | example.
24 |
25 | ```sh
26 | go build
27 | ```
28 |
29 | ### Usage
30 |
31 | You must authenticate using both Email and Password for an account.
32 |
33 | ```
34 | ./mytoken --help
35 | Usage of ./mytoken:
36 | -e string
37 | Account Email
38 | -p string
39 | Account Password
40 | ```
41 |
42 | The below example shows how to start the program using an Email and Password for
43 | authentication.
44 |
45 | ```sh
46 | ./mytoken -e youremail@here.com -p MySecretPassword
47 | ```
48 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/airhorn/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## DiscordGo Airhorn Example
4 |
5 | This example demonstrates how to utilize DiscordGo to listen for an !airhorn
6 | command in a channel and then play a sound to that user's current voice channel.
7 |
8 | **Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
9 | Discord chat channel for support.**
10 |
11 | ### Build
12 |
13 | This assumes you already have a working Go environment setup and that
14 | DiscordGo is correctly installed on your system.
15 |
16 | From within the airhorn example folder, run the below command to compile the
17 | example.
18 |
19 | ```sh
20 | go build
21 | ```
22 |
23 | ### Usage
24 |
25 | ```
26 | Usage of ./airhorn:
27 | -t string
28 | Bot Token
29 | ```
30 |
31 | The below example shows how to start the bot from the airhorn example folder.
32 |
33 | ```sh
34 | ./airhorn -t YOUR_BOT_TOKEN
35 | ```
36 |
37 | ### Creating sounds
38 |
39 | Airhorn bot uses [DCA](https://github.com/bwmarrin/dca) files, which are
40 | pre-computed files that are easy to send to Discord.
41 |
42 | If you would like to create your own DCA files, please use:
43 | * [dca-rs](https://github.com/nstafie/dca-rs)
44 |
45 | See the below example of creating a DCA file from a WAV file. This also works
46 | with MP3, FLAC, and many other file formats. Of course, you will need to
47 | [install](https://github.com/nstafie/dca-rs#installation) dca-rs first :)
48 |
49 | ```sh
50 | ./dca-rs -i --raw >
51 | ```
52 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015, Bruce Marriner
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of discordgo nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
29 |
--------------------------------------------------------------------------------
/static/css/main.css:
--------------------------------------------------------------------------------
1 | /*
2 | * 1. BASICS
3 | */
4 |
5 | *{
6 | box-sizing: border-box;
7 | }
8 |
9 | html{
10 | background: #efefef;
11 | }
12 |
13 | body {
14 | font-family: Garamond, Baskerville, 'Baskerville Old Face', 'Hoefler Text', 'Times New Roman', serif;
15 | font-size: 1.1em;
16 | margin:0 auto;
17 | max-width: 50em;
18 | background-color: white;
19 | padding:1em;
20 | }
21 | /*
22 | *2. Type
23 | */
24 | h1, h2, h3{
25 | font-family: 'Cinzel', serif;
26 | }
27 |
28 | h1{
29 | font-size: 3em;
30 | margin:0 0 0.5em 0;
31 | line-height: 1;
32 | }
33 |
34 | h2{
35 | font-size: 1.5em;
36 | margin: 0 0 1em 0;
37 | line-height: 1;
38 | }
39 |
40 | p{
41 | line-height: 1.5;
42 | margin: 0 0 1.5em 0
43 | }
44 |
45 | kbd{
46 | border: 1px solid #bbc;
47 | background-color: #efefef;
48 | border-radius: 2px;
49 | padding:0 0.3em;
50 | line-height: inherit;
51 | color: #333;
52 | }
53 |
54 | a:link {
55 | color:rgb(232, 51, 53);
56 | }
57 |
58 | a:visited {
59 | color: #666;
60 | }
61 |
62 | a:hover, a:active, a:focus{
63 | color: rgb(158, 35, 36);
64 | }
65 |
66 | /*
67 | * 3.
68 | */
69 |
70 | a.btn{
71 | padding: 0.5em 0.7em;
72 |
73 | background: rgb(232, 51, 53);
74 | border-radius: 5px;
75 |
76 | color: white;
77 | text-decoration: none;
78 | font-weight: bold;
79 |
80 | transition: background-color 0.5s;
81 | }
82 |
83 | a.btn:hover, a.btn:focus, a.btn:active{
84 | background: rgb(158, 35, 36);
85 |
86 | }
87 |
88 | .btn svg{
89 | display: inline-block;
90 | width:1em;
91 | height:1em;
92 | vertical-align: middle;
93 | fill: currentColor;
94 | }
95 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/oauth2_test.go:
--------------------------------------------------------------------------------
1 | package discordgo_test
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | "github.com/bwmarrin/discordgo"
8 | )
9 |
10 | func ExampleApplication() {
11 |
12 | // Authentication Token pulled from environment variable DG_TOKEN
13 | Token := os.Getenv("DG_TOKEN")
14 | if Token == "" {
15 | return
16 | }
17 |
18 | // Create a new Discordgo session
19 | dg, err := discordgo.New(Token)
20 | if err != nil {
21 | log.Println(err)
22 | return
23 | }
24 |
25 | // Create an new Application
26 | ap := &discordgo.Application{}
27 | ap.Name = "TestApp"
28 | ap.Description = "TestDesc"
29 | ap, err = dg.ApplicationCreate(ap)
30 | log.Printf("ApplicationCreate: err: %+v, app: %+v\n", err, ap)
31 |
32 | // Get a specific Application by it's ID
33 | ap, err = dg.Application(ap.ID)
34 | log.Printf("Application: err: %+v, app: %+v\n", err, ap)
35 |
36 | // Update an existing Application with new values
37 | ap.Description = "Whooooa"
38 | ap, err = dg.ApplicationUpdate(ap.ID, ap)
39 | log.Printf("ApplicationUpdate: err: %+v, app: %+v\n", err, ap)
40 |
41 | // create a new bot account for this application
42 | bot, err := dg.ApplicationBotCreate(ap.ID)
43 | log.Printf("BotCreate: err: %+v, bot: %+v\n", err, bot)
44 |
45 | // Get a list of all applications for the authenticated user
46 | apps, err := dg.Applications()
47 | log.Printf("Applications: err: %+v, apps : %+v\n", err, apps)
48 | for k, v := range apps {
49 | log.Printf("Applications: %d : %+v\n", k, v)
50 | }
51 |
52 | // Delete the application we created.
53 | err = dg.ApplicationDelete(ap.ID)
54 | log.Printf("Delete: err: %+v\n", err)
55 |
56 | return
57 | }
58 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/docs/index.md:
--------------------------------------------------------------------------------
1 | ## DiscordGo
2 |
3 |
4 |
5 | [Go](https://golang.org/) (golang) interface for the [Discord](https://discordapp.com/)
6 | chat service. Provides both low-level direct bindings to the
7 | Discord API and helper functions that allow you to make custom clients and chat
8 | bot applications easily.
9 |
10 | [Discord](https://discordapp.com/) is an all-in-one voice and text chat for
11 | gamers that's free, secure, and works on both your desktop and phone.
12 |
13 | ### Why DiscordGo?
14 | * High Performance
15 | * Minimal Memory & CPU Load
16 | * Low-level bindings to Discord REST API Endpoints
17 | * Support for the data websocket interface
18 | * Multi-Server voice connections (send and receive)
19 | * State tracking and caching
20 |
21 | ### Learn More
22 | * Check out the [Getting Started](GettingStarted) section
23 | * Read the reference docs on [Godoc](https://godoc.org/github.com/bwmarrin/discordgo) or [GoWalker](https://gowalker.org/github.com/bwmarrin/discordgo)
24 | * Try the [examples](https://github.com/bwmarrin/discordgo/tree/master/examples)
25 | * Explore [Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo)
26 |
27 | ### Join Us!
28 | Both of the below links take you to chat channels where you can get more
29 | information and support for DiscordGo. There's also a chance to make some
30 | friends :)
31 |
32 | * Join the [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP) chat server dedicated to Go programming.
33 | * Join the [Discord API](https://discord.gg/0SBTUU1wZTWT6sqd) chat server dedicated to the Discord API.
34 |
--------------------------------------------------------------------------------
/playbooks/angel.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!healingtouch",
4 | "Full": "Healing Touch",
5 | "Miss": "You heal nothing. Both you and the person you lied to about healing have to openbrain except you've already missed the roll",
6 | "Hit": "You put some guts back, heal 1 segment but you gotta openbrain now and hope the Pyschic Maelstrom plays nice.",
7 | "Crit": "You put lots of guts back. heal 1 segment."
8 | },
9 | {
10 | "Name": "!kit",
11 | "Full": "Angel Kit - Stabalize at 9:00 or past",
12 | "Miss": "That shoudn't spurt like that. You deal 1-harm to your patient.",
13 | "Hit": "**They're okay, for now. Put them back to 6:00 and choose 1**:\n* They fight you and you have to narco stab them. How long will they be out?\n* The pain and drugs make them babble the truth to you. Ask them what secret they spill.\n* They respond well to the treatment. Recover 1 of the stock you spent, if you spent any.\n* They are at your complete mercy. What do you do to them?\n* Their course of recovery teaches you something about your craft. Mark experience.\n* They owe you for your time, attention, and supplies, and you're going to hold them to it. ",
14 | "Crit": "**They're okay, for now. Put them back to 6:00 and choose 2**:\n* They fight you and you have to narco stab them. How long will they be out?\n* The pain and drugs make them babble the truth to you. Ask them what secret they spill.\n* They respond well to the treatment. Recover 1 of the stock you spent, if you spent any.\n* They are at your complete mercy. What do you do to them?\n* Their course of recovery teaches you something about your craft. Mark experience.\n* They owe you for your time, attention, and supplies, and you're going to hold them to it."
15 | },
16 | ]
17 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/appmaker/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## DiscordGo AppMaker Example
4 |
5 | This example demonstrates how to utilize DiscordGo to create, view, and delete
6 | Bot Applications on your account.
7 |
8 | These tasks are normally accomplished from the
9 | [Discord Developers](https://discordapp.com/developers/applications/me) site.
10 |
11 | **Join [Discord Gophers](https://discord.gg/0f1SbxBZjYoCtNPP)
12 | Discord chat channel for support.**
13 |
14 | ### Build
15 |
16 | This assumes you already have a working Go environment setup and that
17 | DiscordGo is correctly installed on your system.
18 |
19 | From within the appmaker example folder, run the below command to compile the
20 | example.
21 |
22 | ```sh
23 | go build
24 | ```
25 |
26 | ### Usage
27 |
28 | This example only uses authentication tokens for authentication. While
29 | user email/password is supported by DiscordGo, it is not recommended.
30 |
31 | ```
32 | ./appmaker --help
33 | Usage of ./appmaker:
34 | -d string
35 | Application ID to delete
36 | -l List Applications Only
37 | -n string
38 | Name to give App/Bot
39 | -t string
40 | Owner Account Token
41 | ```
42 |
43 | * Account Token is required. The account will be the "owner" of any bot
44 | applications created.
45 |
46 | * If you provide the **-l** flag than appmaker will only display a list of
47 | applications on the provided account.
48 |
49 | * If you provide a **-d** flag with a valid application ID then that application
50 | will be deleted.
51 |
52 | Below example will create a new Bot Application under the given account.
53 | The Bot will be named **DiscordGoRocks**
54 |
55 | ```sh
56 | ./appmaker -t YOUR_USER_TOKEN -n DiscordGoRocks
57 | ```
58 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/types.go:
--------------------------------------------------------------------------------
1 | // Discordgo - Discord bindings for Go
2 | // Available at https://github.com/bwmarrin/discordgo
3 |
4 | // Copyright 2015-2016 Bruce Marriner . All rights reserved.
5 | // Use of this source code is governed by a BSD-style
6 | // license that can be found in the LICENSE file.
7 |
8 | // This file contains custom types, currently only a timestamp wrapper.
9 |
10 | package discordgo
11 |
12 | import (
13 | "encoding/json"
14 | "fmt"
15 | "net/http"
16 | "time"
17 | )
18 |
19 | // Timestamp stores a timestamp, as sent by the Discord API.
20 | type Timestamp string
21 |
22 | // Parse parses a timestamp string into a time.Time object.
23 | // The only time this can fail is if Discord changes their timestamp format.
24 | func (t Timestamp) Parse() (time.Time, error) {
25 | return time.Parse(time.RFC3339, string(t))
26 | }
27 |
28 | // RESTError stores error information about a request with a bad response code.
29 | // Message is not always present, there are cases where api calls can fail
30 | // without returning a json message.
31 | type RESTError struct {
32 | Request *http.Request
33 | Response *http.Response
34 | ResponseBody []byte
35 |
36 | Message *APIErrorMessage // Message may be nil.
37 | }
38 |
39 | func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
40 | restErr := &RESTError{
41 | Request: req,
42 | Response: resp,
43 | ResponseBody: body,
44 | }
45 |
46 | // Attempt to decode the error and assume no message was provided if it fails
47 | var msg *APIErrorMessage
48 | err := json.Unmarshal(body, &msg)
49 | if err == nil {
50 | restErr.Message = msg
51 | }
52 |
53 | return restErr
54 | }
55 |
56 | func (r RESTError) Error() string {
57 | return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
58 | }
59 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "os"
7 | "os/signal"
8 | "syscall"
9 |
10 | "github.com/bwmarrin/discordgo"
11 | )
12 |
13 | // Variables used for command line parameters
14 | var (
15 | Token string
16 | )
17 |
18 | func init() {
19 |
20 | flag.StringVar(&Token, "t", "", "Bot Token")
21 | flag.Parse()
22 | }
23 |
24 | func main() {
25 |
26 | // Create a new Discord session using the provided bot token.
27 | dg, err := discordgo.New("Bot " + Token)
28 | if err != nil {
29 | fmt.Println("error creating Discord session,", err)
30 | return
31 | }
32 |
33 | // Register the messageCreate func as a callback for MessageCreate events.
34 | dg.AddHandler(messageCreate)
35 |
36 | // Open a websocket connection to Discord and begin listening.
37 | err = dg.Open()
38 | if err != nil {
39 | fmt.Println("error opening connection,", err)
40 | return
41 | }
42 |
43 | // Wait here until CTRL-C or other term signal is received.
44 | fmt.Println("Bot is now running. Press CTRL-C to exit.")
45 | sc := make(chan os.Signal, 1)
46 | signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
47 | <-sc
48 |
49 | // Cleanly close down the Discord session.
50 | dg.Close()
51 | }
52 |
53 | // This function will be called (due to AddHandler above) every time a new
54 | // message is created on any channel that the autenticated bot has access to.
55 | func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
56 |
57 | // Ignore all messages created by the bot itself
58 | // This isn't required in this specific example but it's a good practice.
59 | if m.Author.ID == s.State.User.ID {
60 | return
61 | }
62 | // If the message is "ping" reply with "Pong!"
63 | if m.Content == "ping" {
64 | s.ChannelMessageSend(m.ChannelID, "Pong!")
65 | }
66 |
67 | // If the message is "pong" reply with "Ping!"
68 | if m.Content == "pong" {
69 | s.ChannelMessageSend(m.ChannelID, "Ping!")
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/docs/register.md:
--------------------------------------------------------------------------------
1 | # Registering Apocalyptica
2 | > **IN PROGRESS! DO NOT USE!!!** Still testing out some things.
3 |
4 | ## Overview
5 |
6 | This document covers instructions for registering apps with Discord. Looking for information on how to deploy? Go to ['Deploying Apocalyptica'](deploying.md).
7 |
8 | Interested in test driving Apocalyptica before setting up your own? [Go to the Apocalyptica Reference install](https://apocalyptica.social-fiction.net) and invite it to your server.
9 |
10 | ## Steps to Register Apocalyptica in Discord.
11 |
12 | Sign in to Discord and go to the [Developer page, 'My Apps'](https://discordapp.com/developers/applications/me).
13 | 
14 |
15 | Select 'New App'.
16 |
17 | 
18 |
19 | Give your instance of apocalyptica a name, description, and picture.
20 |
21 | 
22 |
23 | Select 'Creat a Bot User', then on the following screen:
24 |
25 | 
26 |
27 | Click 'click to reveal' and copy the token string. You will want to save this in your `env_discord.sample` file in the variable `DISCORD_TOKEN`.
28 |
29 | Above the 'Create a Bot User' information is the client ID of the application. Copy that ID into `env_discord.sample` file in the variable `CLIENT_ID`.
30 |
31 | When you have retrieved that information, save the changes.
32 |
33 | 
34 |
35 |
36 | ## Invite Apocalyptica to a Server
37 |
38 | Now that you have an app registered, you need to invite it to your server. To do that you need an authorization link.
39 |
40 | The authorize link looks like this:
41 | `https://discordapp.com/oauth2/authorize?&client_id=&scope=bot&permissions=0`
42 |
43 | which will take you to a page like this:
44 |
45 | 
46 |
47 | Choose one of your servers from the list, and choose 'Authorize' to invite Apocalyptica to the server!
48 |
--------------------------------------------------------------------------------
/pkg/moves/moves.go:
--------------------------------------------------------------------------------
1 | package moves
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "io/ioutil"
8 | "log"
9 | "math/rand"
10 | "os"
11 | "regexp"
12 | "strings"
13 | "time"
14 | )
15 |
16 | type roll interface {
17 | Roll() int
18 | Display() string
19 | }
20 |
21 | func die(dieType int) int {
22 | now := time.Now()
23 | //now.Unix() seems to return seconds, so all die rolls in the same second
24 | // would be seeded the same. UnixNano() returns a much more fine grained value
25 | rand.Seed(now.UnixNano())
26 | //'Roll' the die, +1 since Intn is 0 based
27 | return (rand.Intn(dieType) + 1)
28 | }
29 |
30 | func (m Move) Roll(bonus int) int {
31 | return (die(6) + die(6) + bonus)
32 | }
33 |
34 | func (m Move) Display(r, bonus int) string {
35 | choice := ""
36 | switch {
37 | case r <= 6:
38 | choice = m[0].Miss
39 | case (r >= 7 && r <= 9):
40 | choice = m[0].Hit
41 | case (r >= 10):
42 | choice = m[0].Crit
43 | }
44 |
45 | return fmt.Sprintf("**%d**[%d *%d*] %s", r, (r - bonus), bonus, choice)
46 | }
47 |
48 | type Move []struct {
49 | Name string `json:"Name"`
50 | Full string `json:"Full"`
51 | Miss string `json:"Miss"`
52 | Hit string `json:"Hit"`
53 | Crit string `json:"Crit"`
54 | }
55 |
56 | // this is going to move over to
57 | func LoadMoves(filename string) Move {
58 | var mv Move
59 | file, e := ioutil.ReadFile(filename)
60 | if e != nil {
61 | log.Printf("File error: %v\n", e)
62 | os.Exit(1)
63 | }
64 | r := bytes.NewReader(file)
65 | if e := json.NewDecoder(r).Decode(&mv); e != nil {
66 | log.Printf("Problems decoding json: %v\n", e)
67 | os.Exit(1)
68 | }
69 | return mv
70 | }
71 |
72 | func FindMove(s string, mv Move) Move {
73 | var Found Move
74 | substr := strings.Split(s, " ")
75 | for _, v := range mv {
76 | r, err := regexp.Compile(v.Name)
77 | if err != nil {
78 | fmt.Printf("Problematic regexp, failing.\n")
79 | os.Exit(1)
80 | }
81 | if r.MatchString(substr[0]) == true {
82 | Found = append(Found, v)
83 | }
84 | }
85 | return Found
86 | }
87 |
--------------------------------------------------------------------------------
/docs/deploying.md:
--------------------------------------------------------------------------------
1 | Deploying Apocalyptica
2 | ---
3 | > **IN PROGRESS! DO NOT USE!!!** Still testing out some things.
4 |
5 | > :memo: current version of the document is for Docker savvyheads! We will include many different deployment profiles soon.
6 |
7 | ## Docker (Quickstart & Custom)
8 |
9 | ### Requirements
10 | If you want to deploy your own instance of Apocalyptica, you will need:
11 |
12 | * A Discord Account and [a registered app](register.md). **Please register app before starting a deploy!** You need `DISCORD_TOKEN` and `CLIENT_ID` in your `.env` file to deploy successfully.
13 | * Docker (or other container runtime engine, though it has only been tested with Docker)
14 | * Instructions are written assuming OSX/Linux access, though it should be easy to adapt to Windows.
15 | * Web server needs access to port 8080.
16 |
17 | ### Docker Quickstart
18 |
19 | > :information_source: The Docker quickstart is when you want to get going quickly and only want/require the default games and playbooks installed.
20 |
21 | To build with defaults:
22 |
23 | * fill out `env_discord.sample` with `DISCORD_TOKEN` and `CLIENT_ID` values, then copy that file to `.env` under the root file of the repo.
24 | * from the root of the repo, run `bin/docker_quickstart`, which is really a wrapper script for:
25 |
26 | `docker run --name apocalyptica --rm --env-file -p8080:8080 ../.env -d gamefiend/apocalyptica`
27 |
28 | ### Docker Custom
29 |
30 | > :information_source: The Docker custom option is when you want to add your own custom games and playbooks to Apocalyptica.
31 |
32 | To build with custom information:
33 |
34 | * fill out `env_discord.sample` with `DISCORD_TOKEN` and `CLIENT_ID` values, then copy that file to `.env` under the root file of the repo.
35 | * load your custom games and playbooks into `data/games`
36 | * from the root of the repo, run `bin/docker_custom`, which is really a wrapper script for:
37 |
38 | ```
39 | docker build -t apocalyptica-custom \
40 | && docker run --name apocalyptica --rm --env-file -p8080:8080 ../.env -d apocalyptica-custom
41 | ```
42 |
43 | This builds a custom container image that uses your custom info instead of using the generally available docker image.
44 |
45 |
46 | ## Deploying to Kubernetes
47 |
48 | > :memo: COMING SOON (REALLY SOON)
49 |
50 |
51 | ## Using Go Binary
52 |
53 | > :memo: COMING SOON
54 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/avatar/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/base64"
5 | "flag"
6 | "fmt"
7 | "io/ioutil"
8 | "net/http"
9 | "os"
10 |
11 | "github.com/bwmarrin/discordgo"
12 | )
13 |
14 | // Variables used for command line parameters
15 | var (
16 | Token string
17 | AvatarFile string
18 | AvatarURL string
19 | )
20 |
21 | func init() {
22 |
23 | flag.StringVar(&Token, "t", "", "Bot Token")
24 | flag.StringVar(&AvatarFile, "f", "", "Avatar File Name")
25 | flag.StringVar(&AvatarURL, "u", "", "URL to the avatar image")
26 | flag.Parse()
27 |
28 | if Token == "" || (AvatarFile == "" && AvatarURL == "") {
29 | flag.Usage()
30 | os.Exit(1)
31 | }
32 | }
33 |
34 | func main() {
35 |
36 | // Create a new Discord session using the provided login information.
37 | dg, err := discordgo.New("Bot " + Token)
38 | if err != nil {
39 | fmt.Println("error creating Discord session,", err)
40 | return
41 | }
42 |
43 | // Declare these here so they can be used in the below two if blocks and
44 | // still carry over to the end of this function.
45 | var base64img string
46 | var contentType string
47 |
48 | // If we're using a URL link for the Avatar
49 | if AvatarURL != "" {
50 |
51 | resp, err := http.Get(AvatarURL)
52 | if err != nil {
53 | fmt.Println("Error retrieving the file, ", err)
54 | return
55 | }
56 |
57 | defer func() {
58 | _ = resp.Body.Close()
59 | }()
60 |
61 | img, err := ioutil.ReadAll(resp.Body)
62 | if err != nil {
63 | fmt.Println("Error reading the response, ", err)
64 | return
65 | }
66 |
67 | contentType = http.DetectContentType(img)
68 | base64img = base64.StdEncoding.EncodeToString(img)
69 | }
70 |
71 | // If we're using a local file for the Avatar
72 | if AvatarFile != "" {
73 | img, err := ioutil.ReadFile(AvatarFile)
74 | if err != nil {
75 | fmt.Println(err)
76 | }
77 |
78 | contentType = http.DetectContentType(img)
79 | base64img = base64.StdEncoding.EncodeToString(img)
80 | }
81 |
82 | // Now lets format our base64 image into the proper format Discord wants
83 | // and then call UserUpdate to set it as our user's Avatar.
84 | avatar := fmt.Sprintf("data:%s;base64,%s", contentType, base64img)
85 | _, err = dg.UserUpdate("", "", "", avatar, "")
86 | if err != nil {
87 | fmt.Println(err)
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/appmaker/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "flag"
6 | "fmt"
7 | "os"
8 |
9 | "github.com/bwmarrin/discordgo"
10 | )
11 |
12 | // Variables used for command line options
13 | var (
14 | Token string
15 | Name string
16 | DeleteID string
17 | ListOnly bool
18 | )
19 |
20 | func init() {
21 |
22 | flag.StringVar(&Token, "t", "", "Owner Account Token")
23 | flag.StringVar(&Name, "n", "", "Name to give App/Bot")
24 | flag.StringVar(&DeleteID, "d", "", "Application ID to delete")
25 | flag.BoolVar(&ListOnly, "l", false, "List Applications Only")
26 | flag.Parse()
27 |
28 | if Token == "" {
29 | flag.Usage()
30 | os.Exit(1)
31 | }
32 | }
33 |
34 | func main() {
35 |
36 | var err error
37 |
38 | // Create a new Discord session using the provided login information.
39 | dg, err := discordgo.New(Token)
40 | if err != nil {
41 | fmt.Println("error creating Discord session,", err)
42 | return
43 | }
44 |
45 | // If -l set, only display a list of existing applications
46 | // for the given account.
47 | if ListOnly {
48 |
49 | aps, err := dg.Applications()
50 | if err != nil {
51 | fmt.Println("error fetching applications,", err)
52 | return
53 | }
54 |
55 | for _, v := range aps {
56 | fmt.Println("-----------------------------------------------------")
57 | b, _ := json.MarshalIndent(v, "", " ")
58 | fmt.Println(string(b))
59 | }
60 | return
61 | }
62 |
63 | // if -d set, delete the given Application
64 | if DeleteID != "" {
65 | err = dg.ApplicationDelete(DeleteID)
66 | if err != nil {
67 | fmt.Println("error deleting application,", err)
68 | }
69 | return
70 | }
71 |
72 | if Name == "" {
73 | flag.Usage()
74 | os.Exit(1)
75 | }
76 |
77 | // Create a new application.
78 | ap := &discordgo.Application{}
79 | ap.Name = Name
80 | ap, err = dg.ApplicationCreate(ap)
81 | if err != nil {
82 | fmt.Println("error creating new applicaiton,", err)
83 | return
84 | }
85 |
86 | fmt.Printf("Application created successfully:\n")
87 | b, _ := json.MarshalIndent(ap, "", " ")
88 | fmt.Println(string(b))
89 |
90 | // Create the bot account under the application we just created
91 | bot, err := dg.ApplicationBotCreate(ap.ID)
92 | if err != nil {
93 | fmt.Println("error creating bot account,", err)
94 | return
95 | }
96 |
97 | fmt.Printf("Bot account created successfully.\n")
98 | b, _ = json.MarshalIndent(bot, "", " ")
99 | fmt.Println(string(b))
100 |
101 | fmt.Println("Please save the above posted info in a secure place.")
102 | fmt.Println("You will need that information to login with your bot account.")
103 | }
104 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/logging.go:
--------------------------------------------------------------------------------
1 | // Discordgo - Discord bindings for Go
2 | // Available at https://github.com/bwmarrin/discordgo
3 |
4 | // Copyright 2015-2016 Bruce Marriner . All rights reserved.
5 | // Use of this source code is governed by a BSD-style
6 | // license that can be found in the LICENSE file.
7 |
8 | // This file contains code related to discordgo package logging
9 |
10 | package discordgo
11 |
12 | import (
13 | "fmt"
14 | "log"
15 | "runtime"
16 | "strings"
17 | )
18 |
19 | const (
20 |
21 | // LogError level is used for critical errors that could lead to data loss
22 | // or panic that would not be returned to a calling function.
23 | LogError int = iota
24 |
25 | // LogWarning level is used for very abnormal events and errors that are
26 | // also returend to a calling function.
27 | LogWarning
28 |
29 | // LogInformational level is used for normal non-error activity
30 | LogInformational
31 |
32 | // LogDebug level is for very detailed non-error activity. This is
33 | // very spammy and will impact performance.
34 | LogDebug
35 | )
36 |
37 | // msglog provides package wide logging consistancy for discordgo
38 | // the format, a... portion this command follows that of fmt.Printf
39 | // msgL : LogLevel of the message
40 | // caller : 1 + the number of callers away from the message source
41 | // format : Printf style message format
42 | // a ... : comma seperated list of values to pass
43 | func msglog(msgL, caller int, format string, a ...interface{}) {
44 |
45 | pc, file, line, _ := runtime.Caller(caller)
46 |
47 | files := strings.Split(file, "/")
48 | file = files[len(files)-1]
49 |
50 | name := runtime.FuncForPC(pc).Name()
51 | fns := strings.Split(name, ".")
52 | name = fns[len(fns)-1]
53 |
54 | msg := fmt.Sprintf(format, a...)
55 |
56 | log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
57 | }
58 |
59 | // helper function that wraps msglog for the Session struct
60 | // This adds a check to insure the message is only logged
61 | // if the session log level is equal or higher than the
62 | // message log level
63 | func (s *Session) log(msgL int, format string, a ...interface{}) {
64 |
65 | if msgL > s.LogLevel {
66 | return
67 | }
68 |
69 | msglog(msgL, 2, format, a...)
70 | }
71 |
72 | // helper function that wraps msglog for the VoiceConnection struct
73 | // This adds a check to insure the message is only logged
74 | // if the voice connection log level is equal or higher than the
75 | // message log level
76 | func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) {
77 |
78 | if msgL > v.LogLevel {
79 | return
80 | }
81 |
82 | msglog(msgL, 2, format, a...)
83 | }
84 |
85 | // printJSON is a helper function to display JSON data in a easy to read format.
86 | /* NOT USED ATM
87 | func printJSON(body []byte) {
88 | var prettyJSON bytes.Buffer
89 | error := json.Indent(&prettyJSON, body, "", "\t")
90 | if error != nil {
91 | log.Print("JSON parse error: ", error)
92 | }
93 | log.Println(string(prettyJSON.Bytes()))
94 | }
95 | */
96 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/template.go:
--------------------------------------------------------------------------------
1 | package moq
2 |
3 | // moqImports are the imports all moq files get.
4 | var moqImports = []string{}
5 |
6 | // moqTemplate is the template for mocked code.
7 | var moqTemplate = `// Code generated by moq; DO NOT EDIT
8 | // github.com/matryer/moq
9 |
10 | package {{.PackageName}}
11 |
12 | import (
13 | {{- range .Imports }}
14 | "{{.}}"
15 | {{- end }}
16 | )
17 |
18 | {{ range $i, $obj := .Objects -}}
19 | var (
20 | {{- range .Methods }}
21 | lock{{$obj.InterfaceName}}Mock{{.Name}} sync.RWMutex
22 | {{- end }}
23 | )
24 |
25 | // {{.InterfaceName}}Mock is a mock implementation of {{.InterfaceName}}.
26 | //
27 | // func TestSomethingThatUses{{.InterfaceName}}(t *testing.T) {
28 | //
29 | // // make and configure a mocked {{.InterfaceName}}
30 | // mocked{{.InterfaceName}} := &{{.InterfaceName}}Mock{ {{ range .Methods }}
31 | // {{.Name}}Func: func({{ .Arglist }}) {{.ReturnArglist}} {
32 | // panic("TODO: mock out the {{.Name}} method")
33 | // },{{- end }}
34 | // }
35 | //
36 | // // TODO: use mocked{{.InterfaceName}} in code that requires {{.InterfaceName}}
37 | // // and then make assertions.
38 | //
39 | // }
40 | type {{.InterfaceName}}Mock struct {
41 | {{- range .Methods }}
42 | // {{.Name}}Func mocks the {{.Name}} method.
43 | {{.Name}}Func func({{ .Arglist }}) {{.ReturnArglist}}
44 | {{ end }}
45 | // calls tracks calls to the methods.
46 | calls struct {
47 | {{- range .Methods }}
48 | // {{ .Name }} holds details about calls to the {{.Name}} method.
49 | {{ .Name }} []struct {
50 | {{- range .Params }}
51 | // {{ .Name | Exported }} is the {{ .Name }} argument value.
52 | {{ .Name | Exported }} {{ .Type }}
53 | {{- end }}
54 | }
55 | {{- end }}
56 | }
57 | }
58 | {{ range .Methods }}
59 | // {{.Name}} calls {{.Name}}Func.
60 | func (mock *{{$obj.InterfaceName}}Mock) {{.Name}}({{.Arglist}}) {{.ReturnArglist}} {
61 | if mock.{{.Name}}Func == nil {
62 | panic("moq: {{$obj.InterfaceName}}Mock.{{.Name}}Func is nil but {{$obj.InterfaceName}}.{{.Name}} was just called")
63 | }
64 | callInfo := struct {
65 | {{- range .Params }}
66 | {{ .Name | Exported }} {{ .Type }}
67 | {{- end }}
68 | }{
69 | {{- range .Params }}
70 | {{ .Name | Exported }}: {{ .Name }},
71 | {{- end }}
72 | }
73 | lock{{$obj.InterfaceName}}Mock{{.Name}}.Lock()
74 | mock.calls.{{.Name}} = append(mock.calls.{{.Name}}, callInfo)
75 | lock{{$obj.InterfaceName}}Mock{{.Name}}.Unlock()
76 | {{- if .ReturnArglist }}
77 | return mock.{{.Name}}Func({{.ArgCallList}})
78 | {{- else }}
79 | mock.{{.Name}}Func({{.ArgCallList}})
80 | {{- end }}
81 | }
82 |
83 | // {{.Name}}Calls gets all the calls that were made to {{.Name}}.
84 | // Check the length with:
85 | // len(mocked{{$obj.InterfaceName}}.{{.Name}}Calls())
86 | func (mock *{{$obj.InterfaceName}}Mock) {{.Name}}Calls() []struct {
87 | {{- range .Params }}
88 | {{ .Name | Exported }} {{ .Type }}
89 | {{- end }}
90 | } {
91 | var calls []struct {
92 | {{- range .Params }}
93 | {{ .Name | Exported }} {{ .Type }}
94 | {{- end }}
95 | }
96 | lock{{$obj.InterfaceName}}Mock{{.Name}}.RLock()
97 | calls = mock.calls.{{.Name}}
98 | lock{{$obj.InterfaceName}}Mock{{.Name}}.RUnlock()
99 | return calls
100 | }
101 | {{ end -}}
102 | {{ end -}}`
103 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/ratelimit_test.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "net/http"
5 | "strconv"
6 | "testing"
7 | "time"
8 | )
9 |
10 | // This test takes ~2 seconds to run
11 | func TestRatelimitReset(t *testing.T) {
12 | rl := NewRatelimiter()
13 |
14 | sendReq := func(endpoint string) {
15 | bucket := rl.LockBucket(endpoint)
16 |
17 | headers := http.Header(make(map[string][]string))
18 |
19 | headers.Set("X-RateLimit-Remaining", "0")
20 | // Reset for approx 2 seconds from now
21 | headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Add(time.Second*2).Unix(), 10))
22 | headers.Set("Date", time.Now().Format(time.RFC850))
23 |
24 | err := bucket.Release(headers)
25 | if err != nil {
26 | t.Errorf("Release returned error: %v", err)
27 | }
28 | }
29 |
30 | sent := time.Now()
31 | sendReq("/guilds/99/channels")
32 | sendReq("/guilds/55/channels")
33 | sendReq("/guilds/66/channels")
34 |
35 | sendReq("/guilds/99/channels")
36 | sendReq("/guilds/55/channels")
37 | sendReq("/guilds/66/channels")
38 |
39 | // We hit the same endpoint 2 times, so we should only be ratelimited 2 second
40 | // And always less than 4 seconds (unless you're on a stoneage computer or using swap or something...)
41 | if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*4 {
42 | t.Log("OK", time.Since(sent))
43 | } else {
44 | t.Error("Did not ratelimit correctly, got:", time.Since(sent))
45 | }
46 | }
47 |
48 | // This test takes ~1 seconds to run
49 | func TestRatelimitGlobal(t *testing.T) {
50 | rl := NewRatelimiter()
51 |
52 | sendReq := func(endpoint string) {
53 | bucket := rl.LockBucket(endpoint)
54 |
55 | headers := http.Header(make(map[string][]string))
56 |
57 | headers.Set("X-RateLimit-Global", "1")
58 | // Reset for approx 1 seconds from now
59 | headers.Set("Retry-After", "1000")
60 |
61 | err := bucket.Release(headers)
62 | if err != nil {
63 | t.Errorf("Release returned error: %v", err)
64 | }
65 | }
66 |
67 | sent := time.Now()
68 |
69 | // This should trigger a global ratelimit
70 | sendReq("/guilds/99/channels")
71 | time.Sleep(time.Millisecond * 100)
72 |
73 | // This shouldn't go through in less than 1 second
74 | sendReq("/guilds/55/channels")
75 |
76 | if time.Since(sent) >= time.Second && time.Since(sent) < time.Second*2 {
77 | t.Log("OK", time.Since(sent))
78 | } else {
79 | t.Error("Did not ratelimit correctly, got:", time.Since(sent))
80 | }
81 | }
82 |
83 | func BenchmarkRatelimitSingleEndpoint(b *testing.B) {
84 | rl := NewRatelimiter()
85 | for i := 0; i < b.N; i++ {
86 | sendBenchReq("/guilds/99/channels", rl)
87 | }
88 | }
89 |
90 | func BenchmarkRatelimitParallelMultiEndpoints(b *testing.B) {
91 | rl := NewRatelimiter()
92 | b.RunParallel(func(pb *testing.PB) {
93 | i := 0
94 | for pb.Next() {
95 | sendBenchReq("/guilds/"+strconv.Itoa(i)+"/channels", rl)
96 | i++
97 | }
98 | })
99 | }
100 |
101 | // Does not actually send requests, but locks the bucket and releases it with made-up headers
102 | func sendBenchReq(endpoint string, rl *RateLimiter) {
103 | bucket := rl.LockBucket(endpoint)
104 |
105 | headers := http.Header(make(map[string][]string))
106 |
107 | headers.Set("X-RateLimit-Remaining", "10")
108 | headers.Set("X-RateLimit-Reset", strconv.FormatInt(time.Now().Unix(), 10))
109 | headers.Set("Date", time.Now().Format(time.RFC850))
110 |
111 | bucket.Release(headers)
112 | }
113 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/README.md:
--------------------------------------------------------------------------------
1 |  [](https://travis-ci.org/matryer/moq) [](https://goreportcard.com/report/github.com/matryer/moq)
2 |
3 | Interface mocking tool for go generate.
4 |
5 | By [Mat Ryer](https://twitter.com/matryer) and [David Hernandez](https://github.com/dahernan), with ideas lovingly stolen from [Ernesto Jimenez](https://github.com/ernesto-jimenez).
6 |
7 | ### What is Moq?
8 |
9 | Moq is a tool that generates a struct from any interface. The struct can be used in test code as a mock of the interface.
10 |
11 | 
12 |
13 | above: Moq generates the code on the right.
14 |
15 | You can read more in the [Meet Moq blog post](http://bit.ly/meetmoq).
16 |
17 | ### Installing
18 |
19 | To start using Moq, just run go get:
20 | ```
21 | $ go get github.com/matryer/moq
22 | ```
23 |
24 | ### Usage
25 |
26 | ```
27 | moq [flags] destination interface [interface2 [interface3 [...]]]
28 | -out string
29 | output file (default stdout)
30 | -pkg string
31 | package name (default will infer)
32 | ```
33 |
34 | In a command line:
35 |
36 | ```
37 | $ moq -out mocks_test.go . MyInterface
38 | ```
39 |
40 | In code (for go generate):
41 |
42 | ```go
43 | package my
44 |
45 | //go:generate moq -out myinterface_moq_test.go . MyInterface
46 |
47 | type MyInterface interface {
48 | Method1() error
49 | Method2(i int)
50 | }
51 | ```
52 |
53 | Then run `go generate` for your package.
54 |
55 | ### How to use it
56 |
57 | Mocking interfaces is a nice way to write unit tests where you can easily control the behaviour of the mocked object.
58 |
59 | Moq creates a struct that has a function field for each method, which you can declare in your test code.
60 |
61 | This this example, Moq generated the `EmailSenderMock` type:
62 |
63 | ```go
64 | func TestCompleteSignup(t *testing.T) {
65 |
66 | var sentTo string
67 |
68 | mockedEmailSender = &EmailSenderMock{
69 | SendFunc: func(to, subject, body string) error {
70 | sentTo = to
71 | return nil
72 | },
73 | }
74 |
75 | CompleteSignUp("me@email.com", mockedEmailSender)
76 |
77 | callsToSend := len(mockedEmailSender.SendCalls())
78 | if callsToSend != 1 {
79 | t.Errorf("Send was called %d times", callsToSend)
80 | }
81 | if sentTo != "me@email.com" {
82 | t.Errorf("unexpected recipient: %s", sentTo)
83 | }
84 |
85 | }
86 |
87 | func CompleteSignUp(to string, sender EmailSender) {
88 | // TODO: this
89 | }
90 | ```
91 |
92 | The mocked structure implements the interface, where each method calls the associated function field.
93 |
94 | ## Tips
95 |
96 | * Keep mocked logic inside the test that is using it
97 | * Only mock the fields you need
98 | * It will panic if a nil function gets called
99 | * Name arguments in the interface for a better experience
100 | * Use closured variables inside your test function to capture details about the calls to the methods
101 | * Use `.MethodCalls()` to track the calls
102 | * Use `go:generate` to invoke the `moq` command
103 |
104 | ## License
105 |
106 | The Moq project (and all code) is licensed under the [MIT License](LICENSE).
107 |
108 | The Moq logo was created by [Chris Ryer](http://chrisryer.co.uk) and is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/).
109 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/tools/cmd/eventhandlers/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bytes"
5 | "go/format"
6 | "go/parser"
7 | "go/token"
8 | "io/ioutil"
9 | "log"
10 | "path/filepath"
11 | "regexp"
12 | "sort"
13 | "strings"
14 | "text/template"
15 | )
16 |
17 | var eventHandlerTmpl = template.Must(template.New("eventHandler").Funcs(template.FuncMap{
18 | "constName": constName,
19 | "isDiscordEvent": isDiscordEvent,
20 | "privateName": privateName,
21 | }).Parse(`// Code generated by \"eventhandlers\"; DO NOT EDIT
22 | // See events.go
23 |
24 | package discordgo
25 |
26 | // Following are all the event types.
27 | // Event type values are used to match the events returned by Discord.
28 | // EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
29 | const ({{range .}}
30 | {{privateName .}}EventType = "{{constName .}}"{{end}}
31 | )
32 | {{range .}}
33 | // {{privateName .}}EventHandler is an event handler for {{.}} events.
34 | type {{privateName .}}EventHandler func(*Session, *{{.}})
35 |
36 | // Type returns the event type for {{.}} events.
37 | func (eh {{privateName .}}EventHandler) Type() string {
38 | return {{privateName .}}EventType
39 | }
40 | {{if isDiscordEvent .}}
41 | // New returns a new instance of {{.}}.
42 | func (eh {{privateName .}}EventHandler) New() interface{} {
43 | return &{{.}}{}
44 | }{{end}}
45 | // Handle is the handler for {{.}} events.
46 | func (eh {{privateName .}}EventHandler) Handle(s *Session, i interface{}) {
47 | if t, ok := i.(*{{.}}); ok {
48 | eh(s, t)
49 | }
50 | }
51 |
52 | {{end}}
53 | func handlerForInterface(handler interface{}) EventHandler {
54 | switch v := handler.(type) {
55 | case func(*Session, interface{}):
56 | return interfaceEventHandler(v){{range .}}
57 | case func(*Session, *{{.}}):
58 | return {{privateName .}}EventHandler(v){{end}}
59 | }
60 |
61 | return nil
62 | }
63 |
64 | func init() { {{range .}}{{if isDiscordEvent .}}
65 | registerInterfaceProvider({{privateName .}}EventHandler(nil)){{end}}{{end}}
66 | }
67 | `))
68 |
69 | func main() {
70 | var buf bytes.Buffer
71 | dir := filepath.Dir(".")
72 |
73 | fs := token.NewFileSet()
74 | parsedFile, err := parser.ParseFile(fs, "events.go", nil, 0)
75 | if err != nil {
76 | log.Fatalf("warning: internal error: could not parse events.go: %s", err)
77 | return
78 | }
79 |
80 | names := []string{}
81 | for object := range parsedFile.Scope.Objects {
82 | names = append(names, object)
83 | }
84 | sort.Strings(names)
85 | eventHandlerTmpl.Execute(&buf, names)
86 |
87 | src, err := format.Source(buf.Bytes())
88 | if err != nil {
89 | log.Println("warning: internal error: invalid Go generated:", err)
90 | src = buf.Bytes()
91 | }
92 |
93 | err = ioutil.WriteFile(filepath.Join(dir, strings.ToLower("eventhandlers.go")), src, 0644)
94 | if err != nil {
95 | log.Fatal(buf, "writing output: %s", err)
96 | }
97 | }
98 |
99 | var constRegexp = regexp.MustCompile("([a-z])([A-Z])")
100 |
101 | func constCase(name string) string {
102 | return strings.ToUpper(constRegexp.ReplaceAllString(name, "${1}_${2}"))
103 | }
104 |
105 | func isDiscordEvent(name string) bool {
106 | switch {
107 | case name == "Connect", name == "Disconnect", name == "Event", name == "RateLimit", name == "Interface":
108 | return false
109 | default:
110 | return true
111 | }
112 | }
113 |
114 | func constName(name string) string {
115 | if !isDiscordEvent(name) {
116 | return "__" + constCase(name) + "__"
117 | }
118 |
119 | return constCase(name)
120 | }
121 |
122 | func privateName(name string) string {
123 | return strings.ToLower(string(name[0])) + name[1:]
124 | }
125 |
--------------------------------------------------------------------------------
/pkg/game/game.go:
--------------------------------------------------------------------------------
1 | // game manages data structures for games and playbooks arranged in format listed in docs/gamebook-playbook.md
2 | package game
3 |
4 | import (
5 | "bytes"
6 | "encoding/json"
7 | "fmt"
8 | "github.com/gamefiend/apocalyptica/pkg/moves"
9 | "github.com/gamefiend/apocalyptica/pkg/playbook"
10 | "io/ioutil"
11 | "log"
12 | "os"
13 | "runtime"
14 | "strings"
15 | )
16 |
17 | var (
18 | _, b, _, _ = runtime.Caller(0)
19 | basepath = filepath.Dir(b)
20 | gamepath = fmt.Sprintf("%s/data/game", basepath)
21 | )
22 |
23 | //Gamebooks interface defines methods to initialize data structs from file structure.
24 | type Gamebooks interface {
25 | NewGame() (g Games)
26 | ListGamesAvailable() (l string)
27 | LoadMovesFromGame(dir string) (m Move)
28 | LoadMovesFromPlaybook(path string) (m Move)
29 | GetCurrentGame(f string) (l string)
30 | SetCurrentGame(f string) bool
31 | }
32 |
33 | //Games type is where `games.json` structure is unmarshalled into.
34 | type Games []struct {
35 | Name string `json:"Name"`
36 | Author string `json:"Author"`
37 | Description string `json:"Description"`
38 | Playbooks map[string]playbook `json:"Playboooks"`
39 | isSelected bool // NewGame sets default to false
40 | }
41 |
42 | //playbook type holds moves from a games' playbooks.
43 | type playbook struct {
44 | Name string `json:"Name"`
45 | File string `json:"Moves"`
46 | }
47 |
48 | func (g *Games) NewGame() (gg Games) {
49 | var gg Games
50 | files, e := ioutil.ReadDir(gamepath)
51 | for _, x := range files {
52 | if x == "game.json" {
53 | f, e := ioutil.ReadFile(x)
54 | if e != nil {
55 | log.Fatalf("Cannot open file %s : %v\n", x, e)
56 | }
57 | r := bytes.NewReader(f)
58 | if e := json.NewDecoder(r).Decode(&g); e != nil {
59 | log.Fatalf("Cannot decode json: %v\n", e)
60 | }
61 | }
62 | }
63 | return
64 |
65 | }
66 |
67 | func (g *Games) ListGamesAvailable() (l string) {
68 | gl := make([]string, 0, len(g))
69 | for _, game := range g {
70 | output := fmt.Sprintf("**%s by %s**\n*%s*", game.Name, game.Author, game.Description)
71 | gl = append(gl, output)
72 | }
73 | return strings.Join(gl, "\n")
74 | }
75 |
76 | func (g *Games) LoadMovesFromGame(dir string) (m Move) {
77 | var m Move
78 | for _, p := range g {
79 | if p.isSelected {
80 | gpath := fmt.Sprintf("%s/%s", gamepath, dir)
81 | for _, x := range p.Playbooks {
82 | ppath := fmt.Sprintf("%s/%s", gpath, x.File)
83 | var pb Move
84 | r := ioutil.NewReader(ppath)
85 | if e := json.NewDecoder(r).Decode(&pb); e != nil {
86 | log.Fatalf("Cannot read playbook %s : %v\n", ppath, e)
87 | }
88 | m = append(m, pb)
89 | }
90 |
91 | }
92 | }
93 | return
94 | }
95 |
96 | func (g *Games) LoadMovesFromPlaybook(path string) (m Move) {
97 | var m Move
98 | f, e := ioutil.ReadFile(filepath)
99 | if e != nil {
100 | log.Fatalf("Cannot read file %s : %v\n", filepath, e)
101 | }
102 | r := bytes.NewReader(f)
103 | if e := json.NewDecoder(r).Decode(&m); e != nil {
104 | log.Fatalf("Problem unmarshalling json : %v", e)
105 | }
106 | return
107 | }
108 |
109 | func (g *Games) GetCurrentGame() string {
110 | for _, x := range g {
111 | if x.isSelected {
112 | return x.Name
113 | }
114 | }
115 | //if we have arrived here, we obviously have nothing selected...
116 | return "NoGameSelected"
117 | }
118 |
119 | func (g *Games) SetCurrentGame(f string) bool {
120 | for _, x := range g {
121 | if x.Name == f {
122 | if x.isSelected {
123 | return true
124 | }
125 | x.isSelected = true
126 | return true
127 | }
128 | x.isSelected = false
129 | }
130 | return false
131 | }
132 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/example/mockpersonstore_test.go:
--------------------------------------------------------------------------------
1 | package example
2 |
3 | // AUTOGENERATED BY MOQ - DO NOT EDIT
4 | // github.com/matryer/moq
5 |
6 | import (
7 | "context"
8 | "sync"
9 | )
10 |
11 | var (
12 | lockPersonStoreMockCreate sync.RWMutex
13 | lockPersonStoreMockGet sync.RWMutex
14 | )
15 |
16 | // PersonStoreMock is a mock implementation of PersonStore.
17 | //
18 | // func TestSomethingThatUsesPersonStore(t *testing.T) {
19 | //
20 | // // make and configure a mocked PersonStore
21 | // mockedPersonStore := &PersonStoreMock{
22 | // CreateFunc: func(ctx context.Context, person *Person, confirm bool) error {
23 | // panic("TODO: mock out the Create method")
24 | // },
25 | // GetFunc: func(ctx context.Context, id string) (*Person, error) {
26 | // panic("TODO: mock out the Get method")
27 | // },
28 | // }
29 | //
30 | // // TODO: use mockedPersonStore in code that requires PersonStore
31 | // // and then make assertions.
32 | //
33 | // }
34 | type PersonStoreMock struct {
35 | // CreateFunc mocks the Create method.
36 | CreateFunc func(ctx context.Context, person *Person, confirm bool) error
37 |
38 | // GetFunc mocks the Get method.
39 | GetFunc func(ctx context.Context, id string) (*Person, error)
40 |
41 | // calls tracks calls to the methods.
42 | calls struct {
43 | // Create holds details about calls to the Create method.
44 | Create []struct {
45 | // Ctx is the ctx argument value.
46 | Ctx context.Context
47 | // Person is the person argument value.
48 | Person *Person
49 | // Confirm is the confirm argument value.
50 | Confirm bool
51 | }
52 | // Get holds details about calls to the Get method.
53 | Get []struct {
54 | // Ctx is the ctx argument value.
55 | Ctx context.Context
56 | // Id is the id argument value.
57 | Id string
58 | }
59 | }
60 | }
61 |
62 | // Create calls CreateFunc.
63 | func (mock *PersonStoreMock) Create(ctx context.Context, person *Person, confirm bool) error {
64 | if mock.CreateFunc == nil {
65 | panic("moq: PersonStoreMock.CreateFunc is nil but PersonStore.Create was just called")
66 | }
67 | callInfo := struct {
68 | Ctx context.Context
69 | Person *Person
70 | Confirm bool
71 | }{
72 | Ctx: ctx,
73 | Person: person,
74 | Confirm: confirm,
75 | }
76 | lockPersonStoreMockCreate.Lock()
77 | mock.calls.Create = append(mock.calls.Create, callInfo)
78 | lockPersonStoreMockCreate.Unlock()
79 | return mock.CreateFunc(ctx, person, confirm)
80 | }
81 |
82 | // CreateCalls gets all the calls that were made to Create.
83 | // Check the length with:
84 | // len(mockedPersonStore.CreateCalls())
85 | func (mock *PersonStoreMock) CreateCalls() []struct {
86 | Ctx context.Context
87 | Person *Person
88 | Confirm bool
89 | } {
90 | var calls []struct {
91 | Ctx context.Context
92 | Person *Person
93 | Confirm bool
94 | }
95 | lockPersonStoreMockCreate.RLock()
96 | calls = mock.calls.Create
97 | lockPersonStoreMockCreate.RUnlock()
98 | return calls
99 | }
100 |
101 | // Get calls GetFunc.
102 | func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*Person, error) {
103 | if mock.GetFunc == nil {
104 | panic("moq: PersonStoreMock.GetFunc is nil but PersonStore.Get was just called")
105 | }
106 | callInfo := struct {
107 | Ctx context.Context
108 | Id string
109 | }{
110 | Ctx: ctx,
111 | Id: id,
112 | }
113 | lockPersonStoreMockGet.Lock()
114 | mock.calls.Get = append(mock.calls.Get, callInfo)
115 | lockPersonStoreMockGet.Unlock()
116 | return mock.GetFunc(ctx, id)
117 | }
118 |
119 | // GetCalls gets all the calls that were made to Get.
120 | // Check the length with:
121 | // len(mockedPersonStore.GetCalls())
122 | func (mock *PersonStoreMock) GetCalls() []struct {
123 | Ctx context.Context
124 | Id string
125 | } {
126 | var calls []struct {
127 | Ctx context.Context
128 | Id string
129 | }
130 | lockPersonStoreMockGet.RLock()
131 | calls = mock.calls.Get
132 | lockPersonStoreMockGet.RUnlock()
133 | return calls
134 | }
135 |
--------------------------------------------------------------------------------
/data/game/fellowship/basic.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!finish",
4 | "Full": "Finish them! (any stat)",
5 | "Miss": "You lose Advantage and **face retaliation**.",
6 | "Hit": "You deal damage to a stat that makes sense & lose Advantage.\n**If an ally was Keeping Them Busy**, they aren't anymore.",
7 | "Crit": "You **destroy them** - tell us what that means."
8 | },
9 | {
10 | "Name": "!overcome",
11 | "Full": "Overcome! (Blood)",
12 | "Miss": "You fail to overcome the harm & **the harm happens** unimpeded",
13 | "Hit": "You can **Pay a Price** for full success,\nor you can **create a temporary solution** which delays the threat.",
14 | "Crit": "You **stop the obstacle**, threat, cut, or Move from causing harm."
15 | },
16 | {
17 | "Name": "!keepbusy",
18 | "Full": "Keep them busy! (Courage)",
19 | "Miss": "Keeping them busy is a bust!",
20 | "Hit": "You can only stall them for a **short time**, and they will **retaliate against you** when time is up.\n**While you Keep Them Busy**, you create an Advantage for someone else.\nMark ammo if using a weapon with Ammo",
21 | "Crit": "You buy **as much time as you need**, and their attention is all on you.\n**While you Keep Them Busy**, you create an Advantage for someone else.\nMark ammo if using a weapon with Ammo"
22 | },
23 | {
24 | "Name": "!getaway",
25 | "Full": "Get away! (Grace)",
26 | "Miss": "Getting away was a bust!",
27 | "Hit": "**Choose one:**\n* You get there quickly, avoiding any harm along the way.\n* You get there quietly, drawing no attention.\n* You grab someone nearby and bring them along with you.",
28 | "Crit": "**Choose two:**\n* You get there quickly, avoiding any harm along the way.\n* You get there quietly, drawing no attention.\n* You grab someone nearby and bring them along with you."
29 | },
30 | {
31 | "Name": "!talksense",
32 | "Full": "Talk sense! (Grace/Sense/Wisdom)",
33 | "Miss": "**They choose one**:\n* The favor you must do is expensive, difficult, or demeaning.\n* They cannot do what you ask of them, and will tell you why.",
34 | "Hit": "**They do as you ask**, to the best of their ability.\nHowever, you **owe them a favor**.\n The more you ask of them, the more they'll ask of you, and they can cash in **at any time**.",
35 | "Crit": "**They do as you ask** to the best of their ability, no favor required."
36 | },
37 | {
38 | "Name": "!lookclosely",
39 | "Full": "Look closely! (Sense)",
40 | "Miss": "**Ask one** (Anyone can answer, Overlord has final say):\n* What is going on here? What do my senses tell me?\n* Is something hidden or out of place? If so, what looks suspicious?\n* Tell me about ____. How could it hurt me? How could it help me?\n* Tell me about ____. What are they doing? What will they do next?\n* What will happen if I ____?",
41 | "Hit": "**Ask two** (Anyone can answer, Overlord has final say):\n* What is going on here? What do my senses tell me?\n* Is something hidden or out of place? If so, what looks suspicious?\n* Tell me about ____. How could it hurt me? How could it help me?\n* Tell me about ____. What are they doing? What will they do next?\n* What will happen if I ____?",
42 | "Crit": "**Ask three** (Anyone can answer, Overlord has final say):\n* What is going on here? What do my senses tell me?\n* Is something hidden or out of place? If so, what looks suspicious?\n* Tell me about ____. How could it hurt me? How could it help me?\n* Tell me about ____. What are they doing? What will they do next?\n* What will happen if I ____?"
43 | },
44 | {
45 | "Name": "!speaksoftly",
46 | "Full": "Speak softly! (Wisdom)",
47 | "Miss": "**Ask one**:\n* What can they tell us about ____?\n* What were tehy doing, and what are they going to do next?\n* What should I be wary of when dealing with them? What do they want, and how could we help them get it?\n* What would they have us do next?",
48 | "Hit": "**Ask two**:\n* What can they tell us about ____?\n* What were tehy doing, and what are they going to do next?\n* What should I be wary of when dealing with them? What do they want, and how could we help them get it?\n* What would they have us do next?",
49 | "Crit": "**Ask three**:\n* What can they tell us about ____?\n* What were tehy doing, and what are they going to do next?\n* What should I be wary of when dealing with them? What do they want, and how could we help them get it?\n* What would they have us do next?"
50 | }
51 | ]
52 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/oauth2.go:
--------------------------------------------------------------------------------
1 | // Discordgo - Discord bindings for Go
2 | // Available at https://github.com/bwmarrin/discordgo
3 |
4 | // Copyright 2015-2016 Bruce Marriner . All rights reserved.
5 | // Use of this source code is governed by a BSD-style
6 | // license that can be found in the LICENSE file.
7 |
8 | // This file contains functions related to Discord OAuth2 endpoints
9 |
10 | package discordgo
11 |
12 | // ------------------------------------------------------------------------------------------------
13 | // Code specific to Discord OAuth2 Applications
14 | // ------------------------------------------------------------------------------------------------
15 |
16 | // An Application struct stores values for a Discord OAuth2 Application
17 | type Application struct {
18 | ID string `json:"id,omitempty"`
19 | Name string `json:"name"`
20 | Description string `json:"description,omitempty"`
21 | Icon string `json:"icon,omitempty"`
22 | Secret string `json:"secret,omitempty"`
23 | RedirectURIs *[]string `json:"redirect_uris,omitempty"`
24 | BotRequireCodeGrant bool `json:"bot_require_code_grant,omitempty"`
25 | BotPublic bool `json:"bot_public,omitempty"`
26 | RPCApplicationState int `json:"rpc_application_state,omitempty"`
27 | Flags int `json:"flags,omitempty"`
28 | Owner *User `json:"owner"`
29 | Bot *User `json:"bot"`
30 | }
31 |
32 | // Application returns an Application structure of a specific Application
33 | // appID : The ID of an Application
34 | func (s *Session) Application(appID string) (st *Application, err error) {
35 |
36 | body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication(""))
37 | if err != nil {
38 | return
39 | }
40 |
41 | err = unmarshal(body, &st)
42 | return
43 | }
44 |
45 | // Applications returns all applications for the authenticated user
46 | func (s *Session) Applications() (st []*Application, err error) {
47 |
48 | body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications)
49 | if err != nil {
50 | return
51 | }
52 |
53 | err = unmarshal(body, &st)
54 | return
55 | }
56 |
57 | // ApplicationCreate creates a new Application
58 | // name : Name of Application / Bot
59 | // uris : Redirect URIs (Not required)
60 | func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error) {
61 |
62 | data := struct {
63 | Name string `json:"name"`
64 | Description string `json:"description"`
65 | RedirectURIs *[]string `json:"redirect_uris,omitempty"`
66 | }{ap.Name, ap.Description, ap.RedirectURIs}
67 |
68 | body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications)
69 | if err != nil {
70 | return
71 | }
72 |
73 | err = unmarshal(body, &st)
74 | return
75 | }
76 |
77 | // ApplicationUpdate updates an existing Application
78 | // var : desc
79 | func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Application, err error) {
80 |
81 | data := struct {
82 | Name string `json:"name"`
83 | Description string `json:"description"`
84 | RedirectURIs *[]string `json:"redirect_uris,omitempty"`
85 | }{ap.Name, ap.Description, ap.RedirectURIs}
86 |
87 | body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication(""))
88 | if err != nil {
89 | return
90 | }
91 |
92 | err = unmarshal(body, &st)
93 | return
94 | }
95 |
96 | // ApplicationDelete deletes an existing Application
97 | // appID : The ID of an Application
98 | func (s *Session) ApplicationDelete(appID string) (err error) {
99 |
100 | _, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication(""))
101 | if err != nil {
102 | return
103 | }
104 |
105 | return
106 | }
107 |
108 | // ------------------------------------------------------------------------------------------------
109 | // Code specific to Discord OAuth2 Application Bots
110 | // ------------------------------------------------------------------------------------------------
111 |
112 | // ApplicationBotCreate creates an Application Bot Account
113 | //
114 | // appID : The ID of an Application
115 | //
116 | // NOTE: func name may change, if I can think up something better.
117 | func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) {
118 |
119 | body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot(""))
120 | if err != nil {
121 | return
122 | }
123 |
124 | err = unmarshal(body, &st)
125 | return
126 | }
127 |
--------------------------------------------------------------------------------
/data/game/apocalypse_world/basic.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Name": "!goaggro",
4 | "Full": "Go Aggro on Someone",
5 | "Miss": "Going aggro is a bust!",
6 | "Hit": "Your opponent chooses 1:\n* Get the hell out of your way.\n* Barricade themselves securely in.\n* Give you something they think you want.\n* Back off calmly, hands where you can see.\n* Tell you what you want to know (or what you want to hear).",
7 | "Crit": "Your opponent can either: force your hand and suck it up, or cave and do what you want."
8 | },
9 |
10 | {
11 | "Name": "!readsitch",
12 | "Full": "Read a Sitch",
13 | "Miss": "You can't get a read on what's going on...",
14 | "Hit": "**Ask the MC 1 of these questions**:\n* Where’s my best escape route / way in / way past?\n* Which enemy is most vulnerable to me?\n* Which enemy is the biggest threat?\n* What should I be on the lookout for?\n* What’s my enemy’s true position?\n* Who’s in control here?",
15 | "Crit": "**Ask the MC 3 of these questions**:\n* Where’s my best escape route / way in / way past?\n* Which enemy is most vulnerable to me?\n* Which enemy is the biggest threat?\n* What should I be on the lookout for?\n* What’s my enemy’s true position?\n* Who’s in control here?"
16 | },
17 | {
18 | "Name": "!openbrain",
19 | "Full": "Open Your Brain to Psychic Maelstrom",
20 | "Miss": "You open your brain to static. ***Prepare for the Worst.***",
21 | "Hit": "The MC **gives you an impression** of something new and interesting about the current situation, and might ask you a question or two; answer them. If you know all there is to know, the MC will tell you.",
22 | "Crit": "The MC provides **great detail** about something new and interesting about the current situation, and might ask you a question or two; answer them. If you know all there is to know, the MC will tell you."
23 | },
24 | {
25 | "Name": "!readperson",
26 | "Full": "Read a Person",
27 | "Miss": "**Ask these questions of the character, but prepare for the worst**:\n* Is your character telling the truth?\n* What’s your character really feeling?\n* What does your character intend to do?\n* What does your character wish I’d do?\n* How could I get your character to __?\n",
28 | "Hit": "Hold 1. **While you are interacting with the character, spend hold 1 for 1 to ask these questions**:\n* Is your character telling the truth?\n* What’s your character really feeling?\n* What does your character intend to do?\n* What does your character wish I’d do?\n* How could I get your character to __?\n",
29 | "Crit": "Hold 3. **While you are interacting with the character, spend hold 1 for 1 to ask these questions**:\n* Is your character telling the truth?\n* What’s your character really feeling?\n* What does your character intend to do?\n* What does your character wish I’d do?\n* How could I get your character to __?\n"
30 | },
31 | {
32 | "Name": "!help",
33 | "Full": "Help Someone",
34 | "Miss": "Your efforts to help have gone awry. ***Prepare for the Worst.***",
35 | "Hit": "The person you help takes **+1 to their roll.**",
36 | "Crit": "The person you help takes **+2 to their roll.**"
37 | },
38 | {
39 | "Name": "!interfere",
40 | "Full": "Interfere with Someone",
41 | "Miss": "Your efforts to interfere have gone awry. ***Prepare for the Worst.***",
42 | "Hit": "The person you interfere with takes **-1 to their roll.**",
43 | "Crit": "The person you interfere with takes **-2 to their roll.**"
44 | },
45 | {
46 | "Name": "!underfire",
47 | "Full": "Do Something Under Fire",
48 | "Miss": "You falter under fire. ***Prepare for the Worst.***",
49 | "Hit": "You flinch, hesitate, or stall: the MC **can offer you a worse outcome or an ugly choice.**",
50 | "Crit": "You do it."
51 | },
52 | {
53 | "Name": "!seducenpc",
54 | "Full": "Seduce or Manipulate an NPC",
55 | "Miss": "Your trickery fails. ***Prepare for the Worst.***",
56 | "Hit": "They’ll go along with you, but they need some concrete assurance, corroboration, or evidence first.",
57 | "Crit": "They'll go along with you, unless or until some fact or action betrays the reason you gave them."
58 | },
59 | {
60 | "Name": "!seducepc",
61 | "Full": "Seduce or Manipulate a PC",
62 | "Miss": "Your trickery fails. ***Prepare for the Worst.***",
63 | "Hit": "**Choose one:**\n* If they go along with you, they mark experience.\n* If they refuse, erase one of their stat highlights for the remainder of the session.\n* *What they do is up to them.*",
64 | "Crit": "**Both are true**\n* If they go along with you, they mark experience.\n* If they refuse, erase one of their stat highlights for the remainder of the session.\n* *What they do is up to them.*"
65 | }
66 | ]
67 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 | "github.com/bwmarrin/discordgo"
7 | "github.com/gamefiend/apocalyptica/pkg/moves"
8 | "log"
9 | "net/http"
10 | "os"
11 | "os/signal"
12 | "regexp"
13 | "strconv"
14 | "strings"
15 | "sync"
16 | "syscall"
17 | "time"
18 | )
19 |
20 | var (
21 | Intro = flag.Bool("intro", false, "Channel Introductions")
22 | )
23 |
24 | var Apoc = moves.LoadMoves("basic.json")
25 | var ApocList = MakeList(Apoc)
26 | var Announce = make(map[string]bool)
27 |
28 | func MakeList(mv moves.Move) []string {
29 | l := make([]string, 0)
30 | for _, c := range mv {
31 | l = append(l, fmt.Sprintf("**%s** (%s)", c.Full, c.Name))
32 | }
33 | return l
34 | }
35 |
36 | func getBonus(s string) int {
37 | reg := regexp.MustCompile(`(?P-?\d+)`)
38 | match := reg.FindStringSubmatch(s)
39 | result := make(map[string]string)
40 | if match == nil {
41 | return 0
42 | }
43 | for i, name := range reg.SubexpNames() {
44 | if i != 0 {
45 | result[name] = match[i]
46 | }
47 | }
48 | fmt.Printf("bonus is %+v\n", result["bonus"])
49 | bns, e := strconv.Atoi(result["bonus"])
50 | if e != nil {
51 | return 0
52 | }
53 | return bns
54 | }
55 |
56 | // Addhandler fires this up when Apocalyptica connects to another channel.
57 | func onReady(s *discordgo.Session, m *discordgo.Ready) {
58 | greetings := "**Apocalyptica**. Apocalypse World 2e bot. !!help for instructions, !moves for moves."
59 | log.Printf("Invited to %s servers\n", len(m.Guilds))
60 | for _, i := range m.Guilds {
61 | log.Printf("CONNECT [%s]\n", i.Name)
62 | // Wait for a few seconds to finish connection, otherwise we miss info.
63 | time.Sleep(2 * time.Second)
64 | for _, c := range i.Channels {
65 | log.Printf("%s.%s [%s]\n", i.Name, c.Name, c.ID)
66 | if c.Type == "text" && *Intro == true {
67 | s.ChannelMessageSend(c.ID, greetings)
68 | fmt.Printf("%s.%s Introduction\n", i.Name, c.Name)
69 | }
70 | }
71 | }
72 | }
73 |
74 | // Addhandler sends a message to this function any time a message on a channel this bot is listening to is created.
75 | func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
76 |
77 | // Ignore our own messages
78 | if m.Author.ID == s.State.User.ID {
79 | return
80 | }
81 | //here is where look for commands.
82 | // going to move this to another package, but just hardwiring this for an MVP right now.
83 | switch {
84 | case m.Content == "!!help":
85 | help := []string{
86 | "**Apocalyptica**. Apocalypse World 2e bot. !!help for instructions, !moves for moves.",
87 | "To use, type ! ",
88 | "**examples:**",
89 | "* !goaggro -1",
90 | "* !goaggro 1",
91 | "*!moves* shows which moves I currently support.",
92 | }
93 | s.ChannelMessageSend(m.ChannelID, strings.Join(help, "\n"))
94 |
95 | case m.Content == "!moves":
96 | help := "I currently support:"
97 | s.ChannelMessageSend(m.ChannelID, help)
98 | s.ChannelMessageSend(m.ChannelID, strings.Join(ApocList, "\n"))
99 | default:
100 | moveMsg := moves.FindMove(m.Content, Apoc)
101 | if len(moveMsg) > 0 {
102 | bonus := getBonus(m.Content)
103 | result := moveMsg.Roll(bonus)
104 | d := moveMsg.Display(result, bonus)
105 | s.ChannelMessageSend(m.ChannelID, d)
106 | log.Println(m.ChannelID, m.Author.ID, result, bonus)
107 | }
108 | }
109 | }
110 |
111 | func init() {
112 | flag.Parse()
113 | }
114 |
115 | func main() {
116 | var wg sync.WaitGroup
117 | //Grab Token from the Environment
118 | Token := os.Getenv("DISCORD_TOKEN")
119 | if len(Token) == 0 {
120 | fmt.Println("environment variable DISCORD_TOKEN not set")
121 | os.Exit(1)
122 | }
123 | //launch a small web server
124 |
125 | go func() {
126 | s, e := os.Getwd()
127 | if e != nil {
128 | log.Println("Can't access working directory", e)
129 | }
130 | httpwd := fmt.Sprintf("%s/static", s)
131 | fmt.Println(httpwd)
132 | fs := http.FileServer(http.Dir(httpwd))
133 | http.Handle("/", fs)
134 | log.Printf("listening on port 8080")
135 | http.ListenAndServe(":8080", nil)
136 | }()
137 | // new discord session with the provided bot token.
138 | dg, err := discordgo.New("Bot " + Token)
139 | if err != nil {
140 | log.Println("error creating Discord session,", err)
141 | }
142 |
143 | // Register the messageCreate func for a callbackto MessageCreate events
144 | dg.AddHandler(messageCreate)
145 | dg.AddHandler(onReady)
146 | // listen on that websocket connection!
147 | err = dg.Open()
148 | if err != nil {
149 | fmt.Println("Error opening connection,", err)
150 | return
151 | }
152 |
153 | //wait until we get Ctl-C/term signal
154 | fmt.Println(
155 | `Loading Moves...
156 | Apocalyptica is Barfing into your channel now...
157 | Press Ctl-C to exit.`)
158 | wg.Add(1)
159 | sc := make(chan os.Signal, 1)
160 | signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
161 | signals := <-sc
162 | dg.Close()
163 | fmt.Println("recieved ", signals)
164 | fmt.Println("Stopping....")
165 | wg.Done()
166 | }
167 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/importer.go:
--------------------------------------------------------------------------------
1 | package moq
2 |
3 | // taken from https://github.com/ernesto-jimenez/gogen
4 | // Copyright (c) 2015 Ernesto Jiménez
5 |
6 | import (
7 | "fmt"
8 | "go/ast"
9 | "go/importer"
10 | "go/parser"
11 | "go/token"
12 | "go/types"
13 | "io/ioutil"
14 | "os"
15 | "path"
16 | "path/filepath"
17 | "strings"
18 | )
19 |
20 | type customImporter struct {
21 | source string
22 | imported map[string]*types.Package
23 | base types.Importer
24 | skipTestFiles bool
25 | }
26 |
27 | func (i *customImporter) Import(path string) (*types.Package, error) {
28 | var err error
29 | if path == "" || path[0] == '.' {
30 | path, err = filepath.Abs(filepath.Clean(path))
31 | if err != nil {
32 | return nil, err
33 | }
34 | path = stripGopath(path)
35 | }
36 | if pkg, ok := i.imported[path]; ok {
37 | return pkg, nil
38 | }
39 | pkg, err := i.fsPkg(path)
40 | if err != nil {
41 | return nil, err
42 | }
43 | i.imported[path] = pkg
44 | return pkg, nil
45 | }
46 |
47 | func gopathDir(source, pkg string) (string, error) {
48 | // check vendor directory
49 | vendorPath, found := vendorPath(source, pkg)
50 | if found {
51 | return vendorPath, nil
52 | }
53 | for _, gopath := range gopaths() {
54 | absPath, err := filepath.Abs(path.Join(gopath, "src", pkg))
55 | if err != nil {
56 | return "", err
57 | }
58 | if dir, err := os.Stat(absPath); err == nil && dir.IsDir() {
59 | return absPath, nil
60 | }
61 | }
62 | return "", fmt.Errorf("%s not in $GOPATH or %s", pkg, path.Join(source, "vendor"))
63 | }
64 |
65 | func vendorPath(source, pkg string) (string, bool) {
66 | for {
67 | if isGopath(source) {
68 | return "", false
69 | }
70 | var err error
71 | source, err = filepath.Abs(source)
72 | if err != nil {
73 | return "", false
74 | }
75 | vendorPath, err := filepath.Abs(path.Join(source, "vendor", pkg))
76 | if err != nil {
77 | return "", false
78 | }
79 | if dir, err := os.Stat(vendorPath); err == nil && dir.IsDir() {
80 | return vendorPath, true
81 | }
82 | source = filepath.Dir(source)
83 | }
84 | }
85 |
86 | func removeGopath(p string) string {
87 | for _, gopath := range gopaths() {
88 | p = strings.Replace(p, path.Join(gopath, "src")+"/", "", 1)
89 | }
90 | return p
91 | }
92 |
93 | func gopaths() []string {
94 | return strings.Split(os.Getenv("GOPATH"), string(filepath.ListSeparator))
95 | }
96 |
97 | func isGopath(path string) bool {
98 | for _, p := range gopaths() {
99 | if p == path {
100 | return true
101 | }
102 | }
103 | return false
104 | }
105 |
106 | func (i *customImporter) fsPkg(pkg string) (*types.Package, error) {
107 | dir, err := gopathDir(i.source, pkg)
108 | if err != nil {
109 | return importOrErr(i.base, pkg, err)
110 | }
111 |
112 | dirFiles, err := ioutil.ReadDir(dir)
113 | if err != nil {
114 | return importOrErr(i.base, pkg, err)
115 | }
116 |
117 | fset := token.NewFileSet()
118 | var files []*ast.File
119 | for _, fileInfo := range dirFiles {
120 | if fileInfo.IsDir() {
121 | continue
122 | }
123 | n := fileInfo.Name()
124 | if path.Ext(fileInfo.Name()) != ".go" {
125 | continue
126 | }
127 | if i.skipTestFiles && strings.Contains(fileInfo.Name(), "_test.go") {
128 | continue
129 | }
130 | file := path.Join(dir, n)
131 | src, err := ioutil.ReadFile(file)
132 | if err != nil {
133 | return nil, err
134 | }
135 | f, err := parser.ParseFile(fset, file, src, 0)
136 | if err != nil {
137 | return nil, err
138 | }
139 | files = append(files, f)
140 | }
141 | conf := types.Config{
142 | Importer: i,
143 | }
144 | p, err := conf.Check(pkg, fset, files, nil)
145 |
146 | if err != nil {
147 | return importOrErr(i.base, pkg, err)
148 | }
149 | return p, nil
150 | }
151 |
152 | func importOrErr(base types.Importer, pkg string, err error) (*types.Package, error) {
153 | p, impErr := base.Import(pkg)
154 | if impErr != nil {
155 | return nil, err
156 | }
157 | return p, nil
158 | }
159 |
160 | // newImporter returns an importer that will try to import code from gopath before using go/importer.Default and skipping test files
161 | func newImporter(source string) types.Importer {
162 | return &customImporter{
163 | source: source,
164 | imported: make(map[string]*types.Package),
165 | base: importer.Default(),
166 | skipTestFiles: true,
167 | }
168 | }
169 |
170 | // // DefaultWithTestFiles same as Default but it parses test files too
171 | // func DefaultWithTestFiles() types.Importer {
172 | // return &customImporter{
173 | // imported: make(map[string]*types.Package),
174 | // base: importer.Default(),
175 | // skipTestFiles: false,
176 | // }
177 | // }
178 |
179 | // stripGopath teks the directory to a package and remove the gopath to get the
180 | // canonical package name.
181 | func stripGopath(p string) string {
182 | for _, gopath := range gopaths() {
183 | p = strings.TrimPrefix(p, path.Join(gopath, "src")+"/")
184 | }
185 | return p
186 | }
187 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/discord.go:
--------------------------------------------------------------------------------
1 | // Discordgo - Discord bindings for Go
2 | // Available at https://github.com/bwmarrin/discordgo
3 |
4 | // Copyright 2015-2016 Bruce Marriner . All rights reserved.
5 | // Use of this source code is governed by a BSD-style
6 | // license that can be found in the LICENSE file.
7 |
8 | // This file contains high level helper functions and easy entry points for the
9 | // entire discordgo package. These functions are beling developed and are very
10 | // experimental at this point. They will most likley change so please use the
11 | // low level functions if that's a problem.
12 |
13 | // Package discordgo provides Discord binding for Go
14 | package discordgo
15 |
16 | import (
17 | "errors"
18 | "fmt"
19 | "net/http"
20 | "time"
21 | )
22 |
23 | // VERSION of DiscordGo, follows Semantic Versioning. (http://semver.org/)
24 | const VERSION = "0.17.0"
25 |
26 | // ErrMFA will be risen by New when the user has 2FA.
27 | var ErrMFA = errors.New("account has 2FA enabled")
28 |
29 | // New creates a new Discord session and will automate some startup
30 | // tasks if given enough information to do so. Currently you can pass zero
31 | // arguments and it will return an empty Discord session.
32 | // There are 3 ways to call New:
33 | // With a single auth token - All requests will use the token blindly,
34 | // no verification of the token will be done and requests may fail.
35 | // IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
36 | // eg: `"Bot "`
37 | // With an email and password - Discord will sign in with the provided
38 | // credentials.
39 | // With an email, password and auth token - Discord will verify the auth
40 | // token, if it is invalid it will sign in with the provided
41 | // credentials. This is the Discord recommended way to sign in.
42 | //
43 | // NOTE: While email/pass authentication is supported by DiscordGo it is
44 | // HIGHLY DISCOURAGED by Discord. Please only use email/pass to obtain a token
45 | // and then use that authentication token for all future connections.
46 | // Also, doing any form of automation with a user (non Bot) account may result
47 | // in that account being permanently banned from Discord.
48 | func New(args ...interface{}) (s *Session, err error) {
49 |
50 | // Create an empty Session interface.
51 | s = &Session{
52 | State: NewState(),
53 | ratelimiter: NewRatelimiter(),
54 | StateEnabled: true,
55 | Compress: true,
56 | ShouldReconnectOnError: true,
57 | ShardID: 0,
58 | ShardCount: 1,
59 | MaxRestRetries: 3,
60 | Client: &http.Client{Timeout: (20 * time.Second)},
61 | sequence: new(int64),
62 | LastHeartbeatAck: time.Now().UTC(),
63 | }
64 |
65 | // If no arguments are passed return the empty Session interface.
66 | if args == nil {
67 | return
68 | }
69 |
70 | // Variables used below when parsing func arguments
71 | var auth, pass string
72 |
73 | // Parse passed arguments
74 | for _, arg := range args {
75 |
76 | switch v := arg.(type) {
77 |
78 | case []string:
79 | if len(v) > 3 {
80 | err = fmt.Errorf("too many string parameters provided")
81 | return
82 | }
83 |
84 | // First string is either token or username
85 | if len(v) > 0 {
86 | auth = v[0]
87 | }
88 |
89 | // If second string exists, it must be a password.
90 | if len(v) > 1 {
91 | pass = v[1]
92 | }
93 |
94 | // If third string exists, it must be an auth token.
95 | if len(v) > 2 {
96 | s.Token = v[2]
97 | }
98 |
99 | case string:
100 | // First string must be either auth token or username.
101 | // Second string must be a password.
102 | // Only 2 input strings are supported.
103 |
104 | if auth == "" {
105 | auth = v
106 | } else if pass == "" {
107 | pass = v
108 | } else if s.Token == "" {
109 | s.Token = v
110 | } else {
111 | err = fmt.Errorf("too many string parameters provided")
112 | return
113 | }
114 |
115 | // case Config:
116 | // TODO: Parse configuration struct
117 |
118 | default:
119 | err = fmt.Errorf("unsupported parameter type provided")
120 | return
121 | }
122 | }
123 |
124 | // If only one string was provided, assume it is an auth token.
125 | // Otherwise get auth token from Discord, if a token was specified
126 | // Discord will verify it for free, or log the user in if it is
127 | // invalid.
128 | if pass == "" {
129 | s.Token = auth
130 | } else {
131 | err = s.Login(auth, pass)
132 | if err != nil || s.Token == "" {
133 | if s.MFA {
134 | err = ErrMFA
135 | } else {
136 | err = fmt.Errorf("Unable to fetch discord authentication token. %v", err)
137 | }
138 | return
139 | }
140 | }
141 |
142 | // The Session is now able to have RestAPI methods called on it.
143 | // It is recommended that you now call Open() so that events will trigger.
144 |
145 | return
146 | }
147 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/ratelimit.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "net/http"
5 | "strconv"
6 | "strings"
7 | "sync"
8 | "sync/atomic"
9 | "time"
10 | )
11 |
12 | // customRateLimit holds information for defining a custom rate limit
13 | type customRateLimit struct {
14 | suffix string
15 | requests int
16 | reset time.Duration
17 | }
18 |
19 | // RateLimiter holds all ratelimit buckets
20 | type RateLimiter struct {
21 | sync.Mutex
22 | global *int64
23 | buckets map[string]*Bucket
24 | globalRateLimit time.Duration
25 | customRateLimits []*customRateLimit
26 | }
27 |
28 | // NewRatelimiter returns a new RateLimiter
29 | func NewRatelimiter() *RateLimiter {
30 |
31 | return &RateLimiter{
32 | buckets: make(map[string]*Bucket),
33 | global: new(int64),
34 | customRateLimits: []*customRateLimit{
35 | &customRateLimit{
36 | suffix: "//reactions//",
37 | requests: 1,
38 | reset: 200 * time.Millisecond,
39 | },
40 | },
41 | }
42 | }
43 |
44 | // getBucket retrieves or creates a bucket
45 | func (r *RateLimiter) getBucket(key string) *Bucket {
46 | r.Lock()
47 | defer r.Unlock()
48 |
49 | if bucket, ok := r.buckets[key]; ok {
50 | return bucket
51 | }
52 |
53 | b := &Bucket{
54 | remaining: 1,
55 | Key: key,
56 | global: r.global,
57 | }
58 |
59 | // Check if there is a custom ratelimit set for this bucket ID.
60 | for _, rl := range r.customRateLimits {
61 | if strings.HasSuffix(b.Key, rl.suffix) {
62 | b.customRateLimit = rl
63 | break
64 | }
65 | }
66 |
67 | r.buckets[key] = b
68 | return b
69 | }
70 |
71 | // LockBucket Locks until a request can be made
72 | func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
73 |
74 | b := r.getBucket(bucketID)
75 |
76 | b.Lock()
77 |
78 | // If we ran out of calls and the reset time is still ahead of us
79 | // then we need to take it easy and relax a little
80 | if b.remaining < 1 && b.reset.After(time.Now()) {
81 | time.Sleep(b.reset.Sub(time.Now()))
82 |
83 | }
84 |
85 | // Check for global ratelimits
86 | sleepTo := time.Unix(0, atomic.LoadInt64(r.global))
87 | if now := time.Now(); now.Before(sleepTo) {
88 | time.Sleep(sleepTo.Sub(now))
89 | }
90 |
91 | b.remaining--
92 | return b
93 | }
94 |
95 | // Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits)
96 | type Bucket struct {
97 | sync.Mutex
98 | Key string
99 | remaining int
100 | limit int
101 | reset time.Time
102 | global *int64
103 |
104 | lastReset time.Time
105 | customRateLimit *customRateLimit
106 | }
107 |
108 | // Release unlocks the bucket and reads the headers to update the buckets ratelimit info
109 | // and locks up the whole thing in case if there's a global ratelimit.
110 | func (b *Bucket) Release(headers http.Header) error {
111 | defer b.Unlock()
112 |
113 | // Check if the bucket uses a custom ratelimiter
114 | if rl := b.customRateLimit; rl != nil {
115 | if time.Now().Sub(b.lastReset) >= rl.reset {
116 | b.remaining = rl.requests - 1
117 | b.lastReset = time.Now()
118 | }
119 | if b.remaining < 1 {
120 | b.reset = time.Now().Add(rl.reset)
121 | }
122 | return nil
123 | }
124 |
125 | if headers == nil {
126 | return nil
127 | }
128 |
129 | remaining := headers.Get("X-RateLimit-Remaining")
130 | reset := headers.Get("X-RateLimit-Reset")
131 | global := headers.Get("X-RateLimit-Global")
132 | retryAfter := headers.Get("Retry-After")
133 |
134 | // Update global and per bucket reset time if the proper headers are available
135 | // If global is set, then it will block all buckets until after Retry-After
136 | // If Retry-After without global is provided it will use that for the new reset
137 | // time since it's more accurate than X-RateLimit-Reset.
138 | // If Retry-After after is not proided, it will update the reset time from X-RateLimit-Reset
139 | if retryAfter != "" {
140 | parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64)
141 | if err != nil {
142 | return err
143 | }
144 |
145 | resetAt := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
146 |
147 | // Lock either this single bucket or all buckets
148 | if global != "" {
149 | atomic.StoreInt64(b.global, resetAt.UnixNano())
150 | } else {
151 | b.reset = resetAt
152 | }
153 | } else if reset != "" {
154 | // Calculate the reset time by using the date header returned from discord
155 | discordTime, err := http.ParseTime(headers.Get("Date"))
156 | if err != nil {
157 | return err
158 | }
159 |
160 | unix, err := strconv.ParseInt(reset, 10, 64)
161 | if err != nil {
162 | return err
163 | }
164 |
165 | // Calculate the time until reset and add it to the current local time
166 | // some extra time is added because without it i still encountered 429's.
167 | // The added amount is the lowest amount that gave no 429's
168 | // in 1k requests
169 | delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250
170 | b.reset = time.Now().Add(delta)
171 | }
172 |
173 | // Udpate remaining if header is present
174 | if remaining != "" {
175 | parsedRemaining, err := strconv.ParseInt(remaining, 10, 32)
176 | if err != nil {
177 | return err
178 | }
179 | b.remaining = int(parsedRemaining)
180 | }
181 |
182 | return nil
183 | }
184 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/binary"
5 | "flag"
6 | "fmt"
7 | "io"
8 | "os"
9 | "os/signal"
10 | "strings"
11 | "syscall"
12 | "time"
13 |
14 | "github.com/bwmarrin/discordgo"
15 | )
16 |
17 | func init() {
18 | flag.StringVar(&token, "t", "", "Bot Token")
19 | flag.Parse()
20 | }
21 |
22 | var token string
23 | var buffer = make([][]byte, 0)
24 |
25 | func main() {
26 |
27 | if token == "" {
28 | fmt.Println("No token provided. Please run: airhorn -t ")
29 | return
30 | }
31 |
32 | // Load the sound file.
33 | err := loadSound()
34 | if err != nil {
35 | fmt.Println("Error loading sound: ", err)
36 | fmt.Println("Please copy $GOPATH/src/github.com/bwmarrin/examples/airhorn/airhorn.dca to this directory.")
37 | return
38 | }
39 |
40 | // Create a new Discord session using the provided bot token.
41 | dg, err := discordgo.New("Bot " + token)
42 | if err != nil {
43 | fmt.Println("Error creating Discord session: ", err)
44 | return
45 | }
46 |
47 | // Register ready as a callback for the ready events.
48 | dg.AddHandler(ready)
49 |
50 | // Register messageCreate as a callback for the messageCreate events.
51 | dg.AddHandler(messageCreate)
52 |
53 | // Register guildCreate as a callback for the guildCreate events.
54 | dg.AddHandler(guildCreate)
55 |
56 | // Open the websocket and begin listening.
57 | err = dg.Open()
58 | if err != nil {
59 | fmt.Println("Error opening Discord session: ", err)
60 | }
61 |
62 | // Wait here until CTRL-C or other term signal is received.
63 | fmt.Println("Airhorn is now running. Press CTRL-C to exit.")
64 | sc := make(chan os.Signal, 1)
65 | signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
66 | <-sc
67 |
68 | // Cleanly close down the Discord session.
69 | dg.Close()
70 | }
71 |
72 | // This function will be called (due to AddHandler above) when the bot receives
73 | // the "ready" event from Discord.
74 | func ready(s *discordgo.Session, event *discordgo.Ready) {
75 |
76 | // Set the playing status.
77 | s.UpdateStatus(0, "!airhorn")
78 | }
79 |
80 | // This function will be called (due to AddHandler above) every time a new
81 | // message is created on any channel that the autenticated bot has access to.
82 | func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
83 |
84 | // Ignore all messages created by the bot itself
85 | // This isn't required in this specific example but it's a good practice.
86 | if m.Author.ID == s.State.User.ID {
87 | return
88 | }
89 |
90 | // check if the message is "!airhorn"
91 | if strings.HasPrefix(m.Content, "!airhorn") {
92 |
93 | // Find the channel that the message came from.
94 | c, err := s.State.Channel(m.ChannelID)
95 | if err != nil {
96 | // Could not find channel.
97 | return
98 | }
99 |
100 | // Find the guild for that channel.
101 | g, err := s.State.Guild(c.GuildID)
102 | if err != nil {
103 | // Could not find guild.
104 | return
105 | }
106 |
107 | // Look for the message sender in that guild's current voice states.
108 | for _, vs := range g.VoiceStates {
109 | if vs.UserID == m.Author.ID {
110 | err = playSound(s, g.ID, vs.ChannelID)
111 | if err != nil {
112 | fmt.Println("Error playing sound:", err)
113 | }
114 |
115 | return
116 | }
117 | }
118 | }
119 | }
120 |
121 | // This function will be called (due to AddHandler above) every time a new
122 | // guild is joined.
123 | func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
124 |
125 | if event.Guild.Unavailable {
126 | return
127 | }
128 |
129 | for _, channel := range event.Guild.Channels {
130 | if channel.ID == event.Guild.ID {
131 | _, _ = s.ChannelMessageSend(channel.ID, "Airhorn is ready! Type !airhorn while in a voice channel to play a sound.")
132 | return
133 | }
134 | }
135 | }
136 |
137 | // loadSound attempts to load an encoded sound file from disk.
138 | func loadSound() error {
139 |
140 | file, err := os.Open("airhorn.dca")
141 | if err != nil {
142 | fmt.Println("Error opening dca file :", err)
143 | return err
144 | }
145 |
146 | var opuslen int16
147 |
148 | for {
149 | // Read opus frame length from dca file.
150 | err = binary.Read(file, binary.LittleEndian, &opuslen)
151 |
152 | // If this is the end of the file, just return.
153 | if err == io.EOF || err == io.ErrUnexpectedEOF {
154 | err := file.Close()
155 | if err != nil {
156 | return err
157 | }
158 | return nil
159 | }
160 |
161 | if err != nil {
162 | fmt.Println("Error reading from dca file :", err)
163 | return err
164 | }
165 |
166 | // Read encoded pcm from dca file.
167 | InBuf := make([]byte, opuslen)
168 | err = binary.Read(file, binary.LittleEndian, &InBuf)
169 |
170 | // Should not be any end of file errors
171 | if err != nil {
172 | fmt.Println("Error reading from dca file :", err)
173 | return err
174 | }
175 |
176 | // Append encoded pcm data to the buffer.
177 | buffer = append(buffer, InBuf)
178 | }
179 | }
180 |
181 | // playSound plays the current buffer to the provided channel.
182 | func playSound(s *discordgo.Session, guildID, channelID string) (err error) {
183 |
184 | // Join the provided voice channel.
185 | vc, err := s.ChannelVoiceJoin(guildID, channelID, false, true)
186 | if err != nil {
187 | return err
188 | }
189 |
190 | // Sleep for a specified amount of time before playing the sound
191 | time.Sleep(250 * time.Millisecond)
192 |
193 | // Start speaking.
194 | vc.Speaking(true)
195 |
196 | // Send the buffer data.
197 | for _, buff := range buffer {
198 | vc.OpusSend <- buff
199 | }
200 |
201 | // Stop speaking
202 | vc.Speaking(false)
203 |
204 | // Sleep for a specificed amount of time before ending.
205 | time.Sleep(250 * time.Millisecond)
206 |
207 | // Disconnect from the provided voice channel.
208 | vc.Disconnect()
209 |
210 | return nil
211 | }
212 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/README.md:
--------------------------------------------------------------------------------
1 | # DiscordGo
2 |
3 | [](https://godoc.org/github.com/bwmarrin/discordgo) [](http://goreportcard.com/report/bwmarrin/discordgo) [](https://travis-ci.org/bwmarrin/discordgo) [](https://discord.gg/0f1SbxBZjYoCtNPP) [](https://discord.gg/0SBTUU1wZTWT6sqd)
4 |
5 |
6 |
7 | DiscordGo is a [Go](https://golang.org/) package that provides low level
8 | bindings to the [Discord](https://discordapp.com/) chat client API. DiscordGo
9 | has nearly complete support for all of the Discord API endpoints, websocket
10 | interface, and voice interface.
11 |
12 | If you would like to help the DiscordGo package please use
13 | [this link](https://discordapp.com/oauth2/authorize?client_id=173113690092994561&scope=bot)
14 | to add the official DiscordGo test bot **dgo** to your server. This provides
15 | indispensable help to this project.
16 |
17 | * See [dgVoice](https://github.com/bwmarrin/dgvoice) package for an example of
18 | additional voice helper functions and features for DiscordGo
19 |
20 | * See [dca](https://github.com/bwmarrin/dca) for an **experimental** stand alone
21 | tool that wraps `ffmpeg` to create opus encoded audio appropriate for use with
22 | Discord (and DiscordGo)
23 |
24 | **For help with this package or general Go discussion, please join the [Discord
25 | Gophers](https://discord.gg/0f1SbxBZjYq9jLBk) chat server.**
26 |
27 | ## Getting Started
28 |
29 | ### master vs develop Branch
30 | * The master branch represents the latest released version of DiscordGo. This
31 | branch will always have a stable and tested version of the library. Each release
32 | is tagged and you can easily download a specific release and view release notes
33 | on the github [releases](https://github.com/bwmarrin/discordgo/releases) page.
34 |
35 | * The develop branch is where all development happens and almost always has
36 | new features over the master branch. However breaking changes are frequently
37 | added to develop and even sometimes bugs are introduced. Bugs get fixed and
38 | the breaking changes get documented before pushing to master.
39 |
40 | *So, what should you use?*
41 |
42 | If you can accept the constant changing nature of *develop* then it is the
43 | recommended branch to use. Otherwise, if you want to tail behind development
44 | slightly and have a more stable package with documented releases then use *master*
45 |
46 | ### Installing
47 |
48 | This assumes you already have a working Go environment, if not please see
49 | [this page](https://golang.org/doc/install) first.
50 |
51 | `go get` *will always pull the latest released version from the master branch.*
52 |
53 | ```sh
54 | go get github.com/bwmarrin/discordgo
55 | ```
56 |
57 | If you want to use the develop branch, follow these steps next.
58 |
59 | ```sh
60 | cd $GOPATH/src/github.com/bwmarrin/discordgo
61 | git checkout develop
62 | ```
63 |
64 | ### Usage
65 |
66 | Import the package into your project.
67 |
68 | ```go
69 | import "github.com/bwmarrin/discordgo"
70 | ```
71 |
72 | Construct a new Discord client which can be used to access the variety of
73 | Discord API functions and to set callback functions for Discord events.
74 |
75 | ```go
76 | discord, err := discordgo.New("authentication token")
77 | ```
78 |
79 | See Documentation and Examples below for more detailed information.
80 |
81 |
82 | ## Documentation
83 |
84 | **NOTICE** : This library and the Discord API are unfinished.
85 | Because of that there may be major changes to library in the future.
86 |
87 | The DiscordGo code is fairly well documented at this point and is currently
88 | the only documentation available. Both GoDoc and GoWalker (below) present
89 | that information in a nice format.
90 |
91 | - [](https://godoc.org/github.com/bwmarrin/discordgo)
92 | - [](https://gowalker.org/github.com/bwmarrin/discordgo)
93 | - Hand crafted documentation coming eventually.
94 |
95 |
96 | ## Examples
97 |
98 | Below is a list of examples and other projects using DiscordGo. Please submit
99 | an issue if you would like your project added or removed from this list
100 |
101 | - [DiscordGo Examples](https://github.com/bwmarrin/discordgo/tree/master/examples) A collection of example programs written with DiscordGo
102 | - [Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo) A curated list of high quality projects using DiscordGo
103 |
104 | ## Troubleshooting
105 | For help with common problems please reference the
106 | [Troubleshooting](https://github.com/bwmarrin/discordgo/wiki/Troubleshooting)
107 | section of the project wiki.
108 |
109 |
110 | ## Contributing
111 | Contributions are very welcomed, however please follow the below guidelines.
112 |
113 | - First open an issue describing the bug or enhancement so it can be
114 | discussed.
115 | - Fork the develop branch and make your changes.
116 | - Try to match current naming conventions as closely as possible.
117 | - This package is intended to be a low level direct mapping of the Discord API
118 | so please avoid adding enhancements outside of that scope without first
119 | discussing it.
120 | - Create a Pull Request with your changes against the develop branch.
121 |
122 |
123 | ## List of Discord APIs
124 |
125 | See [this chart](https://abal.moe/Discord/Libraries.html) for a feature
126 | comparison and list of other Discord API libraries.
127 |
128 | ## Special Thanks
129 |
130 | [Chris Rhodes](https://github.com/iopred) - For the DiscordGo logo and tons of PRs
131 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/moq.go:
--------------------------------------------------------------------------------
1 | package moq
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "fmt"
7 | "go/ast"
8 | "go/format"
9 | "go/parser"
10 | "go/token"
11 | "go/types"
12 | "io"
13 | "os"
14 | "strings"
15 | "text/template"
16 | )
17 |
18 | // Mocker can generate mock structs.
19 | type Mocker struct {
20 | src string
21 | tmpl *template.Template
22 | fset *token.FileSet
23 | pkgs map[string]*ast.Package
24 | pkgName string
25 |
26 | imports map[string]bool
27 | }
28 |
29 | // New makes a new Mocker for the specified package directory.
30 | func New(src, packageName string) (*Mocker, error) {
31 | fset := token.NewFileSet()
32 | noTestFiles := func(i os.FileInfo) bool {
33 | return !strings.HasSuffix(i.Name(), "_test.go")
34 | }
35 | pkgs, err := parser.ParseDir(fset, src, noTestFiles, parser.SpuriousErrors)
36 | if err != nil {
37 | return nil, err
38 | }
39 | if len(packageName) == 0 {
40 | for pkgName := range pkgs {
41 | if strings.Contains(pkgName, "_test") {
42 | continue
43 | }
44 | packageName = pkgName
45 | break
46 | }
47 | }
48 | if len(packageName) == 0 {
49 | return nil, errors.New("failed to determine package name")
50 | }
51 | tmpl, err := template.New("moq").Funcs(templateFuncs).Parse(moqTemplate)
52 | if err != nil {
53 | return nil, err
54 | }
55 | return &Mocker{
56 | src: src,
57 | tmpl: tmpl,
58 | fset: fset,
59 | pkgs: pkgs,
60 | pkgName: packageName,
61 | imports: make(map[string]bool),
62 | }, nil
63 | }
64 |
65 | // Mock generates a mock for the specified interface name.
66 | func (m *Mocker) Mock(w io.Writer, name ...string) error {
67 | if len(name) == 0 {
68 | return errors.New("must specify one interface")
69 | }
70 | doc := doc{
71 | PackageName: m.pkgName,
72 | Imports: moqImports,
73 | }
74 | mocksMethods := false
75 | for _, pkg := range m.pkgs {
76 | i := 0
77 | files := make([]*ast.File, len(pkg.Files))
78 | for _, file := range pkg.Files {
79 | files[i] = file
80 | i++
81 | }
82 | conf := types.Config{Importer: newImporter(m.src)}
83 | tpkg, err := conf.Check(m.src, m.fset, files, nil)
84 | if err != nil {
85 | return err
86 | }
87 | for _, n := range name {
88 | iface := tpkg.Scope().Lookup(n)
89 | if iface == nil {
90 | return fmt.Errorf("cannot find interface %s", n)
91 | }
92 | if !types.IsInterface(iface.Type()) {
93 | return fmt.Errorf("%s (%s) not an interface", n, iface.Type().String())
94 | }
95 | iiface := iface.Type().Underlying().(*types.Interface).Complete()
96 | obj := obj{
97 | InterfaceName: n,
98 | }
99 | for i := 0; i < iiface.NumMethods(); i++ {
100 | mocksMethods = true
101 | meth := iiface.Method(i)
102 | sig := meth.Type().(*types.Signature)
103 | method := &method{
104 | Name: meth.Name(),
105 | }
106 | obj.Methods = append(obj.Methods, method)
107 | method.Params = m.extractArgs(sig, sig.Params(), "in%d")
108 | method.Returns = m.extractArgs(sig, sig.Results(), "out%d")
109 | }
110 | doc.Objects = append(doc.Objects, obj)
111 | }
112 | }
113 | if mocksMethods {
114 | doc.Imports = append(doc.Imports, "sync")
115 | }
116 | for pkgToImport := range m.imports {
117 | doc.Imports = append(doc.Imports, pkgToImport)
118 | }
119 | var buf bytes.Buffer
120 | err := m.tmpl.Execute(&buf, doc)
121 | if err != nil {
122 | return err
123 | }
124 | formatted, err := format.Source(buf.Bytes())
125 | if err != nil {
126 | return fmt.Errorf("go/format: %s", err)
127 | }
128 | if _, err := w.Write(formatted); err != nil {
129 | return err
130 | }
131 | return nil
132 | }
133 |
134 | func (m *Mocker) packageQualifier(pkg *types.Package) string {
135 | if m.pkgName == pkg.Name() {
136 | return ""
137 | }
138 | path := pkg.Path()
139 | if pkg.Path() == "." {
140 | wd, err := os.Getwd()
141 | if err == nil {
142 | path = stripGopath(wd)
143 | }
144 | }
145 | m.imports[path] = true
146 | return pkg.Name()
147 | }
148 |
149 | func (m *Mocker) extractArgs(sig *types.Signature, list *types.Tuple, nameFormat string) []*param {
150 | var params []*param
151 | listLen := list.Len()
152 | for ii := 0; ii < listLen; ii++ {
153 | p := list.At(ii)
154 | name := p.Name()
155 | if name == "" {
156 | name = fmt.Sprintf(nameFormat, ii+1)
157 | }
158 | typename := types.TypeString(p.Type(), m.packageQualifier)
159 | // check for final variadic argument
160 | variadic := sig.Variadic() && ii == listLen-1 && typename[0:2] == "[]"
161 | param := ¶m{
162 | Name: name,
163 | Type: typename,
164 | Variadic: variadic,
165 | }
166 | params = append(params, param)
167 | }
168 | return params
169 | }
170 |
171 | type doc struct {
172 | PackageName string
173 | Objects []obj
174 | Imports []string
175 | }
176 |
177 | type obj struct {
178 | InterfaceName string
179 | Methods []*method
180 | }
181 | type method struct {
182 | Name string
183 | Params []*param
184 | Returns []*param
185 | }
186 |
187 | func (m *method) Arglist() string {
188 | params := make([]string, len(m.Params))
189 | for i, p := range m.Params {
190 | params[i] = p.String()
191 | }
192 | return strings.Join(params, ", ")
193 | }
194 |
195 | func (m *method) ArgCallList() string {
196 | params := make([]string, len(m.Params))
197 | for i, p := range m.Params {
198 | params[i] = p.CallName()
199 | }
200 | return strings.Join(params, ", ")
201 | }
202 |
203 | func (m *method) ReturnArglist() string {
204 | params := make([]string, len(m.Returns))
205 | for i, p := range m.Returns {
206 | params[i] = p.TypeString()
207 | }
208 | if len(m.Returns) > 1 {
209 | return fmt.Sprintf("(%s)", strings.Join(params, ", "))
210 | }
211 | return strings.Join(params, ", ")
212 | }
213 |
214 | type param struct {
215 | Name string
216 | Type string
217 | Variadic bool
218 | }
219 |
220 | func (p param) String() string {
221 | return fmt.Sprintf("%s %s", p.Name, p.TypeString())
222 | }
223 |
224 | func (p param) CallName() string {
225 | if p.Variadic {
226 | return p.Name + "..."
227 | }
228 | return p.Name
229 | }
230 |
231 | func (p param) TypeString() string {
232 | if p.Variadic {
233 | return "..." + p.Type[2:]
234 | }
235 | return p.Type
236 | }
237 |
238 | var templateFuncs = template.FuncMap{
239 | "Exported": func(s string) string {
240 | if s == "" {
241 | return ""
242 | }
243 | return strings.ToUpper(s[0:1]) + s[1:]
244 | },
245 | }
246 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/docs/GettingStarted.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | This page is dedicated to helping you get started on your way to making the
4 | next great Discord bot or client with DiscordGo. Once you've done that please
5 | don't forget to submit it to the
6 | [Awesome DiscordGo](https://github.com/bwmarrin/discordgo/wiki/Awesome-DiscordGo) list :).
7 |
8 |
9 | **First, lets cover a few topics so you can make the best choices on how to
10 | move forward from here.**
11 |
12 |
13 | ### Master vs Develop
14 | **When installing DiscordGo you will need to decide if you want to use the current
15 | master branch or the bleeding edge development branch.**
16 |
17 | * The **master** branch represents the latest released version of DiscordGo. This
18 | branch will always have a stable and tested version of the library. Each
19 | release is tagged and you can easily download a specific release and view the
20 | release notes on the github [releases](https://github.com/bwmarrin/discordgo/releases)
21 | page.
22 |
23 | * The **develop** branch is where all development happens and almost always has
24 | new features over the master branch. However breaking changes are frequently
25 | added the develop branch and sometimes bugs are introduced. Bugs get fixed
26 | and the breaking changes get documented before pushing to master.
27 |
28 | *So, what should you use?*
29 |
30 | Due to the how frequently the Discord API is changing there is a high chance
31 | that the *master* branch may be lacking important features. Because of that, if
32 | you can accept the constant changing nature of the *develop* branch and the
33 | chance that it may occasionally contain bugs then it is the recommended branch
34 | to use. Otherwise, if you want to tail behind development slightly and have a
35 | more stable package with documented releases then please use the *master*
36 | branch instead.
37 |
38 |
39 | ### Client vs Bot
40 |
41 | You probably already know the answer to this but now is a good time to decide
42 | if your goal is to write a client application or a bot. DiscordGo aims to fully
43 | support both client applications and bots but there are some differences
44 | between the two that you should understand.
45 |
46 | #### Client Application
47 | A client application is a program that is intended to be used by a normal user
48 | as a replacement for the official clients that Discord provides. An example of
49 | this would be a terminal client used to read and send messages with your normal
50 | user account or possibly a new desktop client that provides a different set of
51 | features than the official desktop client that Discord already provides.
52 |
53 | Client applications work with normal user accounts and you can login with an
54 | email address and password or a special authentication token. However, normal
55 | user accounts are not allowed to perform any type of automation and doing so can
56 | cause the account to be banned from Discord. Also normal user accounts do not
57 | support multi-server voice connections and some other features that are
58 | exclusive to Bot accounts only.
59 |
60 | To create a new user account (if you have not done so already) visit the
61 | [Discord](https://discordapp.com/) website and click on the
62 | **Try Discord Now, It's Free** button then follow the steps to setup your
63 | new account.
64 |
65 |
66 | #### Bot Application
67 | A bot application is a special program that interacts with the Discord servers
68 | to perform some form of automation or provide some type of service. Examples
69 | are things like number trivia games, music streaming, channel moderation,
70 | sending reminders, playing loud airhorn sounds, comic generators, YouTube
71 | integration, Twitch integration.. You're *almost* only limited by your imagination.
72 |
73 | Bot applications require the use of a special Bot account. These accounts are
74 | tied to your personal user account. Bot accounts cannot login with the normal
75 | user clients and they cannot join servers the same way a user does. They do not
76 | have access to some user client specific features however they gain access to
77 | many Bot specific features.
78 |
79 | To create a new bot account first create yourself a normal user account on
80 | Discord then visit the [My Applications](https://discordapp.com/developers/applications/me)
81 | page and click on the **New Application** box. Follow the prompts from there
82 | to finish creating your account.
83 |
84 |
85 | **More information about Bots vs Client accounts can be found [here](https://discordapp.com/developers/docs/topics/oauth2#bot-vs-user-accounts)**
86 |
87 | # Requirements
88 |
89 | DiscordGo requires Go version 1.4 or higher. It has been tested to compile and
90 | run successfully on Debian Linux 8, FreeBSD 10, and Windows 7. It is expected
91 | that it should work anywhere Go 1.4 or higher works. If you run into problems
92 | please let us know :)
93 |
94 | You must already have a working Go environment setup to use DiscordGo. If you
95 | are new to Go and have not yet installed and tested it on your computer then
96 | please visit [this page](https://golang.org/doc/install) first then I highly
97 | recommend you walk though [A Tour of Go](https://tour.golang.org/welcome/1) to
98 | help get your familiar with the Go language. Also checkout the relevent Go plugin
99 | for your editor - they are hugely helpful when developing Go code.
100 |
101 | * Vim - [vim-go](https://github.com/fatih/vim-go)
102 | * Sublime - [GoSublime](https://github.com/DisposaBoy/GoSublime)
103 | * Atom - [go-plus](https://atom.io/packages/go-plus)
104 | * Visual Studio - [vscode-go](https://github.com/Microsoft/vscode-go)
105 |
106 |
107 | # Install DiscordGo
108 |
109 | Like any other Go package the fist step is to `go get` the package. This will
110 | always pull the latest released version from the master branch. Then run
111 | `go install` to compile and install the libraries on your system.
112 |
113 | #### Linux/BSD
114 |
115 | Run go get to download the package to your GOPATH/src folder.
116 |
117 | ```sh
118 | go get github.com/bwmarrin/discordgo
119 | ```
120 |
121 | If you want to use the develop branch, follow these steps next.
122 |
123 | ```sh
124 | cd $GOPATH/src/github.com/bwmarrin/discordgo
125 | git checkout develop
126 | ```
127 |
128 | Finally, compile and install the package into the GOPATH/pkg folder. This isn't
129 | absolutely required but doing this will allow the Go plugin for your editor to
130 | provide autocomplete for all DiscordGo functions.
131 |
132 | ```sh
133 | cd $GOPATH/src/github.com/bwmarrin/discordgo
134 | go install
135 | ```
136 |
137 | #### Windows
138 | Placeholder.
139 |
140 |
141 | # Next...
142 | More coming soon.
143 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/restapi_test.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | //////////////////////////////////////////////////////////////////////////////
8 | /////////////////////////////////////////////////////////////// START OF TESTS
9 |
10 | // TestChannelMessageSend tests the ChannelMessageSend() function. This should not return an error.
11 | func TestChannelMessageSend(t *testing.T) {
12 |
13 | if envChannel == "" {
14 | t.Skip("Skipping, DG_CHANNEL not set.")
15 | }
16 |
17 | if dg == nil {
18 | t.Skip("Skipping, dg not set.")
19 | }
20 |
21 | _, err := dg.ChannelMessageSend(envChannel, "Running REST API Tests!")
22 | if err != nil {
23 | t.Errorf("ChannelMessageSend returned error: %+v", err)
24 | }
25 | }
26 |
27 | /*
28 | // removed for now, only works on BOT accounts now
29 | func TestUserAvatar(t *testing.T) {
30 |
31 | if dg == nil {
32 | t.Skip("Cannot TestUserAvatar, dg not set.")
33 | }
34 |
35 | u, err := dg.User("@me")
36 | if err != nil {
37 | t.Error("error fetching @me user,", err)
38 | }
39 |
40 | a, err := dg.UserAvatar(u.ID)
41 | if err != nil {
42 | if err.Error() == `HTTP 404 NOT FOUND, {"code": 0, "message": "404: Not Found"}` {
43 | t.Skip("Skipped, @me doesn't have an Avatar")
44 | }
45 | t.Errorf(err.Error())
46 | }
47 |
48 | if a == nil {
49 | t.Errorf("a == nil, should be image.Image")
50 | }
51 | }
52 | */
53 |
54 | /* Running this causes an error due to 2/hour rate limit on username changes
55 | func TestUserUpdate(t *testing.T) {
56 | if dg == nil {
57 | t.Skip("Cannot test logout, dg not set.")
58 | }
59 |
60 | u, err := dg.User("@me")
61 | if err != nil {
62 | t.Errorf(err.Error())
63 | }
64 |
65 | s, err := dg.UserUpdate(envEmail, envPassword, "testname", u.Avatar, "")
66 | if err != nil {
67 | t.Error(err.Error())
68 | }
69 | if s.Username != "testname" {
70 | t.Error("Username != testname")
71 | }
72 | s, err = dg.UserUpdate(envEmail, envPassword, u.Username, u.Avatar, "")
73 | if err != nil {
74 | t.Error(err.Error())
75 | }
76 | if s.Username != u.Username {
77 | t.Error("Username != " + u.Username)
78 | }
79 | }
80 | */
81 |
82 | //func (s *Session) UserChannelCreate(recipientID string) (st *Channel, err error) {
83 |
84 | func TestUserChannelCreate(t *testing.T) {
85 | if dg == nil {
86 | t.Skip("Cannot TestUserChannelCreate, dg not set.")
87 | }
88 |
89 | if envAdmin == "" {
90 | t.Skip("Skipped, DG_ADMIN not set.")
91 | }
92 |
93 | _, err := dg.UserChannelCreate(envAdmin)
94 | if err != nil {
95 | t.Errorf(err.Error())
96 | }
97 |
98 | // TODO make sure the channel was added
99 | }
100 |
101 | func TestUserChannels(t *testing.T) {
102 | if dg == nil {
103 | t.Skip("Cannot TestUserChannels, dg not set.")
104 | }
105 |
106 | _, err := dg.UserChannels()
107 | if err != nil {
108 | t.Errorf(err.Error())
109 | }
110 | }
111 |
112 | func TestUserGuilds(t *testing.T) {
113 | if dg == nil {
114 | t.Skip("Cannot TestUserGuilds, dg not set.")
115 | }
116 |
117 | _, err := dg.UserGuilds(10, "", "")
118 | if err != nil {
119 | t.Errorf(err.Error())
120 | }
121 | }
122 |
123 | func TestUserSettings(t *testing.T) {
124 | if dg == nil {
125 | t.Skip("Cannot TestUserSettings, dg not set.")
126 | }
127 |
128 | _, err := dg.UserSettings()
129 | if err != nil {
130 | t.Errorf(err.Error())
131 | }
132 | }
133 |
134 | func TestUserUpdateStatus(t *testing.T) {
135 | if dg == nil {
136 | t.Skip("Cannot TestUserSettings, dg not set.")
137 | }
138 |
139 | _, err := dg.UserUpdateStatus(StatusDoNotDisturb)
140 | if err != nil {
141 | t.Errorf(err.Error())
142 | }
143 | }
144 |
145 | // TestLogout tests the Logout() function. This should not return an error.
146 | func TestLogout(t *testing.T) {
147 |
148 | if dg == nil {
149 | t.Skip("Cannot TestLogout, dg not set.")
150 | }
151 |
152 | err := dg.Logout()
153 | if err != nil {
154 | t.Errorf("Logout() returned error: %+v", err)
155 | }
156 | }
157 |
158 | func TestGateway(t *testing.T) {
159 |
160 | if dg == nil {
161 | t.Skip("Skipping, dg not set.")
162 | }
163 | _, err := dg.Gateway()
164 | if err != nil {
165 | t.Errorf("Gateway() returned error: %+v", err)
166 | }
167 | }
168 |
169 | func TestGatewayBot(t *testing.T) {
170 |
171 | if dgBot == nil {
172 | t.Skip("Skipping, dgBot not set.")
173 | }
174 | _, err := dgBot.GatewayBot()
175 | if err != nil {
176 | t.Errorf("GatewayBot() returned error: %+v", err)
177 | }
178 | }
179 |
180 | func TestVoiceICE(t *testing.T) {
181 |
182 | if dg == nil {
183 | t.Skip("Skipping, dg not set.")
184 | }
185 |
186 | _, err := dg.VoiceICE()
187 | if err != nil {
188 | t.Errorf("VoiceICE() returned error: %+v", err)
189 | }
190 | }
191 |
192 | func TestVoiceRegions(t *testing.T) {
193 |
194 | if dg == nil {
195 | t.Skip("Skipping, dg not set.")
196 | }
197 |
198 | _, err := dg.VoiceRegions()
199 | if err != nil {
200 | t.Errorf("VoiceRegions() returned error: %+v", err)
201 | }
202 | }
203 | func TestGuildRoles(t *testing.T) {
204 |
205 | if envGuild == "" {
206 | t.Skip("Skipping, DG_GUILD not set.")
207 | }
208 |
209 | if dg == nil {
210 | t.Skip("Skipping, dg not set.")
211 | }
212 |
213 | _, err := dg.GuildRoles(envGuild)
214 | if err != nil {
215 | t.Errorf("GuildRoles(envGuild) returned error: %+v", err)
216 | }
217 |
218 | }
219 |
220 | func TestGuildMemberNickname(t *testing.T) {
221 |
222 | if envGuild == "" {
223 | t.Skip("Skipping, DG_GUILD not set.")
224 | }
225 |
226 | if dg == nil {
227 | t.Skip("Skipping, dg not set.")
228 | }
229 |
230 | err := dg.GuildMemberNickname(envGuild, "@me/nick", "testnickname")
231 | if err != nil {
232 | t.Errorf("GuildNickname returned error: %+v", err)
233 | }
234 | }
235 |
236 | // TestChannelMessageSend2 tests the ChannelMessageSend() function. This should not return an error.
237 | func TestChannelMessageSend2(t *testing.T) {
238 |
239 | if envChannel == "" {
240 | t.Skip("Skipping, DG_CHANNEL not set.")
241 | }
242 |
243 | if dg == nil {
244 | t.Skip("Skipping, dg not set.")
245 | }
246 |
247 | _, err := dg.ChannelMessageSend(envChannel, "All done running REST API Tests!")
248 | if err != nil {
249 | t.Errorf("ChannelMessageSend returned error: %+v", err)
250 | }
251 | }
252 |
253 | // TestGuildPruneCount tests GuildPruneCount() function. This should not return an error.
254 | func TestGuildPruneCount(t *testing.T) {
255 |
256 | if envGuild == "" {
257 | t.Skip("Skipping, DG_GUILD not set.")
258 | }
259 |
260 | if dg == nil {
261 | t.Skip("Skipping, dg not set.")
262 | }
263 |
264 | _, err := dg.GuildPruneCount(envGuild, 1)
265 | if err != nil {
266 | t.Errorf("GuildPruneCount returned error: %+v", err)
267 | }
268 | }
269 |
270 | /*
271 | // TestGuildPrune tests GuildPrune() function. This should not return an error.
272 | func TestGuildPrune(t *testing.T) {
273 |
274 | if envGuild == "" {
275 | t.Skip("Skipping, DG_GUILD not set.")
276 | }
277 |
278 | if dg == nil {
279 | t.Skip("Skipping, dg not set.")
280 | }
281 |
282 | _, err := dg.GuildPrune(envGuild, 1)
283 | if err != nil {
284 | t.Errorf("GuildPrune returned error: %+v", err)
285 | }
286 | }
287 | */
288 |
--------------------------------------------------------------------------------
/vendor/github.com/matryer/moq/pkg/moq/moq_test.go:
--------------------------------------------------------------------------------
1 | package moq
2 |
3 | import (
4 | "bytes"
5 | "os"
6 | "strings"
7 | "testing"
8 | )
9 |
10 | func TestMoq(t *testing.T) {
11 | m, err := New("testpackages/example", "")
12 | if err != nil {
13 | t.Fatalf("moq.New: %s", err)
14 | }
15 | var buf bytes.Buffer
16 | err = m.Mock(&buf, "PersonStore")
17 | if err != nil {
18 | t.Errorf("m.Mock: %s", err)
19 | }
20 | s := buf.String()
21 | // assertions of things that should be mentioned
22 | var strs = []string{
23 | "package example",
24 | "type PersonStoreMock struct",
25 | "CreateFunc func(ctx context.Context, person *Person, confirm bool) error",
26 | "GetFunc func(ctx context.Context, id string) (*Person, error)",
27 | "func (mock *PersonStoreMock) Create(ctx context.Context, person *Person, confirm bool) error",
28 | "func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*Person, error)",
29 | "panic(\"moq: PersonStoreMock.CreateFunc is nil but PersonStore.Create was just called\")",
30 | "panic(\"moq: PersonStoreMock.GetFunc is nil but PersonStore.Get was just called\")",
31 | "lockPersonStoreMockGet.Lock()",
32 | "mock.calls.Get = append(mock.calls.Get, callInfo)",
33 | "lockPersonStoreMockGet.Unlock()",
34 | }
35 | for _, str := range strs {
36 | if !strings.Contains(s, str) {
37 | t.Errorf("expected but missing: \"%s\"", str)
38 | }
39 | }
40 | }
41 |
42 | func TestMoqExplicitPackage(t *testing.T) {
43 | m, err := New("testpackages/example", "different")
44 | if err != nil {
45 | t.Fatalf("moq.New: %s", err)
46 | }
47 | var buf bytes.Buffer
48 | err = m.Mock(&buf, "PersonStore")
49 | if err != nil {
50 | t.Errorf("m.Mock: %s", err)
51 | }
52 | s := buf.String()
53 | // assertions of things that should be mentioned
54 | var strs = []string{
55 | "package different",
56 | "type PersonStoreMock struct",
57 | "CreateFunc func(ctx context.Context, person *example.Person, confirm bool) error",
58 | "GetFunc func(ctx context.Context, id string) (*example.Person, error)",
59 | "func (mock *PersonStoreMock) Create(ctx context.Context, person *example.Person, confirm bool) error",
60 | "func (mock *PersonStoreMock) Get(ctx context.Context, id string) (*example.Person, error)",
61 | }
62 | for _, str := range strs {
63 | if !strings.Contains(s, str) {
64 | t.Errorf("expected but missing: \"%s\"", str)
65 | }
66 | }
67 | }
68 |
69 | // TestVeradicArguments tests to ensure variadic work as
70 | // expected.
71 | // see https://github.com/matryer/moq/issues/5
72 | func TestVariadicArguments(t *testing.T) {
73 | m, err := New("testpackages/variadic", "")
74 | if err != nil {
75 | t.Fatalf("moq.New: %s", err)
76 | }
77 | var buf bytes.Buffer
78 | err = m.Mock(&buf, "Greeter")
79 | if err != nil {
80 | t.Errorf("m.Mock: %s", err)
81 | }
82 | s := buf.String()
83 | // assertions of things that should be mentioned
84 | var strs = []string{
85 | "package variadic",
86 | "type GreeterMock struct",
87 | "GreetFunc func(ctx context.Context, names ...string) string",
88 | "return mock.GreetFunc(ctx, names...)",
89 | }
90 | for _, str := range strs {
91 | if !strings.Contains(s, str) {
92 | t.Errorf("expected but missing: \"%s\"", str)
93 | }
94 | }
95 | }
96 |
97 | func TestNothingToReturn(t *testing.T) {
98 | m, err := New("testpackages/example", "")
99 | if err != nil {
100 | t.Fatalf("moq.New: %s", err)
101 | }
102 | var buf bytes.Buffer
103 | err = m.Mock(&buf, "PersonStore")
104 | if err != nil {
105 | t.Errorf("m.Mock: %s", err)
106 | }
107 | s := buf.String()
108 | if strings.Contains(s, `return mock.ClearCacheFunc(id)`) {
109 | t.Errorf("should not have return for items that have no return arguments")
110 | }
111 | // assertions of things that should be mentioned
112 | var strs = []string{
113 | "mock.ClearCacheFunc(id)",
114 | }
115 | for _, str := range strs {
116 | if !strings.Contains(s, str) {
117 | t.Errorf("expected but missing: \"%s\"", str)
118 | }
119 | }
120 | }
121 |
122 | func TestChannelNames(t *testing.T) {
123 | m, err := New("testpackages/channels", "")
124 | if err != nil {
125 | t.Fatalf("moq.New: %s", err)
126 | }
127 | var buf bytes.Buffer
128 | err = m.Mock(&buf, "Queuer")
129 | if err != nil {
130 | t.Errorf("m.Mock: %s", err)
131 | }
132 | s := buf.String()
133 | var strs = []string{
134 | "func (mock *QueuerMock) Sub(topic string) (<-chan Queue, error)",
135 | }
136 | for _, str := range strs {
137 | if !strings.Contains(s, str) {
138 | t.Errorf("expected but missing: \"%s\"", str)
139 | }
140 | }
141 | }
142 |
143 | func TestImports(t *testing.T) {
144 | m, err := New("testpackages/imports/two", "")
145 | if err != nil {
146 | t.Fatalf("moq.New: %s", err)
147 | }
148 | var buf bytes.Buffer
149 | err = m.Mock(&buf, "DoSomething")
150 | if err != nil {
151 | t.Errorf("m.Mock: %s", err)
152 | }
153 | s := buf.String()
154 | var strs = []string{
155 | ` "sync"`,
156 | ` "github.com/matryer/moq/pkg/moq/testpackages/imports/one"`,
157 | }
158 | for _, str := range strs {
159 | if !strings.Contains(s, str) {
160 | t.Errorf("expected but missing: \"%s\"", str)
161 | }
162 | if len(strings.Split(s, str)) > 2 {
163 | t.Errorf("more than one: \"%s\"", str)
164 | }
165 | }
166 | }
167 |
168 | func TestTemplateFuncs(t *testing.T) {
169 | fn := templateFuncs["Exported"].(func(string) string)
170 | if fn("var") != "Var" {
171 | t.Errorf("exported didn't work: %s", fn("var"))
172 | }
173 | }
174 |
175 | func TestVendoredPackages(t *testing.T) {
176 | m, err := New("testpackages/vendoring/user", "")
177 | if err != nil {
178 | t.Fatalf("moq.New: %s", err)
179 | }
180 | var buf bytes.Buffer
181 | err = m.Mock(&buf, "Service")
182 | if err != nil {
183 | t.Errorf("mock error: %s", err)
184 | }
185 | s := buf.String()
186 | // assertions of things that should be mentioned
187 | var strs = []string{
188 | `"github.com/matryer/somerepo"`,
189 | }
190 | for _, str := range strs {
191 | if !strings.Contains(s, str) {
192 | t.Errorf("expected but missing: \"%s\"", str)
193 | }
194 | }
195 | }
196 |
197 | // TestDotImports tests for https://github.com/matryer/moq/issues/21.
198 | func TestDotImports(t *testing.T) {
199 | preDir, err := os.Getwd()
200 | if err != nil {
201 | t.Errorf("Getwd: %s", err)
202 | }
203 | err = os.Chdir("testpackages/dotimport")
204 | if err != nil {
205 | t.Errorf("Chdir: %s", err)
206 | }
207 | defer func() {
208 | err := os.Chdir(preDir)
209 | if err != nil {
210 | t.Errorf("Chdir back: %s", err)
211 | }
212 | }()
213 | m, err := New(".", "moqtest_test")
214 | if err != nil {
215 | t.Fatalf("moq.New: %s", err)
216 | }
217 | var buf bytes.Buffer
218 | err = m.Mock(&buf, "Service")
219 | if err != nil {
220 | t.Errorf("mock error: %s", err)
221 | }
222 | s := buf.String()
223 | if !strings.Contains(s, `/moq/pkg/moq/testpackages/dotimport"`) {
224 | t.Error("contains invalid dot import")
225 | }
226 | }
227 |
228 | func TestEmptyInterface(t *testing.T) {
229 | m, err := New("testpackages/emptyinterface", "")
230 | if err != nil {
231 | t.Fatalf("moq.New: %s", err)
232 | }
233 | var buf bytes.Buffer
234 | err = m.Mock(&buf, "Empty")
235 | if err != nil {
236 | t.Errorf("mock error: %s", err)
237 | }
238 | s := buf.String()
239 | if strings.Contains(s, `"sync"`) {
240 | t.Error("contains sync import, although this package isn't used")
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/events.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "encoding/json"
5 | )
6 |
7 | // This file contains all the possible structs that can be
8 | // handled by AddHandler/EventHandler.
9 | // DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
10 | //go:generate go run tools/cmd/eventhandlers/main.go
11 |
12 | // Connect is the data for a Connect event.
13 | // This is a sythetic event and is not dispatched by Discord.
14 | type Connect struct{}
15 |
16 | // Disconnect is the data for a Disconnect event.
17 | // This is a sythetic event and is not dispatched by Discord.
18 | type Disconnect struct{}
19 |
20 | // RateLimit is the data for a RateLimit event.
21 | // This is a sythetic event and is not dispatched by Discord.
22 | type RateLimit struct {
23 | *TooManyRequests
24 | URL string
25 | }
26 |
27 | // Event provides a basic initial struct for all websocket events.
28 | type Event struct {
29 | Operation int `json:"op"`
30 | Sequence int64 `json:"s"`
31 | Type string `json:"t"`
32 | RawData json.RawMessage `json:"d"`
33 | // Struct contains one of the other types in this file.
34 | Struct interface{} `json:"-"`
35 | }
36 |
37 | // A Ready stores all data for the websocket READY event.
38 | type Ready struct {
39 | Version int `json:"v"`
40 | SessionID string `json:"session_id"`
41 | User *User `json:"user"`
42 | ReadState []*ReadState `json:"read_state"`
43 | PrivateChannels []*Channel `json:"private_channels"`
44 | Guilds []*Guild `json:"guilds"`
45 |
46 | // Undocumented fields
47 | Settings *Settings `json:"user_settings"`
48 | UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
49 | Relationships []*Relationship `json:"relationships"`
50 | Presences []*Presence `json:"presences"`
51 | Notes map[string]string `json:"notes"`
52 | }
53 |
54 | // ChannelCreate is the data for a ChannelCreate event.
55 | type ChannelCreate struct {
56 | *Channel
57 | }
58 |
59 | // ChannelUpdate is the data for a ChannelUpdate event.
60 | type ChannelUpdate struct {
61 | *Channel
62 | }
63 |
64 | // ChannelDelete is the data for a ChannelDelete event.
65 | type ChannelDelete struct {
66 | *Channel
67 | }
68 |
69 | // ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
70 | type ChannelPinsUpdate struct {
71 | LastPinTimestamp string `json:"last_pin_timestamp"`
72 | ChannelID string `json:"channel_id"`
73 | }
74 |
75 | // GuildCreate is the data for a GuildCreate event.
76 | type GuildCreate struct {
77 | *Guild
78 | }
79 |
80 | // GuildUpdate is the data for a GuildUpdate event.
81 | type GuildUpdate struct {
82 | *Guild
83 | }
84 |
85 | // GuildDelete is the data for a GuildDelete event.
86 | type GuildDelete struct {
87 | *Guild
88 | }
89 |
90 | // GuildBanAdd is the data for a GuildBanAdd event.
91 | type GuildBanAdd struct {
92 | User *User `json:"user"`
93 | GuildID string `json:"guild_id"`
94 | }
95 |
96 | // GuildBanRemove is the data for a GuildBanRemove event.
97 | type GuildBanRemove struct {
98 | User *User `json:"user"`
99 | GuildID string `json:"guild_id"`
100 | }
101 |
102 | // GuildMemberAdd is the data for a GuildMemberAdd event.
103 | type GuildMemberAdd struct {
104 | *Member
105 | }
106 |
107 | // GuildMemberUpdate is the data for a GuildMemberUpdate event.
108 | type GuildMemberUpdate struct {
109 | *Member
110 | }
111 |
112 | // GuildMemberRemove is the data for a GuildMemberRemove event.
113 | type GuildMemberRemove struct {
114 | *Member
115 | }
116 |
117 | // GuildRoleCreate is the data for a GuildRoleCreate event.
118 | type GuildRoleCreate struct {
119 | *GuildRole
120 | }
121 |
122 | // GuildRoleUpdate is the data for a GuildRoleUpdate event.
123 | type GuildRoleUpdate struct {
124 | *GuildRole
125 | }
126 |
127 | // A GuildRoleDelete is the data for a GuildRoleDelete event.
128 | type GuildRoleDelete struct {
129 | RoleID string `json:"role_id"`
130 | GuildID string `json:"guild_id"`
131 | }
132 |
133 | // A GuildEmojisUpdate is the data for a guild emoji update event.
134 | type GuildEmojisUpdate struct {
135 | GuildID string `json:"guild_id"`
136 | Emojis []*Emoji `json:"emojis"`
137 | }
138 |
139 | // A GuildMembersChunk is the data for a GuildMembersChunk event.
140 | type GuildMembersChunk struct {
141 | GuildID string `json:"guild_id"`
142 | Members []*Member `json:"members"`
143 | }
144 |
145 | // GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
146 | type GuildIntegrationsUpdate struct {
147 | GuildID string `json:"guild_id"`
148 | }
149 |
150 | // MessageAck is the data for a MessageAck event.
151 | type MessageAck struct {
152 | MessageID string `json:"message_id"`
153 | ChannelID string `json:"channel_id"`
154 | }
155 |
156 | // MessageCreate is the data for a MessageCreate event.
157 | type MessageCreate struct {
158 | *Message
159 | }
160 |
161 | // MessageUpdate is the data for a MessageUpdate event.
162 | type MessageUpdate struct {
163 | *Message
164 | }
165 |
166 | // MessageDelete is the data for a MessageDelete event.
167 | type MessageDelete struct {
168 | *Message
169 | }
170 |
171 | // MessageReactionAdd is the data for a MessageReactionAdd event.
172 | type MessageReactionAdd struct {
173 | *MessageReaction
174 | }
175 |
176 | // MessageReactionRemove is the data for a MessageReactionRemove event.
177 | type MessageReactionRemove struct {
178 | *MessageReaction
179 | }
180 |
181 | // MessageReactionRemoveAll is the data for a MessageReactionRemoveAll event.
182 | type MessageReactionRemoveAll struct {
183 | *MessageReaction
184 | }
185 |
186 | // PresencesReplace is the data for a PresencesReplace event.
187 | type PresencesReplace []*Presence
188 |
189 | // PresenceUpdate is the data for a PresenceUpdate event.
190 | type PresenceUpdate struct {
191 | Presence
192 | GuildID string `json:"guild_id"`
193 | Roles []string `json:"roles"`
194 | }
195 |
196 | // Resumed is the data for a Resumed event.
197 | type Resumed struct {
198 | Trace []string `json:"_trace"`
199 | }
200 |
201 | // RelationshipAdd is the data for a RelationshipAdd event.
202 | type RelationshipAdd struct {
203 | *Relationship
204 | }
205 |
206 | // RelationshipRemove is the data for a RelationshipRemove event.
207 | type RelationshipRemove struct {
208 | *Relationship
209 | }
210 |
211 | // TypingStart is the data for a TypingStart event.
212 | type TypingStart struct {
213 | UserID string `json:"user_id"`
214 | ChannelID string `json:"channel_id"`
215 | Timestamp int `json:"timestamp"`
216 | }
217 |
218 | // UserUpdate is the data for a UserUpdate event.
219 | type UserUpdate struct {
220 | *User
221 | }
222 |
223 | // UserSettingsUpdate is the data for a UserSettingsUpdate event.
224 | type UserSettingsUpdate map[string]interface{}
225 |
226 | // UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
227 | type UserGuildSettingsUpdate struct {
228 | *UserGuildSettings
229 | }
230 |
231 | // UserNoteUpdate is the data for a UserNoteUpdate event.
232 | type UserNoteUpdate struct {
233 | ID string `json:"id"`
234 | Note string `json:"note"`
235 | }
236 |
237 | // VoiceServerUpdate is the data for a VoiceServerUpdate event.
238 | type VoiceServerUpdate struct {
239 | Token string `json:"token"`
240 | GuildID string `json:"guild_id"`
241 | Endpoint string `json:"endpoint"`
242 | }
243 |
244 | // VoiceStateUpdate is the data for a VoiceStateUpdate event.
245 | type VoiceStateUpdate struct {
246 | *VoiceState
247 | }
248 |
249 | // MessageDeleteBulk is the data for a MessageDeleteBulk event
250 | type MessageDeleteBulk struct {
251 | Messages []string `json:"ids"`
252 | ChannelID string `json:"channel_id"`
253 | }
254 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/event.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | // EventHandler is an interface for Discord events.
4 | type EventHandler interface {
5 | // Type returns the type of event this handler belongs to.
6 | Type() string
7 |
8 | // Handle is called whenever an event of Type() happens.
9 | // It is the recievers responsibility to type assert that the interface
10 | // is the expected struct.
11 | Handle(*Session, interface{})
12 | }
13 |
14 | // EventInterfaceProvider is an interface for providing empty interfaces for
15 | // Discord events.
16 | type EventInterfaceProvider interface {
17 | // Type is the type of event this handler belongs to.
18 | Type() string
19 |
20 | // New returns a new instance of the struct this event handler handles.
21 | // This is called once per event.
22 | // The struct is provided to all handlers of the same Type().
23 | New() interface{}
24 | }
25 |
26 | // interfaceEventType is the event handler type for interface{} events.
27 | const interfaceEventType = "__INTERFACE__"
28 |
29 | // interfaceEventHandler is an event handler for interface{} events.
30 | type interfaceEventHandler func(*Session, interface{})
31 |
32 | // Type returns the event type for interface{} events.
33 | func (eh interfaceEventHandler) Type() string {
34 | return interfaceEventType
35 | }
36 |
37 | // Handle is the handler for an interface{} event.
38 | func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
39 | eh(s, i)
40 | }
41 |
42 | var registeredInterfaceProviders = map[string]EventInterfaceProvider{}
43 |
44 | // registerInterfaceProvider registers a provider so that DiscordGo can
45 | // access it's New() method.
46 | func registerInterfaceProvider(eh EventInterfaceProvider) {
47 | if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
48 | return
49 | // XXX:
50 | // if we should error here, we need to do something with it.
51 | // fmt.Errorf("event %s already registered", eh.Type())
52 | }
53 | registeredInterfaceProviders[eh.Type()] = eh
54 | return
55 | }
56 |
57 | // eventHandlerInstance is a wrapper around an event handler, as functions
58 | // cannot be compared directly.
59 | type eventHandlerInstance struct {
60 | eventHandler EventHandler
61 | }
62 |
63 | // addEventHandler adds an event handler that will be fired anytime
64 | // the Discord WSAPI matching eventHandler.Type() fires.
65 | func (s *Session) addEventHandler(eventHandler EventHandler) func() {
66 | s.handlersMu.Lock()
67 | defer s.handlersMu.Unlock()
68 |
69 | if s.handlers == nil {
70 | s.handlers = map[string][]*eventHandlerInstance{}
71 | }
72 |
73 | ehi := &eventHandlerInstance{eventHandler}
74 | s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)
75 |
76 | return func() {
77 | s.removeEventHandlerInstance(eventHandler.Type(), ehi)
78 | }
79 | }
80 |
81 | // addEventHandler adds an event handler that will be fired the next time
82 | // the Discord WSAPI matching eventHandler.Type() fires.
83 | func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
84 | s.handlersMu.Lock()
85 | defer s.handlersMu.Unlock()
86 |
87 | if s.onceHandlers == nil {
88 | s.onceHandlers = map[string][]*eventHandlerInstance{}
89 | }
90 |
91 | ehi := &eventHandlerInstance{eventHandler}
92 | s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)
93 |
94 | return func() {
95 | s.removeEventHandlerInstance(eventHandler.Type(), ehi)
96 | }
97 | }
98 |
99 | // AddHandler allows you to add an event handler that will be fired anytime
100 | // the Discord WSAPI event that matches the function fires.
101 | // events.go contains all the Discord WSAPI events that can be fired.
102 | // eg:
103 | // Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
104 | // })
105 | //
106 | // or:
107 | // Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
108 | // })
109 | // The return value of this method is a function, that when called will remove the
110 | // event handler.
111 | func (s *Session) AddHandler(handler interface{}) func() {
112 | eh := handlerForInterface(handler)
113 |
114 | if eh == nil {
115 | s.log(LogError, "Invalid handler type, handler will never be called")
116 | return func() {}
117 | }
118 |
119 | return s.addEventHandler(eh)
120 | }
121 |
122 | // AddHandlerOnce allows you to add an event handler that will be fired the next time
123 | // the Discord WSAPI event that matches the function fires.
124 | // See AddHandler for more details.
125 | func (s *Session) AddHandlerOnce(handler interface{}) func() {
126 | eh := handlerForInterface(handler)
127 |
128 | if eh == nil {
129 | s.log(LogError, "Invalid handler type, handler will never be called")
130 | return func() {}
131 | }
132 |
133 | return s.addEventHandlerOnce(eh)
134 | }
135 |
136 | // removeEventHandler instance removes an event handler instance.
137 | func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
138 | s.handlersMu.Lock()
139 | defer s.handlersMu.Unlock()
140 |
141 | handlers := s.handlers[t]
142 | for i := range handlers {
143 | if handlers[i] == ehi {
144 | s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
145 | }
146 | }
147 |
148 | onceHandlers := s.onceHandlers[t]
149 | for i := range onceHandlers {
150 | if onceHandlers[i] == ehi {
151 | s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
152 | }
153 | }
154 | }
155 |
156 | // Handles calling permanent and once handlers for an event type.
157 | func (s *Session) handle(t string, i interface{}) {
158 | for _, eh := range s.handlers[t] {
159 | if s.SyncEvents {
160 | eh.eventHandler.Handle(s, i)
161 | } else {
162 | go eh.eventHandler.Handle(s, i)
163 | }
164 | }
165 |
166 | if len(s.onceHandlers[t]) > 0 {
167 | for _, eh := range s.onceHandlers[t] {
168 | if s.SyncEvents {
169 | eh.eventHandler.Handle(s, i)
170 | } else {
171 | go eh.eventHandler.Handle(s, i)
172 | }
173 | }
174 | s.onceHandlers[t] = nil
175 | }
176 | }
177 |
178 | // Handles an event type by calling internal methods, firing handlers and firing the
179 | // interface{} event.
180 | func (s *Session) handleEvent(t string, i interface{}) {
181 | s.handlersMu.RLock()
182 | defer s.handlersMu.RUnlock()
183 |
184 | // All events are dispatched internally first.
185 | s.onInterface(i)
186 |
187 | // Then they are dispatched to anyone handling interface{} events.
188 | s.handle(interfaceEventType, i)
189 |
190 | // Finally they are dispatched to any typed handlers.
191 | s.handle(t, i)
192 | }
193 |
194 | // setGuildIds will set the GuildID on all the members of a guild.
195 | // This is done as event data does not have it set.
196 | func setGuildIds(g *Guild) {
197 | for _, c := range g.Channels {
198 | c.GuildID = g.ID
199 | }
200 |
201 | for _, m := range g.Members {
202 | m.GuildID = g.ID
203 | }
204 |
205 | for _, vs := range g.VoiceStates {
206 | vs.GuildID = g.ID
207 | }
208 | }
209 |
210 | // onInterface handles all internal events and routes them to the appropriate internal handler.
211 | func (s *Session) onInterface(i interface{}) {
212 | switch t := i.(type) {
213 | case *Ready:
214 | for _, g := range t.Guilds {
215 | setGuildIds(g)
216 | }
217 | s.onReady(t)
218 | case *GuildCreate:
219 | setGuildIds(t.Guild)
220 | case *GuildUpdate:
221 | setGuildIds(t.Guild)
222 | case *VoiceServerUpdate:
223 | go s.onVoiceServerUpdate(t)
224 | case *VoiceStateUpdate:
225 | go s.onVoiceStateUpdate(t)
226 | }
227 | err := s.State.OnInterface(s, i)
228 | if err != nil {
229 | s.log(LogDebug, "error dispatching internal event, %s", err)
230 | }
231 | }
232 |
233 | // onReady handles the ready event.
234 | func (s *Session) onReady(r *Ready) {
235 |
236 | // Store the SessionID within the Session struct.
237 | s.sessionID = r.SessionID
238 | }
239 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/discord_test.go:
--------------------------------------------------------------------------------
1 | package discordgo
2 |
3 | import (
4 | "os"
5 | "runtime"
6 | "sync/atomic"
7 | "testing"
8 | "time"
9 | )
10 |
11 | //////////////////////////////////////////////////////////////////////////////
12 | ////////////////////////////////////////////////////// VARS NEEDED FOR TESTING
13 | var (
14 | dg *Session // Stores a global discordgo user session
15 | dgBot *Session // Stores a global discordgo bot session
16 |
17 | envToken = os.Getenv("DG_TOKEN") // Token to use when authenticating the user account
18 | envBotToken = os.Getenv("DGB_TOKEN") // Token to use when authenticating the bot account
19 | envEmail = os.Getenv("DG_EMAIL") // Email to use when authenticating
20 | envPassword = os.Getenv("DG_PASSWORD") // Password to use when authenticating
21 | envGuild = os.Getenv("DG_GUILD") // Guild ID to use for tests
22 | envChannel = os.Getenv("DG_CHANNEL") // Channel ID to use for tests
23 | // envUser = os.Getenv("DG_USER") // User ID to use for tests
24 | envAdmin = os.Getenv("DG_ADMIN") // User ID of admin user to use for tests
25 | )
26 |
27 | func init() {
28 | if envBotToken != "" {
29 | if d, err := New(envBotToken); err == nil {
30 | dgBot = d
31 | }
32 | }
33 |
34 | if envEmail == "" || envPassword == "" || envToken == "" {
35 | return
36 | }
37 |
38 | if d, err := New(envEmail, envPassword, envToken); err == nil {
39 | dg = d
40 | }
41 | }
42 |
43 | //////////////////////////////////////////////////////////////////////////////
44 | /////////////////////////////////////////////////////////////// START OF TESTS
45 |
46 | // TestNew tests the New() function without any arguments. This should return
47 | // a valid Session{} struct and no errors.
48 | func TestNew(t *testing.T) {
49 |
50 | _, err := New()
51 | if err != nil {
52 | t.Errorf("New() returned error: %+v", err)
53 | }
54 | }
55 |
56 | // TestInvalidToken tests the New() function with an invalid token
57 | func TestInvalidToken(t *testing.T) {
58 | d, err := New("asjkldhflkjasdh")
59 | if err != nil {
60 | t.Fatalf("New(InvalidToken) returned error: %+v", err)
61 | }
62 |
63 | // New with just a token does not do any communication, so attempt an api call.
64 | _, err = d.UserSettings()
65 | if err == nil {
66 | t.Errorf("New(InvalidToken), d.UserSettings returned nil error.")
67 | }
68 | }
69 |
70 | // TestInvalidUserPass tests the New() function with an invalid Email and Pass
71 | func TestInvalidEmailPass(t *testing.T) {
72 |
73 | _, err := New("invalidemail", "invalidpassword")
74 | if err == nil {
75 | t.Errorf("New(InvalidEmail, InvalidPass) returned nil error.")
76 | }
77 |
78 | }
79 |
80 | // TestInvalidPass tests the New() function with an invalid Password
81 | func TestInvalidPass(t *testing.T) {
82 |
83 | if envEmail == "" {
84 | t.Skip("Skipping New(username,InvalidPass), DG_EMAIL not set")
85 | return
86 | }
87 | _, err := New(envEmail, "invalidpassword")
88 | if err == nil {
89 | t.Errorf("New(Email, InvalidPass) returned nil error.")
90 | }
91 | }
92 |
93 | // TestNewUserPass tests the New() function with a username and password.
94 | // This should return a valid Session{}, a valid Session.Token.
95 | func TestNewUserPass(t *testing.T) {
96 |
97 | if envEmail == "" || envPassword == "" {
98 | t.Skip("Skipping New(username,password), DG_EMAIL or DG_PASSWORD not set")
99 | return
100 | }
101 |
102 | d, err := New(envEmail, envPassword)
103 | if err != nil {
104 | t.Fatalf("New(user,pass) returned error: %+v", err)
105 | }
106 |
107 | if d == nil {
108 | t.Fatal("New(user,pass), d is nil, should be Session{}")
109 | }
110 |
111 | if d.Token == "" {
112 | t.Fatal("New(user,pass), d.Token is empty, should be a valid Token.")
113 | }
114 | }
115 |
116 | // TestNewToken tests the New() function with a Token. This should return
117 | // the same as the TestNewUserPass function.
118 | func TestNewToken(t *testing.T) {
119 |
120 | if envToken == "" {
121 | t.Skip("Skipping New(token), DG_TOKEN not set")
122 | }
123 |
124 | d, err := New(envToken)
125 | if err != nil {
126 | t.Fatalf("New(envToken) returned error: %+v", err)
127 | }
128 |
129 | if d == nil {
130 | t.Fatal("New(envToken), d is nil, should be Session{}")
131 | }
132 |
133 | if d.Token == "" {
134 | t.Fatal("New(envToken), d.Token is empty, should be a valid Token.")
135 | }
136 | }
137 |
138 | // TestNewUserPassToken tests the New() function with a username, password and token.
139 | // This should return the same as the TestNewUserPass function.
140 | func TestNewUserPassToken(t *testing.T) {
141 |
142 | if envEmail == "" || envPassword == "" || envToken == "" {
143 | t.Skip("Skipping New(username,password,token), DG_EMAIL, DG_PASSWORD or DG_TOKEN not set")
144 | return
145 | }
146 |
147 | d, err := New(envEmail, envPassword, envToken)
148 | if err != nil {
149 | t.Fatalf("New(user,pass,token) returned error: %+v", err)
150 | }
151 |
152 | if d == nil {
153 | t.Fatal("New(user,pass,token), d is nil, should be Session{}")
154 | }
155 |
156 | if d.Token == "" {
157 | t.Fatal("New(user,pass,token), d.Token is empty, should be a valid Token.")
158 | }
159 | }
160 |
161 | func TestOpenClose(t *testing.T) {
162 | if envToken == "" {
163 | t.Skip("Skipping TestClose, DG_TOKEN not set")
164 | }
165 |
166 | d, err := New(envToken)
167 | if err != nil {
168 | t.Fatalf("TestClose, New(envToken) returned error: %+v", err)
169 | }
170 |
171 | if err = d.Open(); err != nil {
172 | t.Fatalf("TestClose, d.Open failed: %+v", err)
173 | }
174 |
175 | // We need a better way to know the session is ready for use,
176 | // this is totally gross.
177 | start := time.Now()
178 | for {
179 | d.RLock()
180 | if d.DataReady {
181 | d.RUnlock()
182 | break
183 | }
184 | d.RUnlock()
185 |
186 | if time.Since(start) > 10*time.Second {
187 | t.Fatal("DataReady never became true.yy")
188 | }
189 | runtime.Gosched()
190 | }
191 |
192 | // TODO find a better way
193 | // Add a small sleep here to make sure heartbeat and other events
194 | // have enough time to get fired. Need a way to actually check
195 | // those events.
196 | time.Sleep(2 * time.Second)
197 |
198 | // UpdateStatus - maybe we move this into wsapi_test.go but the websocket
199 | // created here is needed. This helps tests that the websocket was setup
200 | // and it is working.
201 | if err = d.UpdateStatus(0, time.Now().String()); err != nil {
202 | t.Errorf("UpdateStatus error: %+v", err)
203 | }
204 |
205 | if err = d.Close(); err != nil {
206 | t.Fatalf("TestClose, d.Close failed: %+v", err)
207 | }
208 | }
209 |
210 | func TestAddHandler(t *testing.T) {
211 |
212 | testHandlerCalled := int32(0)
213 | testHandler := func(s *Session, m *MessageCreate) {
214 | atomic.AddInt32(&testHandlerCalled, 1)
215 | }
216 |
217 | interfaceHandlerCalled := int32(0)
218 | interfaceHandler := func(s *Session, i interface{}) {
219 | atomic.AddInt32(&interfaceHandlerCalled, 1)
220 | }
221 |
222 | bogusHandlerCalled := int32(0)
223 | bogusHandler := func(s *Session, se *Session) {
224 | atomic.AddInt32(&bogusHandlerCalled, 1)
225 | }
226 |
227 | d := Session{}
228 | d.AddHandler(testHandler)
229 | d.AddHandler(testHandler)
230 |
231 | d.AddHandler(interfaceHandler)
232 | d.AddHandler(bogusHandler)
233 |
234 | d.handleEvent(messageCreateEventType, &MessageCreate{})
235 | d.handleEvent(messageDeleteEventType, &MessageDelete{})
236 |
237 | <-time.After(500 * time.Millisecond)
238 |
239 | // testHandler will be called twice because it was added twice.
240 | if atomic.LoadInt32(&testHandlerCalled) != 2 {
241 | t.Fatalf("testHandler was not called twice.")
242 | }
243 |
244 | // interfaceHandler will be called twice, once for each event.
245 | if atomic.LoadInt32(&interfaceHandlerCalled) != 2 {
246 | t.Fatalf("interfaceHandler was not called twice.")
247 | }
248 |
249 | if atomic.LoadInt32(&bogusHandlerCalled) != 0 {
250 | t.Fatalf("bogusHandler was called.")
251 | }
252 | }
253 |
254 | func TestRemoveHandler(t *testing.T) {
255 |
256 | testHandlerCalled := int32(0)
257 | testHandler := func(s *Session, m *MessageCreate) {
258 | atomic.AddInt32(&testHandlerCalled, 1)
259 | }
260 |
261 | d := Session{}
262 | r := d.AddHandler(testHandler)
263 |
264 | d.handleEvent(messageCreateEventType, &MessageCreate{})
265 |
266 | r()
267 |
268 | d.handleEvent(messageCreateEventType, &MessageCreate{})
269 |
270 | <-time.After(500 * time.Millisecond)
271 |
272 | // testHandler will be called once, as it was removed in between calls.
273 | if atomic.LoadInt32(&testHandlerCalled) != 1 {
274 | t.Fatalf("testHandler was not called once.")
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/message.go:
--------------------------------------------------------------------------------
1 | // Discordgo - Discord bindings for Go
2 | // Available at https://github.com/bwmarrin/discordgo
3 |
4 | // Copyright 2015-2016 Bruce Marriner . All rights reserved.
5 | // Use of this source code is governed by a BSD-style
6 | // license that can be found in the LICENSE file.
7 |
8 | // This file contains code related to the Message struct
9 |
10 | package discordgo
11 |
12 | import (
13 | "io"
14 | "regexp"
15 | "strings"
16 | )
17 |
18 | // MessageType is the type of Message
19 | type MessageType int
20 |
21 | // Block contains the valid known MessageType values
22 | const (
23 | MessageTypeDefault MessageType = iota
24 | MessageTypeRecipientAdd
25 | MessageTypeRecipientRemove
26 | MessageTypeCall
27 | MessageTypeChannelNameChange
28 | MessageTypeChannelIconChange
29 | MessageTypeChannelPinnedMessage
30 | MessageTypeGuildMemberJoin
31 | )
32 |
33 | // A Message stores all data related to a specific Discord message.
34 | type Message struct {
35 | ID string `json:"id"`
36 | ChannelID string `json:"channel_id"`
37 | Content string `json:"content"`
38 | Timestamp Timestamp `json:"timestamp"`
39 | EditedTimestamp Timestamp `json:"edited_timestamp"`
40 | MentionRoles []string `json:"mention_roles"`
41 | Tts bool `json:"tts"`
42 | MentionEveryone bool `json:"mention_everyone"`
43 | Author *User `json:"author"`
44 | Attachments []*MessageAttachment `json:"attachments"`
45 | Embeds []*MessageEmbed `json:"embeds"`
46 | Mentions []*User `json:"mentions"`
47 | Reactions []*MessageReactions `json:"reactions"`
48 | Type MessageType `json:"type"`
49 | }
50 |
51 | // File stores info about files you e.g. send in messages.
52 | type File struct {
53 | Name string
54 | ContentType string
55 | Reader io.Reader
56 | }
57 |
58 | // MessageSend stores all parameters you can send with ChannelMessageSendComplex.
59 | type MessageSend struct {
60 | Content string `json:"content,omitempty"`
61 | Embed *MessageEmbed `json:"embed,omitempty"`
62 | Tts bool `json:"tts"`
63 | Files []*File `json:"-"`
64 |
65 | // TODO: Remove this when compatibility is not required.
66 | File *File `json:"-"`
67 | }
68 |
69 | // MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
70 | // is also where you should get the instance from.
71 | type MessageEdit struct {
72 | Content *string `json:"content,omitempty"`
73 | Embed *MessageEmbed `json:"embed,omitempty"`
74 |
75 | ID string
76 | Channel string
77 | }
78 |
79 | // NewMessageEdit returns a MessageEdit struct, initialized
80 | // with the Channel and ID.
81 | func NewMessageEdit(channelID string, messageID string) *MessageEdit {
82 | return &MessageEdit{
83 | Channel: channelID,
84 | ID: messageID,
85 | }
86 | }
87 |
88 | // SetContent is the same as setting the variable Content,
89 | // except it doesn't take a pointer.
90 | func (m *MessageEdit) SetContent(str string) *MessageEdit {
91 | m.Content = &str
92 | return m
93 | }
94 |
95 | // SetEmbed is a convenience function for setting the embed,
96 | // so you can chain commands.
97 | func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
98 | m.Embed = embed
99 | return m
100 | }
101 |
102 | // A MessageAttachment stores data for message attachments.
103 | type MessageAttachment struct {
104 | ID string `json:"id"`
105 | URL string `json:"url"`
106 | ProxyURL string `json:"proxy_url"`
107 | Filename string `json:"filename"`
108 | Width int `json:"width"`
109 | Height int `json:"height"`
110 | Size int `json:"size"`
111 | }
112 |
113 | // MessageEmbedFooter is a part of a MessageEmbed struct.
114 | type MessageEmbedFooter struct {
115 | Text string `json:"text,omitempty"`
116 | IconURL string `json:"icon_url,omitempty"`
117 | ProxyIconURL string `json:"proxy_icon_url,omitempty"`
118 | }
119 |
120 | // MessageEmbedImage is a part of a MessageEmbed struct.
121 | type MessageEmbedImage struct {
122 | URL string `json:"url,omitempty"`
123 | ProxyURL string `json:"proxy_url,omitempty"`
124 | Width int `json:"width,omitempty"`
125 | Height int `json:"height,omitempty"`
126 | }
127 |
128 | // MessageEmbedThumbnail is a part of a MessageEmbed struct.
129 | type MessageEmbedThumbnail struct {
130 | URL string `json:"url,omitempty"`
131 | ProxyURL string `json:"proxy_url,omitempty"`
132 | Width int `json:"width,omitempty"`
133 | Height int `json:"height,omitempty"`
134 | }
135 |
136 | // MessageEmbedVideo is a part of a MessageEmbed struct.
137 | type MessageEmbedVideo struct {
138 | URL string `json:"url,omitempty"`
139 | ProxyURL string `json:"proxy_url,omitempty"`
140 | Width int `json:"width,omitempty"`
141 | Height int `json:"height,omitempty"`
142 | }
143 |
144 | // MessageEmbedProvider is a part of a MessageEmbed struct.
145 | type MessageEmbedProvider struct {
146 | URL string `json:"url,omitempty"`
147 | Name string `json:"name,omitempty"`
148 | }
149 |
150 | // MessageEmbedAuthor is a part of a MessageEmbed struct.
151 | type MessageEmbedAuthor struct {
152 | URL string `json:"url,omitempty"`
153 | Name string `json:"name,omitempty"`
154 | IconURL string `json:"icon_url,omitempty"`
155 | ProxyIconURL string `json:"proxy_icon_url,omitempty"`
156 | }
157 |
158 | // MessageEmbedField is a part of a MessageEmbed struct.
159 | type MessageEmbedField struct {
160 | Name string `json:"name,omitempty"`
161 | Value string `json:"value,omitempty"`
162 | Inline bool `json:"inline,omitempty"`
163 | }
164 |
165 | // An MessageEmbed stores data for message embeds.
166 | type MessageEmbed struct {
167 | URL string `json:"url,omitempty"`
168 | Type string `json:"type,omitempty"`
169 | Title string `json:"title,omitempty"`
170 | Description string `json:"description,omitempty"`
171 | Timestamp string `json:"timestamp,omitempty"`
172 | Color int `json:"color,omitempty"`
173 | Footer *MessageEmbedFooter `json:"footer,omitempty"`
174 | Image *MessageEmbedImage `json:"image,omitempty"`
175 | Thumbnail *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
176 | Video *MessageEmbedVideo `json:"video,omitempty"`
177 | Provider *MessageEmbedProvider `json:"provider,omitempty"`
178 | Author *MessageEmbedAuthor `json:"author,omitempty"`
179 | Fields []*MessageEmbedField `json:"fields,omitempty"`
180 | }
181 |
182 | // MessageReactions holds a reactions object for a message.
183 | type MessageReactions struct {
184 | Count int `json:"count"`
185 | Me bool `json:"me"`
186 | Emoji *Emoji `json:"emoji"`
187 | }
188 |
189 | // ContentWithMentionsReplaced will replace all @ mentions with the
190 | // username of the mention.
191 | func (m *Message) ContentWithMentionsReplaced() (content string) {
192 | content = m.Content
193 |
194 | for _, user := range m.Mentions {
195 | content = strings.NewReplacer(
196 | "<@"+user.ID+">", "@"+user.Username,
197 | "<@!"+user.ID+">", "@"+user.Username,
198 | ).Replace(content)
199 | }
200 | return
201 | }
202 |
203 | var patternChannels = regexp.MustCompile("<#[^>]*>")
204 |
205 | // ContentWithMoreMentionsReplaced will replace all @ mentions with the
206 | // username of the mention, but also role IDs and more.
207 | func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
208 | content = m.Content
209 |
210 | if !s.StateEnabled {
211 | content = m.ContentWithMentionsReplaced()
212 | return
213 | }
214 |
215 | channel, err := s.State.Channel(m.ChannelID)
216 | if err != nil {
217 | content = m.ContentWithMentionsReplaced()
218 | return
219 | }
220 |
221 | for _, user := range m.Mentions {
222 | nick := user.Username
223 |
224 | member, err := s.State.Member(channel.GuildID, user.ID)
225 | if err == nil && member.Nick != "" {
226 | nick = member.Nick
227 | }
228 |
229 | content = strings.NewReplacer(
230 | "<@"+user.ID+">", "@"+user.Username,
231 | "<@!"+user.ID+">", "@"+nick,
232 | ).Replace(content)
233 | }
234 | for _, roleID := range m.MentionRoles {
235 | role, err := s.State.Role(channel.GuildID, roleID)
236 | if err != nil || !role.Mentionable {
237 | continue
238 | }
239 |
240 | content = strings.Replace(content, "<&"+role.ID+">", "@"+role.Name, -1)
241 | }
242 |
243 | content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
244 | channel, err := s.State.Channel(mention[2 : len(mention)-1])
245 | if err != nil || channel.Type == ChannelTypeGuildVoice {
246 | return mention
247 | }
248 |
249 | return "#" + channel.Name
250 | })
251 | return
252 | }
253 |
--------------------------------------------------------------------------------
/vendor/github.com/bwmarrin/discordgo/endpoints.go:
--------------------------------------------------------------------------------
1 | // Discordgo - Discord bindings for Go
2 | // Available at https://github.com/bwmarrin/discordgo
3 |
4 | // Copyright 2015-2016 Bruce Marriner . All rights reserved.
5 | // Use of this source code is governed by a BSD-style
6 | // license that can be found in the LICENSE file.
7 |
8 | // This file contains variables for all known Discord end points. All functions
9 | // throughout the Discordgo package use these variables for all connections
10 | // to Discord. These are all exported and you may modify them if needed.
11 |
12 | package discordgo
13 |
14 | // APIVersion is the Discord API version used for the REST and Websocket API.
15 | var APIVersion = "6"
16 |
17 | // Known Discord API Endpoints.
18 | var (
19 | EndpointStatus = "https://status.discordapp.com/api/v2/"
20 | EndpointSm = EndpointStatus + "scheduled-maintenances/"
21 | EndpointSmActive = EndpointSm + "active.json"
22 | EndpointSmUpcoming = EndpointSm + "upcoming.json"
23 |
24 | EndpointDiscord = "https://discordapp.com/"
25 | EndpointAPI = EndpointDiscord + "api/v" + APIVersion + "/"
26 | EndpointGuilds = EndpointAPI + "guilds/"
27 | EndpointChannels = EndpointAPI + "channels/"
28 | EndpointUsers = EndpointAPI + "users/"
29 | EndpointGateway = EndpointAPI + "gateway"
30 | EndpointGatewayBot = EndpointGateway + "/bot"
31 | EndpointWebhooks = EndpointAPI + "webhooks/"
32 |
33 | EndpointCDN = "https://cdn.discordapp.com/"
34 | EndpointCDNAttachments = EndpointCDN + "attachments/"
35 | EndpointCDNAvatars = EndpointCDN + "avatars/"
36 | EndpointCDNIcons = EndpointCDN + "icons/"
37 | EndpointCDNSplashes = EndpointCDN + "splashes/"
38 | EndpointCDNChannelIcons = EndpointCDN + "channel-icons/"
39 |
40 | EndpointAuth = EndpointAPI + "auth/"
41 | EndpointLogin = EndpointAuth + "login"
42 | EndpointLogout = EndpointAuth + "logout"
43 | EndpointVerify = EndpointAuth + "verify"
44 | EndpointVerifyResend = EndpointAuth + "verify/resend"
45 | EndpointForgotPassword = EndpointAuth + "forgot"
46 | EndpointResetPassword = EndpointAuth + "reset"
47 | EndpointRegister = EndpointAuth + "register"
48 |
49 | EndpointVoice = EndpointAPI + "/voice/"
50 | EndpointVoiceRegions = EndpointVoice + "regions"
51 | EndpointVoiceIce = EndpointVoice + "ice"
52 |
53 | EndpointTutorial = EndpointAPI + "tutorial/"
54 | EndpointTutorialIndicators = EndpointTutorial + "indicators"
55 |
56 | EndpointTrack = EndpointAPI + "track"
57 | EndpointSso = EndpointAPI + "sso"
58 | EndpointReport = EndpointAPI + "report"
59 | EndpointIntegrations = EndpointAPI + "integrations"
60 |
61 | EndpointUser = func(uID string) string { return EndpointUsers + uID }
62 | EndpointUserAvatar = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".png" }
63 | EndpointUserAvatarAnimated = func(uID, aID string) string { return EndpointCDNAvatars + uID + "/" + aID + ".gif" }
64 | EndpointUserSettings = func(uID string) string { return EndpointUsers + uID + "/settings" }
65 | EndpointUserGuilds = func(uID string) string { return EndpointUsers + uID + "/guilds" }
66 | EndpointUserGuild = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID }
67 | EndpointUserGuildSettings = func(uID, gID string) string { return EndpointUsers + uID + "/guilds/" + gID + "/settings" }
68 | EndpointUserChannels = func(uID string) string { return EndpointUsers + uID + "/channels" }
69 | EndpointUserDevices = func(uID string) string { return EndpointUsers + uID + "/devices" }
70 | EndpointUserConnections = func(uID string) string { return EndpointUsers + uID + "/connections" }
71 | EndpointUserNotes = func(uID string) string { return EndpointUsers + "@me/notes/" + uID }
72 |
73 | EndpointGuild = func(gID string) string { return EndpointGuilds + gID }
74 | EndpointGuildInivtes = func(gID string) string { return EndpointGuilds + gID + "/invites" }
75 | EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
76 | EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
77 | EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
78 | EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
79 | EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" }
80 | EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
81 | EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
82 | EndpointGuildIntegration = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID }
83 | EndpointGuildIntegrationSync = func(gID, iID string) string { return EndpointGuilds + gID + "/integrations/" + iID + "/sync" }
84 | EndpointGuildRoles = func(gID string) string { return EndpointGuilds + gID + "/roles" }
85 | EndpointGuildRole = func(gID, rID string) string { return EndpointGuilds + gID + "/roles/" + rID }
86 | EndpointGuildInvites = func(gID string) string { return EndpointGuilds + gID + "/invites" }
87 | EndpointGuildEmbed = func(gID string) string { return EndpointGuilds + gID + "/embed" }
88 | EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
89 | EndpointGuildIcon = func(gID, hash string) string { return EndpointCDNIcons + gID + "/" + hash + ".png" }
90 | EndpointGuildSplash = func(gID, hash string) string { return EndpointCDNSplashes + gID + "/" + hash + ".png" }
91 | EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
92 |
93 | EndpointChannel = func(cID string) string { return EndpointChannels + cID }
94 | EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
95 | EndpointChannelPermission = func(cID, tID string) string { return EndpointChannels + cID + "/permissions/" + tID }
96 | EndpointChannelInvites = func(cID string) string { return EndpointChannels + cID + "/invites" }
97 | EndpointChannelTyping = func(cID string) string { return EndpointChannels + cID + "/typing" }
98 | EndpointChannelMessages = func(cID string) string { return EndpointChannels + cID + "/messages" }
99 | EndpointChannelMessage = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID }
100 | EndpointChannelMessageAck = func(cID, mID string) string { return EndpointChannels + cID + "/messages/" + mID + "/ack" }
101 | EndpointChannelMessagesBulkDelete = func(cID string) string { return EndpointChannel(cID) + "/messages/bulk_delete" }
102 | EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
103 | EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
104 |
105 | EndpointGroupIcon = func(cID, hash string) string { return EndpointCDNChannelIcons + cID + "/" + hash + ".png" }
106 |
107 | EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
108 | EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
109 | EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
110 |
111 | EndpointMessageReactionsAll = func(cID, mID string) string {
112 | return EndpointChannelMessage(cID, mID) + "/reactions"
113 | }
114 | EndpointMessageReactions = func(cID, mID, eID string) string {
115 | return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
116 | }
117 | EndpointMessageReaction = func(cID, mID, eID, uID string) string {
118 | return EndpointMessageReactions(cID, mID, eID) + "/" + uID
119 | }
120 |
121 | EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" }
122 | EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID }
123 | EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
124 |
125 | EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
126 |
127 | EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
128 |
129 | EndpointEmoji = func(eID string) string { return EndpointAPI + "emojis/" + eID + ".png" }
130 |
131 | EndpointOauth2 = EndpointAPI + "oauth2/"
132 | EndpointApplications = EndpointOauth2 + "applications"
133 | EndpointApplication = func(aID string) string { return EndpointApplications + "/" + aID }
134 | EndpointApplicationsBot = func(aID string) string { return EndpointApplications + "/" + aID + "/bot" }
135 | )
136 |
--------------------------------------------------------------------------------