mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-26 20:09:24 +00:00
Compare commits
92 Commits
Author | SHA1 | Date | |
---|---|---|---|
e05a323afd | |||
80895deae2 | |||
eddc691fc9 | |||
deb2d7194d | |||
fd8cfb11fb | |||
9407aa4600 | |||
263b8da37d | |||
b95988b4e2 | |||
35025e164a | |||
32bbab8518 | |||
84c0b745af | |||
8b286fb009 | |||
386fa58b67 | |||
c5cfbc2297 | |||
cd0a2beb11 | |||
73f01ad8d8 | |||
930b639cc9 | |||
58483ea70c | |||
072cac0347 | |||
956d7cf3f3 | |||
7558a2162e | |||
62b165c0b4 | |||
fe258e1b67 | |||
dc37232100 | |||
163f55f9c2 | |||
2d16fd085e | |||
e1a5f5bca5 | |||
6e772ee189 | |||
2b0f178ba3 | |||
79e6c9fa6c | |||
1426ddec5f | |||
e9105003b0 | |||
587bb06558 | |||
53e9664cde | |||
482fbac68f | |||
dcccd43427 | |||
397b8ff892 | |||
38a4cf315a | |||
5f8b24e32c | |||
678a7ceb4e | |||
077d494c7b | |||
09b243d8c2 | |||
991183e514 | |||
9bf10e4b58 | |||
884599d27d | |||
f8a6e65bfd | |||
6df6c5d615 | |||
93114b7682 | |||
9987ac3f13 | |||
01a32b2154 | |||
b3c3142bb2 | |||
77f1a959c3 | |||
e3dda0e812 | |||
38103d36b4 | |||
7685fe1724 | |||
01afe03a3f | |||
7fbbf89c58 | |||
84d259d8b3 | |||
8b47670a74 | |||
7f5dc1d461 | |||
43e765f4f9 | |||
adec73f542 | |||
fee159541f | |||
d81e6bf6ce | |||
70c93d970c | |||
4960273832 | |||
6c018ee6fe | |||
4ef32103ca | |||
e4ec27c5e2 | |||
20c04f7977 | |||
571f50d734 | |||
780ea6f7c0 | |||
4279906f6e | |||
2e54b97fc2 | |||
e1641b2c2e | |||
e0e1e4be80 | |||
d5845ce900 | |||
85f2cde4c3 | |||
cef64e01b3 | |||
94ea775232 | |||
2e4b7fac11 | |||
2867ec459a | |||
cd18d89894 | |||
449ed31e25 | |||
1f36904588 | |||
f7495dd0c3 | |||
a11f77835d | |||
af1ad82c8e | |||
4976338677 | |||
99d130d1ed | |||
4fb0544b0e | |||
14830d9f1c |
20
.github/ISSUE_TEMPLATE.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
Please answer the following questions.
|
||||
|
||||
### Which version of matterbridge are you using?
|
||||
run ```matterbridge -version```
|
||||
|
||||
### If you're having problems with mattermost please specify mattermost version.
|
||||
|
||||
|
||||
### Please describe the expected behavior.
|
||||
|
||||
|
||||
### Please describe the actual behavior.
|
||||
#### Use logs from running ```matterbridge -debug``` if possible.
|
||||
|
||||
|
||||
### Any steps to reproduce the behavior?
|
||||
|
||||
|
||||
### Please add your configuration file
|
||||
#### (be sure to exclude or anonymize private data (tokens/passwords))
|
@ -6,6 +6,6 @@ RUN apk update && apk add go git gcc musl-dev ca-certificates \
|
||||
&& cd /go/src/github.com/42wim/matterbridge \
|
||||
&& export GOPATH=/go \
|
||||
&& go get \
|
||||
&& go build -o /bin/matterbridge \
|
||||
&& go build -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
|
||||
&& rm -rf /go \
|
||||
&& apk del --purge git go gcc musl-dev
|
||||
|
192
README.md
192
README.md
@ -1,54 +1,49 @@
|
||||
# matterbridge
|
||||
Simple bridge between mattermost, IRC, XMPP, Gitter, Slack and Discord
|
||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat)
|
||||

|
||||
|
||||
* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack and Discord. Pick and mix.
|
||||
* Supports multiple channels.
|
||||
* Matterbridge can also work with private groups on your mattermost.
|
||||
Simple bridge between Mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat(via xmpp) and Matrix with REST API.
|
||||
|
||||
# Table of Contents
|
||||
* [Features](#features)
|
||||
* [Requirements](#requirements)
|
||||
* [Installing](#installing)
|
||||
* [Binaries](#binaries)
|
||||
* [Building](#building)
|
||||
* [Configuration](#configuration)
|
||||
* [Examples](#examples)
|
||||
* [Running](#running)
|
||||
* [Docker](#docker)
|
||||
* [Changelog](#changelog)
|
||||
* [FAQ](#faq)
|
||||
* [Thanks](#thanks)
|
||||
|
||||
# Features
|
||||
* Relays public channel messages between multiple mattermost, IRC, XMPP, Gitter, Slack, Discord, Telegram, Rocket.Chat, Hipchat (via xmpp) and Matrix. Pick and mix.
|
||||
* Matterbridge can also work with private groups on your mattermost/slack.
|
||||
* Allow for bridging the same bridges, which means you can eg bridge between multiple mattermosts.
|
||||
* The bridge is now a gateway which has support multiple in and out bridges. (and supports multiple gateways).
|
||||
* REST API to read/post messages to bridges (WIP).
|
||||
|
||||
Look at [matterbridge.toml.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
|
||||
Look at [matterbridge.toml.simple] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.simple) for a simple example.
|
||||
|
||||
## Changelog
|
||||
Since v0.7.0 the configuration has changed. More details in [changelog.md] (https://github.com/42wim/matterbridge/blob/master/changelog.md)
|
||||
|
||||
## Requirements
|
||||
# Requirements
|
||||
Accounts to one of the supported bridges
|
||||
* [Mattermost] (https://github.com/mattermost/platform/)
|
||||
* [IRC] (http://www.mirc.com/servers.html)
|
||||
* [XMPP] (https://jabber.org)
|
||||
* [Gitter] (https://gitter.im)
|
||||
* [Slack] (https://slack.com)
|
||||
* [Discord] (https://discordapp.com)
|
||||
* [Mattermost](https://github.com/mattermost/platform/) 3.5.x - 3.7.x
|
||||
* [IRC](http://www.mirc.com/servers.html)
|
||||
* [XMPP](https://jabber.org)
|
||||
* [Gitter](https://gitter.im)
|
||||
* [Slack](https://slack.com)
|
||||
* [Discord](https://discordapp.com)
|
||||
* [Telegram](https://telegram.org)
|
||||
* [Hipchat](https://www.hipchat.com)
|
||||
* [Rocket.chat](https://rocket.chat)
|
||||
* [Matrix](https://matrix.org)
|
||||
|
||||
## Docker
|
||||
Create your matterbridge.toml file locally eg in ```/tmp/matterbridge.toml```
|
||||
```
|
||||
docker run -ti -v /tmp/matterbridge.toml:/matterbridge.toml 42wim/matterbridge
|
||||
```
|
||||
|
||||
## binaries
|
||||
# Installing
|
||||
## Binaries
|
||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
|
||||
* For use with mattermost 3.5.0+ [v0.8.0](https://github.com/42wim/matterircd/releases/tag/v0.8.0)
|
||||
* For use with mattermost 3.3.0 - 3.4.0 [v0.7.0](https://github.com/42wim/matterircd/releases/tag/v0.7.0)
|
||||
* For use with mattermost 3.0.0 - 3.2.0 [v0.5.0](https://github.com/42wim/matterircd/releases/tag/v0.5.0) (not maintained anymore)
|
||||
* Latest release [v0.10.0](https://github.com/42wim/matterbridge/releases/tag/v0.10.0)
|
||||
|
||||
## Compatibility
|
||||
### Mattermost
|
||||
* Matterbridge v0.8.0 works with mattermost 3.5.0+ [3.5.0 release](https://github.com/mattermost/platform/releases/tag/v3.5.0)
|
||||
* Matterbridge v0.7.0 works with mattermost 3.3.0 - 3.4.0 [3.4.0 release](https://github.com/mattermost/platform/releases/tag/v3.4.0)
|
||||
* Matterbridge v0.5.0 works with mattermost 3.0.0 - 3.2.0 [3.2.0 release](https://github.com/mattermost/platform/releases/tag/v3.2.0)
|
||||
|
||||
|
||||
#### Webhooks version
|
||||
* Configured incoming/outgoing [webhooks](https://www.mattermost.org/webhooks/) on your mattermost instance.
|
||||
|
||||
#### API version
|
||||
* A dedicated user(bot) on your mattermost instance.
|
||||
|
||||
|
||||
## building
|
||||
## Building
|
||||
Go 1.6+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH] (https://golang.org/doc/code.html#GOPATH)
|
||||
|
||||
```
|
||||
@ -63,10 +58,69 @@ $ ls bin/
|
||||
matterbridge
|
||||
```
|
||||
|
||||
## running
|
||||
1) Copy the matterbridge.conf.sample to matterbridge.conf in the same directory as the matterbridge binary.
|
||||
2) Edit matterbridge.conf with the settings for your environment. See below for more config information.
|
||||
3) Now you can run matterbridge.
|
||||
# Configuration
|
||||
* [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
|
||||
* [matterbridge.toml.simple](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.simple) for a simple example.
|
||||
|
||||
## Examples
|
||||
### Bridge mattermost (off-topic) - irc (#testing)
|
||||
```
|
||||
[irc]
|
||||
[irc.freenode]
|
||||
Server="irc.freenode.net:6667"
|
||||
Nick="yourbotname"
|
||||
|
||||
[mattermost]
|
||||
[mattermost.work]
|
||||
useAPI=true
|
||||
Server="yourmattermostserver.tld"
|
||||
Team="yourteam"
|
||||
Login="yourlogin"
|
||||
Password="yourpass"
|
||||
PrefixMessagesWithNick=true
|
||||
|
||||
[[gateway]]
|
||||
name="mygateway"
|
||||
enable=true
|
||||
[[gateway.inout]]
|
||||
account="irc.freenode"
|
||||
channel="#testing"
|
||||
|
||||
[[gateway.inout]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
```
|
||||
|
||||
### Bridge slack (#general) - discord (general)
|
||||
```
|
||||
[slack.test]
|
||||
Token="yourslacktoken"
|
||||
PrefixMessagesWithNick=true
|
||||
|
||||
[discord.test]
|
||||
Token="yourdiscordtoken"
|
||||
Server="yourdiscordservername"
|
||||
|
||||
[general]
|
||||
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
|
||||
|
||||
[[gateway]]
|
||||
name = "mygateway"
|
||||
enable=true
|
||||
|
||||
[[gateway.inout]]
|
||||
account = "discord.test"
|
||||
channel="general"
|
||||
|
||||
[[gateway.inout]]
|
||||
account ="slack.test"
|
||||
channel = "general"
|
||||
```
|
||||
|
||||
# Running
|
||||
1) Copy the matterbridge.toml.sample to matterbridge.toml in the same directory as the matterbridge binary.
|
||||
2) Edit matterbridge.toml with the settings for your environment.
|
||||
3) Now you can run matterbridge. (```./matterbridge```)
|
||||
|
||||
```
|
||||
Usage of ./matterbridge:
|
||||
@ -78,35 +132,39 @@ Usage of ./matterbridge:
|
||||
show version
|
||||
```
|
||||
|
||||
## config
|
||||
### matterbridge
|
||||
matterbridge looks for matterbridge.toml in current directory. (use -conf to specify another file)
|
||||
## Docker
|
||||
Create your matterbridge.toml file locally eg in ```/tmp/matterbridge.toml```
|
||||
```
|
||||
docker run -ti -v /tmp/matterbridge.toml:/matterbridge.toml 42wim/matterbridge
|
||||
```
|
||||
|
||||
Look at [matterbridge.toml.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for an example.
|
||||
# Changelog
|
||||
See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.md)
|
||||
|
||||
### mattermost
|
||||
#### webhooks version
|
||||
You'll have to configure the incoming and outgoing webhooks.
|
||||
# FAQ
|
||||
|
||||
* incoming webhooks
|
||||
Go to "account settings" - integrations - "incoming webhooks".
|
||||
Choose a channel at "Add a new incoming webhook", this will create a webhook URL right below.
|
||||
This URL should be set in the matterbridge.conf in the [mattermost] section (see above)
|
||||
Please look at [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for more information first.
|
||||
|
||||
* outgoing webhooks
|
||||
Go to "account settings" - integrations - "outgoing webhooks".
|
||||
Choose a channel (the same as the one from incoming webhooks) and fill in the address and port of the server matterbridge will run on.
|
||||
|
||||
e.g. http://192.168.1.1:9999 (192.168.1.1:9999 is the BindAddress specified in [mattermost] section of matterbridge.conf)
|
||||
|
||||
## FAQ
|
||||
Please look at [matterbridge.toml.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for more information first.
|
||||
### Mattermost doesn't show the IRC nicks
|
||||
## Mattermost doesn't show the IRC nicks
|
||||
If you're running the webhooks version, this can be fixed by either:
|
||||
* enabling "override usernames". See [mattermost documentation](http://docs.mattermost.com/developer/webhooks-incoming.html#enabling-incoming-webhooks)
|
||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.toml.
|
||||
|
||||
If you're running the plus version you'll need to:
|
||||
If you're running the API version you'll need to:
|
||||
* setting ```PrefixMessagesWithNick``` to ```true``` in ```mattermost``` section of your matterbridge.toml.
|
||||
|
||||
Also look at the ```RemoteNickFormat``` setting.
|
||||
|
||||
|
||||
# Thanks
|
||||
Matterbridge wouldn't exist without these libraries:
|
||||
* discord - https://github.com/bwmarrin/discordgo
|
||||
* echo - https://github.com/labstack/echo
|
||||
* gitter - https://github.com/sromku/go-gitter
|
||||
* irc - https://github.com/thoj/go-ircevent
|
||||
* mattermost - https://github.com/mattermost/platform
|
||||
* matrix - https://github.com/matrix-org/gomatrix
|
||||
* slack - https://github.com/nlopes/slack
|
||||
* telegram - https://github.com/go-telegram-bot-api/telegram-bot-api
|
||||
* xmpp - https://github.com/mattn/go-xmpp
|
||||
|
||||
|
91
bridge/api/api.go
Normal file
91
bridge/api/api.go
Normal file
@ -0,0 +1,91 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/labstack/echo"
|
||||
"github.com/zfjagann/golang-ring"
|
||||
"net/http"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Api struct {
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
Messages ring.Ring
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type ApiMessage struct {
|
||||
Text string `json:"text"`
|
||||
Username string `json:"username"`
|
||||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
var protocol = "api"
|
||||
|
||||
func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Api {
|
||||
b := &Api{}
|
||||
e := echo.New()
|
||||
b.Messages = ring.Ring{}
|
||||
b.Messages.SetCapacity(cfg.Buffer)
|
||||
b.Config = &cfg
|
||||
b.Account = account
|
||||
b.Remote = c
|
||||
e.GET("/api/messages", b.handleMessages)
|
||||
e.POST("/api/message", b.handlePostMessage)
|
||||
go func() {
|
||||
flog.Fatal(e.Start(cfg.BindAddress))
|
||||
}()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Api) Connect() error {
|
||||
return nil
|
||||
}
|
||||
func (b *Api) Disconnect() error {
|
||||
return nil
|
||||
|
||||
}
|
||||
func (b *Api) JoinChannel(channel string) error {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (b *Api) Send(msg config.Message) error {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.Messages.Enqueue(&msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Api) handlePostMessage(c echo.Context) error {
|
||||
message := &ApiMessage{}
|
||||
if err := c.Bind(message); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Remote <- config.Message{
|
||||
Text: message.Text,
|
||||
Username: message.Username,
|
||||
Channel: "api",
|
||||
Avatar: message.Avatar,
|
||||
Account: b.Account,
|
||||
}
|
||||
return c.JSON(http.StatusOK, message)
|
||||
}
|
||||
|
||||
func (b *Api) handleMessages(c echo.Context) error {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
for _, msg := range b.Messages.Values() {
|
||||
c.JSONPretty(http.StatusOK, msg, " ")
|
||||
}
|
||||
b.Messages = ring.Ring{}
|
||||
return nil
|
||||
}
|
@ -1,45 +1,116 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge/api"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/bridge/discord"
|
||||
"github.com/42wim/matterbridge/bridge/gitter"
|
||||
"github.com/42wim/matterbridge/bridge/irc"
|
||||
"github.com/42wim/matterbridge/bridge/matrix"
|
||||
"github.com/42wim/matterbridge/bridge/mattermost"
|
||||
"github.com/42wim/matterbridge/bridge/rocketchat"
|
||||
"github.com/42wim/matterbridge/bridge/slack"
|
||||
"github.com/42wim/matterbridge/bridge/telegram"
|
||||
"github.com/42wim/matterbridge/bridge/xmpp"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Bridge interface {
|
||||
type Bridger interface {
|
||||
Send(msg config.Message) error
|
||||
Name() string
|
||||
Connect() error
|
||||
FullOrigin() string
|
||||
Origin() string
|
||||
Protocol() string
|
||||
JoinChannel(channel string) error
|
||||
Disconnect() error
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) Bridge {
|
||||
type Bridge struct {
|
||||
Config config.Protocol
|
||||
Bridger
|
||||
Name string
|
||||
Account string
|
||||
Protocol string
|
||||
ChannelsIn map[string]config.ChannelOptions
|
||||
ChannelsOut map[string]config.ChannelOptions
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) *Bridge {
|
||||
b := new(Bridge)
|
||||
b.ChannelsIn = make(map[string]config.ChannelOptions)
|
||||
b.ChannelsOut = make(map[string]config.ChannelOptions)
|
||||
accInfo := strings.Split(bridge.Account, ".")
|
||||
protocol := accInfo[0]
|
||||
name := accInfo[1]
|
||||
b.Name = name
|
||||
b.Protocol = protocol
|
||||
b.Account = bridge.Account
|
||||
|
||||
// override config from environment
|
||||
config.OverrideCfgFromEnv(cfg, protocol, name)
|
||||
switch protocol {
|
||||
case "mattermost":
|
||||
return bmattermost.New(cfg.Mattermost[name], name, c)
|
||||
b.Config = cfg.Mattermost[name]
|
||||
b.Bridger = bmattermost.New(cfg.Mattermost[name], bridge.Account, c)
|
||||
case "irc":
|
||||
return birc.New(cfg.IRC[name], name, c)
|
||||
b.Config = cfg.IRC[name]
|
||||
b.Bridger = birc.New(cfg.IRC[name], bridge.Account, c)
|
||||
case "gitter":
|
||||
return bgitter.New(cfg.Gitter[name], name, c)
|
||||
b.Config = cfg.Gitter[name]
|
||||
b.Bridger = bgitter.New(cfg.Gitter[name], bridge.Account, c)
|
||||
case "slack":
|
||||
return bslack.New(cfg.Slack[name], name, c)
|
||||
b.Config = cfg.Slack[name]
|
||||
b.Bridger = bslack.New(cfg.Slack[name], bridge.Account, c)
|
||||
case "xmpp":
|
||||
return bxmpp.New(cfg.Xmpp[name], name, c)
|
||||
b.Config = cfg.Xmpp[name]
|
||||
b.Bridger = bxmpp.New(cfg.Xmpp[name], bridge.Account, c)
|
||||
case "discord":
|
||||
return bdiscord.New(cfg.Discord[name], name, c)
|
||||
b.Config = cfg.Discord[name]
|
||||
b.Bridger = bdiscord.New(cfg.Discord[name], bridge.Account, c)
|
||||
case "telegram":
|
||||
b.Config = cfg.Telegram[name]
|
||||
b.Bridger = btelegram.New(cfg.Telegram[name], bridge.Account, c)
|
||||
case "rocketchat":
|
||||
b.Config = cfg.Rocketchat[name]
|
||||
b.Bridger = brocketchat.New(cfg.Rocketchat[name], bridge.Account, c)
|
||||
case "matrix":
|
||||
b.Config = cfg.Matrix[name]
|
||||
b.Bridger = bmatrix.New(cfg.Matrix[name], bridge.Account, c)
|
||||
case "api":
|
||||
b.Config = cfg.Api[name]
|
||||
b.Bridger = api.New(cfg.Api[name], bridge.Account, c)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bridge) JoinChannels() error {
|
||||
exists := make(map[string]bool)
|
||||
err := b.joinChannels(b.ChannelsIn, exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = b.joinChannels(b.ChannelsOut, exists)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bridge) joinChannels(cMap map[string]config.ChannelOptions, exists map[string]bool) error {
|
||||
mychannel := ""
|
||||
for channel, info := range cMap {
|
||||
if !exists[channel] {
|
||||
mychannel = channel
|
||||
log.Infof("%s: joining %s", b.Account, channel)
|
||||
if b.Protocol == "irc" && info.Key != "" {
|
||||
log.Debugf("using key %s for channel %s", info.Key, channel)
|
||||
mychannel = mychannel + " " + info.Key
|
||||
}
|
||||
err := b.JoinChannel(mychannel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
exists[channel] = true
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -6,24 +6,32 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
EVENT_JOIN_LEAVE = "join_leave"
|
||||
EVENT_FAILURE = "failure"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Text string
|
||||
Channel string
|
||||
Username string
|
||||
Origin string
|
||||
FullOrigin string
|
||||
Protocol string
|
||||
Avatar string
|
||||
Text string
|
||||
Channel string
|
||||
Username string
|
||||
Avatar string
|
||||
Account string
|
||||
Event string
|
||||
Protocol string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
BindAddress string // mattermost, slack
|
||||
Buffer int // api
|
||||
IconURL string // mattermost, slack
|
||||
IgnoreNicks string // all protocols
|
||||
Jid string // xmpp
|
||||
Login string // mattermost
|
||||
Login string // mattermost, matrix
|
||||
Muc string // xmpp
|
||||
Name string // all protocols
|
||||
Nick string // all protocols
|
||||
@ -32,26 +40,32 @@ type Protocol struct {
|
||||
NickServPassword string // IRC
|
||||
NicksPerRow int // mattermost, slack
|
||||
NoTLS bool // mattermost
|
||||
Password string // IRC,mattermost,XMPP
|
||||
Password string // IRC,mattermost,XMPP,matrix
|
||||
PrefixMessagesWithNick bool // mattemost, slack
|
||||
Protocol string //all protocols
|
||||
MessageQueue int // IRC, size of message queue for flood control
|
||||
MessageDelay int // IRC, time in millisecond to wait between messages
|
||||
MessageFormat string // telegram
|
||||
RemoteNickFormat string // all protocols
|
||||
Server string // IRC,mattermost,XMPP,discord
|
||||
ShowJoinPart bool // all protocols
|
||||
SkipTLSVerify bool // IRC, mattermost
|
||||
Team string // mattermost
|
||||
Token string // gitter, slack, discord
|
||||
URL string // mattermost, slack
|
||||
URL string // mattermost, slack, matrix
|
||||
UseAPI bool // mattermost, slack
|
||||
UseSASL bool // IRC
|
||||
UseTLS bool // IRC
|
||||
}
|
||||
|
||||
type ChannelOptions struct {
|
||||
Key string // irc
|
||||
}
|
||||
|
||||
type Bridge struct {
|
||||
Account string
|
||||
Channel string
|
||||
Options ChannelOptions
|
||||
}
|
||||
|
||||
type Gateway struct {
|
||||
@ -59,6 +73,7 @@ type Gateway struct {
|
||||
Enable bool
|
||||
In []Bridge
|
||||
Out []Bridge
|
||||
InOut []Bridge
|
||||
}
|
||||
|
||||
type SameChannelGateway struct {
|
||||
@ -69,12 +84,17 @@ type SameChannelGateway struct {
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Api map[string]Protocol
|
||||
IRC map[string]Protocol
|
||||
Mattermost map[string]Protocol
|
||||
Matrix map[string]Protocol
|
||||
Slack map[string]Protocol
|
||||
Gitter map[string]Protocol
|
||||
Xmpp map[string]Protocol
|
||||
Discord map[string]Protocol
|
||||
Telegram map[string]Protocol
|
||||
Rocketchat map[string]Protocol
|
||||
General Protocol
|
||||
Gateway []Gateway
|
||||
SameChannelGateway []SameChannelGateway
|
||||
}
|
||||
@ -126,16 +146,11 @@ func OverrideCfgFromEnv(cfg *Config, protocol string, account string) {
|
||||
|
||||
func GetIconURL(msg *Message, cfg *Protocol) string {
|
||||
iconURL := cfg.IconURL
|
||||
info := strings.Split(msg.Account, ".")
|
||||
protocol := info[0]
|
||||
name := info[1]
|
||||
iconURL = strings.Replace(iconURL, "{NICK}", msg.Username, -1)
|
||||
iconURL = strings.Replace(iconURL, "{BRIDGE}", msg.Origin, -1)
|
||||
iconURL = strings.Replace(iconURL, "{PROTOCOL}", msg.Protocol, -1)
|
||||
iconURL = strings.Replace(iconURL, "{BRIDGE}", name, -1)
|
||||
iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
|
||||
return iconURL
|
||||
}
|
||||
|
||||
func GetNick(msg *Message, cfg *Protocol) string {
|
||||
nick := cfg.RemoteNickFormat
|
||||
nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
|
||||
nick = strings.Replace(nick, "{BRIDGE}", msg.Origin, -1)
|
||||
nick = strings.Replace(nick, "{PROTOCOL}", msg.Protocol, -1)
|
||||
return nick
|
||||
}
|
||||
|
@ -5,17 +5,20 @@ import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type bdiscord struct {
|
||||
c *discordgo.Session
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
protocol string
|
||||
origin string
|
||||
Channels []*discordgo.Channel
|
||||
Nick string
|
||||
UseChannelID bool
|
||||
c *discordgo.Session
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
Channels []*discordgo.Channel
|
||||
Nick string
|
||||
UseChannelID bool
|
||||
userMemberMap map[string]*discordgo.Member
|
||||
guildID string
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -25,18 +28,21 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *bdiscord {
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *bdiscord {
|
||||
b := &bdiscord{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
b.Account = account
|
||||
b.userMemberMap = make(map[string]*discordgo.Member)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *bdiscord) Connect() error {
|
||||
var err error
|
||||
flog.Info("Connecting")
|
||||
if !strings.HasPrefix(b.Config.Token, "Bot ") {
|
||||
b.Config.Token = "Bot " + b.Config.Token
|
||||
}
|
||||
b.c, err = discordgo.New(b.Config.Token)
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
@ -44,6 +50,7 @@ func (b *bdiscord) Connect() error {
|
||||
}
|
||||
flog.Info("Connection succeeded")
|
||||
b.c.AddHandler(b.messageCreate)
|
||||
b.c.AddHandler(b.memberUpdate)
|
||||
err = b.c.Open()
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
@ -63,6 +70,7 @@ func (b *bdiscord) Connect() error {
|
||||
for _, guild := range guilds {
|
||||
if guild.Name == b.Config.Server {
|
||||
b.Channels, err = b.c.GuildChannels(guild.ID)
|
||||
b.guildID = guild.ID
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
return err
|
||||
@ -72,8 +80,8 @@ func (b *bdiscord) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bdiscord) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
func (b *bdiscord) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bdiscord) JoinChannel(channel string) error {
|
||||
@ -84,18 +92,6 @@ func (b *bdiscord) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bdiscord) Name() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *bdiscord) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *bdiscord) Origin() string {
|
||||
return b.origin
|
||||
}
|
||||
|
||||
func (b *bdiscord) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
channelID := b.getChannelID(msg.Channel)
|
||||
@ -103,8 +99,7 @@ func (b *bdiscord) Send(msg config.Message) error {
|
||||
flog.Errorf("Could not find channelID for %v", msg.Channel)
|
||||
return nil
|
||||
}
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
b.c.ChannelMessageSend(channelID, nick+msg.Text)
|
||||
b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -121,13 +116,50 @@ func (b *bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
|
||||
if m.Content == "" {
|
||||
return
|
||||
}
|
||||
flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.FullOrigin())
|
||||
flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.Account)
|
||||
channelName := b.getChannelName(m.ChannelID)
|
||||
if b.UseChannelID {
|
||||
channelName = "ID:" + m.ChannelID
|
||||
}
|
||||
b.Remote <- config.Message{Username: m.Author.Username, Text: m.ContentWithMentionsReplaced(), Channel: channelName,
|
||||
Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin(), Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg"}
|
||||
username := b.getNick(m.Author)
|
||||
if len(m.MentionRoles) > 0 {
|
||||
m.Message.Content = b.replaceRoleMentions(m.Message.Content)
|
||||
}
|
||||
b.Remote <- config.Message{Username: username, Text: m.ContentWithMentionsReplaced(), Channel: channelName,
|
||||
Account: b.Account, Avatar: "https://cdn.discordapp.com/avatars/" + m.Author.ID + "/" + m.Author.Avatar + ".jpg"}
|
||||
}
|
||||
|
||||
func (b *bdiscord) memberUpdate(s *discordgo.Session, m *discordgo.GuildMemberUpdate) {
|
||||
b.Lock()
|
||||
if _, ok := b.userMemberMap[m.Member.User.ID]; ok {
|
||||
flog.Debugf("%s: memberupdate: user %s (nick %s) changes nick to %s", b.Account, m.Member.User.Username, b.userMemberMap[m.Member.User.ID].Nick, m.Member.Nick)
|
||||
}
|
||||
b.userMemberMap[m.Member.User.ID] = m.Member
|
||||
b.Unlock()
|
||||
}
|
||||
|
||||
func (b *bdiscord) getNick(user *discordgo.User) string {
|
||||
var err error
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
if _, ok := b.userMemberMap[user.ID]; ok {
|
||||
if b.userMemberMap[user.ID].Nick != "" {
|
||||
// only return if nick is set
|
||||
return b.userMemberMap[user.ID].Nick
|
||||
}
|
||||
// otherwise return username
|
||||
return user.Username
|
||||
}
|
||||
// if we didn't find nick, search for it
|
||||
b.userMemberMap[user.ID], err = b.c.GuildMember(b.guildID, user.ID)
|
||||
if err != nil {
|
||||
return user.Username
|
||||
}
|
||||
// only return if nick is set
|
||||
if b.userMemberMap[user.ID].Nick != "" {
|
||||
return b.userMemberMap[user.ID].Nick
|
||||
}
|
||||
return user.Username
|
||||
}
|
||||
|
||||
func (b *bdiscord) getChannelID(name string) string {
|
||||
@ -151,3 +183,15 @@ func (b *bdiscord) getChannelName(id string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *bdiscord) replaceRoleMentions(text string) string {
|
||||
roles, err := b.c.GuildRoles(b.guildID)
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", string(err.(*discordgo.RESTError).ResponseBody))
|
||||
return text
|
||||
}
|
||||
for _, role := range roles {
|
||||
text = strings.Replace(text, "<@&"+role.ID+">", "@"+role.Name, -1)
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
@ -1,20 +1,20 @@
|
||||
package bgitter
|
||||
|
||||
import (
|
||||
"github.com/42wim/go-gitter"
|
||||
"fmt"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/sromku/go-gitter"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Bgitter struct {
|
||||
c *gitter.Gitter
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
protocol string
|
||||
origin string
|
||||
Users []gitter.User
|
||||
Rooms []gitter.Room
|
||||
c *gitter.Gitter
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
Users []gitter.User
|
||||
Rooms []gitter.Room
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -24,12 +24,11 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bgitter {
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bgitter {
|
||||
b := &Bgitter{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
b.Account = account
|
||||
return b
|
||||
}
|
||||
|
||||
@ -47,15 +46,19 @@ func (b *Bgitter) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bgitter) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
func (b *Bgitter) Disconnect() error {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (b *Bgitter) JoinChannel(channel string) error {
|
||||
room := channel
|
||||
roomID := b.getRoomID(room)
|
||||
if roomID == "" {
|
||||
return nil
|
||||
roomID, err := b.c.GetRoomId(channel)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not find roomID for %v. Please create the room on gitter.im", channel)
|
||||
}
|
||||
room, err := b.c.GetRoom(roomID)
|
||||
if err != nil {
|
||||
b.Rooms = append(b.Rooms, *room)
|
||||
}
|
||||
user, err := b.c.GetUser()
|
||||
if err != nil {
|
||||
@ -71,36 +74,23 @@ func (b *Bgitter) JoinChannel(channel string) error {
|
||||
go b.c.Listen(stream)
|
||||
|
||||
go func(stream *gitter.Stream, room string) {
|
||||
for {
|
||||
event := <-stream.Event
|
||||
for event := range stream.Event {
|
||||
switch ev := event.Data.(type) {
|
||||
case *gitter.MessageReceived:
|
||||
// check for ZWSP to see if it's not an echo
|
||||
if !strings.HasSuffix(ev.Message.Text, "") {
|
||||
flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.FullOrigin())
|
||||
flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.Account)
|
||||
b.Remote <- config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room,
|
||||
Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin(), Avatar: b.getAvatar(ev.Message.From.Username)}
|
||||
Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username)}
|
||||
}
|
||||
case *gitter.GitterConnectionClosed:
|
||||
flog.Errorf("connection with gitter closed for room %s", room)
|
||||
}
|
||||
}
|
||||
}(stream, room)
|
||||
}(stream, room.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bgitter) Name() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bgitter) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *Bgitter) Origin() string {
|
||||
return b.origin
|
||||
}
|
||||
|
||||
func (b *Bgitter) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
roomID := b.getRoomID(msg.Channel)
|
||||
@ -108,9 +98,8 @@ func (b *Bgitter) Send(msg config.Message) error {
|
||||
flog.Errorf("Could not find roomID for %v", msg.Channel)
|
||||
return nil
|
||||
}
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
// add ZWSP because gitter echoes our own messages
|
||||
return b.c.SendMessage(roomID, nick+msg.Text+" ")
|
||||
return b.c.SendMessage(roomID, msg.Username+msg.Text+" ")
|
||||
}
|
||||
|
||||
func (b *Bgitter) getRoomID(channel string) string {
|
||||
|
@ -19,11 +19,10 @@ type Birc struct {
|
||||
Nick string
|
||||
names map[string][]string
|
||||
Config *config.Protocol
|
||||
origin string
|
||||
protocol string
|
||||
Remote chan config.Message
|
||||
connected chan struct{}
|
||||
Local chan config.Message // local queue for flood control
|
||||
Account string
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -33,14 +32,13 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Birc {
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Birc {
|
||||
b := &Birc{}
|
||||
b.Config = &cfg
|
||||
b.Nick = b.Config.Nick
|
||||
b.Remote = c
|
||||
b.names = make(map[string][]string)
|
||||
b.origin = origin
|
||||
b.protocol = protocol
|
||||
b.Account = account
|
||||
b.connected = make(chan struct{})
|
||||
if b.Config.MessageDelay == 0 {
|
||||
b.Config.MessageDelay = 1300
|
||||
@ -48,21 +46,21 @@ func New(cfg config.Protocol, origin string, c chan config.Message) *Birc {
|
||||
if b.Config.MessageQueue == 0 {
|
||||
b.Config.MessageQueue = 30
|
||||
}
|
||||
b.Local = make(chan config.Message, b.Config.MessageQueue+10)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Birc) Command(msg *config.Message) string {
|
||||
switch msg.Text {
|
||||
case "!users":
|
||||
b.i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
|
||||
b.i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames)
|
||||
b.i.SendRaw("NAMES " + msg.Channel)
|
||||
b.i.ClearCallback(ircm.RPL_ENDOFNAMES)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Birc) Connect() error {
|
||||
b.Local = make(chan config.Message, b.Config.MessageQueue+10)
|
||||
flog.Infof("Connecting %s", b.Config.Server)
|
||||
i := irc.IRC(b.Config.Nick, b.Config.Nick)
|
||||
if log.GetLevel() == log.DebugLevel {
|
||||
@ -93,8 +91,10 @@ func (b *Birc) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Birc) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
func (b *Birc) Disconnect() error {
|
||||
b.i.Disconnect()
|
||||
close(b.Local)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Birc) JoinChannel(channel string) error {
|
||||
@ -102,34 +102,21 @@ func (b *Birc) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Birc) Name() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Birc) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *Birc) Origin() string {
|
||||
return b.origin
|
||||
}
|
||||
|
||||
func (b *Birc) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
if msg.FullOrigin == b.FullOrigin() {
|
||||
if msg.Account == b.Account {
|
||||
return nil
|
||||
}
|
||||
if strings.HasPrefix(msg.Text, "!") {
|
||||
b.Command(&msg)
|
||||
return nil
|
||||
}
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
for _, text := range strings.Split(msg.Text, "\n") {
|
||||
if len(b.Local) < b.Config.MessageQueue {
|
||||
if len(b.Local) == b.Config.MessageQueue-1 {
|
||||
text = text + " <message clipped>"
|
||||
}
|
||||
b.Local <- config.Message{Text: text, Username: nick, Channel: msg.Channel}
|
||||
b.Local <- config.Message{Text: text, Username: msg.Username, Channel: msg.Channel}
|
||||
} else {
|
||||
flog.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
|
||||
}
|
||||
@ -153,13 +140,15 @@ func (b *Birc) endNames(event *irc.Event) {
|
||||
continued := false
|
||||
for len(b.names[channel]) > maxNamesPerPost {
|
||||
b.Remote <- config.Message{Username: b.Nick, Text: b.formatnicks(b.names[channel][0:maxNamesPerPost], continued),
|
||||
Channel: channel, Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin()}
|
||||
Channel: channel, Account: b.Account}
|
||||
b.names[channel] = b.names[channel][maxNamesPerPost:]
|
||||
continued = true
|
||||
}
|
||||
b.Remote <- config.Message{Username: b.Nick, Text: b.formatnicks(b.names[channel], continued), Channel: channel,
|
||||
Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin()}
|
||||
b.Remote <- config.Message{Username: b.Nick, Text: b.formatnicks(b.names[channel], continued),
|
||||
Channel: channel, Account: b.Account}
|
||||
b.names[channel] = nil
|
||||
b.i.ClearCallback(ircm.RPL_NAMREPLY)
|
||||
b.i.ClearCallback(ircm.RPL_ENDOFNAMES)
|
||||
}
|
||||
|
||||
func (b *Birc) handleNewConnection(event *irc.Event) {
|
||||
@ -169,18 +158,34 @@ func (b *Birc) handleNewConnection(event *irc.Event) {
|
||||
i.AddCallback("PRIVMSG", b.handlePrivMsg)
|
||||
i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
|
||||
i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
|
||||
i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
|
||||
i.AddCallback(ircm.NOTICE, b.handleNotice)
|
||||
//i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) })
|
||||
i.AddCallback("PING", func(e *irc.Event) {
|
||||
i.SendRaw("PONG :" + e.Message())
|
||||
flog.Debugf("PING/PONG")
|
||||
})
|
||||
i.AddCallback("JOIN", b.handleJoinPart)
|
||||
i.AddCallback("PART", b.handleJoinPart)
|
||||
i.AddCallback("QUIT", b.handleJoinPart)
|
||||
i.AddCallback("*", b.handleOther)
|
||||
// we are now fully connected
|
||||
b.connected <- struct{}{}
|
||||
}
|
||||
|
||||
func (b *Birc) handleJoinPart(event *irc.Event) {
|
||||
flog.Debugf("Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||
channel := event.Arguments[0]
|
||||
if event.Code == "QUIT" {
|
||||
if event.Nick == b.Nick && strings.Contains(event.Raw, "Ping timeout") {
|
||||
flog.Infof("%s reconnecting ..", b.Account)
|
||||
b.Remote <- config.Message{Username: "system", Text: "reconnect", Channel: channel, Account: b.Account, Event: config.EVENT_FAILURE}
|
||||
return
|
||||
}
|
||||
}
|
||||
b.Remote <- config.Message{Username: "system", Text: event.Nick + " " + strings.ToLower(event.Code) + "s", Channel: channel, Account: b.Account, Event: config.EVENT_JOIN_LEAVE}
|
||||
flog.Debugf("handle %#v", event)
|
||||
}
|
||||
|
||||
func (b *Birc) handleNotice(event *irc.Event) {
|
||||
if strings.Contains(event.Message(), "This nickname is registered") && event.Nick == b.Config.NickServNick {
|
||||
b.i.Privmsg(b.Config.NickServNick, "IDENTIFY "+b.Config.NickServPassword)
|
||||
@ -215,8 +220,8 @@ func (b *Birc) handlePrivMsg(event *irc.Event) {
|
||||
// strip IRC colors
|
||||
re := regexp.MustCompile(`[[:cntrl:]](\d+,|)\d+`)
|
||||
msg = re.ReplaceAllString(msg, "")
|
||||
flog.Debugf("Sending message from %s on %s to gateway", event.Arguments[0], b.FullOrigin())
|
||||
b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin()}
|
||||
flog.Debugf("Sending message from %s on %s to gateway", event.Arguments[0], b.Account)
|
||||
b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Account: b.Account}
|
||||
}
|
||||
|
||||
func (b *Birc) handleTopicWhoTime(event *irc.Event) {
|
||||
|
117
bridge/matrix/matrix.go
Normal file
117
bridge/matrix/matrix.go
Normal file
@ -0,0 +1,117 @@
|
||||
package bmatrix
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
matrix "github.com/matrix-org/gomatrix"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Bmatrix struct {
|
||||
mc *matrix.Client
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
UserID string
|
||||
RoomMap map[string]string
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
var protocol = "matrix"
|
||||
|
||||
func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmatrix {
|
||||
b := &Bmatrix{}
|
||||
b.RoomMap = make(map[string]string)
|
||||
b.Config = &cfg
|
||||
b.Account = account
|
||||
b.Remote = c
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bmatrix) Connect() error {
|
||||
var err error
|
||||
flog.Infof("Connecting %s", b.Config.Server)
|
||||
b.mc, err = matrix.NewClient(b.Config.Server, "", "")
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
return err
|
||||
}
|
||||
resp, err := b.mc.Login(&matrix.ReqLogin{
|
||||
Type: "m.login.password",
|
||||
User: b.Config.Login,
|
||||
Password: b.Config.Password,
|
||||
})
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
return err
|
||||
}
|
||||
b.mc.SetCredentials(resp.UserID, resp.AccessToken)
|
||||
b.UserID = resp.UserID
|
||||
flog.Info("Connection succeeded")
|
||||
go b.handlematrix()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmatrix) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmatrix) JoinChannel(channel string) error {
|
||||
resp, err := b.mc.JoinRoom(channel, "", nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Lock()
|
||||
b.RoomMap[resp.RoomID] = channel
|
||||
b.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bmatrix) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
channel := b.getRoomID(msg.Channel)
|
||||
flog.Debugf("Sending to channel %s", channel)
|
||||
b.mc.SendText(channel, msg.Username+msg.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmatrix) getRoomID(channel string) string {
|
||||
b.RLock()
|
||||
defer b.RUnlock()
|
||||
for ID, name := range b.RoomMap {
|
||||
if name == channel {
|
||||
return ID
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func (b *Bmatrix) handlematrix() error {
|
||||
syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
|
||||
syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
|
||||
if ev.Content["msgtype"].(string) == "m.text" && ev.Sender != b.UserID {
|
||||
b.RLock()
|
||||
channel, ok := b.RoomMap[ev.RoomID]
|
||||
b.RUnlock()
|
||||
if !ok {
|
||||
flog.Debugf("Unknown room %s", ev.RoomID)
|
||||
return
|
||||
}
|
||||
flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
|
||||
b.Remote <- config.Message{Username: ev.Sender, Text: ev.Content["body"].(string), Channel: channel, Account: b.Account}
|
||||
}
|
||||
flog.Debugf("Received: %#v", ev)
|
||||
})
|
||||
go func() {
|
||||
for {
|
||||
if err := b.mc.Sync(); err != nil {
|
||||
flog.Println("Sync() returned ", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
@ -26,12 +26,11 @@ type MMMessage struct {
|
||||
type Bmattermost struct {
|
||||
MMhook
|
||||
MMapi
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
name string
|
||||
origin string
|
||||
protocol string
|
||||
TeamId string
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
name string
|
||||
TeamId string
|
||||
Account string
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -41,13 +40,11 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bmattermost {
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost {
|
||||
b := &Bmattermost{}
|
||||
b.Config = &cfg
|
||||
b.origin = origin
|
||||
b.Remote = c
|
||||
b.protocol = "mattermost"
|
||||
b.name = cfg.Name
|
||||
b.Account = account
|
||||
b.mmMap = make(map[string]string)
|
||||
return b
|
||||
}
|
||||
@ -80,8 +77,8 @@ func (b *Bmattermost) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmattermost) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
func (b *Bmattermost) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmattermost) JoinChannel(channel string) error {
|
||||
@ -92,21 +89,9 @@ func (b *Bmattermost) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmattermost) Name() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bmattermost) Origin() string {
|
||||
return b.origin
|
||||
}
|
||||
|
||||
func (b *Bmattermost) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *Bmattermost) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
nick := msg.Username
|
||||
message := msg.Text
|
||||
channel := msg.Channel
|
||||
|
||||
@ -144,8 +129,8 @@ func (b *Bmattermost) handleMatter() {
|
||||
go b.handleMatterHook(mchan)
|
||||
}
|
||||
for message := range mchan {
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.FullOrigin())
|
||||
b.Remote <- config.Message{Text: message.Text, Username: message.Username, Channel: message.Channel, Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin()}
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
b.Remote <- config.Message{Text: message.Text, Username: message.Username, Channel: message.Channel, Account: b.Account}
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,8 +144,8 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
|
||||
m.Username = message.Username
|
||||
m.Channel = message.Channel
|
||||
m.Text = message.Text
|
||||
if len(message.Post.Filenames) > 0 {
|
||||
for _, link := range b.mc.GetPublicLinks(message.Post.Filenames) {
|
||||
if len(message.Post.FileIds) > 0 {
|
||||
for _, link := range b.mc.GetPublicLinks(message.Post.FileIds) {
|
||||
m.Text = m.Text + "\n" + link
|
||||
}
|
||||
}
|
||||
|
87
bridge/rocketchat/rocketchat.go
Normal file
87
bridge/rocketchat/rocketchat.go
Normal file
@ -0,0 +1,87 @@
|
||||
package brocketchat
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/hook/rockethook"
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
type MMhook struct {
|
||||
mh *matterhook.Client
|
||||
rh *rockethook.Client
|
||||
}
|
||||
|
||||
type Brocketchat struct {
|
||||
MMhook
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
name string
|
||||
Account string
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
var protocol = "rocketchat"
|
||||
|
||||
func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Brocketchat {
|
||||
b := &Brocketchat{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.Account = account
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Brocketchat) Command(cmd string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Brocketchat) Connect() error {
|
||||
flog.Info("Connecting webhooks")
|
||||
b.mh = matterhook.New(b.Config.URL,
|
||||
matterhook.Config{InsecureSkipVerify: b.Config.SkipTLSVerify,
|
||||
DisableServer: true})
|
||||
b.rh = rockethook.New(b.Config.URL, rockethook.Config{BindAddress: b.Config.BindAddress})
|
||||
go b.handleRocketHook()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Brocketchat) Disconnect() error {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (b *Brocketchat) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Brocketchat) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
matterMessage := matterhook.OMessage{IconURL: b.Config.IconURL}
|
||||
matterMessage.Channel = msg.Channel
|
||||
matterMessage.UserName = msg.Username
|
||||
matterMessage.Type = ""
|
||||
matterMessage.Text = msg.Text
|
||||
err := b.mh.Send(matterMessage)
|
||||
if err != nil {
|
||||
flog.Info(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Brocketchat) handleRocketHook() {
|
||||
for {
|
||||
message := b.rh.Receive()
|
||||
flog.Debugf("Receiving from rockethook %#v", message)
|
||||
// do not loop
|
||||
if message.UserName == b.Config.Nick {
|
||||
continue
|
||||
}
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.UserName, b.Account)
|
||||
b.Remote <- config.Message{Text: message.Text, Username: message.UserName, Channel: message.ChannelName, Account: b.Account}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/nlopes/slack"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -25,8 +26,7 @@ type Bslack struct {
|
||||
Plus bool
|
||||
Remote chan config.Message
|
||||
Users []slack.User
|
||||
protocol string
|
||||
origin string
|
||||
Account string
|
||||
si *slack.Info
|
||||
channels []slack.Channel
|
||||
}
|
||||
@ -38,12 +38,11 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bslack {
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bslack {
|
||||
b := &Bslack{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
b.Account = account
|
||||
return b
|
||||
}
|
||||
|
||||
@ -66,8 +65,9 @@ func (b *Bslack) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bslack) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
func (b *Bslack) Disconnect() error {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (b *Bslack) JoinChannel(channel string) error {
|
||||
@ -81,24 +81,12 @@ func (b *Bslack) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bslack) Name() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bslack) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *Bslack) Origin() string {
|
||||
return b.origin
|
||||
}
|
||||
|
||||
func (b *Bslack) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
if msg.FullOrigin == b.FullOrigin() {
|
||||
if msg.Account == b.Account {
|
||||
return nil
|
||||
}
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
nick := msg.Username
|
||||
message := msg.Text
|
||||
channel := msg.Channel
|
||||
if b.Config.PrefixMessagesWithNick {
|
||||
@ -154,14 +142,26 @@ func (b *Bslack) getAvatar(user string) string {
|
||||
|
||||
func (b *Bslack) getChannelByName(name string) (*slack.Channel, error) {
|
||||
if b.channels == nil {
|
||||
return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.FullOrigin(), name)
|
||||
return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, name)
|
||||
}
|
||||
for _, channel := range b.channels {
|
||||
if channel.Name == name {
|
||||
return &channel, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("%s: channel %s not found", b.FullOrigin(), name)
|
||||
return nil, fmt.Errorf("%s: channel %s not found", b.Account, name)
|
||||
}
|
||||
|
||||
func (b *Bslack) getChannelByID(ID string) (*slack.Channel, error) {
|
||||
if b.channels == nil {
|
||||
return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.Account, ID)
|
||||
}
|
||||
for _, channel := range b.channels {
|
||||
if channel.ID == ID {
|
||||
return &channel, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("%s: channel %s not found", b.Account, ID)
|
||||
}
|
||||
|
||||
func (b *Bslack) handleSlack() {
|
||||
@ -176,13 +176,13 @@ func (b *Bslack) handleSlack() {
|
||||
flog.Debug("Start listening for Slack messages")
|
||||
for message := range mchan {
|
||||
// do not send messages from ourself
|
||||
if message.Username == b.si.User.Name {
|
||||
if b.Config.UseAPI && message.Username == b.si.User.Name {
|
||||
continue
|
||||
}
|
||||
texts := strings.Split(message.Text, "\n")
|
||||
for _, text := range texts {
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.FullOrigin())
|
||||
b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin(), Avatar: b.getAvatar(message.Username)}
|
||||
flog.Debugf("Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username)}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,8 +195,8 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
|
||||
// ignore first message
|
||||
if count > 0 {
|
||||
flog.Debugf("Receiving from slackclient %#v", ev)
|
||||
//ev.ReplyTo
|
||||
channel, err := b.rtm.GetChannelInfo(ev.Channel)
|
||||
// use our own func because rtm.GetChannelInfo doesn't work for private channels
|
||||
channel, err := b.getChannelByID(ev.Channel)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -209,15 +209,26 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
|
||||
m.Channel = channel.Name
|
||||
m.Text = ev.Text
|
||||
m.Raw = ev
|
||||
m.Text = b.replaceMention(m.Text)
|
||||
mchan <- m
|
||||
}
|
||||
count++
|
||||
case *slack.OutgoingErrorEvent:
|
||||
flog.Debugf("%#v", ev.Error())
|
||||
case *slack.ChannelJoinedEvent:
|
||||
b.Users, _ = b.sc.GetUsers()
|
||||
case *slack.ConnectedEvent:
|
||||
b.channels = ev.Info.Channels
|
||||
b.si = ev.Info
|
||||
b.Users, _ = b.sc.GetUsers()
|
||||
// add private channels
|
||||
groups, _ := b.sc.GetGroups(true)
|
||||
for _, g := range groups {
|
||||
channel := new(slack.Channel)
|
||||
channel.ID = g.ID
|
||||
channel.Name = g.Name
|
||||
b.channels = append(b.channels, *channel)
|
||||
}
|
||||
case *slack.InvalidAuthEvent:
|
||||
flog.Fatalf("Invalid Token %#v", ev)
|
||||
default:
|
||||
@ -232,7 +243,29 @@ func (b *Bslack) handleMatterHook(mchan chan *MMMessage) {
|
||||
m := &MMMessage{}
|
||||
m.Username = message.UserName
|
||||
m.Text = message.Text
|
||||
m.Text = b.replaceMention(m.Text)
|
||||
m.Channel = message.ChannelName
|
||||
if m.Username == "slackbot" {
|
||||
continue
|
||||
}
|
||||
mchan <- m
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bslack) userName(id string) string {
|
||||
for _, u := range b.Users {
|
||||
if u.ID == id {
|
||||
return u.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Bslack) replaceMention(text string) string {
|
||||
results := regexp.MustCompile(`<@([a-zA-z0-9]+)>`).FindAllStringSubmatch(text, -1)
|
||||
for _, r := range results {
|
||||
text = strings.Replace(text, "<@"+r[1]+">", "@"+b.userName(r[1]), -1)
|
||||
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
64
bridge/telegram/html.go
Normal file
64
bridge/telegram/html.go
Normal file
@ -0,0 +1,64 @@
|
||||
package btelegram
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/russross/blackfriday"
|
||||
"html"
|
||||
)
|
||||
|
||||
type customHtml struct {
|
||||
blackfriday.Renderer
|
||||
}
|
||||
|
||||
func (options *customHtml) Paragraph(out *bytes.Buffer, text func() bool) {
|
||||
marker := out.Len()
|
||||
|
||||
if !text() {
|
||||
out.Truncate(marker)
|
||||
return
|
||||
}
|
||||
out.WriteString("\n")
|
||||
}
|
||||
|
||||
func (options *customHtml) BlockCode(out *bytes.Buffer, text []byte, lang string) {
|
||||
out.WriteString("<pre>")
|
||||
|
||||
out.WriteString(html.EscapeString(string(text)))
|
||||
out.WriteString("</pre>\n")
|
||||
}
|
||||
|
||||
func (options *customHtml) Header(out *bytes.Buffer, text func() bool, level int, id string) {
|
||||
options.Paragraph(out, text)
|
||||
}
|
||||
|
||||
func (options *customHtml) HRule(out *bytes.Buffer) {
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
|
||||
func (options *customHtml) BlockQuote(out *bytes.Buffer, text []byte) {
|
||||
out.WriteString("> ")
|
||||
out.Write(text)
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
|
||||
func (options *customHtml) List(out *bytes.Buffer, text func() bool, flags int) {
|
||||
options.Paragraph(out, text)
|
||||
}
|
||||
|
||||
func (options *customHtml) ListItem(out *bytes.Buffer, text []byte, flags int) {
|
||||
out.WriteString("- ")
|
||||
out.Write(text)
|
||||
out.WriteByte('\n')
|
||||
}
|
||||
|
||||
func makeHTML(input string) string {
|
||||
return string(blackfriday.Markdown([]byte(input),
|
||||
&customHtml{blackfriday.HtmlRenderer(blackfriday.HTML_USE_XHTML|blackfriday.HTML_SKIP_IMAGES, "", "")},
|
||||
blackfriday.EXTENSION_NO_INTRA_EMPHASIS|
|
||||
blackfriday.EXTENSION_FENCED_CODE|
|
||||
blackfriday.EXTENSION_AUTOLINK|
|
||||
blackfriday.EXTENSION_SPACE_HEADERS|
|
||||
blackfriday.EXTENSION_HEADER_IDS|
|
||||
blackfriday.EXTENSION_BACKSLASH_LINE_BREAK|
|
||||
blackfriday.EXTENSION_DEFINITION_LISTS))
|
||||
}
|
113
bridge/telegram/telegram.go
Normal file
113
bridge/telegram/telegram.go
Normal file
@ -0,0 +1,113 @@
|
||||
package btelegram
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/go-telegram-bot-api/telegram-bot-api"
|
||||
)
|
||||
|
||||
type Btelegram struct {
|
||||
c *tgbotapi.BotAPI
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
var protocol = "telegram"
|
||||
|
||||
func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Btelegram {
|
||||
b := &Btelegram{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.Account = account
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Btelegram) Connect() error {
|
||||
var err error
|
||||
flog.Info("Connecting")
|
||||
b.c, err = tgbotapi.NewBotAPI(b.Config.Token)
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
return err
|
||||
}
|
||||
updates, err := b.c.GetUpdatesChan(tgbotapi.NewUpdate(0))
|
||||
if err != nil {
|
||||
flog.Debugf("%#v", err)
|
||||
return err
|
||||
}
|
||||
flog.Info("Connection succeeded")
|
||||
go b.handleRecv(updates)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Btelegram) Disconnect() error {
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (b *Btelegram) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Btelegram) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
chatid, err := strconv.ParseInt(msg.Channel, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.Config.MessageFormat == "HTML" {
|
||||
msg.Text = makeHTML(msg.Text)
|
||||
}
|
||||
m := tgbotapi.NewMessage(chatid, msg.Username+msg.Text)
|
||||
if b.Config.MessageFormat == "HTML" {
|
||||
m.ParseMode = tgbotapi.ModeHTML
|
||||
}
|
||||
_, err = b.c.Send(m)
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Btelegram) handleRecv(updates <-chan tgbotapi.Update) {
|
||||
username := ""
|
||||
text := ""
|
||||
channel := ""
|
||||
for update := range updates {
|
||||
// handle channels
|
||||
if update.ChannelPost != nil {
|
||||
if update.ChannelPost.From != nil {
|
||||
username = update.ChannelPost.From.FirstName
|
||||
if username == "" {
|
||||
username = update.ChannelPost.From.UserName
|
||||
}
|
||||
}
|
||||
text = update.ChannelPost.Text
|
||||
channel = strconv.FormatInt(update.ChannelPost.Chat.ID, 10)
|
||||
}
|
||||
// handle groups
|
||||
if update.Message != nil {
|
||||
if update.Message.From != nil {
|
||||
username = update.Message.From.FirstName
|
||||
if username == "" {
|
||||
username = update.Message.From.UserName
|
||||
}
|
||||
}
|
||||
text = update.Message.Text
|
||||
channel = strconv.FormatInt(update.Message.Chat.ID, 10)
|
||||
}
|
||||
if username == "" {
|
||||
username = "unknown"
|
||||
}
|
||||
if text != "" {
|
||||
flog.Debugf("Sending message from %s on %s to gateway", username, b.Account)
|
||||
b.Remote <- config.Message{Username: username, Text: text, Channel: channel, Account: b.Account}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package bxmpp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/mattn/go-xmpp"
|
||||
@ -10,12 +11,11 @@ import (
|
||||
)
|
||||
|
||||
type Bxmpp struct {
|
||||
xc *xmpp.Client
|
||||
xmppMap map[string]string
|
||||
Config *config.Protocol
|
||||
origin string
|
||||
protocol string
|
||||
Remote chan config.Message
|
||||
xc *xmpp.Client
|
||||
xmppMap map[string]string
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -25,12 +25,11 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bxmpp {
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bxmpp {
|
||||
b := &Bxmpp{}
|
||||
b.xmppMap = make(map[string]string)
|
||||
b.Config = &cfg
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
b.Account = account
|
||||
b.Remote = c
|
||||
return b
|
||||
}
|
||||
@ -48,8 +47,8 @@ func (b *Bxmpp) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
func (b *Bxmpp) Disconnect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) JoinChannel(channel string) error {
|
||||
@ -57,32 +56,24 @@ func (b *Bxmpp) JoinChannel(channel string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Name() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Protocol() string {
|
||||
return b.protocol
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Origin() string {
|
||||
return b.origin
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Send(msg config.Message) error {
|
||||
flog.Debugf("Receiving %#v", msg)
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: nick + msg.Text})
|
||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
||||
tc := new(tls.Config)
|
||||
tc.InsecureSkipVerify = b.Config.SkipTLSVerify
|
||||
tc.ServerName = strings.Split(b.Config.Server, ":")[0]
|
||||
options := xmpp.Options{
|
||||
Host: b.Config.Server,
|
||||
User: b.Config.Jid,
|
||||
Password: b.Config.Password,
|
||||
NoTLS: true,
|
||||
StartTLS: true,
|
||||
Host: b.Config.Server,
|
||||
User: b.Config.Jid,
|
||||
Password: b.Config.Password,
|
||||
NoTLS: true,
|
||||
StartTLS: true,
|
||||
TLSConfig: tc,
|
||||
|
||||
//StartTLS: false,
|
||||
Debug: true,
|
||||
Session: true,
|
||||
@ -97,19 +88,27 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
||||
return b.xc, err
|
||||
}
|
||||
|
||||
func (b *Bxmpp) xmppKeepAlive() {
|
||||
func (b *Bxmpp) xmppKeepAlive() chan bool {
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
ticker := time.NewTicker(90 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
b.xc.Send(xmpp.Chat{})
|
||||
b.xc.PingC2S("", "")
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return done
|
||||
}
|
||||
|
||||
func (b *Bxmpp) handleXmpp() error {
|
||||
done := b.xmppKeepAlive()
|
||||
defer close(done)
|
||||
nodelay := time.Time{}
|
||||
for {
|
||||
m, err := b.xc.Recv()
|
||||
if err != nil {
|
||||
@ -127,9 +126,9 @@ func (b *Bxmpp) handleXmpp() error {
|
||||
if len(s) == 2 {
|
||||
nick = s[1]
|
||||
}
|
||||
if nick != b.Config.Nick {
|
||||
flog.Debugf("Sending message from %s on %s to gateway", nick, b.FullOrigin())
|
||||
b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin()}
|
||||
if nick != b.Config.Nick && v.Stamp == nodelay && v.Text != "" {
|
||||
flog.Debugf("Sending message from %s on %s to gateway", nick, b.Account)
|
||||
b.Remote <- config.Message{Username: nick, Text: v.Text, Channel: channel, Account: b.Account}
|
||||
}
|
||||
}
|
||||
case xmpp.Presence:
|
||||
|
82
changelog.md
82
changelog.md
@ -1,9 +1,86 @@
|
||||
# v0.8
|
||||
# v0.10.0
|
||||
## New features
|
||||
* matrix: New protocol support added (https://matrix.org)
|
||||
* mattermost: works with mattermost release v3.7.0
|
||||
* discord: Replace role ids in mentions to role names (discord). Closes #133
|
||||
|
||||
## Bugfix
|
||||
* mattermost: Add ReadTimeout to close lingering connections (mattermost). See #125
|
||||
* gitter: Join rooms not already joined by the bot (gitter). See #135
|
||||
* general: Fail when bridge is unable to join a channel (general)
|
||||
|
||||
## Changes
|
||||
* telegram: Do not use HTML parsemode by default. Set ```MessageFormat="HTML"``` to use it. Closes #126
|
||||
|
||||
# v0.9.3
|
||||
## New features
|
||||
* API: rest interface to read / post messages (see API section in matterbridge.toml.sample)
|
||||
|
||||
## Bugfix
|
||||
* slack: fix receiving messages from private channels #118
|
||||
* slack: fix echo when using webhooks #119
|
||||
* mattermost: reconnecting should work better now
|
||||
* irc: keeps reconnecting (every 60 seconds) now after ping timeout/disconnects.
|
||||
|
||||
# v0.9.2
|
||||
## New features
|
||||
* slack: support private channels #118
|
||||
|
||||
## Bugfix
|
||||
* general: make ignorenicks work again #115
|
||||
* telegram: fix receiving from channels and groups #112
|
||||
* telegram: use html for username
|
||||
* telegram: use ```unknown``` as username when username is not visible.
|
||||
* irc: update vendor (fixes some crashes) #117
|
||||
* xmpp: fix tls by setting ServerName #114
|
||||
|
||||
# v0.9.1
|
||||
## New features
|
||||
* Rocket.Chat: New protocol support added (https://rocket.chat)
|
||||
* irc: add channel key support #27 (see matterbrige.toml.sample for example)
|
||||
* xmpp: add SkipTLSVerify #106
|
||||
|
||||
## Bugfix
|
||||
* general: Exit when a bridge fails to start
|
||||
* mattermost: Check errors only on first connect. Keep retrying after first connection succeeds. #95
|
||||
* telegram: fix missing username #102
|
||||
* slack: do not use API functions in webhook (slack) #110
|
||||
|
||||
# v0.9.0
|
||||
## New features
|
||||
* Telegram: New protocol support added (https://telegram.org)
|
||||
* Hipchat: Add sample config to connect to hipchat via xmpp
|
||||
* discord: add "Bot " tag to discord tokens automatically
|
||||
* slack: Add support for dynamic Iconurl #43
|
||||
* general: Add ```gateway.inout``` config option for bidirectional bridges #85
|
||||
* general: Add ```[general]``` section so that ```RemoteNickFormat``` can be set globally
|
||||
|
||||
## Bugfix
|
||||
* general: when using samechannelgateway NickFormat get doubled by the NICK #77
|
||||
* general: fix ShowJoinPart for messages from irc bridge #72
|
||||
* gitter: fix high cpu usage #89
|
||||
* irc: fix !users command #78
|
||||
* xmpp: fix keepalive
|
||||
* xmpp: do not relay delayed/empty messages
|
||||
* slack: Replace id-mentions to usernames #86
|
||||
* mattermost: fix public links not working (API changes)
|
||||
|
||||
# v0.8.1
|
||||
## Bugfix
|
||||
* general: when using samechannelgateway NickFormat get doubled by the NICK #77
|
||||
* irc: fix !users command #78
|
||||
|
||||
# v0.8.0
|
||||
Release because of breaking mattermost API changes
|
||||
## New features
|
||||
* Supports mattermost v3.5.0
|
||||
|
||||
# v0.7
|
||||
# v0.7.1
|
||||
## Bugfix
|
||||
* general: when using samechannelgateway NickFormat get doubled by the NICK #77
|
||||
* irc: fix !users command #78
|
||||
|
||||
# v0.7.0
|
||||
## Breaking config changes from 0.6 to 0.7
|
||||
Matterbridge now uses TOML configuration (https://github.com/toml-lang/toml)
|
||||
See matterbridge.toml.sample for an example
|
||||
@ -37,6 +114,7 @@ See matterbridge.toml.sample for an example
|
||||
# v0.6.1
|
||||
## New features
|
||||
* Slack support added. See matterbridge.conf.sample for more information
|
||||
|
||||
## Bugfix
|
||||
* Fix 100% CPU bug on incorrect closed connections
|
||||
|
||||
|
@ -7,92 +7,141 @@ import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Gateway struct {
|
||||
*config.Config
|
||||
MyConfig *config.Gateway
|
||||
Bridges []bridge.Bridge
|
||||
ChannelsOut map[string][]string
|
||||
ChannelsIn map[string][]string
|
||||
ignoreNicks map[string][]string
|
||||
Name string
|
||||
MyConfig *config.Gateway
|
||||
Bridges map[string]*bridge.Bridge
|
||||
ChannelsOut map[string][]string
|
||||
ChannelsIn map[string][]string
|
||||
ChannelOptions map[string]config.ChannelOptions
|
||||
Name string
|
||||
Message chan config.Message
|
||||
DestChannelFunc func(msg *config.Message, dest string) []string
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, gateway *config.Gateway) error {
|
||||
c := make(chan config.Message)
|
||||
func New(cfg *config.Config, gateway *config.Gateway) *Gateway {
|
||||
gw := &Gateway{}
|
||||
gw.Name = gateway.Name
|
||||
gw.Config = cfg
|
||||
gw.MyConfig = gateway
|
||||
exists := make(map[string]bool)
|
||||
for _, br := range append(gateway.In, gateway.Out...) {
|
||||
if exists[br.Account] {
|
||||
continue
|
||||
}
|
||||
log.Infof("Starting bridge: %s channel: %s", br.Account, br.Channel)
|
||||
gw.Bridges = append(gw.Bridges, bridge.New(cfg, &br, c))
|
||||
exists[br.Account] = true
|
||||
}
|
||||
gw.mapChannels()
|
||||
//TODO fix mapIgnores
|
||||
//gw.mapIgnores()
|
||||
exists = make(map[string]bool)
|
||||
gw.Message = make(chan config.Message)
|
||||
gw.Bridges = make(map[string]*bridge.Bridge)
|
||||
gw.DestChannelFunc = gw.getDestChannel
|
||||
return gw
|
||||
}
|
||||
|
||||
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
|
||||
for _, br := range gw.Bridges {
|
||||
err := br.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Bridge %s failed to start: %v", br.FullOrigin(), err)
|
||||
}
|
||||
for _, channel := range append(gw.ChannelsOut[br.FullOrigin()], gw.ChannelsIn[br.FullOrigin()]...) {
|
||||
if exists[br.FullOrigin()+channel] {
|
||||
continue
|
||||
}
|
||||
log.Infof("%s: joining %s", br.FullOrigin(), channel)
|
||||
br.JoinChannel(channel)
|
||||
exists[br.FullOrigin()+channel] = true
|
||||
if br.Account == cfg.Account {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
gw.handleReceive(c)
|
||||
log.Infof("Starting bridge: %s ", cfg.Account)
|
||||
br := bridge.New(gw.Config, cfg, gw.Message)
|
||||
gw.mapChannelsToBridge(br, gw.ChannelsOut)
|
||||
gw.mapChannelsToBridge(br, gw.ChannelsIn)
|
||||
gw.Bridges[cfg.Account] = br
|
||||
err := br.Connect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Bridge %s failed to start: %v", br.Account, err)
|
||||
}
|
||||
err = br.JoinChannels()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Bridge %s failed to join channel: %v", br.Account, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gw *Gateway) handleReceive(c chan config.Message) {
|
||||
func (gw *Gateway) mapChannelsToBridge(br *bridge.Bridge, cMap map[string][]string) {
|
||||
for _, channel := range cMap[br.Account] {
|
||||
if _, ok := gw.ChannelOptions[br.Account+channel]; ok {
|
||||
br.ChannelsOut[channel] = gw.ChannelOptions[br.Account+channel]
|
||||
} else {
|
||||
br.ChannelsOut[channel] = config.ChannelOptions{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gw *Gateway) Start() error {
|
||||
gw.mapChannels()
|
||||
for _, br := range append(gw.MyConfig.In, append(gw.MyConfig.InOut, gw.MyConfig.Out...)...) {
|
||||
err := gw.AddBridge(&br)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
go gw.handleReceive()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gw *Gateway) handleReceive() {
|
||||
for {
|
||||
select {
|
||||
case msg := <-c:
|
||||
for _, br := range gw.Bridges {
|
||||
gw.handleMessage(msg, br)
|
||||
case msg := <-gw.Message:
|
||||
if msg.Event == config.EVENT_FAILURE {
|
||||
for _, br := range gw.Bridges {
|
||||
if msg.Account == br.Account {
|
||||
go gw.reconnectBridge(br)
|
||||
}
|
||||
}
|
||||
}
|
||||
if !gw.ignoreMessage(&msg) {
|
||||
msg.Timestamp = time.Now()
|
||||
for _, br := range gw.Bridges {
|
||||
gw.handleMessage(msg, br)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gw *Gateway) reconnectBridge(br *bridge.Bridge) {
|
||||
br.Disconnect()
|
||||
time.Sleep(time.Second * 5)
|
||||
RECONNECT:
|
||||
log.Infof("Reconnecting %s", br.Account)
|
||||
err := br.Connect()
|
||||
if err != nil {
|
||||
log.Errorf("Reconnection failed: %s. Trying again in 60 seconds", err)
|
||||
time.Sleep(time.Second * 60)
|
||||
goto RECONNECT
|
||||
}
|
||||
br.JoinChannels()
|
||||
}
|
||||
|
||||
func (gw *Gateway) mapChannels() error {
|
||||
options := make(map[string]config.ChannelOptions)
|
||||
m := make(map[string][]string)
|
||||
for _, br := range gw.MyConfig.Out {
|
||||
m[br.Account] = append(m[br.Account], br.Channel)
|
||||
options[br.Account+br.Channel] = br.Options
|
||||
}
|
||||
gw.ChannelsOut = m
|
||||
m = nil
|
||||
m = make(map[string][]string)
|
||||
for _, br := range gw.MyConfig.In {
|
||||
m[br.Account] = append(m[br.Account], br.Channel)
|
||||
options[br.Account+br.Channel] = br.Options
|
||||
}
|
||||
gw.ChannelsIn = m
|
||||
for _, br := range gw.MyConfig.InOut {
|
||||
gw.ChannelsIn[br.Account] = append(gw.ChannelsIn[br.Account], br.Channel)
|
||||
gw.ChannelsOut[br.Account] = append(gw.ChannelsOut[br.Account], br.Channel)
|
||||
options[br.Account+br.Channel] = br.Options
|
||||
}
|
||||
gw.ChannelOptions = options
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gw *Gateway) mapIgnores() {
|
||||
m := make(map[string][]string)
|
||||
for _, br := range gw.MyConfig.In {
|
||||
accInfo := strings.Split(br.Account, ".")
|
||||
m[br.Account] = strings.Fields(gw.Config.IRC[accInfo[1]].IgnoreNicks)
|
||||
}
|
||||
gw.ignoreNicks = m
|
||||
}
|
||||
|
||||
func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string {
|
||||
channels := gw.ChannelsIn[msg.FullOrigin]
|
||||
channels := gw.ChannelsIn[msg.Account]
|
||||
// broadcast to every out channel (irc QUIT)
|
||||
if msg.Event == config.EVENT_JOIN_LEAVE && msg.Channel == "" {
|
||||
return gw.ChannelsOut[dest]
|
||||
}
|
||||
for _, channel := range channels {
|
||||
if channel == msg.Channel {
|
||||
return gw.ChannelsOut[dest]
|
||||
@ -101,15 +150,16 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
func (gw *Gateway) handleMessage(msg config.Message, dest bridge.Bridge) {
|
||||
if gw.ignoreMessage(&msg) {
|
||||
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
|
||||
// only relay join/part when configged
|
||||
if msg.Event == config.EVENT_JOIN_LEAVE && !gw.Bridges[dest.Account].Config.ShowJoinPart {
|
||||
return
|
||||
}
|
||||
originchannel := msg.Channel
|
||||
channels := gw.getDestChannel(&msg, dest.FullOrigin())
|
||||
channels := gw.DestChannelFunc(&msg, dest.Account)
|
||||
for _, channel := range channels {
|
||||
// do not send the message to the bridge we come from if also the channel is the same
|
||||
if msg.FullOrigin == dest.FullOrigin() && channel == originchannel {
|
||||
if msg.Account == dest.Account && channel == originchannel {
|
||||
continue
|
||||
}
|
||||
msg.Channel = channel
|
||||
@ -117,7 +167,12 @@ func (gw *Gateway) handleMessage(msg config.Message, dest bridge.Bridge) {
|
||||
log.Debug("empty channel")
|
||||
return
|
||||
}
|
||||
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.FullOrigin, originchannel, dest.FullOrigin(), channel)
|
||||
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel)
|
||||
gw.modifyUsername(&msg, dest)
|
||||
// for api we need originchannel as channel
|
||||
if dest.Protocol == "api" {
|
||||
msg.Channel = originchannel
|
||||
}
|
||||
err := dest.Send(msg)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@ -126,26 +181,43 @@ func (gw *Gateway) handleMessage(msg config.Message, dest bridge.Bridge) {
|
||||
}
|
||||
|
||||
func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
|
||||
// should we discard messages ?
|
||||
for _, entry := range gw.ignoreNicks[msg.FullOrigin] {
|
||||
if msg.Text == "" {
|
||||
log.Debugf("ignoring empty message %#v from %s", msg, msg.Account)
|
||||
return true
|
||||
}
|
||||
for _, entry := range strings.Fields(gw.Bridges[msg.Account].Config.IgnoreNicks) {
|
||||
if msg.Username == entry {
|
||||
log.Debugf("ignoring %s from %s", msg.Username, msg.Account)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (gw *Gateway) modifyMessage(msg *config.Message, dest bridge.Bridge) {
|
||||
func (gw *Gateway) modifyMessage(msg *config.Message, dest *bridge.Bridge) {
|
||||
val := reflect.ValueOf(gw.Config).Elem()
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
typeField := val.Type().Field(i)
|
||||
// look for the protocol map (both lowercase)
|
||||
if strings.ToLower(typeField.Name) == dest.Protocol() {
|
||||
if strings.ToLower(typeField.Name) == dest.Protocol {
|
||||
// get the Protocol struct from the map
|
||||
protoCfg := val.Field(i).MapIndex(reflect.ValueOf(dest.Origin()))
|
||||
protoCfg := val.Field(i).MapIndex(reflect.ValueOf(dest.Name))
|
||||
//config.SetNickFormat(msg, protoCfg.Interface().(config.Protocol))
|
||||
val.Field(i).SetMapIndex(reflect.ValueOf(dest.Origin()), protoCfg)
|
||||
val.Field(i).SetMapIndex(reflect.ValueOf(dest.Name), protoCfg)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) {
|
||||
br := gw.Bridges[msg.Account]
|
||||
msg.Protocol = br.Protocol
|
||||
nick := gw.Config.General.RemoteNickFormat
|
||||
if nick == "" {
|
||||
nick = dest.Config.RemoteNickFormat
|
||||
}
|
||||
nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
|
||||
nick = strings.Replace(nick, "{BRIDGE}", br.Name, -1)
|
||||
nick = strings.Replace(nick, "{PROTOCOL}", br.Protocol, -1)
|
||||
msg.Username = nick
|
||||
}
|
||||
|
@ -1,103 +1,49 @@
|
||||
package samechannelgateway
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"strings"
|
||||
"github.com/42wim/matterbridge/gateway"
|
||||
)
|
||||
|
||||
type SameChannelGateway struct {
|
||||
*config.Config
|
||||
MyConfig *config.SameChannelGateway
|
||||
Bridges []bridge.Bridge
|
||||
Channels []string
|
||||
ignoreNicks map[string][]string
|
||||
Name string
|
||||
MyConfig *config.SameChannelGateway
|
||||
Channels []string
|
||||
Name string
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, gateway *config.SameChannelGateway) error {
|
||||
c := make(chan config.Message)
|
||||
gw := &SameChannelGateway{}
|
||||
gw.Name = gateway.Name
|
||||
gw.Config = cfg
|
||||
gw.MyConfig = gateway
|
||||
gw.Channels = gateway.Channels
|
||||
for _, account := range gateway.Accounts {
|
||||
br := config.Bridge{Account: account}
|
||||
log.Infof("Starting bridge: %s", account)
|
||||
gw.Bridges = append(gw.Bridges, bridge.New(cfg, &br, c))
|
||||
}
|
||||
for _, br := range gw.Bridges {
|
||||
err := br.Connect()
|
||||
if err != nil {
|
||||
log.Fatalf("Bridge %s failed to start: %v", br.FullOrigin(), err)
|
||||
}
|
||||
for _, channel := range gw.Channels {
|
||||
log.Infof("%s: joining %s", br.FullOrigin(), channel)
|
||||
br.JoinChannel(channel)
|
||||
func New(cfg *config.Config, gatewayCfg *config.SameChannelGateway) *SameChannelGateway {
|
||||
return &SameChannelGateway{
|
||||
MyConfig: gatewayCfg,
|
||||
Channels: gatewayCfg.Channels,
|
||||
Name: gatewayCfg.Name,
|
||||
Config: cfg}
|
||||
}
|
||||
|
||||
func (sgw *SameChannelGateway) Start() error {
|
||||
gw := gateway.New(sgw.Config, &config.Gateway{Name: sgw.Name})
|
||||
gw.DestChannelFunc = sgw.getDestChannel
|
||||
for _, account := range sgw.MyConfig.Accounts {
|
||||
for _, channel := range sgw.Channels {
|
||||
br := config.Bridge{Account: account, Channel: channel}
|
||||
gw.MyConfig.InOut = append(gw.MyConfig.InOut, br)
|
||||
}
|
||||
}
|
||||
gw.handleReceive(c)
|
||||
return nil
|
||||
return gw.Start()
|
||||
}
|
||||
|
||||
func (gw *SameChannelGateway) handleReceive(c chan config.Message) {
|
||||
for {
|
||||
select {
|
||||
case msg := <-c:
|
||||
for _, br := range gw.Bridges {
|
||||
gw.handleMessage(msg, br)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (gw *SameChannelGateway) handleMessage(msg config.Message, dest bridge.Bridge) {
|
||||
// is this a configured channel
|
||||
if !gw.validChannel(msg.Channel) {
|
||||
return
|
||||
}
|
||||
// do not send the message to the bridge we come from if also the channel is the same
|
||||
if msg.FullOrigin == dest.FullOrigin() {
|
||||
return
|
||||
}
|
||||
gw.modifyMessage(&msg, dest)
|
||||
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.FullOrigin, msg.Channel, dest.FullOrigin(), msg.Channel)
|
||||
err := dest.Send(msg)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func setNickFormat(msg *config.Message, format string) {
|
||||
if format == "" {
|
||||
msg.Username = msg.Protocol + "." + msg.Origin + "-" + msg.Username + ": "
|
||||
return
|
||||
}
|
||||
msg.Username = strings.Replace(format, "{NICK}", msg.Username, -1)
|
||||
msg.Username = strings.Replace(msg.Username, "{BRIDGE}", msg.Origin, -1)
|
||||
msg.Username = strings.Replace(msg.Username, "{PROTOCOL}", msg.Protocol, -1)
|
||||
}
|
||||
|
||||
func (gw *SameChannelGateway) modifyMessage(msg *config.Message, dest bridge.Bridge) {
|
||||
switch dest.Protocol() {
|
||||
case "irc":
|
||||
setNickFormat(msg, gw.Config.IRC[dest.Origin()].RemoteNickFormat)
|
||||
case "mattermost":
|
||||
setNickFormat(msg, gw.Config.Mattermost[dest.Origin()].RemoteNickFormat)
|
||||
case "slack":
|
||||
setNickFormat(msg, gw.Config.Slack[dest.Origin()].RemoteNickFormat)
|
||||
case "discord":
|
||||
setNickFormat(msg, gw.Config.Discord[dest.Origin()].RemoteNickFormat)
|
||||
}
|
||||
}
|
||||
|
||||
func (gw *SameChannelGateway) validChannel(channel string) bool {
|
||||
for _, c := range gw.Channels {
|
||||
func (sgw *SameChannelGateway) validChannel(channel string) bool {
|
||||
for _, c := range sgw.Channels {
|
||||
if c == channel {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (sgw *SameChannelGateway) getDestChannel(msg *config.Message, dest string) []string {
|
||||
if sgw.validChannel(msg.Channel) {
|
||||
return []string{msg.Channel}
|
||||
}
|
||||
return []string{}
|
||||
}
|
||||
|
108
hook/rockethook/rockethook.go
Normal file
108
hook/rockethook/rockethook.go
Normal file
@ -0,0 +1,108 @@
|
||||
package rockethook
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Message for rocketchat outgoing webhook.
|
||||
type Message struct {
|
||||
Token string `json:"token"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
ChannelName string `json:"channel_name"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
UserID string `json:"user_id"`
|
||||
UserName string `json:"user_name"`
|
||||
Text string `json:"text"`
|
||||
}
|
||||
|
||||
// Client for Rocketchat.
|
||||
type Client struct {
|
||||
In chan Message
|
||||
httpclient *http.Client
|
||||
Config
|
||||
}
|
||||
|
||||
// Config for client.
|
||||
type Config struct {
|
||||
BindAddress string // Address to listen on
|
||||
Token string // Only allow this token from Rocketchat. (Allow everything when empty)
|
||||
InsecureSkipVerify bool // disable certificate checking
|
||||
}
|
||||
|
||||
// New Rocketchat client.
|
||||
func New(url string, config Config) *Client {
|
||||
c := &Client{In: make(chan Message), Config: config}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: config.InsecureSkipVerify},
|
||||
}
|
||||
c.httpclient = &http.Client{Transport: tr}
|
||||
_, _, err := net.SplitHostPort(c.BindAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("incorrect bindaddress %s", c.BindAddress)
|
||||
}
|
||||
go c.StartServer()
|
||||
return c
|
||||
}
|
||||
|
||||
// StartServer starts a webserver listening for incoming mattermost POSTS.
|
||||
func (c *Client) StartServer() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", c)
|
||||
log.Printf("Listening on http://%v...\n", c.BindAddress)
|
||||
if err := http.ListenAndServe(c.BindAddress, mux); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// ServeHTTP implementation.
|
||||
func (c *Client) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
log.Println("invalid " + r.Method + " connection from " + r.RemoteAddr)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
msg := Message{}
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
log.Println(string(body))
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
err = json.Unmarshal(body, &msg)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
if msg.Token == "" {
|
||||
log.Println("no token from " + r.RemoteAddr)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
msg.ChannelName = "#" + msg.ChannelName
|
||||
if c.Token != "" {
|
||||
if msg.Token != c.Token {
|
||||
log.Println("invalid token " + msg.Token + " from " + r.RemoteAddr)
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
c.In <- msg
|
||||
}
|
||||
|
||||
// Receive returns an incoming message from mattermost outgoing webhooks URL.
|
||||
func (c *Client) Receive() Message {
|
||||
for {
|
||||
select {
|
||||
case msg := <-c.In:
|
||||
return msg
|
||||
}
|
||||
}
|
||||
}
|
@ -1,287 +0,0 @@
|
||||
#This is configuration for matterbridge.
|
||||
###################################################################
|
||||
#IRC section
|
||||
###################################################################
|
||||
[IRC]
|
||||
#Enable enables this bridge
|
||||
#OPTIONAL (default false)
|
||||
Enable=true
|
||||
#irc server to connect to.
|
||||
#REQUIRED
|
||||
Server="irc.freenode.net:6667"
|
||||
|
||||
#Enable to use TLS connection to your irc server.
|
||||
#OPTIONAL (default false)
|
||||
UseTLS=false
|
||||
|
||||
#Enable SASL (PLAIN) authentication. (freenode requires this from eg AWS hosts)
|
||||
#It uses NickServNick and NickServPassword as login and password
|
||||
#OPTIONAL (default false)
|
||||
UseSASL=false
|
||||
|
||||
#Enable to not verify the certificate on your irc server. i
|
||||
#e.g. when using selfsigned certificates
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#Your nick on irc.
|
||||
#REQUIRED
|
||||
Nick="matterbot"
|
||||
|
||||
#If you registered your bot with a service like Nickserv on freenode.
|
||||
#Also being used when UseSASL=true
|
||||
#OPTIONAL
|
||||
NickServNick="nickserv"
|
||||
NickServPassword="secret"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#OPTIONAL (default {BRIDGE}-{NICK})
|
||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> "
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
###################################################################
|
||||
#XMPP section
|
||||
###################################################################
|
||||
[XMPP]
|
||||
#Enable enables this bridge
|
||||
#OPTIONAL (default false)
|
||||
Enable=true
|
||||
|
||||
#xmpp server to connect to.
|
||||
#REQUIRED
|
||||
Server="jabber.example.com:5222"
|
||||
|
||||
#Jid
|
||||
#REQUIRED
|
||||
Jid="user@example.com"
|
||||
|
||||
#Password
|
||||
#REQUIRED
|
||||
Password="yourpass"
|
||||
|
||||
#MUC
|
||||
#REQUIRED
|
||||
Muc="conference.jabber.example.com"
|
||||
|
||||
#Your nick in the rooms
|
||||
#REQUIRED
|
||||
Nick="xmppbot"
|
||||
|
||||
|
||||
###################################################################
|
||||
#mattermost section
|
||||
###################################################################
|
||||
|
||||
[mattermost]
|
||||
#Enable enables this bridge
|
||||
#OPTIONAL (default false)
|
||||
Enable=true
|
||||
|
||||
#### Settings for webhook matterbridge.
|
||||
#### These settings will not be used when using -plus switch which doesn't use
|
||||
#### webhooks.
|
||||
|
||||
#Url is your incoming webhook url as specified in mattermost.
|
||||
#See account settings - integrations - incoming webhooks on mattermost.
|
||||
#REQUIRED
|
||||
URL="https://yourdomain/hooks/yourhookkey"
|
||||
|
||||
#Address to listen on for outgoing webhook requests from mattermost.
|
||||
#See account settings - integrations - outgoing webhooks on mattermost.
|
||||
#This setting will not be used when using -plus switch which doesn't use
|
||||
#webhooks
|
||||
#REQUIRED
|
||||
BindAddress="0.0.0.0:9999"
|
||||
|
||||
#Icon that will be showed in mattermost.
|
||||
#OPTIONAL
|
||||
IconURL="http://youricon.png"
|
||||
|
||||
#### Settings for matterbridge -plus
|
||||
#### Thse settings will only be used when using the -plus switch.
|
||||
|
||||
#The mattermost hostname.
|
||||
#REQUIRED
|
||||
Server="yourmattermostserver.domain"
|
||||
|
||||
#Your team on mattermost.
|
||||
#REQUIRED
|
||||
Team="yourteam"
|
||||
|
||||
#login/pass of your bot.
|
||||
#Use a dedicated user for this and not your own!
|
||||
#REQUIRED
|
||||
Login="yourlogin"
|
||||
Password="yourpass"
|
||||
|
||||
#Enable this to make a http connection (instead of https) to your mattermost.
|
||||
#OPTIONAL (default false)
|
||||
NoTLS=false
|
||||
|
||||
#### Shared settings for matterbridge and -plus
|
||||
|
||||
#Enable to not verify the certificate on your mattermost server.
|
||||
#e.g. when using selfsigned certificates
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#Enable to show IRC joins/parts in mattermost.
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
#Whether to prefix messages from other bridges to mattermost with the sender's nick.
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#mattermost server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to Mattermost will by default be prefixed by "bridge-" + nick. You can,
|
||||
#however, modify how the messages appear, by setting (and modifying) RemoteNickFormat
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#OPTIONAL (default {BRIDGE}-{NICK})
|
||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> "
|
||||
|
||||
#how to format the list of IRC nicks when displayed in mattermost.
|
||||
#Possible options are "table" and "plain"
|
||||
#OPTIONAL (default plain)
|
||||
NickFormatter=plain
|
||||
#How many nicks to list per row for formatters that support this.
|
||||
#OPTIONAL (default 4)
|
||||
NicksPerRow=4
|
||||
|
||||
#Nicks you want to ignore. Messages from those users will not be bridged.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="mmbot spammer2"
|
||||
|
||||
###################################################################
|
||||
#Gitter section
|
||||
#Best to make a dedicated gitter account for the bot.
|
||||
###################################################################
|
||||
[Gitter]
|
||||
#Enable enables this bridge
|
||||
#OPTIONAL (default false)
|
||||
Enable=true
|
||||
|
||||
#Token to connect with Gitter API
|
||||
#You can get your token by going to https://developer.gitter.im/docs/welcome and SIGN IN
|
||||
#REQUIRED
|
||||
Token="Yourtokenhere"
|
||||
|
||||
#Nicks you want to ignore. Messages of those users will not be bridged.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="spammer1 spammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#OPTIONAL (default {BRIDGE}-{NICK})
|
||||
RemoteNickFormat="[{BRIDGE}] <{NICK}> "
|
||||
|
||||
###################################################################
|
||||
#slack section
|
||||
###################################################################
|
||||
|
||||
[slack]
|
||||
#Enable enables this bridge
|
||||
#OPTIONAL (default false)
|
||||
Enable=true
|
||||
|
||||
#### Settings for webhook matterbridge.
|
||||
#### These settings will not be used when useAPI is enabled
|
||||
|
||||
#Url is your incoming webhook url as specified in slack
|
||||
#See account settings - integrations - incoming webhooks on slack
|
||||
#REQUIRED (unless useAPI=true)
|
||||
URL="https://hooks.slack.com/services/yourhook"
|
||||
|
||||
#Address to listen on for outgoing webhook requests from slack
|
||||
#See account settings - integrations - outgoing webhooks on slack
|
||||
#This setting will not be used when useAPI is eanbled
|
||||
#webhooks
|
||||
#REQUIRED (unless useAPI=true)
|
||||
BindAddress="0.0.0.0:9999"
|
||||
|
||||
#Icon that will be showed in slack
|
||||
#OPTIONAL
|
||||
IconURL="http://youricon.png"
|
||||
|
||||
#### Settings for using slack API
|
||||
#OPTIONAL
|
||||
useAPI=false
|
||||
|
||||
#Token to connect with the Slack API
|
||||
#REQUIRED (when useAPI=true)
|
||||
Token="yourslacktoken"
|
||||
|
||||
#### Shared settings for webhooks and API
|
||||
|
||||
#Whether to prefix messages from other bridges to mattermost with the sender's nick.
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#slack server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to Slack will by default be prefixed by "bridge-" + nick. You can,
|
||||
#however, modify how the messages appear, by setting (and modifying) RemoteNickFormat
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#OPTIONAL (default {BRIDGE}-{NICK})
|
||||
RemoteNickFormat="[{BRIDGE}] <{NICK}>
|
||||
|
||||
#how to format the list of IRC nicks when displayed in slack
|
||||
#Possible options are "table" and "plain"
|
||||
#OPTIONAL (default plain)
|
||||
NickFormatter=plain
|
||||
#How many nicks to list per row for formatters that support this.
|
||||
#OPTIONAL (default 4)
|
||||
NicksPerRow=4
|
||||
|
||||
#Nicks you want to ignore. Messages from those users will not be bridged.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="mmbot spammer2"
|
||||
|
||||
###################################################################
|
||||
#multiple channel config
|
||||
###################################################################
|
||||
#You can specify multiple channels.
|
||||
#The name is just an identifier for you.
|
||||
#REQUIRED (at least 1 channel)
|
||||
[Channel "channel1"]
|
||||
#Choose the IRC channel to send messages to.
|
||||
IRC="#off-topic"
|
||||
#Choose the mattermost channel to messages to.
|
||||
mattermost="off-topic"
|
||||
#Choose the xmpp channel to send messages to.
|
||||
xmpp="off-topic"
|
||||
#Choose the Gitter channel to send messages to.
|
||||
#Gitter channels are named "user/repo"
|
||||
gitter="42wim/matterbridge"
|
||||
#Choose the slack channel to send messages to.
|
||||
slack="general"
|
||||
|
||||
[Channel "testchannel"]
|
||||
IRC="#testing"
|
||||
mattermost="testing"
|
||||
xmpp="testing"
|
||||
gitter="user/repo"
|
||||
slack="testing"
|
||||
|
||||
###################################################################
|
||||
#general
|
||||
###################################################################
|
||||
[general]
|
||||
#request your API key on https://github.com/giphy/GiphyAPI. This is a public beta key.
|
||||
#OPTIONAL
|
||||
GiphyApiKey="dc6zaTOxFJmzC"
|
||||
|
||||
#Enabling plus means you'll use the API version instead of the webhooks one
|
||||
Plus=false
|
@ -9,7 +9,10 @@ import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var version = "0.9.0-dev"
|
||||
var (
|
||||
version = "0.10.0"
|
||||
githash string
|
||||
)
|
||||
|
||||
func init() {
|
||||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
|
||||
@ -21,40 +24,38 @@ func main() {
|
||||
flagVersion := flag.Bool("version", false, "show version")
|
||||
flag.Parse()
|
||||
if *flagVersion {
|
||||
fmt.Println("version:", version)
|
||||
fmt.Printf("version: %s %s\n", version, githash)
|
||||
return
|
||||
}
|
||||
flag.Parse()
|
||||
if *flagDebug {
|
||||
log.Info("enabling debug")
|
||||
log.Info("Enabling debug")
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
fmt.Println("running version", version)
|
||||
log.Printf("Running version %s %s", version, githash)
|
||||
cfg := config.NewConfig(*flagConfig)
|
||||
for _, gw := range cfg.SameChannelGateway {
|
||||
if !gw.Enable {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("starting samechannel gateway %#v\n", gw.Name)
|
||||
go func(gw config.SameChannelGateway) {
|
||||
err := samechannelgateway.New(cfg, &gw)
|
||||
if err != nil {
|
||||
log.Debugf("starting gateway failed %#v", err)
|
||||
}
|
||||
}(gw)
|
||||
log.Printf("Starting samechannel gateway %#v", gw.Name)
|
||||
g := samechannelgateway.New(cfg, &gw)
|
||||
err := g.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Starting gateway failed %#v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, gw := range cfg.Gateway {
|
||||
if !gw.Enable {
|
||||
continue
|
||||
}
|
||||
fmt.Printf("starting gateway %#v\n", gw.Name)
|
||||
go func(gw config.Gateway) {
|
||||
err := gateway.New(cfg, &gw)
|
||||
if err != nil {
|
||||
log.Debugf("starting gateway failed %#v", err)
|
||||
}
|
||||
}(gw)
|
||||
log.Printf("Starting gateway %#v", gw.Name)
|
||||
g := gateway.New(cfg, &gw)
|
||||
err := g.Start()
|
||||
if err != nil {
|
||||
log.Fatalf("Starting gateway failed %#v", err)
|
||||
}
|
||||
}
|
||||
select {}
|
||||
}
|
||||
|
@ -13,6 +13,10 @@
|
||||
#REQUIRED
|
||||
Server="irc.freenode.net:6667"
|
||||
|
||||
#Password for irc server (if necessary)
|
||||
#OPTIONAL (default "")
|
||||
Password=""
|
||||
|
||||
#Enable to use TLS connection to your irc server.
|
||||
#OPTIONAL (default false)
|
||||
UseTLS=false
|
||||
@ -37,18 +41,6 @@ Nick="matterbot"
|
||||
NickServNick="nickserv"
|
||||
NickServPassword="secret"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#Flood control
|
||||
#Delay in milliseconds between each message send to the IRC server
|
||||
#OPTIONAL (default 1300)
|
||||
@ -60,6 +52,22 @@ MessageDelay=1300
|
||||
#OPTIONAL (default 30)
|
||||
MessageQueue=30
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
###################################################################
|
||||
#XMPP section
|
||||
###################################################################
|
||||
@ -89,6 +97,70 @@ Muc="conference.jabber.example.com"
|
||||
#REQUIRED
|
||||
Nick="xmppbot"
|
||||
|
||||
#Enable to not verify the certificate on your xmpp server.
|
||||
#e.g. when using selfsigned certificates
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
|
||||
###################################################################
|
||||
#hipchat section
|
||||
###################################################################
|
||||
#Go to https://www.hipchat.com/account/xmpp this will show you the necessary data
|
||||
#to fill in the section below
|
||||
[xmpp.hipchat]
|
||||
#xmpp server to connect to.
|
||||
#REQUIRED
|
||||
Server="chat.hipchat.com:5222"
|
||||
|
||||
#Jabber ID
|
||||
#REQUIRED
|
||||
Jid="12345_12345@chat.hipchat.com"
|
||||
|
||||
#Password (your hipchat password)
|
||||
#REQUIRED
|
||||
Password="yourpass"
|
||||
|
||||
#Conference (MUC) domain
|
||||
#REQUIRED
|
||||
Muc="conf.hipchat.com"
|
||||
|
||||
#Room nickname
|
||||
#REQUIRED
|
||||
Nick="yourlogin"
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="spammer1 spammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
|
||||
###################################################################
|
||||
#mattermost section
|
||||
@ -150,9 +222,13 @@ NoTLS=false
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#Enable to show IRC joins/parts in mattermost.
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
#how to format the list of IRC nicks when displayed in mattermost.
|
||||
#Possible options are "table" and "plain"
|
||||
#OPTIONAL (default plain)
|
||||
NickFormatter="plain"
|
||||
#How many nicks to list per row for formatters that support this.
|
||||
#OPTIONAL (default 4)
|
||||
NicksPerRow=4
|
||||
|
||||
#Whether to prefix messages from other bridges to mattermost with the sender's nick.
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
@ -162,6 +238,11 @@ ShowJoinPart=false
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
@ -169,17 +250,9 @@ PrefixMessagesWithNick=false
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#how to format the list of IRC nicks when displayed in mattermost.
|
||||
#Possible options are "table" and "plain"
|
||||
#OPTIONAL (default plain)
|
||||
NickFormatter="plain"
|
||||
#How many nicks to list per row for formatters that support this.
|
||||
#OPTIONAL (default 4)
|
||||
NicksPerRow=4
|
||||
|
||||
#Nicks you want to ignore. Messages from those users will not be bridged.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="mmbot spammer2"
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
###################################################################
|
||||
#Gitter section
|
||||
@ -197,9 +270,10 @@ IgnoreNicks="mmbot spammer2"
|
||||
#REQUIRED
|
||||
Token="Yourtokenhere"
|
||||
|
||||
#Nicks you want to ignore. Messages of those users will not be bridged.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="spammer1 spammer2"
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
@ -208,6 +282,10 @@ IgnoreNicks="spammer1 spammer2"
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
###################################################################
|
||||
#slack section
|
||||
###################################################################
|
||||
@ -220,11 +298,15 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
#### Settings for webhook matterbridge.
|
||||
#### These settings will not be used when useAPI is enabled
|
||||
|
||||
#NOT RECOMMENDED TO USE INCOMING/OUTGOING WEBHOOK. USE SLACK API
|
||||
#AND DEDICATED BOT USER WHEN POSSIBLE!
|
||||
#Url is your incoming webhook url as specified in slack
|
||||
#See account settings - integrations - incoming webhooks on slack
|
||||
#REQUIRED (unless useAPI=true)
|
||||
URL="https://hooks.slack.com/services/yourhook"
|
||||
|
||||
#NOT RECOMMENDED TO USE INCOMING/OUTGOING WEBHOOK. USE SLACK API
|
||||
#AND DEDICATED BOT USER WHEN POSSIBLE!
|
||||
#Address to listen on for outgoing webhook requests from slack
|
||||
#See account settings - integrations - outgoing webhooks on slack
|
||||
#This setting will not be used when useAPI is eanbled
|
||||
@ -232,11 +314,13 @@ URL="https://hooks.slack.com/services/yourhook"
|
||||
#REQUIRED (unless useAPI=true)
|
||||
BindAddress="0.0.0.0:9999"
|
||||
|
||||
#### Settings for using slack API
|
||||
#### Settings for using slack API (RECOMMENDED)
|
||||
#OPTIONAL
|
||||
useAPI=false
|
||||
|
||||
#Token to connect with the Slack API
|
||||
#You'll have to use a test/api-token using a dedicated user and not a bot token.
|
||||
#See https://github.com/42wim/matterbridge/issues/75 for more info.
|
||||
#REQUIRED (when useAPI=true)
|
||||
Token="yourslacktoken"
|
||||
|
||||
@ -249,21 +333,6 @@ Token="yourslacktoken"
|
||||
#OPTIONAL
|
||||
IconURL="https://robohash.org/{NICK}.png?size=48x48"
|
||||
|
||||
#Whether to prefix messages from other bridges to mattermost with RemoteNickFormat
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#slack server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to Slack will by default be prefixed by "bridge-" + nick. You can,
|
||||
#however, modify how the messages appear, by setting (and modifying) RemoteNickFormat
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#how to format the list of IRC nicks when displayed in slack
|
||||
#Possible options are "table" and "plain"
|
||||
#OPTIONAL (default plain)
|
||||
@ -272,9 +341,29 @@ NickFormatter="plain"
|
||||
#OPTIONAL (default 4)
|
||||
NicksPerRow=4
|
||||
|
||||
#Nicks you want to ignore. Messages from those users will not be bridged.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="mmbot spammer2"
|
||||
#Whether to prefix messages from other bridges to mattermost with RemoteNickFormat
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#slack server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to Slack will by default be prefixed by "bridge-" + nick. You can,
|
||||
#however, modify how the messages appear, by setting (and modifying) RemoteNickFormat
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
###################################################################
|
||||
#discord section
|
||||
@ -288,15 +377,51 @@ IgnoreNicks="mmbot spammer2"
|
||||
#Token to connect with Discord API
|
||||
#You can get your token by following the instructions on
|
||||
#https://github.com/reactiflux/discord-irc/wiki/Creating-a-discord-bot-&-getting-a-token
|
||||
#The "Bot" tag needs to be added before the token
|
||||
#If you want roles/groups mentions to be shown with names instead of ID, you'll need to give your bot the "Manage Roles" permission.
|
||||
#REQUIRED
|
||||
Token="Bot Yourtokenhere"
|
||||
Token="Yourtokenhere"
|
||||
|
||||
#REQUIRED
|
||||
Server="yourservername"
|
||||
|
||||
#Nicks you want to ignore. Messages of those users will not be bridged.
|
||||
#OPTIONAL
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
###################################################################
|
||||
#telegram section
|
||||
###################################################################
|
||||
[telegram]
|
||||
|
||||
#You can configure multiple servers "[telegram.name]" or "[telegram.name2]"
|
||||
#In this example we use [telegram.secure]
|
||||
#REQUIRED
|
||||
[telegram.secure]
|
||||
#Token to connect with telegram API
|
||||
#See https://core.telegram.org/bots#6-botfather and https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau
|
||||
#REQUIRED
|
||||
Token="Yourtokenhere"
|
||||
|
||||
#OPTIONAL (default empty)
|
||||
#Only supported format is "HTML", messages will be sent in html parsemode.
|
||||
#See https://core.telegram.org/bots/api#html-style
|
||||
MessageFormat=""
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="spammer1 spammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
@ -306,6 +431,145 @@ IgnoreNicks="spammer1 spammer2"
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
|
||||
###################################################################
|
||||
#rocketchat section
|
||||
###################################################################
|
||||
[rocketchat]
|
||||
#You can configure multiple servers "[rocketchat.name]" or "[rocketchat.name2]"
|
||||
#In this example we use [rocketchat.work]
|
||||
#REQUIRED
|
||||
|
||||
[rocketchat.rockme]
|
||||
#Url is your incoming webhook url as specified in rocketchat
|
||||
#Read #https://rocket.chat/docs/administrator-guides/integrations/#how-to-create-a-new-incoming-webhook
|
||||
#See administration - integrations - new integration - incoming webhook
|
||||
#REQUIRED
|
||||
URL="https://yourdomain/hooks/yourhookkey"
|
||||
|
||||
#Address to listen on for outgoing webhook requests from rocketchat.
|
||||
#See administration - integrations - new integration - outgoing webhook
|
||||
#REQUIRED
|
||||
BindAddress="0.0.0.0:9999"
|
||||
|
||||
#Your nick/username as specified in your incoming webhook "Post as" setting
|
||||
#REQUIRED
|
||||
Nick="matterbot"
|
||||
|
||||
#Enable this to make a http connection (instead of https) to your rocketchat
|
||||
#OPTIONAL (default false)
|
||||
NoTLS=false
|
||||
|
||||
#Enable to not verify the certificate on your rocketchat server.
|
||||
#e.g. when using selfsigned certificates
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#Whether to prefix messages from other bridges to rocketchat with the sender's nick.
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#rocketchat server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to rocketchat will by default be prefixed by the RemoteNickFormat setting. i
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
###################################################################
|
||||
#matrix section
|
||||
###################################################################
|
||||
[matrix]
|
||||
#You can configure multiple servers "[matrix.name]" or "[matrix.name2]"
|
||||
#In this example we use [matrix.neo]
|
||||
#REQUIRED
|
||||
|
||||
[matrix.neo]
|
||||
#Server is your homeserver (eg https://matrix.org)
|
||||
#REQUIRED
|
||||
Server="https://matrix.org"
|
||||
|
||||
#login/pass of your bot.
|
||||
#Use a dedicated user for this and not your own!
|
||||
#Messages sent from this user will not be relayed to avoid loops.
|
||||
#REQUIRED
|
||||
Login="yourlogin"
|
||||
Password="yourpass"
|
||||
|
||||
#Whether to prefix messages from other bridges to matrix with the sender's nick.
|
||||
#Useful if username overrides for incoming webhooks isn't enabled on the
|
||||
#matrix server. If you set PrefixMessagesWithNick to true, each message
|
||||
#from bridge to matrix will by default be prefixed by the RemoteNickFormat setting. i
|
||||
#OPTIONAL (default false)
|
||||
PrefixMessagesWithNick=false
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="spammer1 spammer2"
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges (only from irc-bridge at the moment)
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
|
||||
###################################################################
|
||||
#API
|
||||
###################################################################
|
||||
[api]
|
||||
#You can configure multiple API hooks
|
||||
#In this example we use [api.local]
|
||||
#REQUIRED
|
||||
|
||||
[api.local]
|
||||
#Address to listen on for API
|
||||
#REQUIRED
|
||||
BindAddress="127.0.0.1:4242"
|
||||
|
||||
#Amount of messages to keep in memory
|
||||
Buffer=1000
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="{NICK}"
|
||||
|
||||
###################################################################
|
||||
#General configuration
|
||||
###################################################################
|
||||
#Settings here override specific settings for each protocol
|
||||
[general]
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
||||
#The string "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
###################################################################
|
||||
#Gateway configuration
|
||||
@ -318,7 +582,7 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
#from [[gateway.in]] to.
|
||||
#
|
||||
#Most of the time [[gateway.in]] and [[gateway.out]] are the same if you
|
||||
#want bidirectional bridging.
|
||||
#want bidirectional bridging. You can then use [[gateway.inout]]
|
||||
#
|
||||
|
||||
[[gateway]]
|
||||
@ -346,21 +610,47 @@ enable=true
|
||||
#discord - channel (without the #)
|
||||
# - ID:123456789 (where 123456789 is the channel ID)
|
||||
# (https://github.com/42wim/matterbridge/issues/57)
|
||||
#telegram - chatid (a large negative number, eg -123456789)
|
||||
# see (https://www.linkedin.com/pulse/telegram-bots-beginners-marco-frau)
|
||||
#hipchat - id_channel (see https://www.hipchat.com/account/xmpp for the correct channel)
|
||||
#rocketchat - #channel (# is required)
|
||||
#matrix - #channel:server (eg #yourchannel:matrix.org)
|
||||
#REQUIRED
|
||||
channel="#testing"
|
||||
|
||||
[[gateway.in]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
#OPTIONAL - only used for IRC protocol at the moment
|
||||
[gateway.in.options]
|
||||
#OPTIONAL - your irc channel key
|
||||
key="yourkey"
|
||||
|
||||
|
||||
#[[gateway.out]] specifies the account and channels we will sent messages to.
|
||||
[[gateway.out]]
|
||||
account="irc.freenode"
|
||||
channel="#testing"
|
||||
|
||||
[[gateway.out]]
|
||||
#OPTIONAL - only used for IRC protocol at the moment
|
||||
[gateway.out.options]
|
||||
#OPTIONAL - your irc channel key
|
||||
key="yourkey"
|
||||
|
||||
#[[gateway.inout]] can be used when then channel will be used to receive from
|
||||
#and send messages to
|
||||
[[gateway.inout]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
|
||||
#OPTIONAL - only used for IRC protocol at the moment
|
||||
[gateway.inout.options]
|
||||
#OPTIONAL - your irc channel key
|
||||
key="yourkey"
|
||||
|
||||
#API example
|
||||
#[[gateway.inout]]
|
||||
#account="api.local"
|
||||
#channel="api"
|
||||
#To send data to the api:
|
||||
#curl -XPOST -H 'Content-Type: application/json' -d '{"text":"test","username":"randomuser"}' http://localhost:4242/api/message
|
||||
|
||||
#If you want to do a 1:1 mapping between protocols where the channelnames are the same
|
||||
#e.g. slack and mattermost you can use the samechannelgateway configuration
|
||||
|
@ -19,14 +19,14 @@ enable=true
|
||||
account="irc.freenode"
|
||||
channel="#testing"
|
||||
|
||||
[[gateway.in]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
|
||||
[[gateway.out]]
|
||||
account="irc.freenode"
|
||||
channel="#testing"
|
||||
|
||||
[[gateway.in]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
|
||||
[[gateway.out]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
|
@ -80,6 +80,11 @@ func (m *MMClient) SetLogLevel(level string) {
|
||||
}
|
||||
|
||||
func (m *MMClient) Login() error {
|
||||
// check if this is a first connect or a reconnection
|
||||
firstConnection := true
|
||||
if m.WsConnected == true {
|
||||
firstConnection = false
|
||||
}
|
||||
m.WsConnected = false
|
||||
if m.WsQuit {
|
||||
return nil
|
||||
@ -98,6 +103,7 @@ func (m *MMClient) Login() error {
|
||||
// login to mattermost
|
||||
m.Client = model.NewClient(uriScheme + m.Credentials.Server)
|
||||
m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||
m.Client.HttpClient.Timeout = time.Second * 10
|
||||
var myinfo *model.Result
|
||||
var appErr *model.AppError
|
||||
var logmsg = "trying login"
|
||||
@ -125,11 +131,7 @@ func (m *MMClient) Login() error {
|
||||
if appErr != nil {
|
||||
d := b.Duration()
|
||||
m.log.Debug(appErr.DetailedError)
|
||||
//TODO more generic fix needed
|
||||
if !strings.Contains(appErr.DetailedError, "connection refused") &&
|
||||
!strings.Contains(appErr.DetailedError, "invalid character") &&
|
||||
!strings.Contains(appErr.DetailedError, "connection reset by peer") &&
|
||||
!strings.Contains(appErr.DetailedError, "connection timed out") {
|
||||
if firstConnection {
|
||||
if appErr.Message == "" {
|
||||
return errors.New(appErr.DetailedError)
|
||||
}
|
||||
@ -275,7 +277,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateUsers() error {
|
||||
mmusers, err := m.Client.GetProfiles(0, 1000, "")
|
||||
mmusers, err := m.Client.GetProfiles(0, 50000, "")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
@ -469,11 +471,16 @@ func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
|
||||
_, err := m.Client.CreateDirectChannel(toUserId)
|
||||
if err != nil {
|
||||
m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
|
||||
return
|
||||
}
|
||||
channelName := model.GetDMNameFromIds(toUserId, m.User.Id)
|
||||
|
||||
// update our channels
|
||||
mmchannels, _ := m.Client.GetChannels("")
|
||||
mmchannels, err := m.Client.GetChannels("")
|
||||
if err != nil {
|
||||
m.log.Debug("SendDirectMessage: Couldn't update channels")
|
||||
return
|
||||
}
|
||||
m.Lock()
|
||||
m.Team.Channels = mmchannels.Data.(*model.ChannelList)
|
||||
m.Unlock()
|
||||
@ -542,14 +549,12 @@ func (m *MMClient) GetTeamFromChannel(channelId string) string {
|
||||
func (m *MMClient) GetLastViewedAt(channelId string) int64 {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
/*
|
||||
for _, t := range m.OtherTeams {
|
||||
if _, ok := t.Channels.Members[channelId]; ok {
|
||||
return t.Channels.Members[channelId].LastViewedAt
|
||||
}
|
||||
}
|
||||
*/
|
||||
return 0
|
||||
res, err := m.Client.GetChannel(channelId, "")
|
||||
if err != nil {
|
||||
return model.GetMillis()
|
||||
}
|
||||
data := res.Data.(*model.ChannelData)
|
||||
return data.Member.LastViewedAt
|
||||
}
|
||||
|
||||
func (m *MMClient) GetUsers() map[string]*model.User {
|
||||
@ -583,6 +588,27 @@ func (m *MMClient) GetStatus(userId string) string {
|
||||
return "offline"
|
||||
}
|
||||
|
||||
func (m *MMClient) GetStatuses() map[string]string {
|
||||
var ok bool
|
||||
statuses := make(map[string]string)
|
||||
res, err := m.Client.GetStatuses()
|
||||
if err != nil {
|
||||
return statuses
|
||||
}
|
||||
if statuses, ok = res.Data.(map[string]string); ok {
|
||||
for userId, status := range statuses {
|
||||
statuses[userId] = "offline"
|
||||
if status == model.STATUS_AWAY {
|
||||
statuses[userId] = "away"
|
||||
}
|
||||
if status == model.STATUS_ONLINE {
|
||||
statuses[userId] = "online"
|
||||
}
|
||||
}
|
||||
}
|
||||
return statuses
|
||||
}
|
||||
|
||||
func (m *MMClient) GetTeamId() string {
|
||||
return m.Team.Id
|
||||
}
|
||||
@ -623,11 +649,20 @@ func (m *MMClient) initUser() error {
|
||||
//m.log.Debug("initUser(): loading all team data")
|
||||
for _, v := range initData.Teams {
|
||||
m.Client.SetTeamId(v.Id)
|
||||
mmusers, _ := m.Client.GetProfiles(0, 1000, "")
|
||||
mmusers, err := m.Client.GetProfiles(0, 50000, "")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id}
|
||||
mmchannels, _ := m.Client.GetChannels("")
|
||||
mmchannels, err := m.Client.GetChannels("")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
t.Channels = mmchannels.Data.(*model.ChannelList)
|
||||
mmchannels, _ = m.Client.GetMoreChannels("")
|
||||
mmchannels, err = m.Client.GetMoreChannels("")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
t.MoreChannels = mmchannels.Data.(*model.ChannelList)
|
||||
m.OtherTeams = append(m.OtherTeams, t)
|
||||
if v.Name == m.Credentials.Team {
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OMessage for mattermost incoming webhook. (send to mattermost)
|
||||
@ -82,8 +83,14 @@ func New(url string, config Config) *Client {
|
||||
func (c *Client) StartServer() {
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", c)
|
||||
srv := &http.Server{
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
Handler: mux,
|
||||
Addr: c.BindAddress,
|
||||
}
|
||||
log.Printf("Listening on http://%v...\n", c.BindAddress)
|
||||
if err := http.ListenAndServe(c.BindAddress, mux); err != nil {
|
||||
if err := srv.ListenAndServe(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
434
vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
generated
vendored
434
vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
generated
vendored
@ -1,434 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/42wim/matterbridge-plus/matterclient"
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/peterhellberg/giphy"
|
||||
ircm "github.com/sorcix/irc"
|
||||
"github.com/thoj/go-ircevent"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
//type Bridge struct {
|
||||
type MMhook struct {
|
||||
mh *matterhook.Client
|
||||
}
|
||||
|
||||
type MMapi struct {
|
||||
mc *matterclient.MMClient
|
||||
mmMap map[string]string
|
||||
mmIgnoreNicks []string
|
||||
}
|
||||
|
||||
type MMirc struct {
|
||||
i *irc.Connection
|
||||
ircNick string
|
||||
ircMap map[string]string
|
||||
names map[string][]string
|
||||
ircIgnoreNicks []string
|
||||
}
|
||||
|
||||
type MMMessage struct {
|
||||
Text string
|
||||
Channel string
|
||||
Username string
|
||||
}
|
||||
|
||||
type Bridge struct {
|
||||
MMhook
|
||||
MMapi
|
||||
MMirc
|
||||
*Config
|
||||
kind string
|
||||
}
|
||||
|
||||
type FancyLog struct {
|
||||
irc *log.Entry
|
||||
mm *log.Entry
|
||||
}
|
||||
|
||||
var flog FancyLog
|
||||
|
||||
const Legacy = "legacy"
|
||||
|
||||
func initFLog() {
|
||||
flog.irc = log.WithFields(log.Fields{"module": "irc"})
|
||||
flog.mm = log.WithFields(log.Fields{"module": "mattermost"})
|
||||
}
|
||||
|
||||
func NewBridge(name string, config *Config, kind string) *Bridge {
|
||||
initFLog()
|
||||
b := &Bridge{}
|
||||
b.Config = config
|
||||
b.kind = kind
|
||||
b.ircNick = b.Config.IRC.Nick
|
||||
b.ircMap = make(map[string]string)
|
||||
b.MMirc.names = make(map[string][]string)
|
||||
b.ircIgnoreNicks = strings.Fields(b.Config.IRC.IgnoreNicks)
|
||||
b.mmIgnoreNicks = strings.Fields(b.Config.Mattermost.IgnoreNicks)
|
||||
if kind == Legacy {
|
||||
if len(b.Config.Token) > 0 {
|
||||
for _, val := range b.Config.Token {
|
||||
b.ircMap[val.IRCChannel] = val.MMChannel
|
||||
}
|
||||
}
|
||||
|
||||
b.mh = matterhook.New(b.Config.Mattermost.URL,
|
||||
matterhook.Config{Port: b.Config.Mattermost.Port, Token: b.Config.Mattermost.Token,
|
||||
InsecureSkipVerify: b.Config.Mattermost.SkipTLSVerify,
|
||||
BindAddress: b.Config.Mattermost.BindAddress})
|
||||
} else {
|
||||
b.mmMap = make(map[string]string)
|
||||
if len(b.Config.Channel) > 0 {
|
||||
for _, val := range b.Config.Channel {
|
||||
b.ircMap[val.IRC] = val.Mattermost
|
||||
b.mmMap[val.Mattermost] = val.IRC
|
||||
}
|
||||
}
|
||||
b.mc = matterclient.New(b.Config.Mattermost.Login, b.Config.Mattermost.Password,
|
||||
b.Config.Mattermost.Team, b.Config.Mattermost.Server)
|
||||
b.mc.SkipTLSVerify = b.Config.Mattermost.SkipTLSVerify
|
||||
b.mc.NoTLS = b.Config.Mattermost.NoTLS
|
||||
flog.mm.Infof("Trying login %s (team: %s) on %s", b.Config.Mattermost.Login, b.Config.Mattermost.Team, b.Config.Mattermost.Server)
|
||||
err := b.mc.Login()
|
||||
if err != nil {
|
||||
flog.mm.Fatal("Can not connect", err)
|
||||
}
|
||||
flog.mm.Info("Login ok")
|
||||
b.mc.JoinChannel(b.Config.Mattermost.Channel)
|
||||
if len(b.Config.Channel) > 0 {
|
||||
for _, val := range b.Config.Channel {
|
||||
b.mc.JoinChannel(val.Mattermost)
|
||||
}
|
||||
}
|
||||
go b.mc.WsReceiver()
|
||||
}
|
||||
flog.irc.Info("Trying IRC connection")
|
||||
b.i = b.createIRC(name)
|
||||
flog.irc.Info("Connection succeeded")
|
||||
go b.handleMatter()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Bridge) createIRC(name string) *irc.Connection {
|
||||
i := irc.IRC(b.Config.IRC.Nick, b.Config.IRC.Nick)
|
||||
i.UseTLS = b.Config.IRC.UseTLS
|
||||
i.TLSConfig = &tls.Config{InsecureSkipVerify: b.Config.IRC.SkipTLSVerify}
|
||||
if b.Config.IRC.Password != "" {
|
||||
i.Password = b.Config.IRC.Password
|
||||
}
|
||||
i.AddCallback(ircm.RPL_WELCOME, b.handleNewConnection)
|
||||
i.Connect(b.Config.IRC.Server + ":" + strconv.Itoa(b.Config.IRC.Port))
|
||||
return i
|
||||
}
|
||||
|
||||
func (b *Bridge) handleNewConnection(event *irc.Event) {
|
||||
flog.irc.Info("Registering callbacks")
|
||||
i := b.i
|
||||
b.ircNick = event.Arguments[0]
|
||||
i.AddCallback("PRIVMSG", b.handlePrivMsg)
|
||||
i.AddCallback("CTCP_ACTION", b.handlePrivMsg)
|
||||
i.AddCallback(ircm.RPL_ENDOFNAMES, b.endNames)
|
||||
i.AddCallback(ircm.RPL_NAMREPLY, b.storeNames)
|
||||
i.AddCallback(ircm.RPL_TOPICWHOTIME, b.handleTopicWhoTime)
|
||||
i.AddCallback(ircm.NOTICE, b.handleNotice)
|
||||
i.AddCallback(ircm.RPL_MYINFO, func(e *irc.Event) { flog.irc.Infof("%s: %s", e.Code, strings.Join(e.Arguments[1:], " ")) })
|
||||
i.AddCallback("PING", func(e *irc.Event) {
|
||||
i.SendRaw("PONG :" + e.Message())
|
||||
flog.irc.Debugf("PING/PONG")
|
||||
})
|
||||
if b.Config.Mattermost.ShowJoinPart {
|
||||
i.AddCallback("JOIN", b.handleJoinPart)
|
||||
i.AddCallback("PART", b.handleJoinPart)
|
||||
}
|
||||
i.AddCallback("*", b.handleOther)
|
||||
b.setupChannels()
|
||||
}
|
||||
|
||||
func (b *Bridge) setupChannels() {
|
||||
i := b.i
|
||||
if b.Config.IRC.Channel != "" {
|
||||
flog.irc.Infof("Joining %s as %s", b.Config.IRC.Channel, b.ircNick)
|
||||
i.Join(b.Config.IRC.Channel)
|
||||
}
|
||||
if b.kind == Legacy {
|
||||
for _, val := range b.Config.Token {
|
||||
flog.irc.Infof("Joining %s as %s", val.IRCChannel, b.ircNick)
|
||||
i.Join(val.IRCChannel)
|
||||
}
|
||||
} else {
|
||||
for _, val := range b.Config.Channel {
|
||||
flog.irc.Infof("Joining %s as %s", val.IRC, b.ircNick)
|
||||
i.Join(val.IRC)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bridge) handleIrcBotCommand(event *irc.Event) bool {
|
||||
parts := strings.Fields(event.Message())
|
||||
exp, _ := regexp.Compile("[:,]+$")
|
||||
channel := event.Arguments[0]
|
||||
command := ""
|
||||
if len(parts) == 2 {
|
||||
command = parts[1]
|
||||
}
|
||||
if exp.ReplaceAllString(parts[0], "") == b.ircNick {
|
||||
switch command {
|
||||
case "users":
|
||||
usernames := b.mc.UsernamesInChannel(b.getMMChannel(channel))
|
||||
sort.Strings(usernames)
|
||||
b.i.Privmsg(channel, "Users on Mattermost: "+strings.Join(usernames, ", "))
|
||||
default:
|
||||
b.i.Privmsg(channel, "Valid commands are: [users, help]")
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (b *Bridge) ircNickFormat(nick string) string {
|
||||
if nick == b.ircNick {
|
||||
return nick
|
||||
}
|
||||
if b.Config.Mattermost.RemoteNickFormat == nil {
|
||||
return "irc-" + nick
|
||||
}
|
||||
return strings.Replace(*b.Config.Mattermost.RemoteNickFormat, "{NICK}", nick, -1)
|
||||
}
|
||||
|
||||
func (b *Bridge) handlePrivMsg(event *irc.Event) {
|
||||
if b.ignoreMessage(event.Nick, event.Message(), "irc") {
|
||||
return
|
||||
}
|
||||
if b.handleIrcBotCommand(event) {
|
||||
return
|
||||
}
|
||||
msg := ""
|
||||
if event.Code == "CTCP_ACTION" {
|
||||
msg = event.Nick + " "
|
||||
}
|
||||
msg += event.Message()
|
||||
b.Send(b.ircNickFormat(event.Nick), msg, b.getMMChannel(event.Arguments[0]))
|
||||
}
|
||||
|
||||
func (b *Bridge) handleJoinPart(event *irc.Event) {
|
||||
b.Send(b.ircNick, b.ircNickFormat(event.Nick)+" "+strings.ToLower(event.Code)+"s "+event.Message(), b.getMMChannel(event.Arguments[0]))
|
||||
}
|
||||
|
||||
func (b *Bridge) handleNotice(event *irc.Event) {
|
||||
if strings.Contains(event.Message(), "This nickname is registered") {
|
||||
b.i.Privmsg(b.Config.IRC.NickServNick, "IDENTIFY "+b.Config.IRC.NickServPassword)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bridge) nicksPerRow() int {
|
||||
if b.Config.Mattermost.NicksPerRow < 1 {
|
||||
return 4
|
||||
}
|
||||
return b.Config.Mattermost.NicksPerRow
|
||||
}
|
||||
|
||||
func (b *Bridge) formatnicks(nicks []string, continued bool) string {
|
||||
switch b.Config.Mattermost.NickFormatter {
|
||||
case "table":
|
||||
return tableformatter(nicks, b.nicksPerRow(), continued)
|
||||
default:
|
||||
return plainformatter(nicks, b.nicksPerRow())
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bridge) storeNames(event *irc.Event) {
|
||||
channel := event.Arguments[2]
|
||||
b.MMirc.names[channel] = append(
|
||||
b.MMirc.names[channel],
|
||||
strings.Split(strings.TrimSpace(event.Message()), " ")...)
|
||||
}
|
||||
|
||||
func (b *Bridge) endNames(event *irc.Event) {
|
||||
channel := event.Arguments[1]
|
||||
sort.Strings(b.MMirc.names[channel])
|
||||
maxNamesPerPost := (300 / b.nicksPerRow()) * b.nicksPerRow()
|
||||
continued := false
|
||||
for len(b.MMirc.names[channel]) > maxNamesPerPost {
|
||||
b.Send(
|
||||
b.ircNick,
|
||||
b.formatnicks(b.MMirc.names[channel][0:maxNamesPerPost], continued),
|
||||
b.getMMChannel(channel))
|
||||
b.MMirc.names[channel] = b.MMirc.names[channel][maxNamesPerPost:]
|
||||
continued = true
|
||||
}
|
||||
b.Send(b.ircNick, b.formatnicks(b.MMirc.names[channel], continued), b.getMMChannel(channel))
|
||||
b.MMirc.names[channel] = nil
|
||||
}
|
||||
|
||||
func (b *Bridge) handleTopicWhoTime(event *irc.Event) {
|
||||
parts := strings.Split(event.Arguments[2], "!")
|
||||
t, err := strconv.ParseInt(event.Arguments[3], 10, 64)
|
||||
if err != nil {
|
||||
flog.irc.Errorf("Invalid time stamp: %s", event.Arguments[3])
|
||||
}
|
||||
user := parts[0]
|
||||
if len(parts) > 1 {
|
||||
user += " [" + parts[1] + "]"
|
||||
}
|
||||
flog.irc.Infof("%s: Topic set by %s [%s]", event.Code, user, time.Unix(t, 0))
|
||||
}
|
||||
|
||||
func (b *Bridge) handleOther(event *irc.Event) {
|
||||
flog.irc.Debugf("%#v", event)
|
||||
}
|
||||
|
||||
func (b *Bridge) Send(nick string, message string, channel string) error {
|
||||
return b.SendType(nick, message, channel, "")
|
||||
}
|
||||
|
||||
func (b *Bridge) SendType(nick string, message string, channel string, mtype string) error {
|
||||
if b.Config.Mattermost.PrefixMessagesWithNick {
|
||||
if IsMarkup(message) {
|
||||
message = nick + "\n\n" + message
|
||||
} else {
|
||||
message = nick + " " + message
|
||||
}
|
||||
}
|
||||
if b.kind == Legacy {
|
||||
matterMessage := matterhook.OMessage{IconURL: b.Config.Mattermost.IconURL}
|
||||
matterMessage.Channel = channel
|
||||
matterMessage.UserName = nick
|
||||
matterMessage.Type = mtype
|
||||
matterMessage.Text = message
|
||||
err := b.mh.Send(matterMessage)
|
||||
if err != nil {
|
||||
flog.mm.Info(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
flog.mm.Debug("->mattermost channel: ", channel, " ", message)
|
||||
b.mc.PostMessage(channel, message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bridge) handleMatterHook(mchan chan *MMMessage) {
|
||||
for {
|
||||
message := b.mh.Receive()
|
||||
m := &MMMessage{}
|
||||
m.Username = message.UserName
|
||||
m.Text = message.Text
|
||||
m.Channel = message.Token
|
||||
mchan <- m
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bridge) handleMatterClient(mchan chan *MMMessage) {
|
||||
for message := range b.mc.MessageChan {
|
||||
// do not post our own messages back to irc
|
||||
if message.Raw.Action == "posted" && b.mc.User.Username != message.Username {
|
||||
m := &MMMessage{}
|
||||
m.Username = message.Username
|
||||
m.Channel = message.Channel
|
||||
m.Text = message.Text
|
||||
flog.mm.Debugf("<-mattermost channel: %s %#v %#v", message.Channel, message.Post, message.Raw)
|
||||
mchan <- m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bridge) handleMatter() {
|
||||
flog.mm.Infof("Choosing Mattermost connection type %s", b.kind)
|
||||
mchan := make(chan *MMMessage)
|
||||
if b.kind == Legacy {
|
||||
go b.handleMatterHook(mchan)
|
||||
} else {
|
||||
go b.handleMatterClient(mchan)
|
||||
}
|
||||
flog.mm.Info("Start listening for Mattermost messages")
|
||||
for message := range mchan {
|
||||
var username string
|
||||
if b.ignoreMessage(message.Username, message.Text, "mattermost") {
|
||||
continue
|
||||
}
|
||||
username = message.Username + ": "
|
||||
if b.Config.IRC.RemoteNickFormat != "" {
|
||||
username = strings.Replace(b.Config.IRC.RemoteNickFormat, "{NICK}", message.Username, -1)
|
||||
} else if b.Config.IRC.UseSlackCircumfix {
|
||||
username = "<" + message.Username + "> "
|
||||
}
|
||||
cmds := strings.Fields(message.Text)
|
||||
// empty message
|
||||
if len(cmds) == 0 {
|
||||
continue
|
||||
}
|
||||
cmd := cmds[0]
|
||||
switch cmd {
|
||||
case "!users":
|
||||
flog.mm.Info("Received !users from ", message.Username)
|
||||
b.i.SendRaw("NAMES " + b.getIRCChannel(message.Channel))
|
||||
continue
|
||||
case "!gif":
|
||||
message.Text = b.giphyRandom(strings.Fields(strings.Replace(message.Text, "!gif ", "", 1)))
|
||||
b.Send(b.ircNick, message.Text, b.getIRCChannel(message.Channel))
|
||||
continue
|
||||
}
|
||||
texts := strings.Split(message.Text, "\n")
|
||||
for _, text := range texts {
|
||||
flog.mm.Debug("Sending message from " + message.Username + " to " + message.Channel)
|
||||
b.i.Privmsg(b.getIRCChannel(message.Channel), username+text)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bridge) giphyRandom(query []string) string {
|
||||
g := giphy.DefaultClient
|
||||
if b.Config.General.GiphyAPIKey != "" {
|
||||
g.APIKey = b.Config.General.GiphyAPIKey
|
||||
}
|
||||
res, err := g.Random(query)
|
||||
if err != nil {
|
||||
return "error"
|
||||
}
|
||||
return res.Data.FixedHeightDownsampledURL
|
||||
}
|
||||
|
||||
func (b *Bridge) getMMChannel(ircChannel string) string {
|
||||
mmchannel, ok := b.ircMap[ircChannel]
|
||||
if !ok {
|
||||
mmchannel = b.Config.Mattermost.Channel
|
||||
}
|
||||
return mmchannel
|
||||
}
|
||||
|
||||
func (b *Bridge) getIRCChannel(channel string) string {
|
||||
if b.kind == Legacy {
|
||||
ircchannel := b.Config.IRC.Channel
|
||||
_, ok := b.Config.Token[channel]
|
||||
if ok {
|
||||
ircchannel = b.Config.Token[channel].IRCChannel
|
||||
}
|
||||
return ircchannel
|
||||
}
|
||||
ircchannel, ok := b.mmMap[channel]
|
||||
if !ok {
|
||||
ircchannel = b.Config.IRC.Channel
|
||||
}
|
||||
return ircchannel
|
||||
}
|
||||
|
||||
func (b *Bridge) ignoreMessage(nick string, message string, protocol string) bool {
|
||||
var ignoreNicks = b.mmIgnoreNicks
|
||||
if protocol == "irc" {
|
||||
ignoreNicks = b.ircIgnoreNicks
|
||||
}
|
||||
// should we discard messages ?
|
||||
for _, entry := range ignoreNicks {
|
||||
if nick == entry {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
68
vendor/github.com/42wim/matterbridge-plus/bridge/config.go
generated
vendored
68
vendor/github.com/42wim/matterbridge-plus/bridge/config.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"gopkg.in/gcfg.v1"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
IRC struct {
|
||||
UseTLS bool
|
||||
SkipTLSVerify bool
|
||||
Server string
|
||||
Port int
|
||||
Nick string
|
||||
Password string
|
||||
Channel string
|
||||
UseSlackCircumfix bool
|
||||
NickServNick string
|
||||
NickServPassword string
|
||||
RemoteNickFormat string
|
||||
IgnoreNicks string
|
||||
}
|
||||
Mattermost struct {
|
||||
URL string
|
||||
Port int
|
||||
ShowJoinPart bool
|
||||
Token string
|
||||
IconURL string
|
||||
SkipTLSVerify bool
|
||||
BindAddress string
|
||||
Channel string
|
||||
PrefixMessagesWithNick bool
|
||||
NicksPerRow int
|
||||
NickFormatter string
|
||||
Server string
|
||||
Team string
|
||||
Login string
|
||||
Password string
|
||||
RemoteNickFormat *string
|
||||
IgnoreNicks string
|
||||
NoTLS bool
|
||||
}
|
||||
Token map[string]*struct {
|
||||
IRCChannel string
|
||||
MMChannel string
|
||||
}
|
||||
Channel map[string]*struct {
|
||||
IRC string
|
||||
Mattermost string
|
||||
}
|
||||
General struct {
|
||||
GiphyAPIKey string
|
||||
}
|
||||
}
|
||||
|
||||
func NewConfig(cfgfile string) *Config {
|
||||
var cfg Config
|
||||
content, err := ioutil.ReadFile(cfgfile)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = gcfg.ReadStringInto(&cfg, string(content))
|
||||
if err != nil {
|
||||
log.Fatal("Failed to parse "+cfgfile+":", err)
|
||||
}
|
||||
return &cfg
|
||||
}
|
59
vendor/github.com/42wim/matterbridge-plus/bridge/helper.go
generated
vendored
59
vendor/github.com/42wim/matterbridge-plus/bridge/helper.go
generated
vendored
@ -1,59 +0,0 @@
|
||||
package bridge
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func tableformatter(nicks []string, nicksPerRow int, continued bool) string {
|
||||
result := "|IRC users"
|
||||
if continued {
|
||||
result = "|(continued)"
|
||||
}
|
||||
for i := 0; i < 2; i++ {
|
||||
for j := 1; j <= nicksPerRow && j <= len(nicks); j++ {
|
||||
if i == 0 {
|
||||
result += "|"
|
||||
} else {
|
||||
result += ":-|"
|
||||
}
|
||||
}
|
||||
result += "\r\n|"
|
||||
}
|
||||
result += nicks[0] + "|"
|
||||
for i := 1; i < len(nicks); i++ {
|
||||
if i%nicksPerRow == 0 {
|
||||
result += "\r\n|" + nicks[i] + "|"
|
||||
} else {
|
||||
result += nicks[i] + "|"
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func plainformatter(nicks []string, nicksPerRow int) string {
|
||||
return strings.Join(nicks, ", ") + " currently on IRC"
|
||||
}
|
||||
|
||||
func IsMarkup(message string) bool {
|
||||
switch message[0] {
|
||||
case '|':
|
||||
fallthrough
|
||||
case '#':
|
||||
fallthrough
|
||||
case '_':
|
||||
fallthrough
|
||||
case '*':
|
||||
fallthrough
|
||||
case '~':
|
||||
fallthrough
|
||||
case '-':
|
||||
fallthrough
|
||||
case ':':
|
||||
fallthrough
|
||||
case '>':
|
||||
fallthrough
|
||||
case '=':
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
202
vendor/github.com/42wim/matterbridge-plus/matterclient/LICENSE
generated
vendored
202
vendor/github.com/42wim/matterbridge-plus/matterclient/LICENSE
generated
vendored
@ -1,202 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
441
vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
generated
vendored
441
vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
generated
vendored
@ -1,441 +0,0 @@
|
||||
package matterclient
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"net/http"
|
||||
"net/http/cookiejar"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/jpillora/backoff"
|
||||
"github.com/mattermost/platform/model"
|
||||
)
|
||||
|
||||
type Credentials struct {
|
||||
Login string
|
||||
Team string
|
||||
Pass string
|
||||
Server string
|
||||
NoTLS bool
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
Raw *model.Message
|
||||
Post *model.Post
|
||||
Team string
|
||||
Channel string
|
||||
Username string
|
||||
Text string
|
||||
}
|
||||
|
||||
type MMClient struct {
|
||||
*Credentials
|
||||
Client *model.Client
|
||||
WsClient *websocket.Conn
|
||||
WsQuit bool
|
||||
WsAway bool
|
||||
Channels *model.ChannelList
|
||||
MoreChannels *model.ChannelList
|
||||
User *model.User
|
||||
Users map[string]*model.User
|
||||
MessageChan chan *Message
|
||||
Team *model.Team
|
||||
log *log.Entry
|
||||
}
|
||||
|
||||
func New(login, pass, team, server string) *MMClient {
|
||||
cred := &Credentials{Login: login, Pass: pass, Team: team, Server: server}
|
||||
mmclient := &MMClient{Credentials: cred, MessageChan: make(chan *Message, 100)}
|
||||
mmclient.log = log.WithFields(log.Fields{"module": "matterclient"})
|
||||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
|
||||
return mmclient
|
||||
}
|
||||
|
||||
func (m *MMClient) SetLogLevel(level string) {
|
||||
l, err := log.ParseLevel(level)
|
||||
if err != nil {
|
||||
log.SetLevel(log.InfoLevel)
|
||||
return
|
||||
}
|
||||
log.SetLevel(l)
|
||||
}
|
||||
|
||||
func (m *MMClient) Login() error {
|
||||
if m.WsQuit {
|
||||
return nil
|
||||
}
|
||||
b := &backoff.Backoff{
|
||||
Min: time.Second,
|
||||
Max: 5 * time.Minute,
|
||||
Jitter: true,
|
||||
}
|
||||
uriScheme := "https://"
|
||||
wsScheme := "wss://"
|
||||
if m.NoTLS {
|
||||
uriScheme = "http://"
|
||||
wsScheme = "ws://"
|
||||
}
|
||||
// login to mattermost
|
||||
m.Client = model.NewClient(uriScheme + m.Credentials.Server)
|
||||
m.Client.HttpClient.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||
var myinfo *model.Result
|
||||
var appErr *model.AppError
|
||||
var logmsg = "trying login"
|
||||
for {
|
||||
m.log.Debugf("%s %s %s %s", logmsg, m.Credentials.Team, m.Credentials.Login, m.Credentials.Server)
|
||||
if strings.Contains(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN) {
|
||||
m.log.Debugf(logmsg+" with ", model.SESSION_COOKIE_TOKEN)
|
||||
token := strings.Split(m.Credentials.Pass, model.SESSION_COOKIE_TOKEN+"=")
|
||||
m.Client.HttpClient.Jar = m.createCookieJar(token[1])
|
||||
m.Client.MockSession(token[1])
|
||||
myinfo, appErr = m.Client.GetMe("")
|
||||
if myinfo.Data.(*model.User) == nil {
|
||||
m.log.Errorf("LOGIN TOKEN: %s is invalid", m.Credentials.Pass)
|
||||
return errors.New("invalid " + model.SESSION_COOKIE_TOKEN)
|
||||
}
|
||||
} else {
|
||||
myinfo, appErr = m.Client.Login(m.Credentials.Login, m.Credentials.Pass)
|
||||
}
|
||||
if appErr != nil {
|
||||
d := b.Duration()
|
||||
m.log.Debug(appErr.DetailedError)
|
||||
if !strings.Contains(appErr.DetailedError, "connection refused") &&
|
||||
!strings.Contains(appErr.DetailedError, "invalid character") {
|
||||
if appErr.Message == "" {
|
||||
return errors.New(appErr.DetailedError)
|
||||
}
|
||||
return errors.New(appErr.Message)
|
||||
}
|
||||
m.log.Debugf("LOGIN: %s, reconnecting in %s", appErr, d)
|
||||
time.Sleep(d)
|
||||
logmsg = "retrying login"
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
// reset timer
|
||||
b.Reset()
|
||||
|
||||
initLoad, _ := m.Client.GetInitialLoad()
|
||||
initData := initLoad.Data.(*model.InitialLoad)
|
||||
m.User = initData.User
|
||||
for _, v := range initData.Teams {
|
||||
m.log.Debugf("trying %s (id: %s)", v.Name, v.Id)
|
||||
if v.Name == m.Credentials.Team {
|
||||
m.Client.SetTeamId(v.Id)
|
||||
m.Team = v
|
||||
m.log.Debugf("GetallTeamListings: found id %s for team %s", v.Id, v.Name)
|
||||
break
|
||||
}
|
||||
}
|
||||
if m.Team == nil {
|
||||
return errors.New("team not found")
|
||||
}
|
||||
|
||||
// setup websocket connection
|
||||
wsurl := wsScheme + m.Credentials.Server + "/api/v3/users/websocket"
|
||||
header := http.Header{}
|
||||
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
|
||||
|
||||
m.log.Debug("WsClient: making connection")
|
||||
var err error
|
||||
for {
|
||||
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
|
||||
if err != nil {
|
||||
d := b.Duration()
|
||||
m.log.Debugf("WSS: %s, reconnecting in %s", err, d)
|
||||
time.Sleep(d)
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
b.Reset()
|
||||
|
||||
// populating users
|
||||
m.UpdateUsers()
|
||||
|
||||
// populating channels
|
||||
m.UpdateChannels()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) WsReceiver() {
|
||||
var rmsg model.Message
|
||||
for {
|
||||
if m.WsQuit {
|
||||
m.log.Debug("exiting WsReceiver")
|
||||
return
|
||||
}
|
||||
if err := m.WsClient.ReadJSON(&rmsg); err != nil {
|
||||
m.log.Error("error:", err)
|
||||
// reconnect
|
||||
m.Login()
|
||||
}
|
||||
if rmsg.Action == "ping" {
|
||||
m.handleWsPing()
|
||||
continue
|
||||
}
|
||||
msg := &Message{Raw: &rmsg, Team: m.Credentials.Team}
|
||||
m.parseMessage(msg)
|
||||
m.MessageChan <- msg
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (m *MMClient) handleWsPing() {
|
||||
m.log.Debug("Ws PING")
|
||||
if !m.WsQuit && !m.WsAway {
|
||||
m.log.Debug("Ws PONG")
|
||||
m.WsClient.WriteMessage(websocket.PongMessage, []byte{})
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MMClient) parseMessage(rmsg *Message) {
|
||||
switch rmsg.Raw.Action {
|
||||
case model.ACTION_POSTED:
|
||||
m.parseActionPost(rmsg)
|
||||
/*
|
||||
case model.ACTION_USER_REMOVED:
|
||||
m.handleWsActionUserRemoved(&rmsg)
|
||||
case model.ACTION_USER_ADDED:
|
||||
m.handleWsActionUserAdded(&rmsg)
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MMClient) parseActionPost(rmsg *Message) {
|
||||
data := model.PostFromJson(strings.NewReader(rmsg.Raw.Props["post"]))
|
||||
// log.Println("receiving userid", data.UserId)
|
||||
// we don't have the user, refresh the userlist
|
||||
if m.Users[data.UserId] == nil {
|
||||
m.UpdateUsers()
|
||||
}
|
||||
rmsg.Username = m.Users[data.UserId].Username
|
||||
rmsg.Channel = m.GetChannelName(data.ChannelId)
|
||||
// direct message
|
||||
if strings.Contains(rmsg.Channel, "__") {
|
||||
//log.Println("direct message")
|
||||
rcvusers := strings.Split(rmsg.Channel, "__")
|
||||
if rcvusers[0] != m.User.Id {
|
||||
rmsg.Channel = m.Users[rcvusers[0]].Username
|
||||
} else {
|
||||
rmsg.Channel = m.Users[rcvusers[1]].Username
|
||||
}
|
||||
}
|
||||
rmsg.Text = data.Message
|
||||
rmsg.Post = data
|
||||
return
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateUsers() error {
|
||||
mmusers, _ := m.Client.GetProfilesForDirectMessageList(m.Team.Id)
|
||||
m.Users = mmusers.Data.(map[string]*model.User)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateChannels() error {
|
||||
mmchannels, _ := m.Client.GetChannels("")
|
||||
m.Channels = mmchannels.Data.(*model.ChannelList)
|
||||
mmchannels, _ = m.Client.GetMoreChannels("")
|
||||
m.MoreChannels = mmchannels.Data.(*model.ChannelList)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) GetChannelName(id string) string {
|
||||
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
|
||||
if channel.Id == id {
|
||||
return channel.Name
|
||||
}
|
||||
}
|
||||
// not found? could be a new direct message from mattermost. Try to update and check again
|
||||
m.UpdateChannels()
|
||||
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
|
||||
if channel.Id == id {
|
||||
return channel.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MMClient) GetChannelId(name string) string {
|
||||
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
|
||||
if channel.Name == name {
|
||||
return channel.Id
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MMClient) GetChannelHeader(id string) string {
|
||||
for _, channel := range append(m.Channels.Channels, m.MoreChannels.Channels...) {
|
||||
if channel.Id == id {
|
||||
return channel.Header
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *MMClient) PostMessage(channel string, text string) {
|
||||
post := &model.Post{ChannelId: m.GetChannelId(channel), Message: text}
|
||||
m.Client.CreatePost(post)
|
||||
}
|
||||
|
||||
func (m *MMClient) JoinChannel(channel string) error {
|
||||
cleanChan := strings.Replace(channel, "#", "", 1)
|
||||
if m.GetChannelId(cleanChan) == "" {
|
||||
return errors.New("failed to join")
|
||||
}
|
||||
for _, c := range m.Channels.Channels {
|
||||
if c.Name == cleanChan {
|
||||
m.log.Debug("Not joining ", cleanChan, " already joined.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
m.log.Debug("Joining ", cleanChan)
|
||||
_, err := m.Client.JoinChannel(m.GetChannelId(cleanChan))
|
||||
if err != nil {
|
||||
return errors.New("failed to join")
|
||||
}
|
||||
// m.SyncChannel(m.getMMChannelId(strings.Replace(channel, "#", "", 1)), strings.Replace(channel, "#", "", 1))
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPostsSince(channelId string, time int64) *model.PostList {
|
||||
res, err := m.Client.GetPostsSince(channelId, time)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return res.Data.(*model.PostList)
|
||||
}
|
||||
|
||||
func (m *MMClient) SearchPosts(query string) *model.PostList {
|
||||
res, err := m.Client.SearchPosts(query, false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return res.Data.(*model.PostList)
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPosts(channelId string, limit int) *model.PostList {
|
||||
res, err := m.Client.GetPosts(channelId, 0, limit, "")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return res.Data.(*model.PostList)
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPublicLink(filename string) string {
|
||||
res, err := m.Client.GetPublicLink(filename)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return res.Data.(string)
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPublicLinks(filenames []string) []string {
|
||||
var output []string
|
||||
for _, f := range filenames {
|
||||
res, err := m.Client.GetPublicLink(f)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
output = append(output, res.Data.(string))
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateChannelHeader(channelId string, header string) {
|
||||
data := make(map[string]string)
|
||||
data["channel_id"] = channelId
|
||||
data["channel_header"] = header
|
||||
m.log.Debugf("updating channelheader %#v, %#v", channelId, header)
|
||||
_, err := m.Client.UpdateChannelHeader(data)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateLastViewed(channelId string) {
|
||||
m.log.Debugf("posting lastview %#v", channelId)
|
||||
_, err := m.Client.UpdateLastViewedAt(channelId)
|
||||
if err != nil {
|
||||
m.log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MMClient) UsernamesInChannel(channelName string) []string {
|
||||
ceiRes, err := m.Client.GetChannelExtraInfo(m.GetChannelId(channelName), 5000, "")
|
||||
if err != nil {
|
||||
m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelName, err)
|
||||
return []string{}
|
||||
}
|
||||
extra := ceiRes.Data.(*model.ChannelExtra)
|
||||
result := []string{}
|
||||
for _, member := range extra.Members {
|
||||
result = append(result, member.Username)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (m *MMClient) createCookieJar(token string) *cookiejar.Jar {
|
||||
var cookies []*http.Cookie
|
||||
jar, _ := cookiejar.New(nil)
|
||||
firstCookie := &http.Cookie{
|
||||
Name: "MMAUTHTOKEN",
|
||||
Value: token,
|
||||
Path: "/",
|
||||
Domain: m.Credentials.Server,
|
||||
}
|
||||
cookies = append(cookies, firstCookie)
|
||||
cookieURL, _ := url.Parse("https://" + m.Credentials.Server)
|
||||
jar.SetCookies(cookieURL, cookies)
|
||||
return jar
|
||||
}
|
||||
|
||||
func (m *MMClient) GetOtherUserDM(channel string) *model.User {
|
||||
m.UpdateUsers()
|
||||
var rcvuser *model.User
|
||||
if strings.Contains(channel, "__") {
|
||||
rcvusers := strings.Split(channel, "__")
|
||||
if rcvusers[0] != m.User.Id {
|
||||
rcvuser = m.Users[rcvusers[0]]
|
||||
} else {
|
||||
rcvuser = m.Users[rcvusers[1]]
|
||||
}
|
||||
}
|
||||
return rcvuser
|
||||
}
|
||||
|
||||
func (m *MMClient) SendDirectMessage(toUserId string, msg string) {
|
||||
m.log.Debugf("SendDirectMessage to %s, msg %s", toUserId, msg)
|
||||
var channel string
|
||||
// We don't have a DM with this user yet.
|
||||
if m.GetChannelId(toUserId+"__"+m.User.Id) == "" && m.GetChannelId(m.User.Id+"__"+toUserId) == "" {
|
||||
// create DM channel
|
||||
_, err := m.Client.CreateDirectChannel(toUserId)
|
||||
if err != nil {
|
||||
m.log.Debugf("SendDirectMessage to %#v failed: %s", toUserId, err)
|
||||
}
|
||||
// update our channels
|
||||
mmchannels, _ := m.Client.GetChannels("")
|
||||
m.Channels = mmchannels.Data.(*model.ChannelList)
|
||||
}
|
||||
|
||||
// build the channel name
|
||||
if toUserId > m.User.Id {
|
||||
channel = m.User.Id + "__" + toUserId
|
||||
} else {
|
||||
channel = toUserId + "__" + m.User.Id
|
||||
}
|
||||
// build & send the message
|
||||
msg = strings.Replace(msg, "\r", "", -1)
|
||||
post := &model.Post{ChannelId: m.GetChannelId(channel), Message: msg}
|
||||
m.Client.CreatePost(post)
|
||||
}
|
22
vendor/github.com/GeertJohan/go.rice/LICENSE
generated
vendored
Normal file
22
vendor/github.com/GeertJohan/go.rice/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Copyright (c) 2013, Geert-Johan Riemer
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
138
vendor/github.com/GeertJohan/go.rice/appended.go
generated
vendored
Normal file
138
vendor/github.com/GeertJohan/go.rice/appended.go
generated
vendored
Normal file
@ -0,0 +1,138 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/daaku/go.zipexe"
|
||||
"github.com/kardianos/osext"
|
||||
)
|
||||
|
||||
// appendedBox defines an appended box
|
||||
type appendedBox struct {
|
||||
Name string // box name
|
||||
Files map[string]*appendedFile // appended files (*zip.File) by full path
|
||||
}
|
||||
|
||||
type appendedFile struct {
|
||||
zipFile *zip.File
|
||||
dir bool
|
||||
dirInfo *appendedDirInfo
|
||||
children []*appendedFile
|
||||
content []byte
|
||||
}
|
||||
|
||||
// appendedBoxes is a public register of appendes boxes
|
||||
var appendedBoxes = make(map[string]*appendedBox)
|
||||
|
||||
func init() {
|
||||
// find if exec is appended
|
||||
thisFile, err := osext.Executable()
|
||||
if err != nil {
|
||||
return // not appended or cant find self executable
|
||||
}
|
||||
closer, rd, err := zipexe.OpenCloser(thisFile)
|
||||
if err != nil {
|
||||
return // not appended
|
||||
}
|
||||
defer closer.Close()
|
||||
|
||||
for _, f := range rd.File {
|
||||
// get box and file name from f.Name
|
||||
fileParts := strings.SplitN(strings.TrimLeft(filepath.ToSlash(f.Name), "/"), "/", 2)
|
||||
boxName := fileParts[0]
|
||||
var fileName string
|
||||
if len(fileParts) > 1 {
|
||||
fileName = fileParts[1]
|
||||
}
|
||||
|
||||
// find box or create new one if doesn't exist
|
||||
box := appendedBoxes[boxName]
|
||||
if box == nil {
|
||||
box = &appendedBox{
|
||||
Name: boxName,
|
||||
Files: make(map[string]*appendedFile),
|
||||
}
|
||||
appendedBoxes[boxName] = box
|
||||
}
|
||||
|
||||
// create and add file to box
|
||||
af := &appendedFile{
|
||||
zipFile: f,
|
||||
}
|
||||
if f.Comment == "dir" {
|
||||
af.dir = true
|
||||
af.dirInfo = &appendedDirInfo{
|
||||
name: filepath.Base(af.zipFile.Name),
|
||||
//++ TODO: use zip modtime when that is set correctly: af.zipFile.ModTime()
|
||||
time: time.Now(),
|
||||
}
|
||||
} else {
|
||||
// this is a file, we need it's contents so we can create a bytes.Reader when the file is opened
|
||||
// make a new byteslice
|
||||
af.content = make([]byte, af.zipFile.FileInfo().Size())
|
||||
// ignore reading empty files from zip (empty file still is a valid file to be read though!)
|
||||
if len(af.content) > 0 {
|
||||
// open io.ReadCloser
|
||||
rc, err := af.zipFile.Open()
|
||||
if err != nil {
|
||||
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
|
||||
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
|
||||
log.Printf("error opening appended file %s: %v", af.zipFile.Name, err)
|
||||
} else {
|
||||
_, err = rc.Read(af.content)
|
||||
rc.Close()
|
||||
if err != nil {
|
||||
af.content = nil // this will cause an error when the file is being opened or seeked (which is good)
|
||||
// TODO: it's quite blunt to just log this stuff. but this is in init, so rice.Debug can't be changed yet..
|
||||
log.Printf("error reading data for appended file %s: %v", af.zipFile.Name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add appendedFile to box file list
|
||||
box.Files[fileName] = af
|
||||
|
||||
// add to parent dir (if any)
|
||||
dirName := filepath.Dir(fileName)
|
||||
if dirName == "." {
|
||||
dirName = ""
|
||||
}
|
||||
if fileName != "" { // don't make box root dir a child of itself
|
||||
if dir := box.Files[dirName]; dir != nil {
|
||||
dir.children = append(dir.children, af)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// implements os.FileInfo.
|
||||
// used for Readdir()
|
||||
type appendedDirInfo struct {
|
||||
name string
|
||||
time time.Time
|
||||
}
|
||||
|
||||
func (adi *appendedDirInfo) Name() string {
|
||||
return adi.name
|
||||
}
|
||||
func (adi *appendedDirInfo) Size() int64 {
|
||||
return 0
|
||||
}
|
||||
func (adi *appendedDirInfo) Mode() os.FileMode {
|
||||
return os.ModeDir
|
||||
}
|
||||
func (adi *appendedDirInfo) ModTime() time.Time {
|
||||
return adi.time
|
||||
}
|
||||
func (adi *appendedDirInfo) IsDir() bool {
|
||||
return true
|
||||
}
|
||||
func (adi *appendedDirInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
337
vendor/github.com/GeertJohan/go.rice/box.go
generated
vendored
Normal file
337
vendor/github.com/GeertJohan/go.rice/box.go
generated
vendored
Normal file
@ -0,0 +1,337 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
)
|
||||
|
||||
// Box abstracts a directory for resources/files.
|
||||
// It can either load files from disk, or from embedded code (when `rice --embed` was ran).
|
||||
type Box struct {
|
||||
name string
|
||||
absolutePath string
|
||||
embed *embedded.EmbeddedBox
|
||||
appendd *appendedBox
|
||||
}
|
||||
|
||||
var defaultLocateOrder = []LocateMethod{LocateEmbedded, LocateAppended, LocateFS}
|
||||
|
||||
func findBox(name string, order []LocateMethod) (*Box, error) {
|
||||
b := &Box{name: name}
|
||||
|
||||
// no support for absolute paths since gopath can be different on different machines.
|
||||
// therefore, required box must be located relative to package requiring it.
|
||||
if filepath.IsAbs(name) {
|
||||
return nil, errors.New("given name/path is absolute")
|
||||
}
|
||||
|
||||
var err error
|
||||
for _, method := range order {
|
||||
switch method {
|
||||
case LocateEmbedded:
|
||||
if embed := embedded.EmbeddedBoxes[name]; embed != nil {
|
||||
b.embed = embed
|
||||
return b, nil
|
||||
}
|
||||
|
||||
case LocateAppended:
|
||||
appendedBoxName := strings.Replace(name, `/`, `-`, -1)
|
||||
if appendd := appendedBoxes[appendedBoxName]; appendd != nil {
|
||||
b.appendd = appendd
|
||||
return b, nil
|
||||
}
|
||||
|
||||
case LocateFS:
|
||||
// resolve absolute directory path
|
||||
err := b.resolveAbsolutePathFromCaller()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// check if absolutePath exists on filesystem
|
||||
info, err := os.Stat(b.absolutePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// check if absolutePath is actually a directory
|
||||
if !info.IsDir() {
|
||||
err = errors.New("given name/path is not a directory")
|
||||
continue
|
||||
}
|
||||
return b, nil
|
||||
case LocateWorkingDirectory:
|
||||
// resolve absolute directory path
|
||||
err := b.resolveAbsolutePathFromWorkingDirectory()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// check if absolutePath exists on filesystem
|
||||
info, err := os.Stat(b.absolutePath)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
// check if absolutePath is actually a directory
|
||||
if !info.IsDir() {
|
||||
err = errors.New("given name/path is not a directory")
|
||||
continue
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
err = fmt.Errorf("could not locate box %q", name)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// FindBox returns a Box instance for given name.
|
||||
// When the given name is a relative path, it's base path will be the calling pkg/cmd's source root.
|
||||
// When the given name is absolute, it's absolute. derp.
|
||||
// Make sure the path doesn't contain any sensitive information as it might be placed into generated go source (embedded).
|
||||
func FindBox(name string) (*Box, error) {
|
||||
return findBox(name, defaultLocateOrder)
|
||||
}
|
||||
|
||||
// MustFindBox returns a Box instance for given name, like FindBox does.
|
||||
// It does not return an error, instead it panics when an error occurs.
|
||||
func MustFindBox(name string) *Box {
|
||||
box, err := findBox(name, defaultLocateOrder)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return box
|
||||
}
|
||||
|
||||
// This is injected as a mutable function literal so that we can mock it out in
|
||||
// tests and return a fixed test file.
|
||||
var resolveAbsolutePathFromCaller = func(name string, nStackFrames int) (string, error) {
|
||||
_, callingGoFile, _, ok := runtime.Caller(nStackFrames)
|
||||
if !ok {
|
||||
return "", errors.New("couldn't find caller on stack")
|
||||
}
|
||||
|
||||
// resolve to proper path
|
||||
pkgDir := filepath.Dir(callingGoFile)
|
||||
// fix for go cover
|
||||
const coverPath = "_test/_obj_test"
|
||||
if !filepath.IsAbs(pkgDir) {
|
||||
if i := strings.Index(pkgDir, coverPath); i >= 0 {
|
||||
pkgDir = pkgDir[:i] + pkgDir[i+len(coverPath):] // remove coverPath
|
||||
pkgDir = filepath.Join(os.Getenv("GOPATH"), "src", pkgDir) // make absolute
|
||||
}
|
||||
}
|
||||
return filepath.Join(pkgDir, name), nil
|
||||
}
|
||||
|
||||
func (b *Box) resolveAbsolutePathFromCaller() error {
|
||||
path, err := resolveAbsolutePathFromCaller(b.name, 4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.absolutePath = path
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func (b *Box) resolveAbsolutePathFromWorkingDirectory() error {
|
||||
path, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.absolutePath = filepath.Join(path, b.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsEmbedded indicates wether this box was embedded into the application
|
||||
func (b *Box) IsEmbedded() bool {
|
||||
return b.embed != nil
|
||||
}
|
||||
|
||||
// IsAppended indicates wether this box was appended to the application
|
||||
func (b *Box) IsAppended() bool {
|
||||
return b.appendd != nil
|
||||
}
|
||||
|
||||
// Time returns how actual the box is.
|
||||
// When the box is embedded, it's value is saved in the embedding code.
|
||||
// When the box is live, this methods returns time.Now()
|
||||
func (b *Box) Time() time.Time {
|
||||
if b.IsEmbedded() {
|
||||
return b.embed.Time
|
||||
}
|
||||
|
||||
//++ TODO: return time for appended box
|
||||
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// Open opens a File from the box
|
||||
// If there is an error, it will be of type *os.PathError.
|
||||
func (b *Box) Open(name string) (*File, error) {
|
||||
if Debug {
|
||||
fmt.Printf("Open(%s)\n", name)
|
||||
}
|
||||
|
||||
if b.IsEmbedded() {
|
||||
if Debug {
|
||||
fmt.Println("Box is embedded")
|
||||
}
|
||||
|
||||
// trim prefix (paths are relative to box)
|
||||
name = strings.TrimLeft(name, "/")
|
||||
if Debug {
|
||||
fmt.Printf("Trying %s\n", name)
|
||||
}
|
||||
|
||||
// search for file
|
||||
ef := b.embed.Files[name]
|
||||
if ef == nil {
|
||||
if Debug {
|
||||
fmt.Println("Didn't find file in embed")
|
||||
}
|
||||
// file not found, try dir
|
||||
ed := b.embed.Dirs[name]
|
||||
if ed == nil {
|
||||
if Debug {
|
||||
fmt.Println("Didn't find dir in embed")
|
||||
}
|
||||
// dir not found, error out
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
if Debug {
|
||||
fmt.Println("Found dir. Returning virtual dir")
|
||||
}
|
||||
vd := newVirtualDir(ed)
|
||||
return &File{virtualD: vd}, nil
|
||||
}
|
||||
|
||||
// box is embedded
|
||||
if Debug {
|
||||
fmt.Println("Found file. Returning virtual file")
|
||||
}
|
||||
vf := newVirtualFile(ef)
|
||||
return &File{virtualF: vf}, nil
|
||||
}
|
||||
|
||||
if b.IsAppended() {
|
||||
// trim prefix (paths are relative to box)
|
||||
name = strings.TrimLeft(name, "/")
|
||||
|
||||
// search for file
|
||||
appendedFile := b.appendd.Files[name]
|
||||
if appendedFile == nil {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
|
||||
// create new file
|
||||
f := &File{
|
||||
appendedF: appendedFile,
|
||||
}
|
||||
|
||||
// if this file is a directory, we want to be able to read and seek
|
||||
if !appendedFile.dir {
|
||||
// looks like malformed data in zip, error now
|
||||
if appendedFile.content == nil {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: "name",
|
||||
Err: errors.New("error reading data from zip file"),
|
||||
}
|
||||
}
|
||||
// create new bytes.Reader
|
||||
f.appendedFileReader = bytes.NewReader(appendedFile.content)
|
||||
}
|
||||
|
||||
// all done
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// perform os open
|
||||
if Debug {
|
||||
fmt.Printf("Using os.Open(%s)", filepath.Join(b.absolutePath, name))
|
||||
}
|
||||
file, err := os.Open(filepath.Join(b.absolutePath, name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &File{realF: file}, nil
|
||||
}
|
||||
|
||||
// Bytes returns the content of the file with given name as []byte.
|
||||
func (b *Box) Bytes(name string) ([]byte, error) {
|
||||
file, err := b.Open(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
content, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
// MustBytes returns the content of the file with given name as []byte.
|
||||
// panic's on error.
|
||||
func (b *Box) MustBytes(name string) []byte {
|
||||
bts, err := b.Bytes(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bts
|
||||
}
|
||||
|
||||
// String returns the content of the file with given name as string.
|
||||
func (b *Box) String(name string) (string, error) {
|
||||
// check if box is embedded, optimized fast path
|
||||
if b.IsEmbedded() {
|
||||
// find file in embed
|
||||
ef := b.embed.Files[name]
|
||||
if ef == nil {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
// return as string
|
||||
return ef.Content, nil
|
||||
}
|
||||
|
||||
bts, err := b.Bytes(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(bts), nil
|
||||
}
|
||||
|
||||
// MustString returns the content of the file with given name as string.
|
||||
// panic's on error.
|
||||
func (b *Box) MustString(name string) string {
|
||||
str, err := b.String(name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
// Name returns the name of the box
|
||||
func (b *Box) Name() string {
|
||||
return b.name
|
||||
}
|
39
vendor/github.com/GeertJohan/go.rice/config.go
generated
vendored
Normal file
39
vendor/github.com/GeertJohan/go.rice/config.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
package rice
|
||||
|
||||
// LocateMethod defines how a box is located.
|
||||
type LocateMethod int
|
||||
|
||||
const (
|
||||
LocateFS = LocateMethod(iota) // Locate on the filesystem according to package path.
|
||||
LocateAppended // Locate boxes appended to the executable.
|
||||
LocateEmbedded // Locate embedded boxes.
|
||||
LocateWorkingDirectory // Locate on the binary working directory
|
||||
)
|
||||
|
||||
// Config allows customizing the box lookup behavior.
|
||||
type Config struct {
|
||||
// LocateOrder defines the priority order that boxes are searched for. By
|
||||
// default, the package global FindBox searches for embedded boxes first,
|
||||
// then appended boxes, and then finally boxes on the filesystem. That
|
||||
// search order may be customized by provided the ordered list here. Leaving
|
||||
// out a particular method will omit that from the search space. For
|
||||
// example, []LocateMethod{LocateEmbedded, LocateAppended} will never search
|
||||
// the filesystem for boxes.
|
||||
LocateOrder []LocateMethod
|
||||
}
|
||||
|
||||
// FindBox searches for boxes using the LocateOrder of the config.
|
||||
func (c *Config) FindBox(boxName string) (*Box, error) {
|
||||
return findBox(boxName, c.LocateOrder)
|
||||
}
|
||||
|
||||
// MustFindBox searches for boxes using the LocateOrder of the config, like
|
||||
// FindBox does. It does not return an error, instead it panics when an error
|
||||
// occurs.
|
||||
func (c *Config) MustFindBox(boxName string) *Box {
|
||||
box, err := findBox(boxName, c.LocateOrder)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return box
|
||||
}
|
4
vendor/github.com/GeertJohan/go.rice/debug.go
generated
vendored
Normal file
4
vendor/github.com/GeertJohan/go.rice/debug.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
package rice
|
||||
|
||||
// Debug can be set to true to enable debugging.
|
||||
var Debug = false
|
90
vendor/github.com/GeertJohan/go.rice/embedded.go
generated
vendored
Normal file
90
vendor/github.com/GeertJohan/go.rice/embedded.go
generated
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
)
|
||||
|
||||
// re-type to make exported methods invisible to user (godoc)
|
||||
// they're not required for the user
|
||||
// embeddedDirInfo implements os.FileInfo
|
||||
type embeddedDirInfo embedded.EmbeddedDir
|
||||
|
||||
// Name returns the base name of the directory
|
||||
// (implementing os.FileInfo)
|
||||
func (ed *embeddedDirInfo) Name() string {
|
||||
return ed.Filename
|
||||
}
|
||||
|
||||
// Size always returns 0
|
||||
// (implementing os.FileInfo)
|
||||
func (ed *embeddedDirInfo) Size() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Mode returns the file mode bits
|
||||
// (implementing os.FileInfo)
|
||||
func (ed *embeddedDirInfo) Mode() os.FileMode {
|
||||
return os.FileMode(0555 | os.ModeDir) // dr-xr-xr-x
|
||||
}
|
||||
|
||||
// ModTime returns the modification time
|
||||
// (implementing os.FileInfo)
|
||||
func (ed *embeddedDirInfo) ModTime() time.Time {
|
||||
return ed.DirModTime
|
||||
}
|
||||
|
||||
// IsDir returns the abbreviation for Mode().IsDir() (always true)
|
||||
// (implementing os.FileInfo)
|
||||
func (ed *embeddedDirInfo) IsDir() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Sys returns the underlying data source (always nil)
|
||||
// (implementing os.FileInfo)
|
||||
func (ed *embeddedDirInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// re-type to make exported methods invisible to user (godoc)
|
||||
// they're not required for the user
|
||||
// embeddedFileInfo implements os.FileInfo
|
||||
type embeddedFileInfo embedded.EmbeddedFile
|
||||
|
||||
// Name returns the base name of the file
|
||||
// (implementing os.FileInfo)
|
||||
func (ef *embeddedFileInfo) Name() string {
|
||||
return ef.Filename
|
||||
}
|
||||
|
||||
// Size returns the length in bytes for regular files; system-dependent for others
|
||||
// (implementing os.FileInfo)
|
||||
func (ef *embeddedFileInfo) Size() int64 {
|
||||
return int64(len(ef.Content))
|
||||
}
|
||||
|
||||
// Mode returns the file mode bits
|
||||
// (implementing os.FileInfo)
|
||||
func (ef *embeddedFileInfo) Mode() os.FileMode {
|
||||
return os.FileMode(0555) // r-xr-xr-x
|
||||
}
|
||||
|
||||
// ModTime returns the modification time
|
||||
// (implementing os.FileInfo)
|
||||
func (ef *embeddedFileInfo) ModTime() time.Time {
|
||||
return ef.FileModTime
|
||||
}
|
||||
|
||||
// IsDir returns the abbreviation for Mode().IsDir() (always false)
|
||||
// (implementing os.FileInfo)
|
||||
func (ef *embeddedFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Sys returns the underlying data source (always nil)
|
||||
// (implementing os.FileInfo)
|
||||
func (ef *embeddedFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
80
vendor/github.com/GeertJohan/go.rice/embedded/embedded.go
generated
vendored
Normal file
80
vendor/github.com/GeertJohan/go.rice/embedded/embedded.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
// Package embedded defines embedded data types that are shared between the go.rice package and generated code.
|
||||
package embedded
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
EmbedTypeGo = 0
|
||||
EmbedTypeSyso = 1
|
||||
)
|
||||
|
||||
// EmbeddedBox defines an embedded box
|
||||
type EmbeddedBox struct {
|
||||
Name string // box name
|
||||
Time time.Time // embed time
|
||||
EmbedType int // kind of embedding
|
||||
Files map[string]*EmbeddedFile // ALL embedded files by full path
|
||||
Dirs map[string]*EmbeddedDir // ALL embedded dirs by full path
|
||||
}
|
||||
|
||||
// Link creates the ChildDirs and ChildFiles links in all EmbeddedDir's
|
||||
func (e *EmbeddedBox) Link() {
|
||||
for path, ed := range e.Dirs {
|
||||
fmt.Println(path)
|
||||
ed.ChildDirs = make([]*EmbeddedDir, 0)
|
||||
ed.ChildFiles = make([]*EmbeddedFile, 0)
|
||||
}
|
||||
for path, ed := range e.Dirs {
|
||||
parentDirpath, _ := filepath.Split(path)
|
||||
if strings.HasSuffix(parentDirpath, "/") {
|
||||
parentDirpath = parentDirpath[:len(parentDirpath)-1]
|
||||
}
|
||||
parentDir := e.Dirs[parentDirpath]
|
||||
if parentDir == nil {
|
||||
panic("parentDir `" + parentDirpath + "` is missing in embedded box")
|
||||
}
|
||||
parentDir.ChildDirs = append(parentDir.ChildDirs, ed)
|
||||
}
|
||||
for path, ef := range e.Files {
|
||||
dirpath, _ := filepath.Split(path)
|
||||
if strings.HasSuffix(dirpath, "/") {
|
||||
dirpath = dirpath[:len(dirpath)-1]
|
||||
}
|
||||
dir := e.Dirs[dirpath]
|
||||
if dir == nil {
|
||||
panic("dir `" + dirpath + "` is missing in embedded box")
|
||||
}
|
||||
dir.ChildFiles = append(dir.ChildFiles, ef)
|
||||
}
|
||||
}
|
||||
|
||||
// EmbeddedDir is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
|
||||
type EmbeddedDir struct {
|
||||
Filename string
|
||||
DirModTime time.Time
|
||||
ChildDirs []*EmbeddedDir // direct childs, as returned by virtualDir.Readdir()
|
||||
ChildFiles []*EmbeddedFile // direct childs, as returned by virtualDir.Readdir()
|
||||
}
|
||||
|
||||
// EmbeddedFile is instanced in the code generated by the rice tool and contains all necicary information about an embedded file
|
||||
type EmbeddedFile struct {
|
||||
Filename string // filename
|
||||
FileModTime time.Time
|
||||
Content string
|
||||
}
|
||||
|
||||
// EmbeddedBoxes is a public register of embedded boxes
|
||||
var EmbeddedBoxes = make(map[string]*EmbeddedBox)
|
||||
|
||||
// RegisterEmbeddedBox registers an EmbeddedBox
|
||||
func RegisterEmbeddedBox(name string, box *EmbeddedBox) {
|
||||
if _, exists := EmbeddedBoxes[name]; exists {
|
||||
panic(fmt.Sprintf("EmbeddedBox with name `%s` exists already", name))
|
||||
}
|
||||
EmbeddedBoxes[name] = box
|
||||
}
|
69
vendor/github.com/GeertJohan/go.rice/example/example.go
generated
vendored
Normal file
69
vendor/github.com/GeertJohan/go.rice/example/example.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"text/template"
|
||||
|
||||
"github.com/GeertJohan/go.rice"
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
)
|
||||
|
||||
func main() {
|
||||
conf := rice.Config{
|
||||
LocateOrder: []rice.LocateMethod{rice.LocateEmbedded, rice.LocateAppended, rice.LocateFS},
|
||||
}
|
||||
box, err := conf.FindBox("example-files")
|
||||
if err != nil {
|
||||
log.Fatalf("error opening rice.Box: %s\n", err)
|
||||
}
|
||||
// spew.Dump(box)
|
||||
|
||||
contentString, err := box.String("file.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("could not read file contents as string: %s\n", err)
|
||||
}
|
||||
log.Printf("Read some file contents as string:\n%s\n", contentString)
|
||||
|
||||
contentBytes, err := box.Bytes("file.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("could not read file contents as byteSlice: %s\n", err)
|
||||
}
|
||||
log.Printf("Read some file contents as byteSlice:\n%s\n", hex.Dump(contentBytes))
|
||||
|
||||
file, err := box.Open("file.txt")
|
||||
if err != nil {
|
||||
log.Fatalf("could not open file: %s\n", err)
|
||||
}
|
||||
spew.Dump(file)
|
||||
|
||||
// find/create a rice.Box
|
||||
templateBox, err := rice.FindBox("example-templates")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// get file contents as string
|
||||
templateString, err := templateBox.String("message.tmpl")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// parse and execute the template
|
||||
tmplMessage, err := template.New("message").Parse(templateString)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tmplMessage.Execute(os.Stdout, map[string]string{"Message": "Hello, world!"})
|
||||
|
||||
http.Handle("/", http.FileServer(box.HTTPBox()))
|
||||
go func() {
|
||||
fmt.Println("Serving files on :8080, press ctrl-C to exit")
|
||||
err := http.ListenAndServe(":8080", nil)
|
||||
if err != nil {
|
||||
log.Fatalf("error serving files: %v", err)
|
||||
}
|
||||
}()
|
||||
select {}
|
||||
}
|
144
vendor/github.com/GeertJohan/go.rice/file.go
generated
vendored
Normal file
144
vendor/github.com/GeertJohan/go.rice/file.go
generated
vendored
Normal file
@ -0,0 +1,144 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// File implements the io.Reader, io.Seeker, io.Closer and http.File interfaces
|
||||
type File struct {
|
||||
// File abstracts file methods so the user doesn't see the difference between rice.virtualFile, rice.virtualDir and os.File
|
||||
// TODO: maybe use internal File interface and four implementations: *os.File, appendedFile, virtualFile, virtualDir
|
||||
|
||||
// real file on disk
|
||||
realF *os.File
|
||||
|
||||
// when embedded (go)
|
||||
virtualF *virtualFile
|
||||
virtualD *virtualDir
|
||||
|
||||
// when appended (zip)
|
||||
appendedF *appendedFile
|
||||
appendedFileReader *bytes.Reader
|
||||
// TODO: is appendedFileReader subject of races? Might need a lock here..
|
||||
}
|
||||
|
||||
// Close is like (*os.File).Close()
|
||||
// Visit http://golang.org/pkg/os/#File.Close for more information
|
||||
func (f *File) Close() error {
|
||||
if f.appendedF != nil {
|
||||
if f.appendedFileReader == nil {
|
||||
return errors.New("already closed")
|
||||
}
|
||||
f.appendedFileReader = nil
|
||||
return nil
|
||||
}
|
||||
if f.virtualF != nil {
|
||||
return f.virtualF.close()
|
||||
}
|
||||
if f.virtualD != nil {
|
||||
return f.virtualD.close()
|
||||
}
|
||||
return f.realF.Close()
|
||||
}
|
||||
|
||||
// Stat is like (*os.File).Stat()
|
||||
// Visit http://golang.org/pkg/os/#File.Stat for more information
|
||||
func (f *File) Stat() (os.FileInfo, error) {
|
||||
if f.appendedF != nil {
|
||||
if f.appendedF.dir {
|
||||
return f.appendedF.dirInfo, nil
|
||||
}
|
||||
if f.appendedFileReader == nil {
|
||||
return nil, errors.New("file is closed")
|
||||
}
|
||||
return f.appendedF.zipFile.FileInfo(), nil
|
||||
}
|
||||
if f.virtualF != nil {
|
||||
return f.virtualF.stat()
|
||||
}
|
||||
if f.virtualD != nil {
|
||||
return f.virtualD.stat()
|
||||
}
|
||||
return f.realF.Stat()
|
||||
}
|
||||
|
||||
// Readdir is like (*os.File).Readdir()
|
||||
// Visit http://golang.org/pkg/os/#File.Readdir for more information
|
||||
func (f *File) Readdir(count int) ([]os.FileInfo, error) {
|
||||
if f.appendedF != nil {
|
||||
if f.appendedF.dir {
|
||||
fi := make([]os.FileInfo, 0, len(f.appendedF.children))
|
||||
for _, childAppendedFile := range f.appendedF.children {
|
||||
if childAppendedFile.dir {
|
||||
fi = append(fi, childAppendedFile.dirInfo)
|
||||
} else {
|
||||
fi = append(fi, childAppendedFile.zipFile.FileInfo())
|
||||
}
|
||||
}
|
||||
return fi, nil
|
||||
}
|
||||
//++ TODO: is os.ErrInvalid the correct error for Readdir on file?
|
||||
return nil, os.ErrInvalid
|
||||
}
|
||||
if f.virtualF != nil {
|
||||
return f.virtualF.readdir(count)
|
||||
}
|
||||
if f.virtualD != nil {
|
||||
return f.virtualD.readdir(count)
|
||||
}
|
||||
return f.realF.Readdir(count)
|
||||
}
|
||||
|
||||
// Read is like (*os.File).Read()
|
||||
// Visit http://golang.org/pkg/os/#File.Read for more information
|
||||
func (f *File) Read(bts []byte) (int, error) {
|
||||
if f.appendedF != nil {
|
||||
if f.appendedFileReader == nil {
|
||||
return 0, &os.PathError{
|
||||
Op: "read",
|
||||
Path: filepath.Base(f.appendedF.zipFile.Name),
|
||||
Err: errors.New("file is closed"),
|
||||
}
|
||||
}
|
||||
if f.appendedF.dir {
|
||||
return 0, &os.PathError{
|
||||
Op: "read",
|
||||
Path: filepath.Base(f.appendedF.zipFile.Name),
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
return f.appendedFileReader.Read(bts)
|
||||
}
|
||||
if f.virtualF != nil {
|
||||
return f.virtualF.read(bts)
|
||||
}
|
||||
if f.virtualD != nil {
|
||||
return f.virtualD.read(bts)
|
||||
}
|
||||
return f.realF.Read(bts)
|
||||
}
|
||||
|
||||
// Seek is like (*os.File).Seek()
|
||||
// Visit http://golang.org/pkg/os/#File.Seek for more information
|
||||
func (f *File) Seek(offset int64, whence int) (int64, error) {
|
||||
if f.appendedF != nil {
|
||||
if f.appendedFileReader == nil {
|
||||
return 0, &os.PathError{
|
||||
Op: "seek",
|
||||
Path: filepath.Base(f.appendedF.zipFile.Name),
|
||||
Err: errors.New("file is closed"),
|
||||
}
|
||||
}
|
||||
return f.appendedFileReader.Seek(offset, whence)
|
||||
}
|
||||
if f.virtualF != nil {
|
||||
return f.virtualF.seek(offset, whence)
|
||||
}
|
||||
if f.virtualD != nil {
|
||||
return f.virtualD.seek(offset, whence)
|
||||
}
|
||||
return f.realF.Seek(offset, whence)
|
||||
}
|
21
vendor/github.com/GeertJohan/go.rice/http.go
generated
vendored
Normal file
21
vendor/github.com/GeertJohan/go.rice/http.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// HTTPBox implements http.FileSystem which allows the use of Box with a http.FileServer.
|
||||
// e.g.: http.Handle("/", http.FileServer(rice.MustFindBox("http-files").HTTPBox()))
|
||||
type HTTPBox struct {
|
||||
*Box
|
||||
}
|
||||
|
||||
// HTTPBox creates a new HTTPBox from an existing Box
|
||||
func (b *Box) HTTPBox() *HTTPBox {
|
||||
return &HTTPBox{b}
|
||||
}
|
||||
|
||||
// Open returns a File using the http.File interface
|
||||
func (hb *HTTPBox) Open(name string) (http.File, error) {
|
||||
return hb.Box.Open(name)
|
||||
}
|
172
vendor/github.com/GeertJohan/go.rice/rice/append.go
generated
vendored
Normal file
172
vendor/github.com/GeertJohan/go.rice/rice/append.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/daaku/go.zipexe"
|
||||
)
|
||||
|
||||
func operationAppend(pkgs []*build.Package) {
|
||||
if runtime.GOOS == "windows" {
|
||||
_, err := exec.LookPath("zip")
|
||||
if err != nil {
|
||||
fmt.Println("#### WARNING ! ####")
|
||||
fmt.Println("`rice append` is known not to work under windows because the `zip` command is not available. Please let me know if you got this to work (and how).")
|
||||
}
|
||||
}
|
||||
|
||||
// MARKED FOR DELETION
|
||||
// This is actually not required, the append command now has the option --exec required.
|
||||
// // check if package is a command
|
||||
// if !pkg.IsCommand() {
|
||||
// fmt.Println("Error: can not append to non-main package. Please follow instructions at github.com/GeertJohan/go.rice")
|
||||
// os.Exit(1)
|
||||
// }
|
||||
|
||||
// create tmp zipfile
|
||||
tmpZipfileName := filepath.Join(os.TempDir(), fmt.Sprintf("ricebox-%d-%s.zip", time.Now().Unix(), randomString(10)))
|
||||
verbosef("Will create tmp zipfile: %s\n", tmpZipfileName)
|
||||
tmpZipfile, err := os.Create(tmpZipfileName)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer func() {
|
||||
tmpZipfile.Close()
|
||||
os.Remove(tmpZipfileName)
|
||||
}()
|
||||
|
||||
// find abs path for binary file
|
||||
binfileName, err := filepath.Abs(flags.Append.Executable)
|
||||
if err != nil {
|
||||
fmt.Printf("Error finding absolute path for executable to append: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
verbosef("Will append to file: %s\n", binfileName)
|
||||
|
||||
// check that command doesn't already have zip appended
|
||||
if rd, _ := zipexe.Open(binfileName); rd != nil {
|
||||
fmt.Printf("Cannot append to already appended executable. Please remove %s and build a fresh one.\n", binfileName)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// open binfile
|
||||
binfile, err := os.OpenFile(binfileName, os.O_WRONLY, os.ModeAppend)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: unable to open executable file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create zip.Writer
|
||||
zipWriter := zip.NewWriter(tmpZipfile)
|
||||
|
||||
for _, pkg := range pkgs {
|
||||
// find boxes for this command
|
||||
boxMap := findBoxes(pkg)
|
||||
|
||||
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
|
||||
if len(boxMap) == 0 {
|
||||
fmt.Printf("no calls to rice.FindBox() or rice.MustFindBox() found in import path `%s`\n", pkg.ImportPath)
|
||||
continue
|
||||
}
|
||||
|
||||
verbosef("\n")
|
||||
|
||||
for boxname := range boxMap {
|
||||
appendedBoxName := strings.Replace(boxname, `/`, `-`, -1)
|
||||
|
||||
// walk box path's and insert files
|
||||
boxPath := filepath.Clean(filepath.Join(pkg.Dir, boxname))
|
||||
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
|
||||
if info == nil {
|
||||
fmt.Printf("Error: box \"%s\" not found on disk\n", path)
|
||||
os.Exit(1)
|
||||
}
|
||||
// create zipFilename
|
||||
zipFileName := filepath.Join(appendedBoxName, strings.TrimPrefix(path, boxPath))
|
||||
// write directories as empty file with comment "dir"
|
||||
if info.IsDir() {
|
||||
_, err := zipWriter.CreateHeader(&zip.FileHeader{
|
||||
Name: zipFileName,
|
||||
Comment: "dir",
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating dir in tmp zip: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// create zipFileWriter
|
||||
zipFileHeader, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating zip FileHeader: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
zipFileHeader.Name = zipFileName
|
||||
zipFileWriter, err := zipWriter.CreateHeader(zipFileHeader)
|
||||
if err != nil {
|
||||
fmt.Printf("Error creating file in tmp zip: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
srcFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error opening file to append: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = io.Copy(zipFileWriter, srcFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error copying file contents to zip: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
srcFile.Close()
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
err = zipWriter.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Error closing tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = tmpZipfile.Sync()
|
||||
if err != nil {
|
||||
fmt.Printf("Error syncing tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = tmpZipfile.Seek(0, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("Error seeking tmp zipfile: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
_, err = binfile.Seek(0, 2)
|
||||
if err != nil {
|
||||
fmt.Printf("Error seeking bin file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
_, err = io.Copy(binfile, tmpZipfile)
|
||||
if err != nil {
|
||||
fmt.Printf("Error appending zipfile to executable: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
zipA := exec.Command("zip", "-A", binfileName)
|
||||
err = zipA.Run()
|
||||
if err != nil {
|
||||
fmt.Printf("Error setting zip offset: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
33
vendor/github.com/GeertJohan/go.rice/rice/clean.go
generated
vendored
Normal file
33
vendor/github.com/GeertJohan/go.rice/rice/clean.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func operationClean(pkg *build.Package) {
|
||||
filepath.Walk(pkg.Dir, func(filename string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Printf("error walking pkg dir to clean files: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
verbosef("checking file '%s'\n", filename)
|
||||
if filepath.Base(filename) == "rice-box.go" ||
|
||||
strings.HasSuffix(filename, ".rice-box.go") ||
|
||||
strings.HasSuffix(filename, ".rice-box.syso") {
|
||||
err := os.Remove(filename)
|
||||
if err != nil {
|
||||
fmt.Printf("error removing file (%s): %s\n", filename, err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
verbosef("removed file '%s'\n", filename)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
158
vendor/github.com/GeertJohan/go.rice/rice/embed-go.go
generated
vendored
Normal file
158
vendor/github.com/GeertJohan/go.rice/rice/embed-go.go
generated
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const boxFilename = "rice-box.go"
|
||||
|
||||
func operationEmbedGo(pkg *build.Package) {
|
||||
|
||||
boxMap := findBoxes(pkg)
|
||||
|
||||
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
|
||||
if len(boxMap) == 0 {
|
||||
fmt.Println("no calls to rice.FindBox() found")
|
||||
return
|
||||
}
|
||||
|
||||
verbosef("\n")
|
||||
var boxes []*boxDataType
|
||||
|
||||
for boxname := range boxMap {
|
||||
// find path and filename for this box
|
||||
boxPath := filepath.Join(pkg.Dir, boxname)
|
||||
|
||||
// Check to see if the path for the box is a symbolic link. If so, simply
|
||||
// box what the symbolic link points to. Note: the filepath.Walk function
|
||||
// will NOT follow any nested symbolic links. This only handles the case
|
||||
// where the root of the box is a symbolic link.
|
||||
symPath, serr := os.Readlink(boxPath)
|
||||
if serr == nil {
|
||||
boxPath = symPath
|
||||
}
|
||||
|
||||
// verbose info
|
||||
verbosef("embedding box '%s' to '%s'\n", boxname, boxFilename)
|
||||
|
||||
// read box metadata
|
||||
boxInfo, ierr := os.Stat(boxPath)
|
||||
if ierr != nil {
|
||||
fmt.Printf("Error: unable to access box at %s\n", boxPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create box datastructure (used by template)
|
||||
box := &boxDataType{
|
||||
BoxName: boxname,
|
||||
UnixNow: boxInfo.ModTime().Unix(),
|
||||
Files: make([]*fileDataType, 0),
|
||||
Dirs: make(map[string]*dirDataType),
|
||||
}
|
||||
|
||||
if !boxInfo.IsDir() {
|
||||
fmt.Printf("Error: Box %s must point to a directory but points to %s instead\n",
|
||||
boxname, boxPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// fill box datastructure with file data
|
||||
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Printf("error walking box: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
filename := strings.TrimPrefix(path, boxPath)
|
||||
filename = strings.Replace(filename, "\\", "/", -1)
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
if info.IsDir() {
|
||||
dirData := &dirDataType{
|
||||
Identifier: "dir" + nextIdentifier(),
|
||||
FileName: filename,
|
||||
ModTime: info.ModTime().Unix(),
|
||||
ChildFiles: make([]*fileDataType, 0),
|
||||
ChildDirs: make([]*dirDataType, 0),
|
||||
}
|
||||
verbosef("\tincludes dir: '%s'\n", dirData.FileName)
|
||||
box.Dirs[dirData.FileName] = dirData
|
||||
|
||||
// add tree entry (skip for root, it'll create a recursion)
|
||||
if dirData.FileName != "" {
|
||||
pathParts := strings.Split(dirData.FileName, "/")
|
||||
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
|
||||
parentDir.ChildDirs = append(parentDir.ChildDirs, dirData)
|
||||
}
|
||||
} else {
|
||||
fileData := &fileDataType{
|
||||
Identifier: "file" + nextIdentifier(),
|
||||
FileName: filename,
|
||||
ModTime: info.ModTime().Unix(),
|
||||
}
|
||||
verbosef("\tincludes file: '%s'\n", fileData.FileName)
|
||||
fileData.Content, err = ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading file content while walking box: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
box.Files = append(box.Files, fileData)
|
||||
|
||||
// add tree entry
|
||||
pathParts := strings.Split(fileData.FileName, "/")
|
||||
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
|
||||
if parentDir == nil {
|
||||
fmt.Printf("Error: parent of %s is not within the box\n", path)
|
||||
os.Exit(1)
|
||||
}
|
||||
parentDir.ChildFiles = append(parentDir.ChildFiles, fileData)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
boxes = append(boxes, box)
|
||||
|
||||
}
|
||||
|
||||
embedSourceUnformated := bytes.NewBuffer(make([]byte, 0))
|
||||
|
||||
// execute template to buffer
|
||||
err := tmplEmbeddedBox.Execute(
|
||||
embedSourceUnformated,
|
||||
embedFileDataType{pkg.Name, boxes},
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("error writing embedded box to file (template execute): %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// format the source code
|
||||
embedSource, err := format.Source(embedSourceUnformated.Bytes())
|
||||
if err != nil {
|
||||
log.Printf("error formatting embedSource: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create go file for box
|
||||
boxFile, err := os.Create(filepath.Join(pkg.Dir, boxFilename))
|
||||
if err != nil {
|
||||
log.Printf("error creating embedded box file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer boxFile.Close()
|
||||
|
||||
// write source to file
|
||||
_, err = io.Copy(boxFile, bytes.NewBuffer(embedSource))
|
||||
if err != nil {
|
||||
log.Printf("error writing embedSource to file: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
204
vendor/github.com/GeertJohan/go.rice/rice/embed-syso.go
generated
vendored
Normal file
204
vendor/github.com/GeertJohan/go.rice/rice/embed-syso.go
generated
vendored
Normal file
@ -0,0 +1,204 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
"github.com/akavel/rsrc/coff"
|
||||
)
|
||||
|
||||
type sizedReader struct {
|
||||
*bytes.Reader
|
||||
}
|
||||
|
||||
func (s sizedReader) Size() int64 {
|
||||
return int64(s.Len())
|
||||
}
|
||||
|
||||
var tmplEmbeddedSysoHelper *template.Template
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
tmplEmbeddedSysoHelper, err = template.New("embeddedSysoHelper").Parse(`package {{.Package}}
|
||||
// ############# GENERATED CODE #####################
|
||||
// ## This file was generated by the rice tool.
|
||||
// ## Do not edit unless you know what you're doing.
|
||||
// ##################################################
|
||||
|
||||
// extern char _bricebox_{{.Symname}}[], _ericebox_{{.Symname}};
|
||||
// int get_{{.Symname}}_length() {
|
||||
// return &_ericebox_{{.Symname}} - _bricebox_{{.Symname}};
|
||||
// }
|
||||
import "C"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/gob"
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ptr := unsafe.Pointer(&C._bricebox_{{.Symname}})
|
||||
bts := C.GoBytes(ptr, C.get_{{.Symname}}_length())
|
||||
embeddedBox := &embedded.EmbeddedBox{}
|
||||
err := gob.NewDecoder(bytes.NewReader(bts)).Decode(embeddedBox)
|
||||
if err != nil {
|
||||
panic("error decoding embedded box: "+err.Error())
|
||||
}
|
||||
embeddedBox.Link()
|
||||
embedded.RegisterEmbeddedBox(embeddedBox.Name, embeddedBox)
|
||||
}`)
|
||||
if err != nil {
|
||||
panic("could not parse template embeddedSysoHelper: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
type embeddedSysoHelperData struct {
|
||||
Package string
|
||||
Symname string
|
||||
}
|
||||
|
||||
func operationEmbedSyso(pkg *build.Package) {
|
||||
|
||||
regexpSynameReplacer := regexp.MustCompile(`[^a-z0-9_]`)
|
||||
|
||||
boxMap := findBoxes(pkg)
|
||||
|
||||
// notify user when no calls to rice.FindBox are made (is this an error and therefore os.Exit(1) ?
|
||||
if len(boxMap) == 0 {
|
||||
fmt.Println("no calls to rice.FindBox() found")
|
||||
return
|
||||
}
|
||||
|
||||
verbosef("\n")
|
||||
|
||||
for boxname := range boxMap {
|
||||
// find path and filename for this box
|
||||
boxPath := filepath.Join(pkg.Dir, boxname)
|
||||
boxFilename := strings.Replace(boxname, "/", "-", -1)
|
||||
boxFilename = strings.Replace(boxFilename, "..", "back", -1)
|
||||
boxFilename = strings.Replace(boxFilename, ".", "-", -1)
|
||||
|
||||
// verbose info
|
||||
verbosef("embedding box '%s'\n", boxname)
|
||||
verbosef("\tto file %s\n", boxFilename)
|
||||
|
||||
// read box metadata
|
||||
boxInfo, ierr := os.Stat(boxPath)
|
||||
if ierr != nil {
|
||||
fmt.Printf("Error: unable to access box at %s\n", boxPath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// create box datastructure (used by template)
|
||||
box := &embedded.EmbeddedBox{
|
||||
Name: boxname,
|
||||
Time: boxInfo.ModTime(),
|
||||
EmbedType: embedded.EmbedTypeSyso,
|
||||
Files: make(map[string]*embedded.EmbeddedFile),
|
||||
Dirs: make(map[string]*embedded.EmbeddedDir),
|
||||
}
|
||||
|
||||
// fill box datastructure with file data
|
||||
filepath.Walk(boxPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Printf("error walking box: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
filename := strings.TrimPrefix(path, boxPath)
|
||||
filename = strings.Replace(filename, "\\", "/", -1)
|
||||
filename = strings.TrimPrefix(filename, "/")
|
||||
if info.IsDir() {
|
||||
embeddedDir := &embedded.EmbeddedDir{
|
||||
Filename: filename,
|
||||
DirModTime: info.ModTime(),
|
||||
}
|
||||
verbosef("\tincludes dir: '%s'\n", embeddedDir.Filename)
|
||||
box.Dirs[embeddedDir.Filename] = embeddedDir
|
||||
|
||||
// add tree entry (skip for root, it'll create a recursion)
|
||||
if embeddedDir.Filename != "" {
|
||||
pathParts := strings.Split(embeddedDir.Filename, "/")
|
||||
parentDir := box.Dirs[strings.Join(pathParts[:len(pathParts)-1], "/")]
|
||||
parentDir.ChildDirs = append(parentDir.ChildDirs, embeddedDir)
|
||||
}
|
||||
} else {
|
||||
embeddedFile := &embedded.EmbeddedFile{
|
||||
Filename: filename,
|
||||
FileModTime: info.ModTime(),
|
||||
Content: "",
|
||||
}
|
||||
verbosef("\tincludes file: '%s'\n", embeddedFile.Filename)
|
||||
contentBytes, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading file content while walking box: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
embeddedFile.Content = string(contentBytes)
|
||||
box.Files[embeddedFile.Filename] = embeddedFile
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// encode embedded box to gob file
|
||||
boxGobBuf := &bytes.Buffer{}
|
||||
err := gob.NewEncoder(boxGobBuf).Encode(box)
|
||||
if err != nil {
|
||||
fmt.Printf("error encoding box to gob: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
verbosef("gob-encoded embeddedBox is %d bytes large\n", boxGobBuf.Len())
|
||||
|
||||
// write coff
|
||||
symname := regexpSynameReplacer.ReplaceAllString(boxname, "_")
|
||||
createCoffSyso(boxname, symname, "386", boxGobBuf.Bytes())
|
||||
createCoffSyso(boxname, symname, "amd64", boxGobBuf.Bytes())
|
||||
|
||||
// write go
|
||||
sysoHelperData := embeddedSysoHelperData{
|
||||
Package: pkg.Name,
|
||||
Symname: symname,
|
||||
}
|
||||
fileSysoHelper, err := os.Create(boxFilename + ".rice-box.go")
|
||||
if err != nil {
|
||||
fmt.Printf("error creating syso helper: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
err = tmplEmbeddedSysoHelper.Execute(fileSysoHelper, sysoHelperData)
|
||||
if err != nil {
|
||||
fmt.Printf("error executing tmplEmbeddedSysoHelper: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func createCoffSyso(boxFilename string, symname string, arch string, data []byte) {
|
||||
boxCoff := coff.NewRDATA()
|
||||
switch arch {
|
||||
case "386":
|
||||
case "amd64":
|
||||
boxCoff.FileHeader.Machine = 0x8664
|
||||
default:
|
||||
panic("invalid arch")
|
||||
}
|
||||
boxCoff.AddData("_bricebox_"+symname, sizedReader{bytes.NewReader(data)})
|
||||
boxCoff.AddData("_ericebox_"+symname, io.NewSectionReader(strings.NewReader("\000\000"), 0, 2)) // TODO: why? copied from rsrc, which copied it from as-generated
|
||||
boxCoff.Freeze()
|
||||
err := writeCoff(boxCoff, boxFilename+"_"+arch+".rice-box.syso")
|
||||
if err != nil {
|
||||
fmt.Printf("error writing %s coff/.syso: %v\n", arch, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
150
vendor/github.com/GeertJohan/go.rice/rice/find.go
generated
vendored
Normal file
150
vendor/github.com/GeertJohan/go.rice/rice/find.go
generated
vendored
Normal file
@ -0,0 +1,150 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/build"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func badArgument(fileset *token.FileSet, p token.Pos) {
|
||||
pos := fileset.Position(p)
|
||||
filename := pos.Filename
|
||||
base, err := os.Getwd()
|
||||
if err == nil {
|
||||
rpath, perr := filepath.Rel(base, pos.Filename)
|
||||
if perr == nil {
|
||||
filename = rpath
|
||||
}
|
||||
}
|
||||
msg := fmt.Sprintf("%s:%d: Error: found call to rice.FindBox, "+
|
||||
"but argument must be a string literal.\n", filename, pos.Line)
|
||||
fmt.Println(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func findBoxes(pkg *build.Package) map[string]bool {
|
||||
// create map of boxes to embed
|
||||
var boxMap = make(map[string]bool)
|
||||
|
||||
// create one list of files for this package
|
||||
filenames := make([]string, 0, len(pkg.GoFiles)+len(pkg.CgoFiles))
|
||||
filenames = append(filenames, pkg.GoFiles...)
|
||||
filenames = append(filenames, pkg.CgoFiles...)
|
||||
|
||||
// loop over files, search for rice.FindBox(..) calls
|
||||
for _, filename := range filenames {
|
||||
// find full filepath
|
||||
fullpath := filepath.Join(pkg.Dir, filename)
|
||||
if strings.HasSuffix(filename, "rice-box.go") {
|
||||
// Ignore *.rice-box.go files
|
||||
verbosef("skipping file %q\n", fullpath)
|
||||
continue
|
||||
}
|
||||
verbosef("scanning file %q\n", fullpath)
|
||||
|
||||
fset := token.NewFileSet()
|
||||
f, err := parser.ParseFile(fset, fullpath, nil, 0)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var riceIsImported bool
|
||||
ricePkgName := "rice"
|
||||
for _, imp := range f.Imports {
|
||||
if strings.HasSuffix(imp.Path.Value, "go.rice\"") {
|
||||
if imp.Name != nil {
|
||||
ricePkgName = imp.Name.Name
|
||||
}
|
||||
riceIsImported = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !riceIsImported {
|
||||
// Rice wasn't imported, so we won't find a box.
|
||||
continue
|
||||
}
|
||||
if ricePkgName == "_" {
|
||||
// Rice pkg is unnamed, so we won't find a box.
|
||||
continue
|
||||
}
|
||||
|
||||
// Inspect AST, looking for calls to (Must)?FindBox.
|
||||
// First parameter of the func must be a basic literal.
|
||||
// Identifiers won't be resolved.
|
||||
var nextIdentIsBoxFunc bool
|
||||
var nextBasicLitParamIsBoxName bool
|
||||
var boxCall token.Pos
|
||||
var variableToRemember string
|
||||
var validVariablesForBoxes map[string]bool = make(map[string]bool)
|
||||
|
||||
ast.Inspect(f, func(node ast.Node) bool {
|
||||
if node == nil {
|
||||
return false
|
||||
}
|
||||
switch x := node.(type) {
|
||||
// this case fixes the var := func() style assignments, not assignments to vars declared separately from the assignment.
|
||||
case *ast.AssignStmt:
|
||||
var assign = node.(*ast.AssignStmt)
|
||||
name, found := assign.Lhs[0].(*ast.Ident)
|
||||
if found {
|
||||
variableToRemember = name.Name
|
||||
composite, first := assign.Rhs[0].(*ast.CompositeLit)
|
||||
if first {
|
||||
riceSelector, second := composite.Type.(*ast.SelectorExpr)
|
||||
|
||||
if second {
|
||||
callCorrect := riceSelector.Sel.Name == "Config"
|
||||
packageName, third := riceSelector.X.(*ast.Ident)
|
||||
|
||||
if third && callCorrect && packageName.Name == ricePkgName {
|
||||
validVariablesForBoxes[name.Name] = true
|
||||
verbosef("\tfound variable, saving to scan for boxes: %q\n", name.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case *ast.Ident:
|
||||
if nextIdentIsBoxFunc || ricePkgName == "." {
|
||||
nextIdentIsBoxFunc = false
|
||||
if x.Name == "FindBox" || x.Name == "MustFindBox" {
|
||||
nextBasicLitParamIsBoxName = true
|
||||
boxCall = x.Pos()
|
||||
}
|
||||
} else {
|
||||
if x.Name == ricePkgName || validVariablesForBoxes[x.Name] {
|
||||
nextIdentIsBoxFunc = true
|
||||
}
|
||||
}
|
||||
case *ast.BasicLit:
|
||||
if nextBasicLitParamIsBoxName {
|
||||
if x.Kind == token.STRING {
|
||||
nextBasicLitParamIsBoxName = false
|
||||
// trim "" or ``
|
||||
name := x.Value[1 : len(x.Value)-1]
|
||||
boxMap[name] = true
|
||||
verbosef("\tfound box %q\n", name)
|
||||
} else {
|
||||
badArgument(fset, boxCall)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
if nextIdentIsBoxFunc {
|
||||
nextIdentIsBoxFunc = false
|
||||
}
|
||||
if nextBasicLitParamIsBoxName {
|
||||
badArgument(fset, boxCall)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
return boxMap
|
||||
}
|
80
vendor/github.com/GeertJohan/go.rice/rice/flags.go
generated
vendored
Normal file
80
vendor/github.com/GeertJohan/go.rice/rice/flags.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
|
||||
goflags "github.com/jessevdk/go-flags" // rename import to `goflags` (file scope) so we can use `var flags` (package scope)
|
||||
)
|
||||
|
||||
// flags
|
||||
var flags struct {
|
||||
Verbose bool `long:"verbose" short:"v" description:"Show verbose debug information"`
|
||||
ImportPaths []string `long:"import-path" short:"i" description:"Import path(s) to use. Using PWD when left empty. Specify multiple times for more import paths to append"`
|
||||
|
||||
Append struct {
|
||||
Executable string `long:"exec" description:"Executable to append" required:"true"`
|
||||
} `command:"append"`
|
||||
|
||||
EmbedGo struct{} `command:"embed-go" alias:"embed"`
|
||||
EmbedSyso struct{} `command:"embed-syso"`
|
||||
Clean struct{} `command:"clean"`
|
||||
}
|
||||
|
||||
// flags parser
|
||||
var flagsParser *goflags.Parser
|
||||
|
||||
// initFlags parses the given flags.
|
||||
// when the user asks for help (-h or --help): the application exists with status 0
|
||||
// when unexpected flags is given: the application exits with status 1
|
||||
func parseArguments() {
|
||||
// create flags parser in global var, for flagsParser.Active.Name (operation)
|
||||
flagsParser = goflags.NewParser(&flags, goflags.Default)
|
||||
|
||||
// parse flags
|
||||
args, err := flagsParser.Parse()
|
||||
if err != nil {
|
||||
// assert the err to be a flags.Error
|
||||
flagError := err.(*goflags.Error)
|
||||
if flagError.Type == goflags.ErrHelp {
|
||||
// user asked for help on flags.
|
||||
// program can exit successfully
|
||||
os.Exit(0)
|
||||
}
|
||||
if flagError.Type == goflags.ErrUnknownFlag {
|
||||
fmt.Println("Use --help to view available options.")
|
||||
os.Exit(1)
|
||||
}
|
||||
if flagError.Type == goflags.ErrRequired {
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Printf("Error parsing flags: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// error on left-over arguments
|
||||
if len(args) > 0 {
|
||||
fmt.Printf("Unexpected arguments: %s\nUse --help to view available options.", args)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// default ImportPath to pwd when not set
|
||||
if len(flags.ImportPaths) == 0 {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("error getting pwd: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
verbosef("using pwd as import path\n")
|
||||
// find non-absolute path for this pwd
|
||||
pkg, err := build.ImportDir(pwd, build.FindOnly)
|
||||
if err != nil {
|
||||
fmt.Printf("error using current directory as import path: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
flags.ImportPaths = append(flags.ImportPaths, pkg.ImportPath)
|
||||
verbosef("using import paths: %s\n", flags.ImportPaths)
|
||||
return
|
||||
}
|
||||
}
|
14
vendor/github.com/GeertJohan/go.rice/rice/identifier.go
generated
vendored
Normal file
14
vendor/github.com/GeertJohan/go.rice/rice/identifier.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/GeertJohan/go.incremental"
|
||||
)
|
||||
|
||||
var identifierCount incremental.Uint64
|
||||
|
||||
func nextIdentifier() string {
|
||||
num := identifierCount.Next()
|
||||
return strconv.FormatUint(num, 36) // 0123456789abcdefghijklmnopqrstuvwxyz
|
||||
}
|
68
vendor/github.com/GeertJohan/go.rice/rice/main.go
generated
vendored
Normal file
68
vendor/github.com/GeertJohan/go.rice/rice/main.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// parser arguments
|
||||
parseArguments()
|
||||
|
||||
// find package for path
|
||||
var pkgs []*build.Package
|
||||
for _, importPath := range flags.ImportPaths {
|
||||
pkg := pkgForPath(importPath)
|
||||
pkgs = append(pkgs, pkg)
|
||||
}
|
||||
|
||||
// switch on the operation to perform
|
||||
switch flagsParser.Active.Name {
|
||||
case "embed", "embed-go":
|
||||
for _, pkg := range pkgs {
|
||||
operationEmbedGo(pkg)
|
||||
}
|
||||
case "embed-syso":
|
||||
log.Println("WARNING: embedding .syso is experimental..")
|
||||
for _, pkg := range pkgs {
|
||||
operationEmbedSyso(pkg)
|
||||
}
|
||||
case "append":
|
||||
operationAppend(pkgs)
|
||||
case "clean":
|
||||
for _, pkg := range pkgs {
|
||||
operationClean(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
// all done
|
||||
verbosef("\n")
|
||||
verbosef("rice finished successfully\n")
|
||||
}
|
||||
|
||||
// helper function to get *build.Package for given path
|
||||
func pkgForPath(path string) *build.Package {
|
||||
// get pwd for relative imports
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
fmt.Printf("error getting pwd (required for relative imports): %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// read full package information
|
||||
pkg, err := build.Import(path, pwd, 0)
|
||||
if err != nil {
|
||||
fmt.Printf("error reading package: %s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
return pkg
|
||||
}
|
||||
|
||||
func verbosef(format string, stuff ...interface{}) {
|
||||
if flags.Verbose {
|
||||
log.Printf(format, stuff...)
|
||||
}
|
||||
}
|
98
vendor/github.com/GeertJohan/go.rice/rice/templates.go
generated
vendored
Normal file
98
vendor/github.com/GeertJohan/go.rice/rice/templates.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var tmplEmbeddedBox *template.Template
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
// parse embedded box template
|
||||
tmplEmbeddedBox, err = template.New("embeddedBox").Parse(`package {{.Package}}
|
||||
|
||||
import (
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
"time"
|
||||
)
|
||||
|
||||
{{range .Boxes}}
|
||||
func init() {
|
||||
|
||||
// define files
|
||||
{{range .Files}}{{.Identifier}} := &embedded.EmbeddedFile{
|
||||
Filename: ` + "`" + `{{.FileName}}` + "`" + `,
|
||||
FileModTime: time.Unix({{.ModTime}}, 0),
|
||||
Content: string({{.Content | printf "%q"}}),
|
||||
}
|
||||
{{end}}
|
||||
|
||||
// define dirs
|
||||
{{range .Dirs}}{{.Identifier}} := &embedded.EmbeddedDir{
|
||||
Filename: ` + "`" + `{{.FileName}}` + "`" + `,
|
||||
DirModTime: time.Unix({{.ModTime}}, 0),
|
||||
ChildFiles: []*embedded.EmbeddedFile{
|
||||
{{range .ChildFiles}}{{.Identifier}}, // {{.FileName}}
|
||||
{{end}}
|
||||
},
|
||||
}
|
||||
{{end}}
|
||||
|
||||
// link ChildDirs
|
||||
{{range .Dirs}}{{.Identifier}}.ChildDirs = []*embedded.EmbeddedDir{
|
||||
{{range .ChildDirs}}{{.Identifier}}, // {{.FileName}}
|
||||
{{end}}
|
||||
}
|
||||
{{end}}
|
||||
|
||||
// register embeddedBox
|
||||
embedded.RegisterEmbeddedBox(` + "`" + `{{.BoxName}}` + "`" + `, &embedded.EmbeddedBox{
|
||||
Name: ` + "`" + `{{.BoxName}}` + "`" + `,
|
||||
Time: time.Unix({{.UnixNow}}, 0),
|
||||
Dirs: map[string]*embedded.EmbeddedDir{
|
||||
{{range .Dirs}}"{{.FileName}}": {{.Identifier}},
|
||||
{{end}}
|
||||
},
|
||||
Files: map[string]*embedded.EmbeddedFile{
|
||||
{{range .Files}}"{{.FileName}}": {{.Identifier}},
|
||||
{{end}}
|
||||
},
|
||||
})
|
||||
}
|
||||
{{end}}`)
|
||||
if err != nil {
|
||||
fmt.Printf("error parsing embedded box template: %s\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
type embedFileDataType struct {
|
||||
Package string
|
||||
Boxes []*boxDataType
|
||||
}
|
||||
|
||||
type boxDataType struct {
|
||||
BoxName string
|
||||
UnixNow int64
|
||||
Files []*fileDataType
|
||||
Dirs map[string]*dirDataType
|
||||
}
|
||||
|
||||
type fileDataType struct {
|
||||
Identifier string
|
||||
FileName string
|
||||
Content []byte
|
||||
ModTime int64
|
||||
}
|
||||
|
||||
type dirDataType struct {
|
||||
Identifier string
|
||||
FileName string
|
||||
Content []byte
|
||||
ModTime int64
|
||||
ChildDirs []*dirDataType
|
||||
ChildFiles []*fileDataType
|
||||
}
|
22
vendor/github.com/GeertJohan/go.rice/rice/util.go
generated
vendored
Normal file
22
vendor/github.com/GeertJohan/go.rice/rice/util.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// randomString generates a pseudo-random alpha-numeric string with given length.
|
||||
func randomString(length int) string {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
k := make([]rune, length)
|
||||
for i := 0; i < length; i++ {
|
||||
c := rand.Intn(35)
|
||||
if c < 10 {
|
||||
c += 48 // numbers (0-9) (0+48 == 48 == '0', 9+48 == 57 == '9')
|
||||
} else {
|
||||
c += 87 // lower case alphabets (a-z) (10+87 == 97 == 'a', 35+87 == 122 = 'z')
|
||||
}
|
||||
k[i] = rune(c)
|
||||
}
|
||||
return string(k)
|
||||
}
|
42
vendor/github.com/GeertJohan/go.rice/rice/writecoff.go
generated
vendored
Normal file
42
vendor/github.com/GeertJohan/go.rice/rice/writecoff.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
|
||||
"github.com/akavel/rsrc/binutil"
|
||||
"github.com/akavel/rsrc/coff"
|
||||
)
|
||||
|
||||
// copied from github.com/akavel/rsrc
|
||||
// LICENSE: MIT
|
||||
// Copyright 2013-2014 The rsrc Authors. (https://github.com/akavel/rsrc/blob/master/AUTHORS)
|
||||
func writeCoff(coff *coff.Coff, fnameout string) error {
|
||||
out, err := os.Create(fnameout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
w := binutil.Writer{W: out}
|
||||
|
||||
// write the resulting file to disk
|
||||
binutil.Walk(coff, func(v reflect.Value, path string) error {
|
||||
if binutil.Plain(v.Kind()) {
|
||||
w.WriteLE(v.Interface())
|
||||
return nil
|
||||
}
|
||||
vv, ok := v.Interface().(binutil.SizedReader)
|
||||
if ok {
|
||||
w.WriteFromSized(vv)
|
||||
return binutil.WALK_SKIP
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if w.Err != nil {
|
||||
return fmt.Errorf("Error writing output file: %s", w.Err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
19
vendor/github.com/GeertJohan/go.rice/sort.go
generated
vendored
Normal file
19
vendor/github.com/GeertJohan/go.rice/sort.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
package rice
|
||||
|
||||
import "os"
|
||||
|
||||
// SortByName allows an array of os.FileInfo objects
|
||||
// to be easily sorted by filename using sort.Sort(SortByName(array))
|
||||
type SortByName []os.FileInfo
|
||||
|
||||
func (f SortByName) Len() int { return len(f) }
|
||||
func (f SortByName) Less(i, j int) bool { return f[i].Name() < f[j].Name() }
|
||||
func (f SortByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
||||
|
||||
// SortByModified allows an array of os.FileInfo objects
|
||||
// to be easily sorted by modified date using sort.Sort(SortByModified(array))
|
||||
type SortByModified []os.FileInfo
|
||||
|
||||
func (f SortByModified) Len() int { return len(f) }
|
||||
func (f SortByModified) Less(i, j int) bool { return f[i].ModTime().Unix() > f[j].ModTime().Unix() }
|
||||
func (f SortByModified) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
252
vendor/github.com/GeertJohan/go.rice/virtual.go
generated
vendored
Normal file
252
vendor/github.com/GeertJohan/go.rice/virtual.go
generated
vendored
Normal file
@ -0,0 +1,252 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/GeertJohan/go.rice/embedded"
|
||||
)
|
||||
|
||||
//++ TODO: IDEA: merge virtualFile and virtualDir, this decreases work done by rice.File
|
||||
|
||||
// Error indicating some function is not implemented yet (but available to satisfy an interface)
|
||||
var ErrNotImplemented = errors.New("not implemented yet")
|
||||
|
||||
// virtualFile is a 'stateful' virtual file.
|
||||
// virtualFile wraps an *EmbeddedFile for a call to Box.Open() and virtualizes 'read cursor' (offset) and 'closing'.
|
||||
// virtualFile is only internally visible and should be exposed through rice.File
|
||||
type virtualFile struct {
|
||||
*embedded.EmbeddedFile // the actual embedded file, embedded to obtain methods
|
||||
offset int64 // read position on the virtual file
|
||||
closed bool // closed when true
|
||||
}
|
||||
|
||||
// create a new virtualFile for given EmbeddedFile
|
||||
func newVirtualFile(ef *embedded.EmbeddedFile) *virtualFile {
|
||||
vf := &virtualFile{
|
||||
EmbeddedFile: ef,
|
||||
offset: 0,
|
||||
closed: false,
|
||||
}
|
||||
return vf
|
||||
}
|
||||
|
||||
//++ TODO check for nil pointers in all these methods. When so: return os.PathError with Err: os.ErrInvalid
|
||||
|
||||
func (vf *virtualFile) close() error {
|
||||
if vf.closed {
|
||||
return &os.PathError{
|
||||
Op: "close",
|
||||
Path: vf.EmbeddedFile.Filename,
|
||||
Err: errors.New("already closed"),
|
||||
}
|
||||
}
|
||||
vf.EmbeddedFile = nil
|
||||
vf.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vf *virtualFile) stat() (os.FileInfo, error) {
|
||||
if vf.closed {
|
||||
return nil, &os.PathError{
|
||||
Op: "stat",
|
||||
Path: vf.EmbeddedFile.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
return (*embeddedFileInfo)(vf.EmbeddedFile), nil
|
||||
}
|
||||
|
||||
func (vf *virtualFile) readdir(count int) ([]os.FileInfo, error) {
|
||||
if vf.closed {
|
||||
return nil, &os.PathError{
|
||||
Op: "readdir",
|
||||
Path: vf.EmbeddedFile.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
//TODO: return proper error for a readdir() call on a file
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
|
||||
func (vf *virtualFile) read(bts []byte) (int, error) {
|
||||
if vf.closed {
|
||||
return 0, &os.PathError{
|
||||
Op: "read",
|
||||
Path: vf.EmbeddedFile.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
|
||||
end := vf.offset + int64(len(bts))
|
||||
|
||||
if end >= int64(len(vf.Content)) {
|
||||
// end of file, so return what we have + EOF
|
||||
n := copy(bts, vf.Content[vf.offset:])
|
||||
vf.offset = 0
|
||||
return n, io.EOF
|
||||
}
|
||||
|
||||
n := copy(bts, vf.Content[vf.offset:end])
|
||||
vf.offset += int64(n)
|
||||
return n, nil
|
||||
|
||||
}
|
||||
|
||||
func (vf *virtualFile) seek(offset int64, whence int) (int64, error) {
|
||||
if vf.closed {
|
||||
return 0, &os.PathError{
|
||||
Op: "seek",
|
||||
Path: vf.EmbeddedFile.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
var e error
|
||||
|
||||
//++ TODO: check if this is correct implementation for seek
|
||||
switch whence {
|
||||
case os.SEEK_SET:
|
||||
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
|
||||
vf.offset = offset
|
||||
case os.SEEK_CUR:
|
||||
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
|
||||
vf.offset += offset
|
||||
case os.SEEK_END:
|
||||
//++ check if new offset isn't out of bounds, set e when it is, then break out of switch
|
||||
vf.offset = int64(len(vf.EmbeddedFile.Content)) - offset
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
return 0, &os.PathError{
|
||||
Op: "seek",
|
||||
Path: vf.Filename,
|
||||
Err: e,
|
||||
}
|
||||
}
|
||||
|
||||
return vf.offset, nil
|
||||
}
|
||||
|
||||
// virtualDir is a 'stateful' virtual directory.
|
||||
// virtualDir wraps an *EmbeddedDir for a call to Box.Open() and virtualizes 'closing'.
|
||||
// virtualDir is only internally visible and should be exposed through rice.File
|
||||
type virtualDir struct {
|
||||
*embedded.EmbeddedDir
|
||||
offset int // readdir position on the directory
|
||||
closed bool
|
||||
}
|
||||
|
||||
// create a new virtualDir for given EmbeddedDir
|
||||
func newVirtualDir(ed *embedded.EmbeddedDir) *virtualDir {
|
||||
vd := &virtualDir{
|
||||
EmbeddedDir: ed,
|
||||
offset: 0,
|
||||
closed: false,
|
||||
}
|
||||
return vd
|
||||
}
|
||||
|
||||
func (vd *virtualDir) close() error {
|
||||
//++ TODO: needs sync mutex?
|
||||
if vd.closed {
|
||||
return &os.PathError{
|
||||
Op: "close",
|
||||
Path: vd.EmbeddedDir.Filename,
|
||||
Err: errors.New("already closed"),
|
||||
}
|
||||
}
|
||||
vd.closed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (vd *virtualDir) stat() (os.FileInfo, error) {
|
||||
if vd.closed {
|
||||
return nil, &os.PathError{
|
||||
Op: "stat",
|
||||
Path: vd.EmbeddedDir.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
return (*embeddedDirInfo)(vd.EmbeddedDir), nil
|
||||
}
|
||||
|
||||
func (vd *virtualDir) readdir(n int) (fi []os.FileInfo, err error) {
|
||||
|
||||
if vd.closed {
|
||||
return nil, &os.PathError{
|
||||
Op: "readdir",
|
||||
Path: vd.EmbeddedDir.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
|
||||
// Build up the array of our contents
|
||||
var files []os.FileInfo
|
||||
|
||||
// Add the child directories
|
||||
for _, child := range vd.ChildDirs {
|
||||
child.Filename = filepath.Base(child.Filename)
|
||||
files = append(files, (*embeddedDirInfo)(child))
|
||||
}
|
||||
|
||||
// Add the child files
|
||||
for _, child := range vd.ChildFiles {
|
||||
child.Filename = filepath.Base(child.Filename)
|
||||
files = append(files, (*embeddedFileInfo)(child))
|
||||
}
|
||||
|
||||
// Sort it by filename (lexical order)
|
||||
sort.Sort(SortByName(files))
|
||||
|
||||
// Return all contents if that's what is requested
|
||||
if n <= 0 {
|
||||
vd.offset = 0
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// If user has requested past the end of our list
|
||||
// return what we can and send an EOF
|
||||
if vd.offset+n >= len(files) {
|
||||
offset := vd.offset
|
||||
vd.offset = 0
|
||||
return files[offset:], io.EOF
|
||||
}
|
||||
|
||||
offset := vd.offset
|
||||
vd.offset += n
|
||||
return files[offset : offset+n], nil
|
||||
|
||||
}
|
||||
|
||||
func (vd *virtualDir) read(bts []byte) (int, error) {
|
||||
if vd.closed {
|
||||
return 0, &os.PathError{
|
||||
Op: "read",
|
||||
Path: vd.EmbeddedDir.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
return 0, &os.PathError{
|
||||
Op: "read",
|
||||
Path: vd.EmbeddedDir.Filename,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
||||
|
||||
func (vd *virtualDir) seek(offset int64, whence int) (int64, error) {
|
||||
if vd.closed {
|
||||
return 0, &os.PathError{
|
||||
Op: "seek",
|
||||
Path: vd.EmbeddedDir.Filename,
|
||||
Err: errors.New("bad file descriptor"),
|
||||
}
|
||||
}
|
||||
return 0, &os.PathError{
|
||||
Op: "seek",
|
||||
Path: vd.Filename,
|
||||
Err: errors.New("is a directory"),
|
||||
}
|
||||
}
|
122
vendor/github.com/GeertJohan/go.rice/walk.go
generated
vendored
Normal file
122
vendor/github.com/GeertJohan/go.rice/walk.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
package rice
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Walk is like filepath.Walk()
|
||||
// Visit http://golang.org/pkg/path/filepath/#Walk for more information
|
||||
func (b *Box) Walk(path string, walkFn filepath.WalkFunc) error {
|
||||
|
||||
pathFile, err := b.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer pathFile.Close()
|
||||
|
||||
pathInfo, err := pathFile.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if b.IsAppended() || b.IsEmbedded() {
|
||||
return b.walk(path, pathInfo, walkFn)
|
||||
}
|
||||
|
||||
// We don't have any embedded or appended box so use live filesystem mode
|
||||
return filepath.Walk(b.absolutePath+string(os.PathSeparator)+path, func(path string, info os.FileInfo, err error) error {
|
||||
|
||||
// Strip out the box name from the returned paths
|
||||
path = strings.TrimPrefix(path, b.absolutePath+string(os.PathSeparator))
|
||||
return walkFn(path, info, err)
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// walk recursively descends path.
|
||||
// See walk() in $GOROOT/src/pkg/path/filepath/path.go
|
||||
func (b *Box) walk(path string, info os.FileInfo, walkFn filepath.WalkFunc) error {
|
||||
|
||||
err := walkFn(path, info, nil)
|
||||
if err != nil {
|
||||
if info.IsDir() && err == filepath.SkipDir {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
names, err := b.readDirNames(path)
|
||||
if err != nil {
|
||||
return walkFn(path, info, err)
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
|
||||
filename := filepath.Join(path, name)
|
||||
fileObject, err := b.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileObject.Close()
|
||||
|
||||
fileInfo, err := fileObject.Stat()
|
||||
if err != nil {
|
||||
if err := walkFn(filename, fileInfo, err); err != nil && err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = b.walk(filename, fileInfo, walkFn)
|
||||
if err != nil {
|
||||
if !fileInfo.IsDir() || err != filepath.SkipDir {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
// readDirNames reads the directory named by path and returns a sorted list of directory entries.
|
||||
// See readDirNames() in $GOROOT/pkg/path/filepath/path.go
|
||||
func (b *Box) readDirNames(path string) ([]string, error) {
|
||||
|
||||
f, err := b.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
infos, err := f.Readdir(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var names []string
|
||||
|
||||
for _, info := range infos {
|
||||
names = append(names, info.Name())
|
||||
}
|
||||
|
||||
sort.Strings(names)
|
||||
return names, nil
|
||||
|
||||
}
|
144
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
144
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
@ -13,13 +13,10 @@
|
||||
// Package discordgo provides Discord binding for Go
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
|
||||
const VERSION = "0.13.0"
|
||||
const VERSION = "0.15.0"
|
||||
|
||||
// New creates a new Discord session and will automate some startup
|
||||
// tasks if given enough information to do so. Currently you can pass zero
|
||||
@ -27,6 +24,8 @@ const VERSION = "0.13.0"
|
||||
// There are 3 ways to call New:
|
||||
// With a single auth token - All requests will use the token blindly,
|
||||
// no verification of the token will be done and requests may fail.
|
||||
// IF THE TOKEN IS FOR A BOT, IT MUST BE PREFIXED WITH `BOT `
|
||||
// eg: `"Bot <token>"`
|
||||
// With an email and password - Discord will sign in with the provided
|
||||
// credentials.
|
||||
// With an email, password and auth token - Discord will verify the auth
|
||||
@ -37,11 +36,13 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||
// Create an empty Session interface.
|
||||
s = &Session{
|
||||
State: NewState(),
|
||||
ratelimiter: NewRatelimiter(),
|
||||
StateEnabled: true,
|
||||
Compress: true,
|
||||
ShouldReconnectOnError: true,
|
||||
ShardID: 0,
|
||||
ShardCount: 1,
|
||||
MaxRestRetries: 3,
|
||||
}
|
||||
|
||||
// If no arguments are passed return the empty Session interface.
|
||||
@ -122,136 +123,3 @@ func New(args ...interface{}) (s *Session, err error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validateHandler takes an event handler func, and returns the type of event.
|
||||
// eg.
|
||||
// Session.validateHandler(func (s *discordgo.Session, m *discordgo.MessageCreate))
|
||||
// will return the reflect.Type of *discordgo.MessageCreate
|
||||
func (s *Session) validateHandler(handler interface{}) reflect.Type {
|
||||
|
||||
handlerType := reflect.TypeOf(handler)
|
||||
|
||||
if handlerType.NumIn() != 2 {
|
||||
panic("Unable to add event handler, handler must be of the type func(*discordgo.Session, *discordgo.EventType).")
|
||||
}
|
||||
|
||||
if handlerType.In(0) != reflect.TypeOf(s) {
|
||||
panic("Unable to add event handler, first argument must be of type *discordgo.Session.")
|
||||
}
|
||||
|
||||
eventType := handlerType.In(1)
|
||||
|
||||
// Support handlers of type interface{}, this is a special handler, which is triggered on every event.
|
||||
if eventType.Kind() == reflect.Interface {
|
||||
eventType = nil
|
||||
}
|
||||
|
||||
return eventType
|
||||
}
|
||||
|
||||
// AddHandler allows you to add an event handler that will be fired anytime
|
||||
// the Discord WSAPI event that matches the interface fires.
|
||||
// eventToInterface in events.go has a list of all the Discord WSAPI events
|
||||
// and their respective interface.
|
||||
// eg:
|
||||
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
// })
|
||||
//
|
||||
// or:
|
||||
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
|
||||
// })
|
||||
// The return value of this method is a function, that when called will remove the
|
||||
// event handler.
|
||||
func (s *Session) AddHandler(handler interface{}) func() {
|
||||
|
||||
s.initialize()
|
||||
|
||||
eventType := s.validateHandler(handler)
|
||||
|
||||
s.handlersMu.Lock()
|
||||
defer s.handlersMu.Unlock()
|
||||
|
||||
h := reflect.ValueOf(handler)
|
||||
|
||||
s.handlers[eventType] = append(s.handlers[eventType], h)
|
||||
|
||||
// This must be done as we need a consistent reference to the
|
||||
// reflected value, otherwise a RemoveHandler method would have
|
||||
// been nice.
|
||||
return func() {
|
||||
s.handlersMu.Lock()
|
||||
defer s.handlersMu.Unlock()
|
||||
|
||||
handlers := s.handlers[eventType]
|
||||
for i, v := range handlers {
|
||||
if h == v {
|
||||
s.handlers[eventType] = append(handlers[:i], handlers[i+1:]...)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle calls any handlers that match the event type and any handlers of
|
||||
// interface{}.
|
||||
func (s *Session) handle(event interface{}) {
|
||||
|
||||
s.handlersMu.RLock()
|
||||
defer s.handlersMu.RUnlock()
|
||||
|
||||
if s.handlers == nil {
|
||||
return
|
||||
}
|
||||
|
||||
handlerParameters := []reflect.Value{reflect.ValueOf(s), reflect.ValueOf(event)}
|
||||
|
||||
if handlers, ok := s.handlers[nil]; ok {
|
||||
for _, handler := range handlers {
|
||||
go handler.Call(handlerParameters)
|
||||
}
|
||||
}
|
||||
|
||||
if handlers, ok := s.handlers[reflect.TypeOf(event)]; ok {
|
||||
for _, handler := range handlers {
|
||||
go handler.Call(handlerParameters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize adds all internal handlers and state tracking handlers.
|
||||
func (s *Session) initialize() {
|
||||
|
||||
s.log(LogInformational, "called")
|
||||
|
||||
s.handlersMu.Lock()
|
||||
if s.handlers != nil {
|
||||
s.handlersMu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
s.handlers = map[interface{}][]reflect.Value{}
|
||||
s.handlersMu.Unlock()
|
||||
|
||||
s.AddHandler(s.onReady)
|
||||
s.AddHandler(s.onResumed)
|
||||
s.AddHandler(s.onVoiceServerUpdate)
|
||||
s.AddHandler(s.onVoiceStateUpdate)
|
||||
s.AddHandler(s.State.onInterface)
|
||||
}
|
||||
|
||||
// onReady handles the ready event.
|
||||
func (s *Session) onReady(se *Session, r *Ready) {
|
||||
|
||||
// Store the SessionID within the Session struct.
|
||||
s.sessionID = r.SessionID
|
||||
|
||||
// Start the heartbeat to keep the connection alive.
|
||||
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
|
||||
}
|
||||
|
||||
// onResumed handles the resumed event.
|
||||
func (s *Session) onResumed(se *Session, r *Resumed) {
|
||||
|
||||
// Start the heartbeat to keep the connection alive.
|
||||
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
|
||||
}
|
||||
|
18
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
18
vendor/github.com/bwmarrin/discordgo/endpoints.go
generated
vendored
@ -24,6 +24,7 @@ var (
|
||||
EndpointChannels = EndpointAPI + "channels/"
|
||||
EndpointUsers = EndpointAPI + "users/"
|
||||
EndpointGateway = EndpointAPI + "gateway"
|
||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
||||
|
||||
EndpointAuth = EndpointAPI + "auth/"
|
||||
EndpointLogin = EndpointAuth + "login"
|
||||
@ -61,6 +62,7 @@ var (
|
||||
EndpointGuildChannels = func(gID string) string { return EndpointGuilds + gID + "/channels" }
|
||||
EndpointGuildMembers = func(gID string) string { return EndpointGuilds + gID + "/members" }
|
||||
EndpointGuildMember = func(gID, uID string) string { return EndpointGuilds + gID + "/members/" + uID }
|
||||
EndpointGuildMemberRole = func(gID, uID, rID string) string { return EndpointGuilds + gID + "/members/" + uID + "/roles/" + rID }
|
||||
EndpointGuildBans = func(gID string) string { return EndpointGuilds + gID + "/bans" }
|
||||
EndpointGuildBan = func(gID, uID string) string { return EndpointGuilds + gID + "/bans/" + uID }
|
||||
EndpointGuildIntegrations = func(gID string) string { return EndpointGuilds + gID + "/integrations" }
|
||||
@ -73,6 +75,7 @@ var (
|
||||
EndpointGuildPrune = func(gID string) string { return EndpointGuilds + gID + "/prune" }
|
||||
EndpointGuildIcon = func(gID, hash string) string { return EndpointGuilds + gID + "/icons/" + hash + ".jpg" }
|
||||
EndpointGuildSplash = func(gID, hash string) string { return EndpointGuilds + gID + "/splashes/" + hash + ".jpg" }
|
||||
EndpointGuildWebhooks = func(gID string) string { return EndpointGuilds + gID + "/webhooks" }
|
||||
|
||||
EndpointChannel = func(cID string) string { return EndpointChannels + cID }
|
||||
EndpointChannelPermissions = func(cID string) string { return EndpointChannels + cID + "/permissions" }
|
||||
@ -86,6 +89,21 @@ var (
|
||||
EndpointChannelMessagesPins = func(cID string) string { return EndpointChannel(cID) + "/pins" }
|
||||
EndpointChannelMessagePin = func(cID, mID string) string { return EndpointChannel(cID) + "/pins/" + mID }
|
||||
|
||||
EndpointChannelWebhooks = func(cID string) string { return EndpointChannel(cID) + "/webhooks" }
|
||||
EndpointWebhook = func(wID string) string { return EndpointWebhooks + wID }
|
||||
EndpointWebhookToken = func(wID, token string) string { return EndpointWebhooks + wID + "/" + token }
|
||||
|
||||
EndpointMessageReactions = func(cID, mID, eID string) string {
|
||||
return EndpointChannelMessage(cID, mID) + "/reactions/" + eID
|
||||
}
|
||||
EndpointMessageReaction = func(cID, mID, eID, uID string) string {
|
||||
return EndpointMessageReactions(cID, mID, eID) + "/" + uID
|
||||
}
|
||||
|
||||
EndpointRelationships = func() string { return EndpointUsers + "@me" + "/relationships" }
|
||||
EndpointRelationship = func(uID string) string { return EndpointRelationships() + "/" + uID }
|
||||
EndpointRelationshipsMutual = func(uID string) string { return EndpointUsers + uID + "/relationships" }
|
||||
|
||||
EndpointInvite = func(iID string) string { return EndpointAPI + "invite/" + iID }
|
||||
|
||||
EndpointIntegrationsJoin = func(iID string) string { return EndpointAPI + "integrations/" + iID + "/join" }
|
||||
|
238
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
Normal file
238
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
Normal file
@ -0,0 +1,238 @@
|
||||
package discordgo
|
||||
|
||||
import "fmt"
|
||||
|
||||
// EventHandler is an interface for Discord events.
|
||||
type EventHandler interface {
|
||||
// Type returns the type of event this handler belongs to.
|
||||
Type() string
|
||||
|
||||
// Handle is called whenever an event of Type() happens.
|
||||
// It is the recievers responsibility to type assert that the interface
|
||||
// is the expected struct.
|
||||
Handle(*Session, interface{})
|
||||
}
|
||||
|
||||
// EventInterfaceProvider is an interface for providing empty interfaces for
|
||||
// Discord events.
|
||||
type EventInterfaceProvider interface {
|
||||
// Type is the type of event this handler belongs to.
|
||||
Type() string
|
||||
|
||||
// New returns a new instance of the struct this event handler handles.
|
||||
// This is called once per event.
|
||||
// The struct is provided to all handlers of the same Type().
|
||||
New() interface{}
|
||||
}
|
||||
|
||||
// interfaceEventType is the event handler type for interface{} events.
|
||||
const interfaceEventType = "__INTERFACE__"
|
||||
|
||||
// interfaceEventHandler is an event handler for interface{} events.
|
||||
type interfaceEventHandler func(*Session, interface{})
|
||||
|
||||
// Type returns the event type for interface{} events.
|
||||
func (eh interfaceEventHandler) Type() string {
|
||||
return interfaceEventType
|
||||
}
|
||||
|
||||
// Handle is the handler for an interface{} event.
|
||||
func (eh interfaceEventHandler) Handle(s *Session, i interface{}) {
|
||||
eh(s, i)
|
||||
}
|
||||
|
||||
var registeredInterfaceProviders = map[string]EventInterfaceProvider{}
|
||||
|
||||
// registerInterfaceProvider registers a provider so that DiscordGo can
|
||||
// access it's New() method.
|
||||
func registerInterfaceProvider(eh EventInterfaceProvider) error {
|
||||
if _, ok := registeredInterfaceProviders[eh.Type()]; ok {
|
||||
return fmt.Errorf("event %s already registered", eh.Type())
|
||||
}
|
||||
registeredInterfaceProviders[eh.Type()] = eh
|
||||
return nil
|
||||
}
|
||||
|
||||
// eventHandlerInstance is a wrapper around an event handler, as functions
|
||||
// cannot be compared directly.
|
||||
type eventHandlerInstance struct {
|
||||
eventHandler EventHandler
|
||||
}
|
||||
|
||||
// addEventHandler adds an event handler that will be fired anytime
|
||||
// the Discord WSAPI matching eventHandler.Type() fires.
|
||||
func (s *Session) addEventHandler(eventHandler EventHandler) func() {
|
||||
s.handlersMu.Lock()
|
||||
defer s.handlersMu.Unlock()
|
||||
|
||||
if s.handlers == nil {
|
||||
s.handlers = map[string][]*eventHandlerInstance{}
|
||||
}
|
||||
|
||||
ehi := &eventHandlerInstance{eventHandler}
|
||||
s.handlers[eventHandler.Type()] = append(s.handlers[eventHandler.Type()], ehi)
|
||||
|
||||
return func() {
|
||||
s.removeEventHandlerInstance(eventHandler.Type(), ehi)
|
||||
}
|
||||
}
|
||||
|
||||
// addEventHandler adds an event handler that will be fired the next time
|
||||
// the Discord WSAPI matching eventHandler.Type() fires.
|
||||
func (s *Session) addEventHandlerOnce(eventHandler EventHandler) func() {
|
||||
s.handlersMu.Lock()
|
||||
defer s.handlersMu.Unlock()
|
||||
|
||||
if s.onceHandlers == nil {
|
||||
s.onceHandlers = map[string][]*eventHandlerInstance{}
|
||||
}
|
||||
|
||||
ehi := &eventHandlerInstance{eventHandler}
|
||||
s.onceHandlers[eventHandler.Type()] = append(s.onceHandlers[eventHandler.Type()], ehi)
|
||||
|
||||
return func() {
|
||||
s.removeEventHandlerInstance(eventHandler.Type(), ehi)
|
||||
}
|
||||
}
|
||||
|
||||
// AddHandler allows you to add an event handler that will be fired anytime
|
||||
// the Discord WSAPI event that matches the function fires.
|
||||
// events.go contains all the Discord WSAPI events that can be fired.
|
||||
// eg:
|
||||
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
// })
|
||||
//
|
||||
// or:
|
||||
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
|
||||
// })
|
||||
// The return value of this method is a function, that when called will remove the
|
||||
// event handler.
|
||||
func (s *Session) AddHandler(handler interface{}) func() {
|
||||
eh := handlerForInterface(handler)
|
||||
|
||||
if eh == nil {
|
||||
s.log(LogError, "Invalid handler type, handler will never be called")
|
||||
return func() {}
|
||||
}
|
||||
|
||||
return s.addEventHandler(eh)
|
||||
}
|
||||
|
||||
// AddHandlerOnce allows you to add an event handler that will be fired the next time
|
||||
// the Discord WSAPI event that matches the function fires.
|
||||
// See AddHandler for more details.
|
||||
func (s *Session) AddHandlerOnce(handler interface{}) func() {
|
||||
eh := handlerForInterface(handler)
|
||||
|
||||
if eh == nil {
|
||||
s.log(LogError, "Invalid handler type, handler will never be called")
|
||||
return func() {}
|
||||
}
|
||||
|
||||
return s.addEventHandlerOnce(eh)
|
||||
}
|
||||
|
||||
// removeEventHandler instance removes an event handler instance.
|
||||
func (s *Session) removeEventHandlerInstance(t string, ehi *eventHandlerInstance) {
|
||||
s.handlersMu.Lock()
|
||||
defer s.handlersMu.Unlock()
|
||||
|
||||
handlers := s.handlers[t]
|
||||
for i := range handlers {
|
||||
if handlers[i] == ehi {
|
||||
s.handlers[t] = append(handlers[:i], handlers[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
onceHandlers := s.onceHandlers[t]
|
||||
for i := range onceHandlers {
|
||||
if onceHandlers[i] == ehi {
|
||||
s.onceHandlers[t] = append(onceHandlers[:i], handlers[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles calling permanent and once handlers for an event type.
|
||||
func (s *Session) handle(t string, i interface{}) {
|
||||
for _, eh := range s.handlers[t] {
|
||||
go eh.eventHandler.Handle(s, i)
|
||||
}
|
||||
|
||||
if len(s.onceHandlers[t]) > 0 {
|
||||
for _, eh := range s.onceHandlers[t] {
|
||||
go eh.eventHandler.Handle(s, i)
|
||||
}
|
||||
s.onceHandlers[t] = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Handles an event type by calling internal methods, firing handlers and firing the
|
||||
// interface{} event.
|
||||
func (s *Session) handleEvent(t string, i interface{}) {
|
||||
s.handlersMu.RLock()
|
||||
defer s.handlersMu.RUnlock()
|
||||
|
||||
// All events are dispatched internally first.
|
||||
s.onInterface(i)
|
||||
|
||||
// Then they are dispatched to anyone handling interface{} events.
|
||||
s.handle(interfaceEventType, i)
|
||||
|
||||
// Finally they are dispatched to any typed handlers.
|
||||
s.handle(t, i)
|
||||
}
|
||||
|
||||
// setGuildIds will set the GuildID on all the members of a guild.
|
||||
// This is done as event data does not have it set.
|
||||
func setGuildIds(g *Guild) {
|
||||
for _, c := range g.Channels {
|
||||
c.GuildID = g.ID
|
||||
}
|
||||
|
||||
for _, m := range g.Members {
|
||||
m.GuildID = g.ID
|
||||
}
|
||||
|
||||
for _, vs := range g.VoiceStates {
|
||||
vs.GuildID = g.ID
|
||||
}
|
||||
}
|
||||
|
||||
// onInterface handles all internal events and routes them to the appropriate internal handler.
|
||||
func (s *Session) onInterface(i interface{}) {
|
||||
switch t := i.(type) {
|
||||
case *Ready:
|
||||
for _, g := range t.Guilds {
|
||||
setGuildIds(g)
|
||||
}
|
||||
s.onReady(t)
|
||||
case *GuildCreate:
|
||||
setGuildIds(t.Guild)
|
||||
case *GuildUpdate:
|
||||
setGuildIds(t.Guild)
|
||||
case *Resumed:
|
||||
s.onResumed(t)
|
||||
case *VoiceServerUpdate:
|
||||
go s.onVoiceServerUpdate(t)
|
||||
case *VoiceStateUpdate:
|
||||
go s.onVoiceStateUpdate(t)
|
||||
}
|
||||
s.State.onInterface(s, i)
|
||||
}
|
||||
|
||||
// onReady handles the ready event.
|
||||
func (s *Session) onReady(r *Ready) {
|
||||
|
||||
// Store the SessionID within the Session struct.
|
||||
s.sessionID = r.SessionID
|
||||
|
||||
// Start the heartbeat to keep the connection alive.
|
||||
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
|
||||
}
|
||||
|
||||
// onResumed handles the resumed event.
|
||||
func (s *Session) onResumed(r *Resumed) {
|
||||
|
||||
// Start the heartbeat to keep the connection alive.
|
||||
go s.heartbeat(s.wsConn, s.listening, r.HeartbeatInterval)
|
||||
}
|
977
vendor/github.com/bwmarrin/discordgo/eventhandlers.go
generated
vendored
Normal file
977
vendor/github.com/bwmarrin/discordgo/eventhandlers.go
generated
vendored
Normal file
@ -0,0 +1,977 @@
|
||||
// Code generated by \"eventhandlers\"; DO NOT EDIT
|
||||
// See events.go
|
||||
|
||||
package discordgo
|
||||
|
||||
// Following are all the event types.
|
||||
// Event type values are used to match the events returned by Discord.
|
||||
// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
|
||||
const (
|
||||
channelCreateEventType = "CHANNEL_CREATE"
|
||||
channelDeleteEventType = "CHANNEL_DELETE"
|
||||
channelPinsUpdateEventType = "CHANNEL_PINS_UPDATE"
|
||||
channelUpdateEventType = "CHANNEL_UPDATE"
|
||||
connectEventType = "__CONNECT__"
|
||||
disconnectEventType = "__DISCONNECT__"
|
||||
eventEventType = "__EVENT__"
|
||||
guildBanAddEventType = "GUILD_BAN_ADD"
|
||||
guildBanRemoveEventType = "GUILD_BAN_REMOVE"
|
||||
guildCreateEventType = "GUILD_CREATE"
|
||||
guildDeleteEventType = "GUILD_DELETE"
|
||||
guildEmojisUpdateEventType = "GUILD_EMOJIS_UPDATE"
|
||||
guildIntegrationsUpdateEventType = "GUILD_INTEGRATIONS_UPDATE"
|
||||
guildMemberAddEventType = "GUILD_MEMBER_ADD"
|
||||
guildMemberRemoveEventType = "GUILD_MEMBER_REMOVE"
|
||||
guildMemberUpdateEventType = "GUILD_MEMBER_UPDATE"
|
||||
guildMembersChunkEventType = "GUILD_MEMBERS_CHUNK"
|
||||
guildRoleCreateEventType = "GUILD_ROLE_CREATE"
|
||||
guildRoleDeleteEventType = "GUILD_ROLE_DELETE"
|
||||
guildRoleUpdateEventType = "GUILD_ROLE_UPDATE"
|
||||
guildUpdateEventType = "GUILD_UPDATE"
|
||||
messageAckEventType = "MESSAGE_ACK"
|
||||
messageCreateEventType = "MESSAGE_CREATE"
|
||||
messageDeleteEventType = "MESSAGE_DELETE"
|
||||
messageReactionAddEventType = "MESSAGE_REACTION_ADD"
|
||||
messageReactionRemoveEventType = "MESSAGE_REACTION_REMOVE"
|
||||
messageUpdateEventType = "MESSAGE_UPDATE"
|
||||
presenceUpdateEventType = "PRESENCE_UPDATE"
|
||||
presencesReplaceEventType = "PRESENCES_REPLACE"
|
||||
rateLimitEventType = "__RATE_LIMIT__"
|
||||
readyEventType = "READY"
|
||||
relationshipAddEventType = "RELATIONSHIP_ADD"
|
||||
relationshipRemoveEventType = "RELATIONSHIP_REMOVE"
|
||||
resumedEventType = "RESUMED"
|
||||
typingStartEventType = "TYPING_START"
|
||||
userGuildSettingsUpdateEventType = "USER_GUILD_SETTINGS_UPDATE"
|
||||
userSettingsUpdateEventType = "USER_SETTINGS_UPDATE"
|
||||
userUpdateEventType = "USER_UPDATE"
|
||||
voiceServerUpdateEventType = "VOICE_SERVER_UPDATE"
|
||||
voiceStateUpdateEventType = "VOICE_STATE_UPDATE"
|
||||
)
|
||||
|
||||
// channelCreateEventHandler is an event handler for ChannelCreate events.
|
||||
type channelCreateEventHandler func(*Session, *ChannelCreate)
|
||||
|
||||
// Type returns the event type for ChannelCreate events.
|
||||
func (eh channelCreateEventHandler) Type() string {
|
||||
return channelCreateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ChannelCreate.
|
||||
func (eh channelCreateEventHandler) New() interface{} {
|
||||
return &ChannelCreate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ChannelCreate events.
|
||||
func (eh channelCreateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ChannelCreate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// channelDeleteEventHandler is an event handler for ChannelDelete events.
|
||||
type channelDeleteEventHandler func(*Session, *ChannelDelete)
|
||||
|
||||
// Type returns the event type for ChannelDelete events.
|
||||
func (eh channelDeleteEventHandler) Type() string {
|
||||
return channelDeleteEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ChannelDelete.
|
||||
func (eh channelDeleteEventHandler) New() interface{} {
|
||||
return &ChannelDelete{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ChannelDelete events.
|
||||
func (eh channelDeleteEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ChannelDelete); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// channelPinsUpdateEventHandler is an event handler for ChannelPinsUpdate events.
|
||||
type channelPinsUpdateEventHandler func(*Session, *ChannelPinsUpdate)
|
||||
|
||||
// Type returns the event type for ChannelPinsUpdate events.
|
||||
func (eh channelPinsUpdateEventHandler) Type() string {
|
||||
return channelPinsUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ChannelPinsUpdate.
|
||||
func (eh channelPinsUpdateEventHandler) New() interface{} {
|
||||
return &ChannelPinsUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ChannelPinsUpdate events.
|
||||
func (eh channelPinsUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ChannelPinsUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// channelUpdateEventHandler is an event handler for ChannelUpdate events.
|
||||
type channelUpdateEventHandler func(*Session, *ChannelUpdate)
|
||||
|
||||
// Type returns the event type for ChannelUpdate events.
|
||||
func (eh channelUpdateEventHandler) Type() string {
|
||||
return channelUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of ChannelUpdate.
|
||||
func (eh channelUpdateEventHandler) New() interface{} {
|
||||
return &ChannelUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for ChannelUpdate events.
|
||||
func (eh channelUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*ChannelUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// connectEventHandler is an event handler for Connect events.
|
||||
type connectEventHandler func(*Session, *Connect)
|
||||
|
||||
// Type returns the event type for Connect events.
|
||||
func (eh connectEventHandler) Type() string {
|
||||
return connectEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of Connect.
|
||||
func (eh connectEventHandler) New() interface{} {
|
||||
return &Connect{}
|
||||
}
|
||||
|
||||
// Handle is the handler for Connect events.
|
||||
func (eh connectEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*Connect); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// disconnectEventHandler is an event handler for Disconnect events.
|
||||
type disconnectEventHandler func(*Session, *Disconnect)
|
||||
|
||||
// Type returns the event type for Disconnect events.
|
||||
func (eh disconnectEventHandler) Type() string {
|
||||
return disconnectEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of Disconnect.
|
||||
func (eh disconnectEventHandler) New() interface{} {
|
||||
return &Disconnect{}
|
||||
}
|
||||
|
||||
// Handle is the handler for Disconnect events.
|
||||
func (eh disconnectEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*Disconnect); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// eventEventHandler is an event handler for Event events.
|
||||
type eventEventHandler func(*Session, *Event)
|
||||
|
||||
// Type returns the event type for Event events.
|
||||
func (eh eventEventHandler) Type() string {
|
||||
return eventEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of Event.
|
||||
func (eh eventEventHandler) New() interface{} {
|
||||
return &Event{}
|
||||
}
|
||||
|
||||
// Handle is the handler for Event events.
|
||||
func (eh eventEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*Event); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildBanAddEventHandler is an event handler for GuildBanAdd events.
|
||||
type guildBanAddEventHandler func(*Session, *GuildBanAdd)
|
||||
|
||||
// Type returns the event type for GuildBanAdd events.
|
||||
func (eh guildBanAddEventHandler) Type() string {
|
||||
return guildBanAddEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildBanAdd.
|
||||
func (eh guildBanAddEventHandler) New() interface{} {
|
||||
return &GuildBanAdd{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildBanAdd events.
|
||||
func (eh guildBanAddEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildBanAdd); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildBanRemoveEventHandler is an event handler for GuildBanRemove events.
|
||||
type guildBanRemoveEventHandler func(*Session, *GuildBanRemove)
|
||||
|
||||
// Type returns the event type for GuildBanRemove events.
|
||||
func (eh guildBanRemoveEventHandler) Type() string {
|
||||
return guildBanRemoveEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildBanRemove.
|
||||
func (eh guildBanRemoveEventHandler) New() interface{} {
|
||||
return &GuildBanRemove{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildBanRemove events.
|
||||
func (eh guildBanRemoveEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildBanRemove); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildCreateEventHandler is an event handler for GuildCreate events.
|
||||
type guildCreateEventHandler func(*Session, *GuildCreate)
|
||||
|
||||
// Type returns the event type for GuildCreate events.
|
||||
func (eh guildCreateEventHandler) Type() string {
|
||||
return guildCreateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildCreate.
|
||||
func (eh guildCreateEventHandler) New() interface{} {
|
||||
return &GuildCreate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildCreate events.
|
||||
func (eh guildCreateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildCreate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildDeleteEventHandler is an event handler for GuildDelete events.
|
||||
type guildDeleteEventHandler func(*Session, *GuildDelete)
|
||||
|
||||
// Type returns the event type for GuildDelete events.
|
||||
func (eh guildDeleteEventHandler) Type() string {
|
||||
return guildDeleteEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildDelete.
|
||||
func (eh guildDeleteEventHandler) New() interface{} {
|
||||
return &GuildDelete{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildDelete events.
|
||||
func (eh guildDeleteEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildDelete); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildEmojisUpdateEventHandler is an event handler for GuildEmojisUpdate events.
|
||||
type guildEmojisUpdateEventHandler func(*Session, *GuildEmojisUpdate)
|
||||
|
||||
// Type returns the event type for GuildEmojisUpdate events.
|
||||
func (eh guildEmojisUpdateEventHandler) Type() string {
|
||||
return guildEmojisUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildEmojisUpdate.
|
||||
func (eh guildEmojisUpdateEventHandler) New() interface{} {
|
||||
return &GuildEmojisUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildEmojisUpdate events.
|
||||
func (eh guildEmojisUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildEmojisUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildIntegrationsUpdateEventHandler is an event handler for GuildIntegrationsUpdate events.
|
||||
type guildIntegrationsUpdateEventHandler func(*Session, *GuildIntegrationsUpdate)
|
||||
|
||||
// Type returns the event type for GuildIntegrationsUpdate events.
|
||||
func (eh guildIntegrationsUpdateEventHandler) Type() string {
|
||||
return guildIntegrationsUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildIntegrationsUpdate.
|
||||
func (eh guildIntegrationsUpdateEventHandler) New() interface{} {
|
||||
return &GuildIntegrationsUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildIntegrationsUpdate events.
|
||||
func (eh guildIntegrationsUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildIntegrationsUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildMemberAddEventHandler is an event handler for GuildMemberAdd events.
|
||||
type guildMemberAddEventHandler func(*Session, *GuildMemberAdd)
|
||||
|
||||
// Type returns the event type for GuildMemberAdd events.
|
||||
func (eh guildMemberAddEventHandler) Type() string {
|
||||
return guildMemberAddEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildMemberAdd.
|
||||
func (eh guildMemberAddEventHandler) New() interface{} {
|
||||
return &GuildMemberAdd{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildMemberAdd events.
|
||||
func (eh guildMemberAddEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildMemberAdd); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildMemberRemoveEventHandler is an event handler for GuildMemberRemove events.
|
||||
type guildMemberRemoveEventHandler func(*Session, *GuildMemberRemove)
|
||||
|
||||
// Type returns the event type for GuildMemberRemove events.
|
||||
func (eh guildMemberRemoveEventHandler) Type() string {
|
||||
return guildMemberRemoveEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildMemberRemove.
|
||||
func (eh guildMemberRemoveEventHandler) New() interface{} {
|
||||
return &GuildMemberRemove{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildMemberRemove events.
|
||||
func (eh guildMemberRemoveEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildMemberRemove); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildMemberUpdateEventHandler is an event handler for GuildMemberUpdate events.
|
||||
type guildMemberUpdateEventHandler func(*Session, *GuildMemberUpdate)
|
||||
|
||||
// Type returns the event type for GuildMemberUpdate events.
|
||||
func (eh guildMemberUpdateEventHandler) Type() string {
|
||||
return guildMemberUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildMemberUpdate.
|
||||
func (eh guildMemberUpdateEventHandler) New() interface{} {
|
||||
return &GuildMemberUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildMemberUpdate events.
|
||||
func (eh guildMemberUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildMemberUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildMembersChunkEventHandler is an event handler for GuildMembersChunk events.
|
||||
type guildMembersChunkEventHandler func(*Session, *GuildMembersChunk)
|
||||
|
||||
// Type returns the event type for GuildMembersChunk events.
|
||||
func (eh guildMembersChunkEventHandler) Type() string {
|
||||
return guildMembersChunkEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildMembersChunk.
|
||||
func (eh guildMembersChunkEventHandler) New() interface{} {
|
||||
return &GuildMembersChunk{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildMembersChunk events.
|
||||
func (eh guildMembersChunkEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildMembersChunk); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildRoleCreateEventHandler is an event handler for GuildRoleCreate events.
|
||||
type guildRoleCreateEventHandler func(*Session, *GuildRoleCreate)
|
||||
|
||||
// Type returns the event type for GuildRoleCreate events.
|
||||
func (eh guildRoleCreateEventHandler) Type() string {
|
||||
return guildRoleCreateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildRoleCreate.
|
||||
func (eh guildRoleCreateEventHandler) New() interface{} {
|
||||
return &GuildRoleCreate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildRoleCreate events.
|
||||
func (eh guildRoleCreateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildRoleCreate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildRoleDeleteEventHandler is an event handler for GuildRoleDelete events.
|
||||
type guildRoleDeleteEventHandler func(*Session, *GuildRoleDelete)
|
||||
|
||||
// Type returns the event type for GuildRoleDelete events.
|
||||
func (eh guildRoleDeleteEventHandler) Type() string {
|
||||
return guildRoleDeleteEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildRoleDelete.
|
||||
func (eh guildRoleDeleteEventHandler) New() interface{} {
|
||||
return &GuildRoleDelete{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildRoleDelete events.
|
||||
func (eh guildRoleDeleteEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildRoleDelete); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildRoleUpdateEventHandler is an event handler for GuildRoleUpdate events.
|
||||
type guildRoleUpdateEventHandler func(*Session, *GuildRoleUpdate)
|
||||
|
||||
// Type returns the event type for GuildRoleUpdate events.
|
||||
func (eh guildRoleUpdateEventHandler) Type() string {
|
||||
return guildRoleUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildRoleUpdate.
|
||||
func (eh guildRoleUpdateEventHandler) New() interface{} {
|
||||
return &GuildRoleUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildRoleUpdate events.
|
||||
func (eh guildRoleUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildRoleUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// guildUpdateEventHandler is an event handler for GuildUpdate events.
|
||||
type guildUpdateEventHandler func(*Session, *GuildUpdate)
|
||||
|
||||
// Type returns the event type for GuildUpdate events.
|
||||
func (eh guildUpdateEventHandler) Type() string {
|
||||
return guildUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of GuildUpdate.
|
||||
func (eh guildUpdateEventHandler) New() interface{} {
|
||||
return &GuildUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for GuildUpdate events.
|
||||
func (eh guildUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*GuildUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// messageAckEventHandler is an event handler for MessageAck events.
|
||||
type messageAckEventHandler func(*Session, *MessageAck)
|
||||
|
||||
// Type returns the event type for MessageAck events.
|
||||
func (eh messageAckEventHandler) Type() string {
|
||||
return messageAckEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of MessageAck.
|
||||
func (eh messageAckEventHandler) New() interface{} {
|
||||
return &MessageAck{}
|
||||
}
|
||||
|
||||
// Handle is the handler for MessageAck events.
|
||||
func (eh messageAckEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*MessageAck); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// messageCreateEventHandler is an event handler for MessageCreate events.
|
||||
type messageCreateEventHandler func(*Session, *MessageCreate)
|
||||
|
||||
// Type returns the event type for MessageCreate events.
|
||||
func (eh messageCreateEventHandler) Type() string {
|
||||
return messageCreateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of MessageCreate.
|
||||
func (eh messageCreateEventHandler) New() interface{} {
|
||||
return &MessageCreate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for MessageCreate events.
|
||||
func (eh messageCreateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*MessageCreate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// messageDeleteEventHandler is an event handler for MessageDelete events.
|
||||
type messageDeleteEventHandler func(*Session, *MessageDelete)
|
||||
|
||||
// Type returns the event type for MessageDelete events.
|
||||
func (eh messageDeleteEventHandler) Type() string {
|
||||
return messageDeleteEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of MessageDelete.
|
||||
func (eh messageDeleteEventHandler) New() interface{} {
|
||||
return &MessageDelete{}
|
||||
}
|
||||
|
||||
// Handle is the handler for MessageDelete events.
|
||||
func (eh messageDeleteEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*MessageDelete); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// messageReactionAddEventHandler is an event handler for MessageReactionAdd events.
|
||||
type messageReactionAddEventHandler func(*Session, *MessageReactionAdd)
|
||||
|
||||
// Type returns the event type for MessageReactionAdd events.
|
||||
func (eh messageReactionAddEventHandler) Type() string {
|
||||
return messageReactionAddEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of MessageReactionAdd.
|
||||
func (eh messageReactionAddEventHandler) New() interface{} {
|
||||
return &MessageReactionAdd{}
|
||||
}
|
||||
|
||||
// Handle is the handler for MessageReactionAdd events.
|
||||
func (eh messageReactionAddEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*MessageReactionAdd); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// messageReactionRemoveEventHandler is an event handler for MessageReactionRemove events.
|
||||
type messageReactionRemoveEventHandler func(*Session, *MessageReactionRemove)
|
||||
|
||||
// Type returns the event type for MessageReactionRemove events.
|
||||
func (eh messageReactionRemoveEventHandler) Type() string {
|
||||
return messageReactionRemoveEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of MessageReactionRemove.
|
||||
func (eh messageReactionRemoveEventHandler) New() interface{} {
|
||||
return &MessageReactionRemove{}
|
||||
}
|
||||
|
||||
// Handle is the handler for MessageReactionRemove events.
|
||||
func (eh messageReactionRemoveEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*MessageReactionRemove); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// messageUpdateEventHandler is an event handler for MessageUpdate events.
|
||||
type messageUpdateEventHandler func(*Session, *MessageUpdate)
|
||||
|
||||
// Type returns the event type for MessageUpdate events.
|
||||
func (eh messageUpdateEventHandler) Type() string {
|
||||
return messageUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of MessageUpdate.
|
||||
func (eh messageUpdateEventHandler) New() interface{} {
|
||||
return &MessageUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for MessageUpdate events.
|
||||
func (eh messageUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*MessageUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// presenceUpdateEventHandler is an event handler for PresenceUpdate events.
|
||||
type presenceUpdateEventHandler func(*Session, *PresenceUpdate)
|
||||
|
||||
// Type returns the event type for PresenceUpdate events.
|
||||
func (eh presenceUpdateEventHandler) Type() string {
|
||||
return presenceUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of PresenceUpdate.
|
||||
func (eh presenceUpdateEventHandler) New() interface{} {
|
||||
return &PresenceUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for PresenceUpdate events.
|
||||
func (eh presenceUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*PresenceUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// presencesReplaceEventHandler is an event handler for PresencesReplace events.
|
||||
type presencesReplaceEventHandler func(*Session, *PresencesReplace)
|
||||
|
||||
// Type returns the event type for PresencesReplace events.
|
||||
func (eh presencesReplaceEventHandler) Type() string {
|
||||
return presencesReplaceEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of PresencesReplace.
|
||||
func (eh presencesReplaceEventHandler) New() interface{} {
|
||||
return &PresencesReplace{}
|
||||
}
|
||||
|
||||
// Handle is the handler for PresencesReplace events.
|
||||
func (eh presencesReplaceEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*PresencesReplace); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// rateLimitEventHandler is an event handler for RateLimit events.
|
||||
type rateLimitEventHandler func(*Session, *RateLimit)
|
||||
|
||||
// Type returns the event type for RateLimit events.
|
||||
func (eh rateLimitEventHandler) Type() string {
|
||||
return rateLimitEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of RateLimit.
|
||||
func (eh rateLimitEventHandler) New() interface{} {
|
||||
return &RateLimit{}
|
||||
}
|
||||
|
||||
// Handle is the handler for RateLimit events.
|
||||
func (eh rateLimitEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*RateLimit); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// readyEventHandler is an event handler for Ready events.
|
||||
type readyEventHandler func(*Session, *Ready)
|
||||
|
||||
// Type returns the event type for Ready events.
|
||||
func (eh readyEventHandler) Type() string {
|
||||
return readyEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of Ready.
|
||||
func (eh readyEventHandler) New() interface{} {
|
||||
return &Ready{}
|
||||
}
|
||||
|
||||
// Handle is the handler for Ready events.
|
||||
func (eh readyEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*Ready); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// relationshipAddEventHandler is an event handler for RelationshipAdd events.
|
||||
type relationshipAddEventHandler func(*Session, *RelationshipAdd)
|
||||
|
||||
// Type returns the event type for RelationshipAdd events.
|
||||
func (eh relationshipAddEventHandler) Type() string {
|
||||
return relationshipAddEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of RelationshipAdd.
|
||||
func (eh relationshipAddEventHandler) New() interface{} {
|
||||
return &RelationshipAdd{}
|
||||
}
|
||||
|
||||
// Handle is the handler for RelationshipAdd events.
|
||||
func (eh relationshipAddEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*RelationshipAdd); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// relationshipRemoveEventHandler is an event handler for RelationshipRemove events.
|
||||
type relationshipRemoveEventHandler func(*Session, *RelationshipRemove)
|
||||
|
||||
// Type returns the event type for RelationshipRemove events.
|
||||
func (eh relationshipRemoveEventHandler) Type() string {
|
||||
return relationshipRemoveEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of RelationshipRemove.
|
||||
func (eh relationshipRemoveEventHandler) New() interface{} {
|
||||
return &RelationshipRemove{}
|
||||
}
|
||||
|
||||
// Handle is the handler for RelationshipRemove events.
|
||||
func (eh relationshipRemoveEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*RelationshipRemove); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// resumedEventHandler is an event handler for Resumed events.
|
||||
type resumedEventHandler func(*Session, *Resumed)
|
||||
|
||||
// Type returns the event type for Resumed events.
|
||||
func (eh resumedEventHandler) Type() string {
|
||||
return resumedEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of Resumed.
|
||||
func (eh resumedEventHandler) New() interface{} {
|
||||
return &Resumed{}
|
||||
}
|
||||
|
||||
// Handle is the handler for Resumed events.
|
||||
func (eh resumedEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*Resumed); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// typingStartEventHandler is an event handler for TypingStart events.
|
||||
type typingStartEventHandler func(*Session, *TypingStart)
|
||||
|
||||
// Type returns the event type for TypingStart events.
|
||||
func (eh typingStartEventHandler) Type() string {
|
||||
return typingStartEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of TypingStart.
|
||||
func (eh typingStartEventHandler) New() interface{} {
|
||||
return &TypingStart{}
|
||||
}
|
||||
|
||||
// Handle is the handler for TypingStart events.
|
||||
func (eh typingStartEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*TypingStart); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// userGuildSettingsUpdateEventHandler is an event handler for UserGuildSettingsUpdate events.
|
||||
type userGuildSettingsUpdateEventHandler func(*Session, *UserGuildSettingsUpdate)
|
||||
|
||||
// Type returns the event type for UserGuildSettingsUpdate events.
|
||||
func (eh userGuildSettingsUpdateEventHandler) Type() string {
|
||||
return userGuildSettingsUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of UserGuildSettingsUpdate.
|
||||
func (eh userGuildSettingsUpdateEventHandler) New() interface{} {
|
||||
return &UserGuildSettingsUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for UserGuildSettingsUpdate events.
|
||||
func (eh userGuildSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*UserGuildSettingsUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// userSettingsUpdateEventHandler is an event handler for UserSettingsUpdate events.
|
||||
type userSettingsUpdateEventHandler func(*Session, *UserSettingsUpdate)
|
||||
|
||||
// Type returns the event type for UserSettingsUpdate events.
|
||||
func (eh userSettingsUpdateEventHandler) Type() string {
|
||||
return userSettingsUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of UserSettingsUpdate.
|
||||
func (eh userSettingsUpdateEventHandler) New() interface{} {
|
||||
return &UserSettingsUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for UserSettingsUpdate events.
|
||||
func (eh userSettingsUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*UserSettingsUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// userUpdateEventHandler is an event handler for UserUpdate events.
|
||||
type userUpdateEventHandler func(*Session, *UserUpdate)
|
||||
|
||||
// Type returns the event type for UserUpdate events.
|
||||
func (eh userUpdateEventHandler) Type() string {
|
||||
return userUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of UserUpdate.
|
||||
func (eh userUpdateEventHandler) New() interface{} {
|
||||
return &UserUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for UserUpdate events.
|
||||
func (eh userUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*UserUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// voiceServerUpdateEventHandler is an event handler for VoiceServerUpdate events.
|
||||
type voiceServerUpdateEventHandler func(*Session, *VoiceServerUpdate)
|
||||
|
||||
// Type returns the event type for VoiceServerUpdate events.
|
||||
func (eh voiceServerUpdateEventHandler) Type() string {
|
||||
return voiceServerUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of VoiceServerUpdate.
|
||||
func (eh voiceServerUpdateEventHandler) New() interface{} {
|
||||
return &VoiceServerUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for VoiceServerUpdate events.
|
||||
func (eh voiceServerUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*VoiceServerUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
// voiceStateUpdateEventHandler is an event handler for VoiceStateUpdate events.
|
||||
type voiceStateUpdateEventHandler func(*Session, *VoiceStateUpdate)
|
||||
|
||||
// Type returns the event type for VoiceStateUpdate events.
|
||||
func (eh voiceStateUpdateEventHandler) Type() string {
|
||||
return voiceStateUpdateEventType
|
||||
}
|
||||
|
||||
// New returns a new instance of VoiceStateUpdate.
|
||||
func (eh voiceStateUpdateEventHandler) New() interface{} {
|
||||
return &VoiceStateUpdate{}
|
||||
}
|
||||
|
||||
// Handle is the handler for VoiceStateUpdate events.
|
||||
func (eh voiceStateUpdateEventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*VoiceStateUpdate); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
|
||||
func handlerForInterface(handler interface{}) EventHandler {
|
||||
switch v := handler.(type) {
|
||||
case func(*Session, interface{}):
|
||||
return interfaceEventHandler(v)
|
||||
case func(*Session, *ChannelCreate):
|
||||
return channelCreateEventHandler(v)
|
||||
case func(*Session, *ChannelDelete):
|
||||
return channelDeleteEventHandler(v)
|
||||
case func(*Session, *ChannelPinsUpdate):
|
||||
return channelPinsUpdateEventHandler(v)
|
||||
case func(*Session, *ChannelUpdate):
|
||||
return channelUpdateEventHandler(v)
|
||||
case func(*Session, *Connect):
|
||||
return connectEventHandler(v)
|
||||
case func(*Session, *Disconnect):
|
||||
return disconnectEventHandler(v)
|
||||
case func(*Session, *Event):
|
||||
return eventEventHandler(v)
|
||||
case func(*Session, *GuildBanAdd):
|
||||
return guildBanAddEventHandler(v)
|
||||
case func(*Session, *GuildBanRemove):
|
||||
return guildBanRemoveEventHandler(v)
|
||||
case func(*Session, *GuildCreate):
|
||||
return guildCreateEventHandler(v)
|
||||
case func(*Session, *GuildDelete):
|
||||
return guildDeleteEventHandler(v)
|
||||
case func(*Session, *GuildEmojisUpdate):
|
||||
return guildEmojisUpdateEventHandler(v)
|
||||
case func(*Session, *GuildIntegrationsUpdate):
|
||||
return guildIntegrationsUpdateEventHandler(v)
|
||||
case func(*Session, *GuildMemberAdd):
|
||||
return guildMemberAddEventHandler(v)
|
||||
case func(*Session, *GuildMemberRemove):
|
||||
return guildMemberRemoveEventHandler(v)
|
||||
case func(*Session, *GuildMemberUpdate):
|
||||
return guildMemberUpdateEventHandler(v)
|
||||
case func(*Session, *GuildMembersChunk):
|
||||
return guildMembersChunkEventHandler(v)
|
||||
case func(*Session, *GuildRoleCreate):
|
||||
return guildRoleCreateEventHandler(v)
|
||||
case func(*Session, *GuildRoleDelete):
|
||||
return guildRoleDeleteEventHandler(v)
|
||||
case func(*Session, *GuildRoleUpdate):
|
||||
return guildRoleUpdateEventHandler(v)
|
||||
case func(*Session, *GuildUpdate):
|
||||
return guildUpdateEventHandler(v)
|
||||
case func(*Session, *MessageAck):
|
||||
return messageAckEventHandler(v)
|
||||
case func(*Session, *MessageCreate):
|
||||
return messageCreateEventHandler(v)
|
||||
case func(*Session, *MessageDelete):
|
||||
return messageDeleteEventHandler(v)
|
||||
case func(*Session, *MessageReactionAdd):
|
||||
return messageReactionAddEventHandler(v)
|
||||
case func(*Session, *MessageReactionRemove):
|
||||
return messageReactionRemoveEventHandler(v)
|
||||
case func(*Session, *MessageUpdate):
|
||||
return messageUpdateEventHandler(v)
|
||||
case func(*Session, *PresenceUpdate):
|
||||
return presenceUpdateEventHandler(v)
|
||||
case func(*Session, *PresencesReplace):
|
||||
return presencesReplaceEventHandler(v)
|
||||
case func(*Session, *RateLimit):
|
||||
return rateLimitEventHandler(v)
|
||||
case func(*Session, *Ready):
|
||||
return readyEventHandler(v)
|
||||
case func(*Session, *RelationshipAdd):
|
||||
return relationshipAddEventHandler(v)
|
||||
case func(*Session, *RelationshipRemove):
|
||||
return relationshipRemoveEventHandler(v)
|
||||
case func(*Session, *Resumed):
|
||||
return resumedEventHandler(v)
|
||||
case func(*Session, *TypingStart):
|
||||
return typingStartEventHandler(v)
|
||||
case func(*Session, *UserGuildSettingsUpdate):
|
||||
return userGuildSettingsUpdateEventHandler(v)
|
||||
case func(*Session, *UserSettingsUpdate):
|
||||
return userSettingsUpdateEventHandler(v)
|
||||
case func(*Session, *UserUpdate):
|
||||
return userUpdateEventHandler(v)
|
||||
case func(*Session, *VoiceServerUpdate):
|
||||
return voiceServerUpdateEventHandler(v)
|
||||
case func(*Session, *VoiceStateUpdate):
|
||||
return voiceStateUpdateEventHandler(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func init() {
|
||||
registerInterfaceProvider(channelCreateEventHandler(nil))
|
||||
registerInterfaceProvider(channelDeleteEventHandler(nil))
|
||||
registerInterfaceProvider(channelPinsUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(channelUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(guildBanAddEventHandler(nil))
|
||||
registerInterfaceProvider(guildBanRemoveEventHandler(nil))
|
||||
registerInterfaceProvider(guildCreateEventHandler(nil))
|
||||
registerInterfaceProvider(guildDeleteEventHandler(nil))
|
||||
registerInterfaceProvider(guildEmojisUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(guildIntegrationsUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(guildMemberAddEventHandler(nil))
|
||||
registerInterfaceProvider(guildMemberRemoveEventHandler(nil))
|
||||
registerInterfaceProvider(guildMemberUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(guildMembersChunkEventHandler(nil))
|
||||
registerInterfaceProvider(guildRoleCreateEventHandler(nil))
|
||||
registerInterfaceProvider(guildRoleDeleteEventHandler(nil))
|
||||
registerInterfaceProvider(guildRoleUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(guildUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(messageAckEventHandler(nil))
|
||||
registerInterfaceProvider(messageCreateEventHandler(nil))
|
||||
registerInterfaceProvider(messageDeleteEventHandler(nil))
|
||||
registerInterfaceProvider(messageReactionAddEventHandler(nil))
|
||||
registerInterfaceProvider(messageReactionRemoveEventHandler(nil))
|
||||
registerInterfaceProvider(messageUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(presenceUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(presencesReplaceEventHandler(nil))
|
||||
registerInterfaceProvider(readyEventHandler(nil))
|
||||
registerInterfaceProvider(relationshipAddEventHandler(nil))
|
||||
registerInterfaceProvider(relationshipRemoveEventHandler(nil))
|
||||
registerInterfaceProvider(resumedEventHandler(nil))
|
||||
registerInterfaceProvider(typingStartEventHandler(nil))
|
||||
registerInterfaceProvider(userGuildSettingsUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(userSettingsUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(userUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(voiceServerUpdateEventHandler(nil))
|
||||
registerInterfaceProvider(voiceStateUpdateEventHandler(nil))
|
||||
}
|
243
vendor/github.com/bwmarrin/discordgo/events.go
generated
vendored
243
vendor/github.com/bwmarrin/discordgo/events.go
generated
vendored
@ -1,159 +1,238 @@
|
||||
package discordgo
|
||||
|
||||
// eventToInterface is a mapping of Discord WSAPI events to their
|
||||
// DiscordGo event container.
|
||||
// Each Discord WSAPI event maps to a unique interface.
|
||||
// Use Session.AddHandler with one of these types to handle that
|
||||
// type of event.
|
||||
// eg:
|
||||
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
// })
|
||||
//
|
||||
// or:
|
||||
// Session.AddHandler(func(s *discordgo.Session, m *discordgo.PresenceUpdate) {
|
||||
// })
|
||||
var eventToInterface = map[string]interface{}{
|
||||
"CHANNEL_CREATE": ChannelCreate{},
|
||||
"CHANNEL_UPDATE": ChannelUpdate{},
|
||||
"CHANNEL_DELETE": ChannelDelete{},
|
||||
"GUILD_CREATE": GuildCreate{},
|
||||
"GUILD_UPDATE": GuildUpdate{},
|
||||
"GUILD_DELETE": GuildDelete{},
|
||||
"GUILD_BAN_ADD": GuildBanAdd{},
|
||||
"GUILD_BAN_REMOVE": GuildBanRemove{},
|
||||
"GUILD_MEMBER_ADD": GuildMemberAdd{},
|
||||
"GUILD_MEMBER_UPDATE": GuildMemberUpdate{},
|
||||
"GUILD_MEMBER_REMOVE": GuildMemberRemove{},
|
||||
"GUILD_ROLE_CREATE": GuildRoleCreate{},
|
||||
"GUILD_ROLE_UPDATE": GuildRoleUpdate{},
|
||||
"GUILD_ROLE_DELETE": GuildRoleDelete{},
|
||||
"GUILD_INTEGRATIONS_UPDATE": GuildIntegrationsUpdate{},
|
||||
"GUILD_EMOJIS_UPDATE": GuildEmojisUpdate{},
|
||||
"MESSAGE_ACK": MessageAck{},
|
||||
"MESSAGE_CREATE": MessageCreate{},
|
||||
"MESSAGE_UPDATE": MessageUpdate{},
|
||||
"MESSAGE_DELETE": MessageDelete{},
|
||||
"PRESENCE_UPDATE": PresenceUpdate{},
|
||||
"PRESENCES_REPLACE": PresencesReplace{},
|
||||
"READY": Ready{},
|
||||
"USER_UPDATE": UserUpdate{},
|
||||
"USER_SETTINGS_UPDATE": UserSettingsUpdate{},
|
||||
"USER_GUILD_SETTINGS_UPDATE": UserGuildSettingsUpdate{},
|
||||
"TYPING_START": TypingStart{},
|
||||
"VOICE_SERVER_UPDATE": VoiceServerUpdate{},
|
||||
"VOICE_STATE_UPDATE": VoiceStateUpdate{},
|
||||
"RESUMED": Resumed{},
|
||||
}
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Connect is an empty struct for an event.
|
||||
// This file contains all the possible structs that can be
|
||||
// handled by AddHandler/EventHandler.
|
||||
// DO NOT ADD ANYTHING BUT EVENT HANDLER STRUCTS TO THIS FILE.
|
||||
//go:generate go run tools/cmd/eventhandlers/main.go
|
||||
|
||||
// Connect is the data for a Connect event.
|
||||
// This is a sythetic event and is not dispatched by Discord.
|
||||
type Connect struct{}
|
||||
|
||||
// Disconnect is an empty struct for an event.
|
||||
// Disconnect is the data for a Disconnect event.
|
||||
// This is a sythetic event and is not dispatched by Discord.
|
||||
type Disconnect struct{}
|
||||
|
||||
// RateLimit is a struct for the RateLimited event
|
||||
// RateLimit is the data for a RateLimit event.
|
||||
// This is a sythetic event and is not dispatched by Discord.
|
||||
type RateLimit struct {
|
||||
*TooManyRequests
|
||||
URL string
|
||||
}
|
||||
|
||||
// MessageCreate is a wrapper struct for an event.
|
||||
type MessageCreate struct {
|
||||
*Message
|
||||
// Event provides a basic initial struct for all websocket events.
|
||||
type Event struct {
|
||||
Operation int `json:"op"`
|
||||
Sequence int `json:"s"`
|
||||
Type string `json:"t"`
|
||||
RawData json.RawMessage `json:"d"`
|
||||
// Struct contains one of the other types in this file.
|
||||
Struct interface{} `json:"-"`
|
||||
}
|
||||
|
||||
// MessageUpdate is a wrapper struct for an event.
|
||||
type MessageUpdate struct {
|
||||
*Message
|
||||
// A Ready stores all data for the websocket READY event.
|
||||
type Ready struct {
|
||||
Version int `json:"v"`
|
||||
SessionID string `json:"session_id"`
|
||||
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
|
||||
User *User `json:"user"`
|
||||
ReadState []*ReadState `json:"read_state"`
|
||||
PrivateChannels []*Channel `json:"private_channels"`
|
||||
Guilds []*Guild `json:"guilds"`
|
||||
|
||||
// Undocumented fields
|
||||
Settings *Settings `json:"user_settings"`
|
||||
UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
|
||||
Relationships []*Relationship `json:"relationships"`
|
||||
Presences []*Presence `json:"presences"`
|
||||
}
|
||||
|
||||
// MessageDelete is a wrapper struct for an event.
|
||||
type MessageDelete struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// ChannelCreate is a wrapper struct for an event.
|
||||
// ChannelCreate is the data for a ChannelCreate event.
|
||||
type ChannelCreate struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// ChannelUpdate is a wrapper struct for an event.
|
||||
// ChannelUpdate is the data for a ChannelUpdate event.
|
||||
type ChannelUpdate struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// ChannelDelete is a wrapper struct for an event.
|
||||
// ChannelDelete is the data for a ChannelDelete event.
|
||||
type ChannelDelete struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// GuildCreate is a wrapper struct for an event.
|
||||
// ChannelPinsUpdate stores data for a ChannelPinsUpdate event.
|
||||
type ChannelPinsUpdate struct {
|
||||
LastPinTimestamp string `json:"last_pin_timestamp"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
}
|
||||
|
||||
// GuildCreate is the data for a GuildCreate event.
|
||||
type GuildCreate struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildUpdate is a wrapper struct for an event.
|
||||
// GuildUpdate is the data for a GuildUpdate event.
|
||||
type GuildUpdate struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildDelete is a wrapper struct for an event.
|
||||
// GuildDelete is the data for a GuildDelete event.
|
||||
type GuildDelete struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildBanAdd is a wrapper struct for an event.
|
||||
// GuildBanAdd is the data for a GuildBanAdd event.
|
||||
type GuildBanAdd struct {
|
||||
*GuildBan
|
||||
User *User `json:"user"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// GuildBanRemove is a wrapper struct for an event.
|
||||
// GuildBanRemove is the data for a GuildBanRemove event.
|
||||
type GuildBanRemove struct {
|
||||
*GuildBan
|
||||
User *User `json:"user"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// GuildMemberAdd is a wrapper struct for an event.
|
||||
// GuildMemberAdd is the data for a GuildMemberAdd event.
|
||||
type GuildMemberAdd struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildMemberUpdate is a wrapper struct for an event.
|
||||
// GuildMemberUpdate is the data for a GuildMemberUpdate event.
|
||||
type GuildMemberUpdate struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildMemberRemove is a wrapper struct for an event.
|
||||
// GuildMemberRemove is the data for a GuildMemberRemove event.
|
||||
type GuildMemberRemove struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildRoleCreate is a wrapper struct for an event.
|
||||
// GuildRoleCreate is the data for a GuildRoleCreate event.
|
||||
type GuildRoleCreate struct {
|
||||
*GuildRole
|
||||
}
|
||||
|
||||
// GuildRoleUpdate is a wrapper struct for an event.
|
||||
// GuildRoleUpdate is the data for a GuildRoleUpdate event.
|
||||
type GuildRoleUpdate struct {
|
||||
*GuildRole
|
||||
}
|
||||
|
||||
// PresencesReplace is an array of Presences for an event.
|
||||
type PresencesReplace []*Presence
|
||||
|
||||
// VoiceStateUpdate is a wrapper struct for an event.
|
||||
type VoiceStateUpdate struct {
|
||||
*VoiceState
|
||||
// A GuildRoleDelete is the data for a GuildRoleDelete event.
|
||||
type GuildRoleDelete struct {
|
||||
RoleID string `json:"role_id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// UserUpdate is a wrapper struct for an event.
|
||||
// A GuildEmojisUpdate is the data for a guild emoji update event.
|
||||
type GuildEmojisUpdate struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
Emojis []*Emoji `json:"emojis"`
|
||||
}
|
||||
|
||||
// A GuildMembersChunk is the data for a GuildMembersChunk event.
|
||||
type GuildMembersChunk struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
Members []*Member `json:"members"`
|
||||
}
|
||||
|
||||
// GuildIntegrationsUpdate is the data for a GuildIntegrationsUpdate event.
|
||||
type GuildIntegrationsUpdate struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// MessageAck is the data for a MessageAck event.
|
||||
type MessageAck struct {
|
||||
MessageID string `json:"message_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
}
|
||||
|
||||
// MessageCreate is the data for a MessageCreate event.
|
||||
type MessageCreate struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// MessageUpdate is the data for a MessageUpdate event.
|
||||
type MessageUpdate struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// MessageDelete is the data for a MessageDelete event.
|
||||
type MessageDelete struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// MessageReactionAdd is the data for a MessageReactionAdd event.
|
||||
type MessageReactionAdd struct {
|
||||
*MessageReaction
|
||||
}
|
||||
|
||||
// MessageReactionRemove is the data for a MessageReactionRemove event.
|
||||
type MessageReactionRemove struct {
|
||||
*MessageReaction
|
||||
}
|
||||
|
||||
// PresencesReplace is the data for a PresencesReplace event.
|
||||
type PresencesReplace []*Presence
|
||||
|
||||
// PresenceUpdate is the data for a PresenceUpdate event.
|
||||
type PresenceUpdate struct {
|
||||
Presence
|
||||
GuildID string `json:"guild_id"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
// Resumed is the data for a Resumed event.
|
||||
type Resumed struct {
|
||||
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
|
||||
Trace []string `json:"_trace"`
|
||||
}
|
||||
|
||||
// RelationshipAdd is the data for a RelationshipAdd event.
|
||||
type RelationshipAdd struct {
|
||||
*Relationship
|
||||
}
|
||||
|
||||
// RelationshipRemove is the data for a RelationshipRemove event.
|
||||
type RelationshipRemove struct {
|
||||
*Relationship
|
||||
}
|
||||
|
||||
// TypingStart is the data for a TypingStart event.
|
||||
type TypingStart struct {
|
||||
UserID string `json:"user_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
Timestamp int `json:"timestamp"`
|
||||
}
|
||||
|
||||
// UserUpdate is the data for a UserUpdate event.
|
||||
type UserUpdate struct {
|
||||
*User
|
||||
}
|
||||
|
||||
// UserSettingsUpdate is a map for an event.
|
||||
// UserSettingsUpdate is the data for a UserSettingsUpdate event.
|
||||
type UserSettingsUpdate map[string]interface{}
|
||||
|
||||
// UserGuildSettingsUpdate is a map for an event.
|
||||
// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
|
||||
type UserGuildSettingsUpdate struct {
|
||||
*UserGuildSettings
|
||||
}
|
||||
|
||||
// VoiceServerUpdate is the data for a VoiceServerUpdate event.
|
||||
type VoiceServerUpdate struct {
|
||||
Token string `json:"token"`
|
||||
GuildID string `json:"guild_id"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
}
|
||||
|
||||
// VoiceStateUpdate is the data for a VoiceStateUpdate event.
|
||||
type VoiceStateUpdate struct {
|
||||
*VoiceState
|
||||
}
|
||||
|
12
vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go
generated
vendored
12
vendor/github.com/bwmarrin/discordgo/examples/airhorn/main.go
generated
vendored
@ -13,7 +13,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&token, "t", "", "Account Token")
|
||||
flag.StringVar(&token, "t", "", "Bot Token")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new Discord session using the provided token.
|
||||
dg, err := discordgo.New(token)
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + token)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating Discord session: ", err)
|
||||
return
|
||||
@ -102,7 +102,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
// This function will be called (due to AddHandler above) every time a new
|
||||
// guild is joined.
|
||||
func guildCreate(s *discordgo.Session, event *discordgo.GuildCreate) {
|
||||
if event.Guild.Unavailable != nil {
|
||||
if event.Guild.Unavailable {
|
||||
return
|
||||
}
|
||||
|
||||
@ -131,6 +131,10 @@ func loadSound() error {
|
||||
|
||||
// If this is the end of the file, just return.
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
13
vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go
generated
vendored
13
vendor/github.com/bwmarrin/discordgo/examples/new_basic/main.go
generated
vendored
@ -10,24 +10,19 @@ import (
|
||||
|
||||
// Variables used for command line parameters
|
||||
var (
|
||||
Email string
|
||||
Password string
|
||||
Token string
|
||||
Token string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
flag.StringVar(&Email, "e", "", "Account Email")
|
||||
flag.StringVar(&Password, "p", "", "Account Password")
|
||||
flag.StringVar(&Token, "t", "", "Account Token")
|
||||
flag.StringVar(&Token, "t", "", "Bot Token")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a new Discord session using the provided login information.
|
||||
// Use discordgo.New(Token) to just use a token for login.
|
||||
dg, err := discordgo.New(Email, Password, Token)
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + Token)
|
||||
if err != nil {
|
||||
fmt.Println("error creating Discord session,", err)
|
||||
return
|
||||
|
14
vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go
generated
vendored
14
vendor/github.com/bwmarrin/discordgo/examples/pingpong/main.go
generated
vendored
@ -9,24 +9,20 @@ import (
|
||||
|
||||
// Variables used for command line parameters
|
||||
var (
|
||||
Email string
|
||||
Password string
|
||||
Token string
|
||||
BotID string
|
||||
Token string
|
||||
BotID string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
flag.StringVar(&Email, "e", "", "Account Email")
|
||||
flag.StringVar(&Password, "p", "", "Account Password")
|
||||
flag.StringVar(&Token, "t", "", "Account Token")
|
||||
flag.StringVar(&Token, "t", "", "Bot Token")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a new Discord session using the provided login information.
|
||||
dg, err := discordgo.New(Email, Password, Token)
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + Token)
|
||||
if err != nil {
|
||||
fmt.Println("error creating Discord session,", err)
|
||||
return
|
||||
|
100
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
100
vendor/github.com/bwmarrin/discordgo/message.go
generated
vendored
@ -19,8 +19,8 @@ type Message struct {
|
||||
ID string `json:"id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
Content string `json:"content"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
EditedTimestamp string `json:"edited_timestamp"`
|
||||
Timestamp Timestamp `json:"timestamp"`
|
||||
EditedTimestamp Timestamp `json:"edited_timestamp"`
|
||||
MentionRoles []string `json:"mention_roles"`
|
||||
Tts bool `json:"tts"`
|
||||
MentionEveryone bool `json:"mention_everyone"`
|
||||
@ -28,6 +28,7 @@ type Message struct {
|
||||
Attachments []*MessageAttachment `json:"attachments"`
|
||||
Embeds []*MessageEmbed `json:"embeds"`
|
||||
Mentions []*User `json:"mentions"`
|
||||
Reactions []*MessageReactions `json:"reactions"`
|
||||
}
|
||||
|
||||
// A MessageAttachment stores data for message attachments.
|
||||
@ -41,31 +42,80 @@ type MessageAttachment struct {
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
// MessageEmbedFooter is a part of a MessageEmbed struct.
|
||||
type MessageEmbedFooter struct {
|
||||
Text string `json:"text,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
ProxyIconURL string `json:"proxy_icon_url,omitempty"`
|
||||
}
|
||||
|
||||
// MessageEmbedImage is a part of a MessageEmbed struct.
|
||||
type MessageEmbedImage struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
ProxyURL string `json:"proxy_url,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
}
|
||||
|
||||
// MessageEmbedThumbnail is a part of a MessageEmbed struct.
|
||||
type MessageEmbedThumbnail struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
ProxyURL string `json:"proxy_url,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
}
|
||||
|
||||
// MessageEmbedVideo is a part of a MessageEmbed struct.
|
||||
type MessageEmbedVideo struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
ProxyURL string `json:"proxy_url,omitempty"`
|
||||
Width int `json:"width,omitempty"`
|
||||
Height int `json:"height,omitempty"`
|
||||
}
|
||||
|
||||
// MessageEmbedProvider is a part of a MessageEmbed struct.
|
||||
type MessageEmbedProvider struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
}
|
||||
|
||||
// MessageEmbedAuthor is a part of a MessageEmbed struct.
|
||||
type MessageEmbedAuthor struct {
|
||||
URL string `json:"url,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
IconURL string `json:"icon_url,omitempty"`
|
||||
ProxyIconURL string `json:"proxy_icon_url,omitempty"`
|
||||
}
|
||||
|
||||
// MessageEmbedField is a part of a MessageEmbed struct.
|
||||
type MessageEmbedField struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Value string `json:"value,omitempty"`
|
||||
Inline bool `json:"inline,omitempty"`
|
||||
}
|
||||
|
||||
// An MessageEmbed stores data for message embeds.
|
||||
type MessageEmbed struct {
|
||||
URL string `json:"url"`
|
||||
Type string `json:"type"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
Thumbnail *struct {
|
||||
URL string `json:"url"`
|
||||
ProxyURL string `json:"proxy_url"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"thumbnail"`
|
||||
Provider *struct {
|
||||
URL string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
} `json:"provider"`
|
||||
Author *struct {
|
||||
URL string `json:"url"`
|
||||
Name string `json:"name"`
|
||||
} `json:"author"`
|
||||
Video *struct {
|
||||
URL string `json:"url"`
|
||||
Width int `json:"width"`
|
||||
Height int `json:"height"`
|
||||
} `json:"video"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Timestamp string `json:"timestamp,omitempty"`
|
||||
Color int `json:"color,omitempty"`
|
||||
Footer *MessageEmbedFooter `json:"footer,omitempty"`
|
||||
Image *MessageEmbedImage `json:"image,omitempty"`
|
||||
Thumbnail *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
|
||||
Video *MessageEmbedVideo `json:"video,omitempty"`
|
||||
Provider *MessageEmbedProvider `json:"provider,omitempty"`
|
||||
Author *MessageEmbedAuthor `json:"author,omitempty"`
|
||||
Fields []*MessageEmbedField `json:"fields,omitempty"`
|
||||
}
|
||||
|
||||
// MessageReactions holds a reactions object for a message.
|
||||
type MessageReactions struct {
|
||||
Count int `json:"count"`
|
||||
Me bool `json:"me"`
|
||||
Emoji *Emoji `json:"emoji"`
|
||||
}
|
||||
|
||||
// ContentWithMentionsReplaced will replace all @<id> mentions with the
|
||||
|
13
vendor/github.com/bwmarrin/discordgo/oauth2.go
generated
vendored
13
vendor/github.com/bwmarrin/discordgo/oauth2.go
generated
vendored
@ -21,13 +21,14 @@ type Application struct {
|
||||
Icon string `json:"icon,omitempty"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
RedirectURIs *[]string `json:"redirect_uris,omitempty"`
|
||||
Owner *User `json:"owner"`
|
||||
}
|
||||
|
||||
// Application returns an Application structure of a specific Application
|
||||
// appID : The ID of an Application
|
||||
func (s *Session) Application(appID string) (st *Application, err error) {
|
||||
|
||||
body, err := s.Request("GET", EndpointApplication(appID), nil)
|
||||
body, err := s.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication(""))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -39,7 +40,7 @@ func (s *Session) Application(appID string) (st *Application, err error) {
|
||||
// Applications returns all applications for the authenticated user
|
||||
func (s *Session) Applications() (st []*Application, err error) {
|
||||
|
||||
body, err := s.Request("GET", EndpointApplications, nil)
|
||||
body, err := s.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -59,7 +60,7 @@ func (s *Session) ApplicationCreate(ap *Application) (st *Application, err error
|
||||
RedirectURIs *[]string `json:"redirect_uris,omitempty"`
|
||||
}{ap.Name, ap.Description, ap.RedirectURIs}
|
||||
|
||||
body, err := s.Request("POST", EndpointApplications, data)
|
||||
body, err := s.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -78,7 +79,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat
|
||||
RedirectURIs *[]string `json:"redirect_uris,omitempty"`
|
||||
}{ap.Name, ap.Description, ap.RedirectURIs}
|
||||
|
||||
body, err := s.Request("PUT", EndpointApplication(appID), data)
|
||||
body, err := s.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication(""))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -91,7 +92,7 @@ func (s *Session) ApplicationUpdate(appID string, ap *Application) (st *Applicat
|
||||
// appID : The ID of an Application
|
||||
func (s *Session) ApplicationDelete(appID string) (err error) {
|
||||
|
||||
_, err = s.Request("DELETE", EndpointApplication(appID), nil)
|
||||
_, err = s.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication(""))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -110,7 +111,7 @@ func (s *Session) ApplicationDelete(appID string) (err error) {
|
||||
// NOTE: func name may change, if I can think up something better.
|
||||
func (s *Session) ApplicationBotCreate(appID string) (st *User, err error) {
|
||||
|
||||
body, err := s.Request("POST", EndpointApplicationsBot(appID), nil)
|
||||
body, err := s.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot(""))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
157
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
Normal file
157
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// RateLimiter holds all ratelimit buckets
|
||||
type RateLimiter struct {
|
||||
sync.Mutex
|
||||
global *Bucket
|
||||
buckets map[string]*Bucket
|
||||
globalRateLimit time.Duration
|
||||
}
|
||||
|
||||
// NewRatelimiter returns a new RateLimiter
|
||||
func NewRatelimiter() *RateLimiter {
|
||||
|
||||
return &RateLimiter{
|
||||
buckets: make(map[string]*Bucket),
|
||||
global: &Bucket{Key: "global"},
|
||||
}
|
||||
}
|
||||
|
||||
// getBucket retrieves or creates a bucket
|
||||
func (r *RateLimiter) getBucket(key string) *Bucket {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
if bucket, ok := r.buckets[key]; ok {
|
||||
return bucket
|
||||
}
|
||||
|
||||
b := &Bucket{
|
||||
remaining: 1,
|
||||
Key: key,
|
||||
global: r.global,
|
||||
}
|
||||
|
||||
r.buckets[key] = b
|
||||
return b
|
||||
}
|
||||
|
||||
// LockBucket Locks until a request can be made
|
||||
func (r *RateLimiter) LockBucket(bucketID string) *Bucket {
|
||||
|
||||
b := r.getBucket(bucketID)
|
||||
|
||||
b.Lock()
|
||||
|
||||
// If we ran out of calls and the reset time is still ahead of us
|
||||
// then we need to take it easy and relax a little
|
||||
if b.remaining < 1 && b.reset.After(time.Now()) {
|
||||
time.Sleep(b.reset.Sub(time.Now()))
|
||||
|
||||
}
|
||||
|
||||
// Check for global ratelimits
|
||||
r.global.Lock()
|
||||
r.global.Unlock()
|
||||
|
||||
b.remaining--
|
||||
return b
|
||||
}
|
||||
|
||||
// Bucket represents a ratelimit bucket, each bucket gets ratelimited individually (-global ratelimits)
|
||||
type Bucket struct {
|
||||
sync.Mutex
|
||||
Key string
|
||||
remaining int
|
||||
limit int
|
||||
reset time.Time
|
||||
global *Bucket
|
||||
}
|
||||
|
||||
// Release unlocks the bucket and reads the headers to update the buckets ratelimit info
|
||||
// and locks up the whole thing in case if there's a global ratelimit.
|
||||
func (b *Bucket) Release(headers http.Header) error {
|
||||
|
||||
defer b.Unlock()
|
||||
if headers == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
remaining := headers.Get("X-RateLimit-Remaining")
|
||||
reset := headers.Get("X-RateLimit-Reset")
|
||||
global := headers.Get("X-RateLimit-Global")
|
||||
retryAfter := headers.Get("Retry-After")
|
||||
|
||||
// If it's global just keep the main ratelimit mutex locked
|
||||
if global != "" {
|
||||
parsedAfter, err := strconv.Atoi(retryAfter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Lock it in a new goroutine so that this isn't a blocking call
|
||||
go func() {
|
||||
// Make sure if several requests were waiting we don't sleep for n * retry-after
|
||||
// where n is the amount of requests that were going on
|
||||
sleepTo := time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
|
||||
|
||||
b.global.Lock()
|
||||
|
||||
sleepDuration := sleepTo.Sub(time.Now())
|
||||
if sleepDuration > 0 {
|
||||
time.Sleep(sleepDuration)
|
||||
}
|
||||
|
||||
b.global.Unlock()
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update reset time if either retry after or reset headers are present
|
||||
// Prefer retryafter because it's more accurate with time sync and whatnot
|
||||
if retryAfter != "" {
|
||||
parsedAfter, err := strconv.ParseInt(retryAfter, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.reset = time.Now().Add(time.Duration(parsedAfter) * time.Millisecond)
|
||||
|
||||
} else if reset != "" {
|
||||
// Calculate the reset time by using the date header returned from discord
|
||||
discordTime, err := http.ParseTime(headers.Get("Date"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unix, err := strconv.ParseInt(reset, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Calculate the time until reset and add it to the current local time
|
||||
// some extra time is added because without it i still encountered 429's.
|
||||
// The added amount is the lowest amount that gave no 429's
|
||||
// in 1k requests
|
||||
delta := time.Unix(unix, 0).Sub(discordTime) + time.Millisecond*250
|
||||
b.reset = time.Now().Add(delta)
|
||||
}
|
||||
|
||||
// Udpate remaining if header is present
|
||||
if remaining != "" {
|
||||
parsedRemaining, err := strconv.ParseInt(remaining, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.remaining = int(parsedRemaining)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
712
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
712
vendor/github.com/bwmarrin/discordgo/restapi.go
generated
vendored
File diff suppressed because it is too large
Load Diff
124
vendor/github.com/bwmarrin/discordgo/state.go
generated
vendored
124
vendor/github.com/bwmarrin/discordgo/state.go
generated
vendored
@ -55,33 +55,6 @@ func NewState() *State {
|
||||
}
|
||||
}
|
||||
|
||||
// OnReady takes a Ready event and updates all internal state.
|
||||
func (s *State) OnReady(r *Ready) error {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
s.Ready = *r
|
||||
|
||||
for _, g := range s.Guilds {
|
||||
s.guildMap[g.ID] = g
|
||||
|
||||
for _, c := range g.Channels {
|
||||
c.GuildID = g.ID
|
||||
s.channelMap[c.ID] = c
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range s.PrivateChannels {
|
||||
s.channelMap[c.ID] = c
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GuildAdd adds a guild to the current world state, or
|
||||
// updates it if it already exists.
|
||||
func (s *State) GuildAdd(guild *Guild) error {
|
||||
@ -94,20 +67,30 @@ func (s *State) GuildAdd(guild *Guild) error {
|
||||
|
||||
// Update the channels to point to the right guild, adding them to the channelMap as we go
|
||||
for _, c := range guild.Channels {
|
||||
c.GuildID = guild.ID
|
||||
s.channelMap[c.ID] = c
|
||||
}
|
||||
|
||||
// If the guild exists, replace it.
|
||||
if g, ok := s.guildMap[guild.ID]; ok {
|
||||
// If this guild already exists with data, don't stomp on props.
|
||||
if g.Unavailable != nil && !*g.Unavailable {
|
||||
// We are about to replace `g` in the state with `guild`, but first we need to
|
||||
// make sure we preserve any fields that the `guild` doesn't contain from `g`.
|
||||
if guild.Roles == nil {
|
||||
guild.Roles = g.Roles
|
||||
}
|
||||
if guild.Emojis == nil {
|
||||
guild.Emojis = g.Emojis
|
||||
}
|
||||
if guild.Members == nil {
|
||||
guild.Members = g.Members
|
||||
}
|
||||
if guild.Presences == nil {
|
||||
guild.Presences = g.Presences
|
||||
}
|
||||
if guild.Channels == nil {
|
||||
guild.Channels = g.Channels
|
||||
}
|
||||
if guild.VoiceStates == nil {
|
||||
guild.VoiceStates = g.VoiceStates
|
||||
}
|
||||
|
||||
*g = *guild
|
||||
return nil
|
||||
}
|
||||
@ -325,8 +308,12 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
||||
|
||||
// If the channel exists, replace it
|
||||
if c, ok := s.channelMap[channel.ID]; ok {
|
||||
channel.Messages = c.Messages
|
||||
channel.PermissionOverwrites = c.PermissionOverwrites
|
||||
if channel.Messages == nil {
|
||||
channel.Messages = c.Messages
|
||||
}
|
||||
if channel.PermissionOverwrites == nil {
|
||||
channel.PermissionOverwrites = c.PermissionOverwrites
|
||||
}
|
||||
|
||||
*c = *channel
|
||||
return nil
|
||||
@ -511,6 +498,12 @@ func (s *State) MessageAdd(message *Message) error {
|
||||
if message.Attachments != nil {
|
||||
m.Attachments = message.Attachments
|
||||
}
|
||||
if message.Timestamp != "" {
|
||||
m.Timestamp = message.Timestamp
|
||||
}
|
||||
if message.Author != nil {
|
||||
m.Author = message.Author
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -602,18 +595,63 @@ func (s *State) Message(channelID, messageID string) (*Message, error) {
|
||||
return nil, errors.New("Message not found.")
|
||||
}
|
||||
|
||||
// OnReady takes a Ready event and updates all internal state.
|
||||
func (s *State) onReady(se *Session, r *Ready) (err error) {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
// We must track at least the current user for Voice, even
|
||||
// if state is disabled, store the bare essentials.
|
||||
if !se.StateEnabled {
|
||||
ready := Ready{
|
||||
Version: r.Version,
|
||||
SessionID: r.SessionID,
|
||||
HeartbeatInterval: r.HeartbeatInterval,
|
||||
User: r.User,
|
||||
}
|
||||
|
||||
s.Ready = ready
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
s.Ready = *r
|
||||
|
||||
for _, g := range s.Guilds {
|
||||
s.guildMap[g.ID] = g
|
||||
|
||||
for _, c := range g.Channels {
|
||||
s.channelMap[c.ID] = c
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range s.PrivateChannels {
|
||||
s.channelMap[c.ID] = c
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// onInterface handles all events related to states.
|
||||
func (s *State) onInterface(se *Session, i interface{}) (err error) {
|
||||
if s == nil {
|
||||
return ErrNilState
|
||||
}
|
||||
|
||||
r, ok := i.(*Ready)
|
||||
if ok {
|
||||
return s.onReady(se, r)
|
||||
}
|
||||
|
||||
if !se.StateEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch t := i.(type) {
|
||||
case *Ready:
|
||||
err = s.OnReady(t)
|
||||
case *GuildCreate:
|
||||
err = s.GuildAdd(t.Guild)
|
||||
case *GuildUpdate:
|
||||
@ -685,6 +723,9 @@ func (s *State) onInterface(se *Session, i interface{}) (err error) {
|
||||
// userID : The ID of the user to calculate permissions for.
|
||||
// channelID : The ID of the channel to calculate permission for.
|
||||
func (s *State) UserChannelPermissions(userID, channelID string) (apermissions int, err error) {
|
||||
if s == nil {
|
||||
return 0, ErrNilState
|
||||
}
|
||||
|
||||
channel, err := s.Channel(channelID)
|
||||
if err != nil {
|
||||
@ -706,6 +747,13 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
|
||||
return
|
||||
}
|
||||
|
||||
for _, role := range guild.Roles {
|
||||
if role.ID == guild.ID {
|
||||
apermissions |= role.Permissions
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for _, role := range guild.Roles {
|
||||
for _, roleID := range member.Roles {
|
||||
if role.ID == roleID {
|
||||
@ -715,7 +763,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
|
||||
}
|
||||
}
|
||||
|
||||
if apermissions&PermissionManageRoles > 0 {
|
||||
if apermissions&PermissionAdministrator > 0 {
|
||||
apermissions |= PermissionAll
|
||||
}
|
||||
|
||||
@ -738,7 +786,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
|
||||
}
|
||||
}
|
||||
|
||||
if apermissions&PermissionManageRoles > 0 {
|
||||
if apermissions&PermissionAdministrator > 0 {
|
||||
apermissions |= PermissionAllChannel
|
||||
}
|
||||
|
||||
|
272
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
272
vendor/github.com/bwmarrin/discordgo/structs.go
generated
vendored
@ -13,7 +13,7 @@ package discordgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -53,6 +53,9 @@ type Session struct {
|
||||
// Whether the Data Websocket is ready
|
||||
DataReady bool // NOTE: Maye be deprecated soon
|
||||
|
||||
// Max number of REST API retries
|
||||
MaxRestRetries int
|
||||
|
||||
// Status stores the currect status of the websocket connection
|
||||
// this is being tested, may stay, may go away.
|
||||
status int32
|
||||
@ -70,13 +73,10 @@ type Session struct {
|
||||
// StateEnabled is true.
|
||||
State *State
|
||||
|
||||
handlersMu sync.RWMutex
|
||||
// This is a mapping of event struct to a reflected value
|
||||
// for event handlers.
|
||||
// We store the reflected value instead of the function
|
||||
// reference as it is more performant, instead of re-reflecting
|
||||
// the function each event.
|
||||
handlers map[interface{}][]reflect.Value
|
||||
// Event handlers
|
||||
handlersMu sync.RWMutex
|
||||
handlers map[string][]*eventHandlerInstance
|
||||
onceHandlers map[string][]*eventHandlerInstance
|
||||
|
||||
// The websocket connection.
|
||||
wsConn *websocket.Conn
|
||||
@ -85,9 +85,7 @@ type Session struct {
|
||||
listening chan interface{}
|
||||
|
||||
// used to deal with rate limits
|
||||
// may switch to slices later
|
||||
// TODO: performance test map vs slices
|
||||
rateLimit rateLimitMutex
|
||||
ratelimiter *RateLimiter
|
||||
|
||||
// sequence tracks the current gateway api websocket sequence number
|
||||
sequence int
|
||||
@ -108,12 +106,6 @@ type rateLimitMutex struct {
|
||||
// bucket map[string]*sync.Mutex // TODO :)
|
||||
}
|
||||
|
||||
// A Resumed struct holds the data received in a RESUMED event
|
||||
type Resumed struct {
|
||||
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
|
||||
Trace []string `json:"_trace"`
|
||||
}
|
||||
|
||||
// A VoiceRegion stores data for a specific voice region server.
|
||||
type VoiceRegion struct {
|
||||
ID string `json:"id"`
|
||||
@ -137,17 +129,17 @@ type ICEServer struct {
|
||||
|
||||
// A Invite stores all data related to a specific Discord Guild or Channel invite.
|
||||
type Invite struct {
|
||||
Guild *Guild `json:"guild"`
|
||||
Channel *Channel `json:"channel"`
|
||||
Inviter *User `json:"inviter"`
|
||||
Code string `json:"code"`
|
||||
CreatedAt string `json:"created_at"` // TODO make timestamp
|
||||
MaxAge int `json:"max_age"`
|
||||
Uses int `json:"uses"`
|
||||
MaxUses int `json:"max_uses"`
|
||||
XkcdPass string `json:"xkcdpass"`
|
||||
Revoked bool `json:"revoked"`
|
||||
Temporary bool `json:"temporary"`
|
||||
Guild *Guild `json:"guild"`
|
||||
Channel *Channel `json:"channel"`
|
||||
Inviter *User `json:"inviter"`
|
||||
Code string `json:"code"`
|
||||
CreatedAt Timestamp `json:"created_at"`
|
||||
MaxAge int `json:"max_age"`
|
||||
Uses int `json:"uses"`
|
||||
MaxUses int `json:"max_uses"`
|
||||
XkcdPass string `json:"xkcdpass"`
|
||||
Revoked bool `json:"revoked"`
|
||||
Temporary bool `json:"temporary"`
|
||||
}
|
||||
|
||||
// A Channel holds all data related to an individual Discord channel.
|
||||
@ -183,6 +175,17 @@ type Emoji struct {
|
||||
RequireColons bool `json:"require_colons"`
|
||||
}
|
||||
|
||||
// APIName returns an correctly formatted API name for use in the MessageReactions endpoints.
|
||||
func (e *Emoji) APIName() string {
|
||||
if e.ID != "" && e.Name != "" {
|
||||
return e.Name + ":" + e.ID
|
||||
}
|
||||
if e.Name != "" {
|
||||
return e.Name
|
||||
}
|
||||
return e.ID
|
||||
}
|
||||
|
||||
// VerificationLevel type defination
|
||||
type VerificationLevel int
|
||||
|
||||
@ -204,9 +207,10 @@ type Guild struct {
|
||||
AfkChannelID string `json:"afk_channel_id"`
|
||||
EmbedChannelID string `json:"embed_channel_id"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
JoinedAt string `json:"joined_at"` // make this a timestamp
|
||||
JoinedAt Timestamp `json:"joined_at"`
|
||||
Splash string `json:"splash"`
|
||||
AfkTimeout int `json:"afk_timeout"`
|
||||
MemberCount int `json:"member_count"`
|
||||
VerificationLevel VerificationLevel `json:"verification_level"`
|
||||
EmbedEnabled bool `json:"embed_enabled"`
|
||||
Large bool `json:"large"` // ??
|
||||
@ -217,7 +221,16 @@ type Guild struct {
|
||||
Presences []*Presence `json:"presences"`
|
||||
Channels []*Channel `json:"channels"`
|
||||
VoiceStates []*VoiceState `json:"voice_states"`
|
||||
Unavailable *bool `json:"unavailable"`
|
||||
Unavailable bool `json:"unavailable"`
|
||||
}
|
||||
|
||||
// A UserGuild holds a brief version of a Guild
|
||||
type UserGuild struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Icon string `json:"icon"`
|
||||
Owner bool `json:"owner"`
|
||||
Permissions int `json:"permissions"`
|
||||
}
|
||||
|
||||
// A GuildParams stores all the data needed to update discord guild settings
|
||||
@ -232,6 +245,7 @@ type Role struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Managed bool `json:"managed"`
|
||||
Mentionable bool `json:"mentionable"`
|
||||
Hoist bool `json:"hoist"`
|
||||
Color int `json:"color"`
|
||||
Position int `json:"position"`
|
||||
@ -253,9 +267,11 @@ type VoiceState struct {
|
||||
|
||||
// A Presence stores the online, offline, or idle and game status of Guild members.
|
||||
type Presence struct {
|
||||
User *User `json:"user"`
|
||||
Status string `json:"status"`
|
||||
Game *Game `json:"game"`
|
||||
User *User `json:"user"`
|
||||
Status Status `json:"status"`
|
||||
Game *Game `json:"game"`
|
||||
Nick string `json:"nick"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
// A Game struct holds the name of the "playing .." game for a user
|
||||
@ -265,6 +281,38 @@ type Game struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals json to Game struct
|
||||
func (g *Game) UnmarshalJSON(bytes []byte) error {
|
||||
temp := &struct {
|
||||
Name string `json:"name"`
|
||||
Type json.RawMessage `json:"type"`
|
||||
URL string `json:"url"`
|
||||
}{}
|
||||
err := json.Unmarshal(bytes, temp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
g.Name = temp.Name
|
||||
g.URL = temp.URL
|
||||
|
||||
if temp.Type != nil {
|
||||
err = json.Unmarshal(temp.Type, &g.Type)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s := ""
|
||||
err = json.Unmarshal(temp.Type, &s)
|
||||
if err == nil {
|
||||
g.Type, err = strconv.Atoi(s)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// A Member stores user information for Guild members.
|
||||
type Member struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
@ -291,21 +339,35 @@ type User struct {
|
||||
|
||||
// A Settings stores data for a specific users Discord client settings.
|
||||
type Settings struct {
|
||||
RenderEmbeds bool `json:"render_embeds"`
|
||||
InlineEmbedMedia bool `json:"inline_embed_media"`
|
||||
InlineAttachmentMedia bool `json:"inline_attachment_media"`
|
||||
EnableTtsCommand bool `json:"enable_tts_command"`
|
||||
MessageDisplayCompact bool `json:"message_display_compact"`
|
||||
ShowCurrentGame bool `json:"show_current_game"`
|
||||
AllowEmailFriendRequest bool `json:"allow_email_friend_request"`
|
||||
ConvertEmoticons bool `json:"convert_emoticons"`
|
||||
Locale string `json:"locale"`
|
||||
Theme string `json:"theme"`
|
||||
GuildPositions []string `json:"guild_positions"`
|
||||
RestrictedGuilds []string `json:"restricted_guilds"`
|
||||
FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"`
|
||||
RenderEmbeds bool `json:"render_embeds"`
|
||||
InlineEmbedMedia bool `json:"inline_embed_media"`
|
||||
InlineAttachmentMedia bool `json:"inline_attachment_media"`
|
||||
EnableTtsCommand bool `json:"enable_tts_command"`
|
||||
MessageDisplayCompact bool `json:"message_display_compact"`
|
||||
ShowCurrentGame bool `json:"show_current_game"`
|
||||
ConvertEmoticons bool `json:"convert_emoticons"`
|
||||
Locale string `json:"locale"`
|
||||
Theme string `json:"theme"`
|
||||
GuildPositions []string `json:"guild_positions"`
|
||||
RestrictedGuilds []string `json:"restricted_guilds"`
|
||||
FriendSourceFlags *FriendSourceFlags `json:"friend_source_flags"`
|
||||
Status Status `json:"status"`
|
||||
DetectPlatformAccounts bool `json:"detect_platform_accounts"`
|
||||
DeveloperMode bool `json:"developer_mode"`
|
||||
}
|
||||
|
||||
// Status type defination
|
||||
type Status string
|
||||
|
||||
// Constants for Status with the different current available status
|
||||
const (
|
||||
StatusOnline Status = "online"
|
||||
StatusIdle Status = "idle"
|
||||
StatusDoNotDisturb Status = "dnd"
|
||||
StatusInvisible Status = "invisible"
|
||||
StatusOffline Status = "offline"
|
||||
)
|
||||
|
||||
// FriendSourceFlags stores ... TODO :)
|
||||
type FriendSourceFlags struct {
|
||||
All bool `json:"all"`
|
||||
@ -313,32 +375,6 @@ type FriendSourceFlags struct {
|
||||
MutualFriends bool `json:"mutual_friends"`
|
||||
}
|
||||
|
||||
// An Event provides a basic initial struct for all websocket event.
|
||||
type Event struct {
|
||||
Operation int `json:"op"`
|
||||
Sequence int `json:"s"`
|
||||
Type string `json:"t"`
|
||||
RawData json.RawMessage `json:"d"`
|
||||
Struct interface{} `json:"-"`
|
||||
}
|
||||
|
||||
// A Ready stores all data for the websocket READY event.
|
||||
type Ready struct {
|
||||
Version int `json:"v"`
|
||||
SessionID string `json:"session_id"`
|
||||
HeartbeatInterval time.Duration `json:"heartbeat_interval"`
|
||||
User *User `json:"user"`
|
||||
ReadState []*ReadState `json:"read_state"`
|
||||
PrivateChannels []*Channel `json:"private_channels"`
|
||||
Guilds []*Guild `json:"guilds"`
|
||||
|
||||
// Undocumented fields
|
||||
Settings *Settings `json:"user_settings"`
|
||||
UserGuildSettings []*UserGuildSettings `json:"user_guild_settings"`
|
||||
Relationships []*Relationship `json:"relationships"`
|
||||
Presences []*Presence `json:"presences"`
|
||||
}
|
||||
|
||||
// A Relationship between the logged in user and Relationship.User
|
||||
type Relationship struct {
|
||||
User *User `json:"user"`
|
||||
@ -361,54 +397,21 @@ type ReadState struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// A TypingStart stores data for the typing start websocket event.
|
||||
type TypingStart struct {
|
||||
UserID string `json:"user_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
Timestamp int `json:"timestamp"`
|
||||
// An Ack is used to ack messages
|
||||
type Ack struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// A PresenceUpdate stores data for the presence update websocket event.
|
||||
type PresenceUpdate struct {
|
||||
Presence
|
||||
GuildID string `json:"guild_id"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
// A MessageAck stores data for the message ack websocket event.
|
||||
type MessageAck struct {
|
||||
MessageID string `json:"message_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
}
|
||||
|
||||
// A GuildIntegrationsUpdate stores data for the guild integrations update
|
||||
// websocket event.
|
||||
type GuildIntegrationsUpdate struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// A GuildRole stores data for guild role websocket events.
|
||||
// A GuildRole stores data for guild roles.
|
||||
type GuildRole struct {
|
||||
Role *Role `json:"role"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// A GuildRoleDelete stores data for the guild role delete websocket event.
|
||||
type GuildRoleDelete struct {
|
||||
RoleID string `json:"role_id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// A GuildBan stores data for a guild ban.
|
||||
type GuildBan struct {
|
||||
User *User `json:"user"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// A GuildEmojisUpdate stores data for a guild emoji update event.
|
||||
type GuildEmojisUpdate struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
Emojis []*Emoji `json:"emojis"`
|
||||
Reason string `json:"reason"`
|
||||
User *User `json:"user"`
|
||||
}
|
||||
|
||||
// A GuildIntegration stores data for a guild integration.
|
||||
@ -464,6 +467,41 @@ type UserGuildSettingsEdit struct {
|
||||
ChannelOverrides map[string]*UserGuildSettingsChannelOverride `json:"channel_overrides"`
|
||||
}
|
||||
|
||||
// An APIErrorMessage is an api error message returned from discord
|
||||
type APIErrorMessage struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// Webhook stores the data for a webhook.
|
||||
type Webhook struct {
|
||||
ID string `json:"id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
User *User `json:"user"`
|
||||
Name string `json:"name"`
|
||||
Avatar string `json:"avatar"`
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// WebhookParams is a struct for webhook params, used in the WebhookExecute command.
|
||||
type WebhookParams struct {
|
||||
Content string `json:"content,omitempty"`
|
||||
Username string `json:"username,omitempty"`
|
||||
AvatarURL string `json:"avatar_url,omitempty"`
|
||||
TTS bool `json:"tts,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
Embeds []*MessageEmbed `json:"embeds,omitempty"`
|
||||
}
|
||||
|
||||
// MessageReaction stores the data for a message reaction.
|
||||
type MessageReaction struct {
|
||||
UserID string `json:"user_id"`
|
||||
MessageID string `json:"message_id"`
|
||||
Emoji Emoji `json:"emoji"`
|
||||
ChannelID string `json:"channel_id"`
|
||||
}
|
||||
|
||||
// Constants for the different bit offsets of text channel permissions
|
||||
const (
|
||||
PermissionReadMessages = 1 << (iota + 10)
|
||||
@ -474,6 +512,7 @@ const (
|
||||
PermissionAttachFiles
|
||||
PermissionReadMessageHistory
|
||||
PermissionMentionEveryone
|
||||
PermissionUseExternalEmojis
|
||||
)
|
||||
|
||||
// Constants for the different bit offsets of voice permissions
|
||||
@ -486,12 +525,21 @@ const (
|
||||
PermissionVoiceUseVAD
|
||||
)
|
||||
|
||||
// Constants for general management.
|
||||
const (
|
||||
PermissionChangeNickname = 1 << (iota + 26)
|
||||
PermissionManageNicknames
|
||||
PermissionManageRoles
|
||||
PermissionManageWebhooks
|
||||
PermissionManageEmojis
|
||||
)
|
||||
|
||||
// Constants for the different bit offsets of general permissions
|
||||
const (
|
||||
PermissionCreateInstantInvite = 1 << iota
|
||||
PermissionKickMembers
|
||||
PermissionBanMembers
|
||||
PermissionManageRoles
|
||||
PermissionAdministrator
|
||||
PermissionManageChannels
|
||||
PermissionManageServer
|
||||
|
||||
|
123
vendor/github.com/bwmarrin/discordgo/tools/cmd/eventhandlers/main.go
generated
vendored
Normal file
123
vendor/github.com/bwmarrin/discordgo/tools/cmd/eventhandlers/main.go
generated
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var eventHandlerTmpl = template.Must(template.New("eventHandler").Funcs(template.FuncMap{
|
||||
"constName": constName,
|
||||
"isDiscordEvent": isDiscordEvent,
|
||||
"privateName": privateName,
|
||||
}).Parse(`// Code generated by \"eventhandlers\"; DO NOT EDIT
|
||||
// See events.go
|
||||
|
||||
package discordgo
|
||||
|
||||
// Following are all the event types.
|
||||
// Event type values are used to match the events returned by Discord.
|
||||
// EventTypes surrounded by __ are synthetic and are internal to DiscordGo.
|
||||
const ({{range .}}
|
||||
{{privateName .}}EventType = "{{constName .}}"{{end}}
|
||||
)
|
||||
{{range .}}
|
||||
// {{privateName .}}EventHandler is an event handler for {{.}} events.
|
||||
type {{privateName .}}EventHandler func(*Session, *{{.}})
|
||||
|
||||
// Type returns the event type for {{.}} events.
|
||||
func (eh {{privateName .}}EventHandler) Type() string {
|
||||
return {{privateName .}}EventType
|
||||
}
|
||||
|
||||
// New returns a new instance of {{.}}.
|
||||
func (eh {{privateName .}}EventHandler) New() interface{} {
|
||||
return &{{.}}{}
|
||||
}
|
||||
|
||||
// Handle is the handler for {{.}} events.
|
||||
func (eh {{privateName .}}EventHandler) Handle(s *Session, i interface{}) {
|
||||
if t, ok := i.(*{{.}}); ok {
|
||||
eh(s, t)
|
||||
}
|
||||
}
|
||||
{{end}}
|
||||
func handlerForInterface(handler interface{}) EventHandler {
|
||||
switch v := handler.(type) {
|
||||
case func(*Session, interface{}):
|
||||
return interfaceEventHandler(v){{range .}}
|
||||
case func(*Session, *{{.}}):
|
||||
return {{privateName .}}EventHandler(v){{end}}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
func init() { {{range .}}{{if isDiscordEvent .}}
|
||||
registerInterfaceProvider({{privateName .}}EventHandler(nil)){{end}}{{end}}
|
||||
}
|
||||
`))
|
||||
|
||||
func main() {
|
||||
var buf bytes.Buffer
|
||||
dir := filepath.Dir(".")
|
||||
|
||||
fs := token.NewFileSet()
|
||||
parsedFile, err := parser.ParseFile(fs, "events.go", nil, 0)
|
||||
if err != nil {
|
||||
log.Fatalf("warning: internal error: could not parse events.go: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
names := []string{}
|
||||
for object := range parsedFile.Scope.Objects {
|
||||
names = append(names, object)
|
||||
}
|
||||
sort.Strings(names)
|
||||
eventHandlerTmpl.Execute(&buf, names)
|
||||
|
||||
src, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Println("warning: internal error: invalid Go generated:", err)
|
||||
src = buf.Bytes()
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(dir, strings.ToLower("eventhandlers.go")), src, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(buf, "writing output: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
var constRegexp = regexp.MustCompile("([a-z])([A-Z])")
|
||||
|
||||
func constCase(name string) string {
|
||||
return strings.ToUpper(constRegexp.ReplaceAllString(name, "${1}_${2}"))
|
||||
}
|
||||
|
||||
func isDiscordEvent(name string) bool {
|
||||
switch {
|
||||
case name == "Connect", name == "Disconnect", name == "Event", name == "RateLimit", name == "Interface":
|
||||
return false
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func constName(name string) string {
|
||||
if !isDiscordEvent(name) {
|
||||
return "__" + constCase(name) + "__"
|
||||
}
|
||||
|
||||
return constCase(name)
|
||||
}
|
||||
|
||||
func privateName(name string) string {
|
||||
return strings.ToLower(string(name[0])) + name[1:]
|
||||
}
|
58
vendor/github.com/bwmarrin/discordgo/types.go
generated
vendored
Normal file
58
vendor/github.com/bwmarrin/discordgo/types.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
// Discordgo - Discord bindings for Go
|
||||
// Available at https://github.com/bwmarrin/discordgo
|
||||
|
||||
// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// This file contains custom types, currently only a timestamp wrapper.
|
||||
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Timestamp stores a timestamp, as sent by the Discord API.
|
||||
type Timestamp string
|
||||
|
||||
// Parse parses a timestamp string into a time.Time object.
|
||||
// The only time this can fail is if Discord changes their timestamp format.
|
||||
func (t Timestamp) Parse() (time.Time, error) {
|
||||
return time.Parse(time.RFC3339, string(t))
|
||||
}
|
||||
|
||||
// RESTError stores error information about a request with a bad response code.
|
||||
// Message is not always present, there are cases where api calls can fail
|
||||
// without returning a json message.
|
||||
type RESTError struct {
|
||||
Request *http.Request
|
||||
Response *http.Response
|
||||
ResponseBody []byte
|
||||
|
||||
Message *APIErrorMessage // Message may be nil.
|
||||
}
|
||||
|
||||
func newRestError(req *http.Request, resp *http.Response, body []byte) *RESTError {
|
||||
restErr := &RESTError{
|
||||
Request: req,
|
||||
Response: resp,
|
||||
ResponseBody: body,
|
||||
}
|
||||
|
||||
// Attempt to decode the error and assume no message was provided if it fails
|
||||
var msg *APIErrorMessage
|
||||
err := json.Unmarshal(body, &msg)
|
||||
if err == nil {
|
||||
restErr.Message = msg
|
||||
}
|
||||
|
||||
return restErr
|
||||
}
|
||||
|
||||
func (r RESTError) Error() string {
|
||||
return fmt.Sprintf("HTTP %s, %s", r.Response.Status, r.ResponseBody)
|
||||
}
|
4
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
4
vendor/github.com/bwmarrin/discordgo/voice.go
generated
vendored
@ -441,7 +441,7 @@ func (v *VoiceConnection) onEvent(message []byte) {
|
||||
}
|
||||
|
||||
default:
|
||||
v.log(LogError, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
|
||||
v.log(LogDebug, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
|
||||
}
|
||||
|
||||
return
|
||||
@ -570,7 +570,7 @@ func (v *VoiceConnection) udpOpen() (err error) {
|
||||
return fmt.Errorf("received udp packet too small")
|
||||
}
|
||||
|
||||
// Loop over position 4 though 20 to grab the IP address
|
||||
// Loop over position 4 through 20 to grab the IP address
|
||||
// Should never be beyond position 20.
|
||||
var ip string
|
||||
for i := 4; i < 20; i++ {
|
||||
|
108
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
108
vendor/github.com/bwmarrin/discordgo/wsapi.go
generated
vendored
@ -17,9 +17,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
@ -47,6 +45,17 @@ func (s *Session) Open() (err error) {
|
||||
}
|
||||
}()
|
||||
|
||||
// A basic state is a hard requirement for Voice.
|
||||
if s.State == nil {
|
||||
state := NewState()
|
||||
state.TrackChannels = false
|
||||
state.TrackEmojis = false
|
||||
state.TrackMembers = false
|
||||
state.TrackRoles = false
|
||||
state.TrackVoice = false
|
||||
s.State = state
|
||||
}
|
||||
|
||||
if s.wsConn != nil {
|
||||
err = errors.New("Web socket already opened.")
|
||||
return
|
||||
@ -111,9 +120,8 @@ func (s *Session) Open() (err error) {
|
||||
|
||||
s.Unlock()
|
||||
|
||||
s.initialize()
|
||||
s.log(LogInformational, "emit connect event")
|
||||
s.handle(&Connect{})
|
||||
s.handleEvent(connectEventType, &Connect{})
|
||||
|
||||
s.log(LogInformational, "exiting")
|
||||
return
|
||||
@ -269,6 +277,44 @@ func (s *Session) UpdateStatus(idle int, game string) (err error) {
|
||||
return s.UpdateStreamingStatus(idle, game, "")
|
||||
}
|
||||
|
||||
type requestGuildMembersData struct {
|
||||
GuildID string `json:"guild_id"`
|
||||
Query string `json:"query"`
|
||||
Limit int `json:"limit"`
|
||||
}
|
||||
|
||||
type requestGuildMembersOp struct {
|
||||
Op int `json:"op"`
|
||||
Data requestGuildMembersData `json:"d"`
|
||||
}
|
||||
|
||||
// RequestGuildMembers requests guild members from the gateway
|
||||
// The gateway responds with GuildMembersChunk events
|
||||
// guildID : The ID of the guild to request members of
|
||||
// query : String that username starts with, leave empty to return all members
|
||||
// limit : Max number of items to return, or 0 to request all members matched
|
||||
func (s *Session) RequestGuildMembers(guildID, query string, limit int) (err error) {
|
||||
s.log(LogInformational, "called")
|
||||
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
if s.wsConn == nil {
|
||||
return errors.New("no websocket connection exists")
|
||||
}
|
||||
|
||||
data := requestGuildMembersData{
|
||||
GuildID: guildID,
|
||||
Query: query,
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
s.wsMutex.Lock()
|
||||
err = s.wsConn.WriteJSON(requestGuildMembersOp{8, data})
|
||||
s.wsMutex.Unlock()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// onEvent is the "event handler" for all messages received on the
|
||||
// Discord Gateway API websocket connection.
|
||||
//
|
||||
@ -361,16 +407,12 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
||||
// Store the message sequence
|
||||
s.sequence = e.Sequence
|
||||
|
||||
// Map event to registered event handlers and pass it along
|
||||
// to any registered functions
|
||||
i := eventToInterface[e.Type]
|
||||
if i != nil {
|
||||
|
||||
// Create a new instance of the event type.
|
||||
i = reflect.New(reflect.TypeOf(i)).Interface()
|
||||
// Map event to registered event handlers and pass it along to any registered handlers.
|
||||
if eh, ok := registeredInterfaceProviders[e.Type]; ok {
|
||||
e.Struct = eh.New()
|
||||
|
||||
// Attempt to unmarshal our event.
|
||||
if err = json.Unmarshal(e.RawData, i); err != nil {
|
||||
if err = json.Unmarshal(e.RawData, e.Struct); err != nil {
|
||||
s.log(LogError, "error unmarshalling %s event, %s", e.Type, err)
|
||||
}
|
||||
|
||||
@ -381,30 +423,19 @@ func (s *Session) onEvent(messageType int, message []byte) {
|
||||
// it's better to pass along what we received than nothing at all.
|
||||
// TODO: Think about that decision :)
|
||||
// Either way, READY events must fire, even with errors.
|
||||
go s.handle(i)
|
||||
|
||||
s.handleEvent(e.Type, e.Struct)
|
||||
} else {
|
||||
s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData))
|
||||
}
|
||||
|
||||
// Emit event to the OnEvent handler
|
||||
e.Struct = i
|
||||
go s.handle(e)
|
||||
// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
|
||||
s.handleEvent(eventEventType, e)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Code related to voice connections that initiate over the data websocket
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// A VoiceServerUpdate stores the data received during the Voice Server Update
|
||||
// data websocket event. This data is used during the initial Voice Channel
|
||||
// join handshaking.
|
||||
type VoiceServerUpdate struct {
|
||||
Token string `json:"token"`
|
||||
GuildID string `json:"guild_id"`
|
||||
Endpoint string `json:"endpoint"`
|
||||
}
|
||||
|
||||
type voiceChannelJoinData struct {
|
||||
GuildID *string `json:"guild_id"`
|
||||
ChannelID *string `json:"channel_id"`
|
||||
@ -461,7 +492,7 @@ func (s *Session) ChannelVoiceJoin(gID, cID string, mute, deaf bool) (voice *Voi
|
||||
}
|
||||
|
||||
// onVoiceStateUpdate handles Voice State Update events on the data websocket.
|
||||
func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
|
||||
func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
|
||||
|
||||
// If we don't have a connection for the channel, don't bother
|
||||
if st.ChannelID == "" {
|
||||
@ -474,22 +505,13 @@ func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
// Need to have this happen at login and store it in the Session
|
||||
// TODO : This should be done upon connecting to Discord, or
|
||||
// be moved to a small helper function
|
||||
self, err := s.User("@me") // TODO: move to Login/New
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// We only care about events that are about us
|
||||
if st.UserID != self.ID {
|
||||
// We only care about events that are about us.
|
||||
if s.State.User.ID != st.UserID {
|
||||
return
|
||||
}
|
||||
|
||||
// Store the SessionID for later use.
|
||||
voice.UserID = self.ID // TODO: Review
|
||||
voice.UserID = st.UserID
|
||||
voice.sessionID = st.SessionID
|
||||
}
|
||||
|
||||
@ -498,7 +520,7 @@ func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
|
||||
// This is also fired if the Guild's voice region changes while connected
|
||||
// to a voice channel. In that case, need to re-establish connection to
|
||||
// the new region endpoint.
|
||||
func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) {
|
||||
func (s *Session) onVoiceServerUpdate(st *VoiceServerUpdate) {
|
||||
|
||||
s.log(LogInformational, "called")
|
||||
|
||||
@ -655,7 +677,7 @@ func (s *Session) Close() (err error) {
|
||||
// frame and wait for the server to close the connection.
|
||||
err := s.wsConn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
|
||||
if err != nil {
|
||||
s.log(LogError, "error closing websocket, %s", err)
|
||||
s.log(LogInformational, "error closing websocket, %s", err)
|
||||
}
|
||||
|
||||
// TODO: Wait for Discord to actually close the connection.
|
||||
@ -664,7 +686,7 @@ func (s *Session) Close() (err error) {
|
||||
s.log(LogInformational, "closing gateway websocket")
|
||||
err = s.wsConn.Close()
|
||||
if err != nil {
|
||||
s.log(LogError, "error closing websocket, %s", err)
|
||||
s.log(LogInformational, "error closing websocket, %s", err)
|
||||
}
|
||||
|
||||
s.wsConn = nil
|
||||
@ -673,7 +695,7 @@ func (s *Session) Close() (err error) {
|
||||
s.Unlock()
|
||||
|
||||
s.log(LogInformational, "emit disconnect event")
|
||||
s.handle(&Disconnect{})
|
||||
s.handleEvent(disconnectEventType, &Disconnect{})
|
||||
|
||||
return
|
||||
}
|
||||
|
21
vendor/github.com/daaku/go.zipexe/license
generated
vendored
Normal file
21
vendor/github.com/daaku/go.zipexe/license
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright © 2012-2015 Carlos Castillo
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the “Software”), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
142
vendor/github.com/daaku/go.zipexe/zipexe.go
generated
vendored
Normal file
142
vendor/github.com/daaku/go.zipexe/zipexe.go
generated
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
// Package zipexe attempts to open an executable binary file as a zip file.
|
||||
package zipexe
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Opens a zip file by path.
|
||||
func Open(path string) (*zip.Reader, error) {
|
||||
_, rd, err := OpenCloser(path)
|
||||
return rd, err
|
||||
}
|
||||
|
||||
// OpenCloser is like Open but returns an additional Closer to avoid leaking open files.
|
||||
func OpenCloser(path string) (io.Closer, *zip.Reader, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
finfo, err := file.Stat()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
zr, err := NewReader(file, finfo.Size())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return file, zr, nil
|
||||
}
|
||||
|
||||
// Open a zip file, specially handling various binaries that may have been
|
||||
// augmented with zip data.
|
||||
func NewReader(rda io.ReaderAt, size int64) (*zip.Reader, error) {
|
||||
handlers := []func(io.ReaderAt, int64) (*zip.Reader, error){
|
||||
zip.NewReader,
|
||||
zipExeReaderMacho,
|
||||
zipExeReaderElf,
|
||||
zipExeReaderPe,
|
||||
}
|
||||
|
||||
for _, handler := range handlers {
|
||||
zfile, err := handler(rda, size)
|
||||
if err == nil {
|
||||
return zfile, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New("Couldn't Open As Executable")
|
||||
}
|
||||
|
||||
// zipExeReaderMacho treats the file as a Mach-O binary
|
||||
// (Mac OS X / Darwin executable) and attempts to find a zip archive.
|
||||
func zipExeReaderMacho(rda io.ReaderAt, size int64) (*zip.Reader, error) {
|
||||
file, err := macho.NewFile(rda)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var max int64
|
||||
for _, load := range file.Loads {
|
||||
seg, ok := load.(*macho.Segment)
|
||||
if ok {
|
||||
// Check if the segment contains a zip file
|
||||
if zfile, err := zip.NewReader(seg, int64(seg.Filesz)); err == nil {
|
||||
return zfile, nil
|
||||
}
|
||||
|
||||
// Otherwise move end of file pointer
|
||||
end := int64(seg.Offset + seg.Filesz)
|
||||
if end > max {
|
||||
max = end
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No zip file within binary, try appended to end
|
||||
section := io.NewSectionReader(rda, max, size-max)
|
||||
return zip.NewReader(section, section.Size())
|
||||
}
|
||||
|
||||
// zipExeReaderPe treats the file as a Portable Exectuable binary
|
||||
// (Windows executable) and attempts to find a zip archive.
|
||||
func zipExeReaderPe(rda io.ReaderAt, size int64) (*zip.Reader, error) {
|
||||
file, err := pe.NewFile(rda)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var max int64
|
||||
for _, sec := range file.Sections {
|
||||
// Check if this section has a zip file
|
||||
if zfile, err := zip.NewReader(sec, int64(sec.Size)); err == nil {
|
||||
return zfile, nil
|
||||
}
|
||||
|
||||
// Otherwise move end of file pointer
|
||||
end := int64(sec.Offset + sec.Size)
|
||||
if end > max {
|
||||
max = end
|
||||
}
|
||||
}
|
||||
|
||||
// No zip file within binary, try appended to end
|
||||
section := io.NewSectionReader(rda, max, size-max)
|
||||
return zip.NewReader(section, section.Size())
|
||||
}
|
||||
|
||||
// zipExeReaderElf treats the file as a ELF binary
|
||||
// (linux/BSD/etc... executable) and attempts to find a zip archive.
|
||||
func zipExeReaderElf(rda io.ReaderAt, size int64) (*zip.Reader, error) {
|
||||
file, err := elf.NewFile(rda)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var max int64
|
||||
for _, sect := range file.Sections {
|
||||
if sect.Type == elf.SHT_NOBITS {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check if this section has a zip file
|
||||
if zfile, err := zip.NewReader(sect, int64(sect.Size)); err == nil {
|
||||
return zfile, nil
|
||||
}
|
||||
|
||||
// Otherwise move end of file pointer
|
||||
end := int64(sect.Offset + sect.Size)
|
||||
if end > max {
|
||||
max = end
|
||||
}
|
||||
}
|
||||
|
||||
// No zip file within binary, try appended to end
|
||||
section := io.NewSectionReader(rda, max, size-max)
|
||||
return zip.NewReader(section, section.Size())
|
||||
}
|
8
vendor/github.com/dgrijalva/jwt-go/LICENSE
generated
vendored
Normal file
8
vendor/github.com/dgrijalva/jwt-go/LICENSE
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
Copyright (c) 2012 Dave Grijalva
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
134
vendor/github.com/dgrijalva/jwt-go/claims.go
generated
vendored
Normal file
134
vendor/github.com/dgrijalva/jwt-go/claims.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// For a type to be a Claims object, it must just have a Valid method that determines
|
||||
// if the token is invalid for any supported reason
|
||||
type Claims interface {
|
||||
Valid() error
|
||||
}
|
||||
|
||||
// Structured version of Claims Section, as referenced at
|
||||
// https://tools.ietf.org/html/rfc7519#section-4.1
|
||||
// See examples for how to use this with your own claim types
|
||||
type StandardClaims struct {
|
||||
Audience string `json:"aud,omitempty"`
|
||||
ExpiresAt int64 `json:"exp,omitempty"`
|
||||
Id string `json:"jti,omitempty"`
|
||||
IssuedAt int64 `json:"iat,omitempty"`
|
||||
Issuer string `json:"iss,omitempty"`
|
||||
NotBefore int64 `json:"nbf,omitempty"`
|
||||
Subject string `json:"sub,omitempty"`
|
||||
}
|
||||
|
||||
// Validates time based claims "exp, iat, nbf".
|
||||
// There is no accounting for clock skew.
|
||||
// As well, if any of the above claims are not in the token, it will still
|
||||
// be considered a valid claim.
|
||||
func (c StandardClaims) Valid() error {
|
||||
vErr := new(ValidationError)
|
||||
now := TimeFunc().Unix()
|
||||
|
||||
// The claims below are optional, by default, so if they are set to the
|
||||
// default value in Go, let's not fail the verification for them.
|
||||
if c.VerifyExpiresAt(now, false) == false {
|
||||
delta := time.Unix(now, 0).Sub(time.Unix(c.ExpiresAt, 0))
|
||||
vErr.Inner = fmt.Errorf("token is expired by %v", delta)
|
||||
vErr.Errors |= ValidationErrorExpired
|
||||
}
|
||||
|
||||
if c.VerifyIssuedAt(now, false) == false {
|
||||
vErr.Inner = fmt.Errorf("Token used before issued")
|
||||
vErr.Errors |= ValidationErrorIssuedAt
|
||||
}
|
||||
|
||||
if c.VerifyNotBefore(now, false) == false {
|
||||
vErr.Inner = fmt.Errorf("token is not valid yet")
|
||||
vErr.Errors |= ValidationErrorNotValidYet
|
||||
}
|
||||
|
||||
if vErr.valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return vErr
|
||||
}
|
||||
|
||||
// Compares the aud claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyAudience(cmp string, req bool) bool {
|
||||
return verifyAud(c.Audience, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the exp claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||
return verifyExp(c.ExpiresAt, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the iat claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyIssuedAt(cmp int64, req bool) bool {
|
||||
return verifyIat(c.IssuedAt, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the iss claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyIssuer(cmp string, req bool) bool {
|
||||
return verifyIss(c.Issuer, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the nbf claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (c *StandardClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||
return verifyNbf(c.NotBefore, cmp, req)
|
||||
}
|
||||
|
||||
// ----- helpers
|
||||
|
||||
func verifyAud(aud string, cmp string, required bool) bool {
|
||||
if aud == "" {
|
||||
return !required
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(aud), []byte(cmp)) != 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func verifyExp(exp int64, now int64, required bool) bool {
|
||||
if exp == 0 {
|
||||
return !required
|
||||
}
|
||||
return now <= exp
|
||||
}
|
||||
|
||||
func verifyIat(iat int64, now int64, required bool) bool {
|
||||
if iat == 0 {
|
||||
return !required
|
||||
}
|
||||
return now >= iat
|
||||
}
|
||||
|
||||
func verifyIss(iss string, cmp string, required bool) bool {
|
||||
if iss == "" {
|
||||
return !required
|
||||
}
|
||||
if subtle.ConstantTimeCompare([]byte(iss), []byte(cmp)) != 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func verifyNbf(nbf int64, now int64, required bool) bool {
|
||||
if nbf == 0 {
|
||||
return !required
|
||||
}
|
||||
return now >= nbf
|
||||
}
|
282
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
generated
vendored
Normal file
282
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/app.go
generated
vendored
Normal file
@ -0,0 +1,282 @@
|
||||
// A useful example app. You can use this to debug your tokens on the command line.
|
||||
// This is also a great place to look at how you might use this library.
|
||||
//
|
||||
// Example usage:
|
||||
// The following will create and sign a token, then verify it and output the original claims.
|
||||
// echo {\"foo\":\"bar\"} | bin/jwt -key test/sample_key -alg RS256 -sign - | bin/jwt -key test/sample_key.pub -verify -
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
var (
|
||||
// Options
|
||||
flagAlg = flag.String("alg", "", "signing algorithm identifier")
|
||||
flagKey = flag.String("key", "", "path to key file or '-' to read from stdin")
|
||||
flagCompact = flag.Bool("compact", false, "output compact JSON")
|
||||
flagDebug = flag.Bool("debug", false, "print out all kinds of debug data")
|
||||
flagClaims = make(ArgList)
|
||||
flagHead = make(ArgList)
|
||||
|
||||
// Modes - exactly one of these is required
|
||||
flagSign = flag.String("sign", "", "path to claims object to sign, '-' to read from stdin, or '+' to use only -claim args")
|
||||
flagVerify = flag.String("verify", "", "path to JWT token to verify or '-' to read from stdin")
|
||||
flagShow = flag.String("show", "", "path to JWT file or '-' to read from stdin")
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Plug in Var flags
|
||||
flag.Var(flagClaims, "claim", "add additional claims. may be used more than once")
|
||||
flag.Var(flagHead, "header", "add additional header params. may be used more than once")
|
||||
|
||||
// Usage message if you ask for -help or if you mess up inputs.
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
|
||||
fmt.Fprintf(os.Stderr, " One of the following flags is required: sign, verify\n")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
||||
// Parse command line options
|
||||
flag.Parse()
|
||||
|
||||
// Do the thing. If something goes wrong, print error to stderr
|
||||
// and exit with a non-zero status code
|
||||
if err := start(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Figure out which thing to do and then do that
|
||||
func start() error {
|
||||
if *flagSign != "" {
|
||||
return signToken()
|
||||
} else if *flagVerify != "" {
|
||||
return verifyToken()
|
||||
} else if *flagShow != "" {
|
||||
return showToken()
|
||||
} else {
|
||||
flag.Usage()
|
||||
return fmt.Errorf("None of the required flags are present. What do you want me to do?")
|
||||
}
|
||||
}
|
||||
|
||||
// Helper func: Read input from specified file or stdin
|
||||
func loadData(p string) ([]byte, error) {
|
||||
if p == "" {
|
||||
return nil, fmt.Errorf("No path specified")
|
||||
}
|
||||
|
||||
var rdr io.Reader
|
||||
if p == "-" {
|
||||
rdr = os.Stdin
|
||||
} else if p == "+" {
|
||||
return []byte("{}"), nil
|
||||
} else {
|
||||
if f, err := os.Open(p); err == nil {
|
||||
rdr = f
|
||||
defer f.Close()
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return ioutil.ReadAll(rdr)
|
||||
}
|
||||
|
||||
// Print a json object in accordance with the prophecy (or the command line options)
|
||||
func printJSON(j interface{}) error {
|
||||
var out []byte
|
||||
var err error
|
||||
|
||||
if *flagCompact == false {
|
||||
out, err = json.MarshalIndent(j, "", " ")
|
||||
} else {
|
||||
out, err = json.Marshal(j)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
fmt.Println(string(out))
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify a token and output the claims. This is a great example
|
||||
// of how to verify and view a token.
|
||||
func verifyToken() error {
|
||||
// get the token
|
||||
tokData, err := loadData(*flagVerify)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read token: %v", err)
|
||||
}
|
||||
|
||||
// trim possible whitespace from token
|
||||
tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
|
||||
if *flagDebug {
|
||||
fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
|
||||
}
|
||||
|
||||
// Parse the token. Load the key from command line option
|
||||
token, err := jwt.Parse(string(tokData), func(t *jwt.Token) (interface{}, error) {
|
||||
data, err := loadData(*flagKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isEs() {
|
||||
return jwt.ParseECPublicKeyFromPEM(data)
|
||||
} else if isRs() {
|
||||
return jwt.ParseRSAPublicKeyFromPEM(data)
|
||||
}
|
||||
return data, nil
|
||||
})
|
||||
|
||||
// Print some debug data
|
||||
if *flagDebug && token != nil {
|
||||
fmt.Fprintf(os.Stderr, "Header:\n%v\n", token.Header)
|
||||
fmt.Fprintf(os.Stderr, "Claims:\n%v\n", token.Claims)
|
||||
}
|
||||
|
||||
// Print an error if we can't parse for some reason
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't parse token: %v", err)
|
||||
}
|
||||
|
||||
// Is token invalid?
|
||||
if !token.Valid {
|
||||
return fmt.Errorf("Token is invalid")
|
||||
}
|
||||
|
||||
// Print the token details
|
||||
if err := printJSON(token.Claims); err != nil {
|
||||
return fmt.Errorf("Failed to output claims: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create, sign, and output a token. This is a great, simple example of
|
||||
// how to use this library to create and sign a token.
|
||||
func signToken() error {
|
||||
// get the token data from command line arguments
|
||||
tokData, err := loadData(*flagSign)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read token: %v", err)
|
||||
} else if *flagDebug {
|
||||
fmt.Fprintf(os.Stderr, "Token: %v bytes", len(tokData))
|
||||
}
|
||||
|
||||
// parse the JSON of the claims
|
||||
var claims jwt.MapClaims
|
||||
if err := json.Unmarshal(tokData, &claims); err != nil {
|
||||
return fmt.Errorf("Couldn't parse claims JSON: %v", err)
|
||||
}
|
||||
|
||||
// add command line claims
|
||||
if len(flagClaims) > 0 {
|
||||
for k, v := range flagClaims {
|
||||
claims[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// get the key
|
||||
var key interface{}
|
||||
key, err = loadData(*flagKey)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read key: %v", err)
|
||||
}
|
||||
|
||||
// get the signing alg
|
||||
alg := jwt.GetSigningMethod(*flagAlg)
|
||||
if alg == nil {
|
||||
return fmt.Errorf("Couldn't find signing method: %v", *flagAlg)
|
||||
}
|
||||
|
||||
// create a new token
|
||||
token := jwt.NewWithClaims(alg, claims)
|
||||
|
||||
// add command line headers
|
||||
if len(flagHead) > 0 {
|
||||
for k, v := range flagHead {
|
||||
token.Header[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
if isEs() {
|
||||
if k, ok := key.([]byte); !ok {
|
||||
return fmt.Errorf("Couldn't convert key data to key")
|
||||
} else {
|
||||
key, err = jwt.ParseECPrivateKeyFromPEM(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if isRs() {
|
||||
if k, ok := key.([]byte); !ok {
|
||||
return fmt.Errorf("Couldn't convert key data to key")
|
||||
} else {
|
||||
key, err = jwt.ParseRSAPrivateKeyFromPEM(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if out, err := token.SignedString(key); err == nil {
|
||||
fmt.Println(out)
|
||||
} else {
|
||||
return fmt.Errorf("Error signing token: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// showToken pretty-prints the token on the command line.
|
||||
func showToken() error {
|
||||
// get the token
|
||||
tokData, err := loadData(*flagShow)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Couldn't read token: %v", err)
|
||||
}
|
||||
|
||||
// trim possible whitespace from token
|
||||
tokData = regexp.MustCompile(`\s*$`).ReplaceAll(tokData, []byte{})
|
||||
if *flagDebug {
|
||||
fmt.Fprintf(os.Stderr, "Token len: %v bytes\n", len(tokData))
|
||||
}
|
||||
|
||||
token, err := jwt.Parse(string(tokData), nil)
|
||||
if token == nil {
|
||||
return fmt.Errorf("malformed token: %v", err)
|
||||
}
|
||||
|
||||
// Print the token details
|
||||
fmt.Println("Header:")
|
||||
if err := printJSON(token.Header); err != nil {
|
||||
return fmt.Errorf("Failed to output header: %v", err)
|
||||
}
|
||||
|
||||
fmt.Println("Claims:")
|
||||
if err := printJSON(token.Claims); err != nil {
|
||||
return fmt.Errorf("Failed to output claims: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isEs() bool {
|
||||
return strings.HasPrefix(*flagAlg, "ES")
|
||||
}
|
||||
|
||||
func isRs() bool {
|
||||
return strings.HasPrefix(*flagAlg, "RS")
|
||||
}
|
23
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/args.go
generated
vendored
Normal file
23
vendor/github.com/dgrijalva/jwt-go/cmd/jwt/args.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ArgList map[string]string
|
||||
|
||||
func (l ArgList) String() string {
|
||||
data, _ := json.Marshal(l)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (l ArgList) Set(arg string) error {
|
||||
parts := strings.SplitN(arg, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("Invalid argument '%v'. Must use format 'key=value'. %v", arg, parts)
|
||||
}
|
||||
l[parts[0]] = parts[1]
|
||||
return nil
|
||||
}
|
4
vendor/github.com/dgrijalva/jwt-go/doc.go
generated
vendored
Normal file
4
vendor/github.com/dgrijalva/jwt-go/doc.go
generated
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
// Package jwt is a Go implementation of JSON Web Tokens: http://self-issued.info/docs/draft-jones-json-web-token.html
|
||||
//
|
||||
// See README.md for more info.
|
||||
package jwt
|
147
vendor/github.com/dgrijalva/jwt-go/ecdsa.go
generated
vendored
Normal file
147
vendor/github.com/dgrijalva/jwt-go/ecdsa.go
generated
vendored
Normal file
@ -0,0 +1,147 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
var (
|
||||
// Sadly this is missing from crypto/ecdsa compared to crypto/rsa
|
||||
ErrECDSAVerification = errors.New("crypto/ecdsa: verification error")
|
||||
)
|
||||
|
||||
// Implements the ECDSA family of signing methods signing methods
|
||||
type SigningMethodECDSA struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
KeySize int
|
||||
CurveBits int
|
||||
}
|
||||
|
||||
// Specific instances for EC256 and company
|
||||
var (
|
||||
SigningMethodES256 *SigningMethodECDSA
|
||||
SigningMethodES384 *SigningMethodECDSA
|
||||
SigningMethodES512 *SigningMethodECDSA
|
||||
)
|
||||
|
||||
func init() {
|
||||
// ES256
|
||||
SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256}
|
||||
RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod {
|
||||
return SigningMethodES256
|
||||
})
|
||||
|
||||
// ES384
|
||||
SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384}
|
||||
RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod {
|
||||
return SigningMethodES384
|
||||
})
|
||||
|
||||
// ES512
|
||||
SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521}
|
||||
RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod {
|
||||
return SigningMethodES512
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SigningMethodECDSA) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Implements the Verify method from SigningMethod
|
||||
// For this verify method, key must be an ecdsa.PublicKey struct
|
||||
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = DecodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the key
|
||||
var ecdsaKey *ecdsa.PublicKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PublicKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return ErrInvalidKeyType
|
||||
}
|
||||
|
||||
if len(sig) != 2*m.KeySize {
|
||||
return ErrECDSAVerification
|
||||
}
|
||||
|
||||
r := big.NewInt(0).SetBytes(sig[:m.KeySize])
|
||||
s := big.NewInt(0).SetBytes(sig[m.KeySize:])
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Verify the signature
|
||||
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
|
||||
return nil
|
||||
} else {
|
||||
return ErrECDSAVerification
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod
|
||||
// For this signing method, key must be an ecdsa.PrivateKey struct
|
||||
func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) {
|
||||
// Get the key
|
||||
var ecdsaKey *ecdsa.PrivateKey
|
||||
switch k := key.(type) {
|
||||
case *ecdsa.PrivateKey:
|
||||
ecdsaKey = k
|
||||
default:
|
||||
return "", ErrInvalidKeyType
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return r, s
|
||||
if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil {
|
||||
curveBits := ecdsaKey.Curve.Params().BitSize
|
||||
|
||||
if m.CurveBits != curveBits {
|
||||
return "", ErrInvalidKey
|
||||
}
|
||||
|
||||
keyBytes := curveBits / 8
|
||||
if curveBits%8 > 0 {
|
||||
keyBytes += 1
|
||||
}
|
||||
|
||||
// We serialize the outpus (r and s) into big-endian byte arrays and pad
|
||||
// them with zeros on the left to make sure the sizes work out. Both arrays
|
||||
// must be keyBytes long, and the output must be 2*keyBytes long.
|
||||
rBytes := r.Bytes()
|
||||
rBytesPadded := make([]byte, keyBytes)
|
||||
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
||||
|
||||
sBytes := s.Bytes()
|
||||
sBytesPadded := make([]byte, keyBytes)
|
||||
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
||||
|
||||
out := append(rBytesPadded, sBytesPadded...)
|
||||
|
||||
return EncodeSegment(out), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
67
vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go
generated
vendored
Normal file
67
vendor/github.com/dgrijalva/jwt-go/ecdsa_utils.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNotECPublicKey = errors.New("Key is not a valid ECDSA public key")
|
||||
ErrNotECPrivateKey = errors.New("Key is not a valid ECDSA private key")
|
||||
)
|
||||
|
||||
// Parse PEM encoded Elliptic Curve Private Key Structure
|
||||
func ParseECPrivateKeyFromPEM(key []byte) (*ecdsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParseECPrivateKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var pkey *ecdsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*ecdsa.PrivateKey); !ok {
|
||||
return nil, ErrNotECPrivateKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseECPublicKeyFromPEM(key []byte) (*ecdsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *ecdsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*ecdsa.PublicKey); !ok {
|
||||
return nil, ErrNotECPublicKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
59
vendor/github.com/dgrijalva/jwt-go/errors.go
generated
vendored
Normal file
59
vendor/github.com/dgrijalva/jwt-go/errors.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Error constants
|
||||
var (
|
||||
ErrInvalidKey = errors.New("key is invalid")
|
||||
ErrInvalidKeyType = errors.New("key is of invalid type")
|
||||
ErrHashUnavailable = errors.New("the requested hash function is unavailable")
|
||||
)
|
||||
|
||||
// The errors that might occur when parsing and validating a token
|
||||
const (
|
||||
ValidationErrorMalformed uint32 = 1 << iota // Token is malformed
|
||||
ValidationErrorUnverifiable // Token could not be verified because of signing problems
|
||||
ValidationErrorSignatureInvalid // Signature validation failed
|
||||
|
||||
// Standard Claim validation errors
|
||||
ValidationErrorAudience // AUD validation failed
|
||||
ValidationErrorExpired // EXP validation failed
|
||||
ValidationErrorIssuedAt // IAT validation failed
|
||||
ValidationErrorIssuer // ISS validation failed
|
||||
ValidationErrorNotValidYet // NBF validation failed
|
||||
ValidationErrorId // JTI validation failed
|
||||
ValidationErrorClaimsInvalid // Generic claims validation error
|
||||
)
|
||||
|
||||
// Helper for constructing a ValidationError with a string error message
|
||||
func NewValidationError(errorText string, errorFlags uint32) *ValidationError {
|
||||
return &ValidationError{
|
||||
text: errorText,
|
||||
Errors: errorFlags,
|
||||
}
|
||||
}
|
||||
|
||||
// The error from Parse if token is not valid
|
||||
type ValidationError struct {
|
||||
Inner error // stores the error returned by external dependencies, i.e.: KeyFunc
|
||||
Errors uint32 // bitfield. see ValidationError... constants
|
||||
text string // errors that do not have a valid error just have text
|
||||
}
|
||||
|
||||
// Validation error is an error type
|
||||
func (e ValidationError) Error() string {
|
||||
if e.Inner != nil {
|
||||
return e.Inner.Error()
|
||||
} else if e.text != "" {
|
||||
return e.text
|
||||
} else {
|
||||
return "token is invalid"
|
||||
}
|
||||
}
|
||||
|
||||
// No errors
|
||||
func (e *ValidationError) valid() bool {
|
||||
return e.Errors == 0
|
||||
}
|
94
vendor/github.com/dgrijalva/jwt-go/hmac.go
generated
vendored
Normal file
94
vendor/github.com/dgrijalva/jwt-go/hmac.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/hmac"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Implements the HMAC-SHA family of signing methods signing methods
|
||||
type SigningMethodHMAC struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// Specific instances for HS256 and company
|
||||
var (
|
||||
SigningMethodHS256 *SigningMethodHMAC
|
||||
SigningMethodHS384 *SigningMethodHMAC
|
||||
SigningMethodHS512 *SigningMethodHMAC
|
||||
ErrSignatureInvalid = errors.New("signature is invalid")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// HS256
|
||||
SigningMethodHS256 = &SigningMethodHMAC{"HS256", crypto.SHA256}
|
||||
RegisterSigningMethod(SigningMethodHS256.Alg(), func() SigningMethod {
|
||||
return SigningMethodHS256
|
||||
})
|
||||
|
||||
// HS384
|
||||
SigningMethodHS384 = &SigningMethodHMAC{"HS384", crypto.SHA384}
|
||||
RegisterSigningMethod(SigningMethodHS384.Alg(), func() SigningMethod {
|
||||
return SigningMethodHS384
|
||||
})
|
||||
|
||||
// HS512
|
||||
SigningMethodHS512 = &SigningMethodHMAC{"HS512", crypto.SHA512}
|
||||
RegisterSigningMethod(SigningMethodHS512.Alg(), func() SigningMethod {
|
||||
return SigningMethodHS512
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SigningMethodHMAC) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Verify the signature of HSXXX tokens. Returns nil if the signature is valid.
|
||||
func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error {
|
||||
// Verify the key is the right type
|
||||
keyBytes, ok := key.([]byte)
|
||||
if !ok {
|
||||
return ErrInvalidKeyType
|
||||
}
|
||||
|
||||
// Decode signature, for comparison
|
||||
sig, err := DecodeSegment(signature)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Can we use the specified hashing method?
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
|
||||
// This signing method is symmetric, so we validate the signature
|
||||
// by reproducing the signature from the signing string and key, then
|
||||
// comparing that against the provided signature.
|
||||
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||
hasher.Write([]byte(signingString))
|
||||
if !hmac.Equal(sig, hasher.Sum(nil)) {
|
||||
return ErrSignatureInvalid
|
||||
}
|
||||
|
||||
// No validation errors. Signature is good.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod for this signing method.
|
||||
// Key must be []byte
|
||||
func (m *SigningMethodHMAC) Sign(signingString string, key interface{}) (string, error) {
|
||||
if keyBytes, ok := key.([]byte); ok {
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := hmac.New(m.Hash.New, keyBytes)
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
return EncodeSegment(hasher.Sum(nil)), nil
|
||||
}
|
||||
|
||||
return "", ErrInvalidKey
|
||||
}
|
94
vendor/github.com/dgrijalva/jwt-go/map_claims.go
generated
vendored
Normal file
94
vendor/github.com/dgrijalva/jwt-go/map_claims.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
// "fmt"
|
||||
)
|
||||
|
||||
// Claims type that uses the map[string]interface{} for JSON decoding
|
||||
// This is the default claims type if you don't supply one
|
||||
type MapClaims map[string]interface{}
|
||||
|
||||
// Compares the aud claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (m MapClaims) VerifyAudience(cmp string, req bool) bool {
|
||||
aud, _ := m["aud"].(string)
|
||||
return verifyAud(aud, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the exp claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (m MapClaims) VerifyExpiresAt(cmp int64, req bool) bool {
|
||||
switch exp := m["exp"].(type) {
|
||||
case float64:
|
||||
return verifyExp(int64(exp), cmp, req)
|
||||
case json.Number:
|
||||
v, _ := exp.Int64()
|
||||
return verifyExp(v, cmp, req)
|
||||
}
|
||||
return req == false
|
||||
}
|
||||
|
||||
// Compares the iat claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (m MapClaims) VerifyIssuedAt(cmp int64, req bool) bool {
|
||||
switch iat := m["iat"].(type) {
|
||||
case float64:
|
||||
return verifyIat(int64(iat), cmp, req)
|
||||
case json.Number:
|
||||
v, _ := iat.Int64()
|
||||
return verifyIat(v, cmp, req)
|
||||
}
|
||||
return req == false
|
||||
}
|
||||
|
||||
// Compares the iss claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (m MapClaims) VerifyIssuer(cmp string, req bool) bool {
|
||||
iss, _ := m["iss"].(string)
|
||||
return verifyIss(iss, cmp, req)
|
||||
}
|
||||
|
||||
// Compares the nbf claim against cmp.
|
||||
// If required is false, this method will return true if the value matches or is unset
|
||||
func (m MapClaims) VerifyNotBefore(cmp int64, req bool) bool {
|
||||
switch nbf := m["nbf"].(type) {
|
||||
case float64:
|
||||
return verifyNbf(int64(nbf), cmp, req)
|
||||
case json.Number:
|
||||
v, _ := nbf.Int64()
|
||||
return verifyNbf(v, cmp, req)
|
||||
}
|
||||
return req == false
|
||||
}
|
||||
|
||||
// Validates time based claims "exp, iat, nbf".
|
||||
// There is no accounting for clock skew.
|
||||
// As well, if any of the above claims are not in the token, it will still
|
||||
// be considered a valid claim.
|
||||
func (m MapClaims) Valid() error {
|
||||
vErr := new(ValidationError)
|
||||
now := TimeFunc().Unix()
|
||||
|
||||
if m.VerifyExpiresAt(now, false) == false {
|
||||
vErr.Inner = errors.New("Token is expired")
|
||||
vErr.Errors |= ValidationErrorExpired
|
||||
}
|
||||
|
||||
if m.VerifyIssuedAt(now, false) == false {
|
||||
vErr.Inner = errors.New("Token used before issued")
|
||||
vErr.Errors |= ValidationErrorIssuedAt
|
||||
}
|
||||
|
||||
if m.VerifyNotBefore(now, false) == false {
|
||||
vErr.Inner = errors.New("Token is not valid yet")
|
||||
vErr.Errors |= ValidationErrorNotValidYet
|
||||
}
|
||||
|
||||
if vErr.valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return vErr
|
||||
}
|
52
vendor/github.com/dgrijalva/jwt-go/none.go
generated
vendored
Normal file
52
vendor/github.com/dgrijalva/jwt-go/none.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
package jwt
|
||||
|
||||
// Implements the none signing method. This is required by the spec
|
||||
// but you probably should never use it.
|
||||
var SigningMethodNone *signingMethodNone
|
||||
|
||||
const UnsafeAllowNoneSignatureType unsafeNoneMagicConstant = "none signing method allowed"
|
||||
|
||||
var NoneSignatureTypeDisallowedError error
|
||||
|
||||
type signingMethodNone struct{}
|
||||
type unsafeNoneMagicConstant string
|
||||
|
||||
func init() {
|
||||
SigningMethodNone = &signingMethodNone{}
|
||||
NoneSignatureTypeDisallowedError = NewValidationError("'none' signature type is not allowed", ValidationErrorSignatureInvalid)
|
||||
|
||||
RegisterSigningMethod(SigningMethodNone.Alg(), func() SigningMethod {
|
||||
return SigningMethodNone
|
||||
})
|
||||
}
|
||||
|
||||
func (m *signingMethodNone) Alg() string {
|
||||
return "none"
|
||||
}
|
||||
|
||||
// Only allow 'none' alg type if UnsafeAllowNoneSignatureType is specified as the key
|
||||
func (m *signingMethodNone) Verify(signingString, signature string, key interface{}) (err error) {
|
||||
// Key must be UnsafeAllowNoneSignatureType to prevent accidentally
|
||||
// accepting 'none' signing method
|
||||
if _, ok := key.(unsafeNoneMagicConstant); !ok {
|
||||
return NoneSignatureTypeDisallowedError
|
||||
}
|
||||
// If signing method is none, signature must be an empty string
|
||||
if signature != "" {
|
||||
return NewValidationError(
|
||||
"'none' signing method with non-empty signature",
|
||||
ValidationErrorSignatureInvalid,
|
||||
)
|
||||
}
|
||||
|
||||
// Accept 'none' signing method.
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only allow 'none' signing if UnsafeAllowNoneSignatureType is specified as the key
|
||||
func (m *signingMethodNone) Sign(signingString string, key interface{}) (string, error) {
|
||||
if _, ok := key.(unsafeNoneMagicConstant); ok {
|
||||
return "", nil
|
||||
}
|
||||
return "", NoneSignatureTypeDisallowedError
|
||||
}
|
131
vendor/github.com/dgrijalva/jwt-go/parser.go
generated
vendored
Normal file
131
vendor/github.com/dgrijalva/jwt-go/parser.go
generated
vendored
Normal file
@ -0,0 +1,131 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Parser struct {
|
||||
ValidMethods []string // If populated, only these methods will be considered valid
|
||||
UseJSONNumber bool // Use JSON Number format in JSON decoder
|
||||
SkipClaimsValidation bool // Skip claims validation during token parsing
|
||||
}
|
||||
|
||||
// Parse, validate, and return a token.
|
||||
// keyFunc will receive the parsed token and should return the key for validating.
|
||||
// If everything is kosher, err will be nil
|
||||
func (p *Parser) Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
||||
return p.ParseWithClaims(tokenString, MapClaims{}, keyFunc)
|
||||
}
|
||||
|
||||
func (p *Parser) ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
|
||||
parts := strings.Split(tokenString, ".")
|
||||
if len(parts) != 3 {
|
||||
return nil, NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
|
||||
}
|
||||
|
||||
var err error
|
||||
token := &Token{Raw: tokenString}
|
||||
|
||||
// parse Header
|
||||
var headerBytes []byte
|
||||
if headerBytes, err = DecodeSegment(parts[0]); err != nil {
|
||||
if strings.HasPrefix(strings.ToLower(tokenString), "bearer ") {
|
||||
return token, NewValidationError("tokenstring should not contain 'bearer '", ValidationErrorMalformed)
|
||||
}
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
if err = json.Unmarshal(headerBytes, &token.Header); err != nil {
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// parse Claims
|
||||
var claimBytes []byte
|
||||
token.Claims = claims
|
||||
|
||||
if claimBytes, err = DecodeSegment(parts[1]); err != nil {
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
dec := json.NewDecoder(bytes.NewBuffer(claimBytes))
|
||||
if p.UseJSONNumber {
|
||||
dec.UseNumber()
|
||||
}
|
||||
// JSON Decode. Special case for map type to avoid weird pointer behavior
|
||||
if c, ok := token.Claims.(MapClaims); ok {
|
||||
err = dec.Decode(&c)
|
||||
} else {
|
||||
err = dec.Decode(&claims)
|
||||
}
|
||||
// Handle decode error
|
||||
if err != nil {
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorMalformed}
|
||||
}
|
||||
|
||||
// Lookup signature method
|
||||
if method, ok := token.Header["alg"].(string); ok {
|
||||
if token.Method = GetSigningMethod(method); token.Method == nil {
|
||||
return token, NewValidationError("signing method (alg) is unavailable.", ValidationErrorUnverifiable)
|
||||
}
|
||||
} else {
|
||||
return token, NewValidationError("signing method (alg) is unspecified.", ValidationErrorUnverifiable)
|
||||
}
|
||||
|
||||
// Verify signing method is in the required set
|
||||
if p.ValidMethods != nil {
|
||||
var signingMethodValid = false
|
||||
var alg = token.Method.Alg()
|
||||
for _, m := range p.ValidMethods {
|
||||
if m == alg {
|
||||
signingMethodValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !signingMethodValid {
|
||||
// signing method is not in the listed set
|
||||
return token, NewValidationError(fmt.Sprintf("signing method %v is invalid", alg), ValidationErrorSignatureInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup key
|
||||
var key interface{}
|
||||
if keyFunc == nil {
|
||||
// keyFunc was not provided. short circuiting validation
|
||||
return token, NewValidationError("no Keyfunc was provided.", ValidationErrorUnverifiable)
|
||||
}
|
||||
if key, err = keyFunc(token); err != nil {
|
||||
// keyFunc returned an error
|
||||
return token, &ValidationError{Inner: err, Errors: ValidationErrorUnverifiable}
|
||||
}
|
||||
|
||||
vErr := &ValidationError{}
|
||||
|
||||
// Validate Claims
|
||||
if !p.SkipClaimsValidation {
|
||||
if err := token.Claims.Valid(); err != nil {
|
||||
|
||||
// If the Claims Valid returned an error, check if it is a validation error,
|
||||
// If it was another error type, create a ValidationError with a generic ClaimsInvalid flag set
|
||||
if e, ok := err.(*ValidationError); !ok {
|
||||
vErr = &ValidationError{Inner: err, Errors: ValidationErrorClaimsInvalid}
|
||||
} else {
|
||||
vErr = e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform validation
|
||||
token.Signature = parts[2]
|
||||
if err = token.Method.Verify(strings.Join(parts[0:2], "."), token.Signature, key); err != nil {
|
||||
vErr.Inner = err
|
||||
vErr.Errors |= ValidationErrorSignatureInvalid
|
||||
}
|
||||
|
||||
if vErr.valid() {
|
||||
token.Valid = true
|
||||
return token, nil
|
||||
}
|
||||
|
||||
return token, vErr
|
||||
}
|
7
vendor/github.com/dgrijalva/jwt-go/request/doc.go
generated
vendored
Normal file
7
vendor/github.com/dgrijalva/jwt-go/request/doc.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
// Utility package for extracting JWT tokens from
|
||||
// HTTP requests.
|
||||
//
|
||||
// The main function is ParseFromRequest and it's WithClaims variant.
|
||||
// See examples for how to use the various Extractor implementations
|
||||
// or roll your own.
|
||||
package request
|
81
vendor/github.com/dgrijalva/jwt-go/request/extractor.go
generated
vendored
Normal file
81
vendor/github.com/dgrijalva/jwt-go/request/extractor.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
ErrNoTokenInRequest = errors.New("no token present in request")
|
||||
)
|
||||
|
||||
// Interface for extracting a token from an HTTP request.
|
||||
// The ExtractToken method should return a token string or an error.
|
||||
// If no token is present, you must return ErrNoTokenInRequest.
|
||||
type Extractor interface {
|
||||
ExtractToken(*http.Request) (string, error)
|
||||
}
|
||||
|
||||
// Extractor for finding a token in a header. Looks at each specified
|
||||
// header in order until there's a match
|
||||
type HeaderExtractor []string
|
||||
|
||||
func (e HeaderExtractor) ExtractToken(req *http.Request) (string, error) {
|
||||
// loop over header names and return the first one that contains data
|
||||
for _, header := range e {
|
||||
if ah := req.Header.Get(header); ah != "" {
|
||||
return ah, nil
|
||||
}
|
||||
}
|
||||
return "", ErrNoTokenInRequest
|
||||
}
|
||||
|
||||
// Extract token from request arguments. This includes a POSTed form or
|
||||
// GET URL arguments. Argument names are tried in order until there's a match.
|
||||
// This extractor calls `ParseMultipartForm` on the request
|
||||
type ArgumentExtractor []string
|
||||
|
||||
func (e ArgumentExtractor) ExtractToken(req *http.Request) (string, error) {
|
||||
// Make sure form is parsed
|
||||
req.ParseMultipartForm(10e6)
|
||||
|
||||
// loop over arg names and return the first one that contains data
|
||||
for _, arg := range e {
|
||||
if ah := req.Form.Get(arg); ah != "" {
|
||||
return ah, nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", ErrNoTokenInRequest
|
||||
}
|
||||
|
||||
// Tries Extractors in order until one returns a token string or an error occurs
|
||||
type MultiExtractor []Extractor
|
||||
|
||||
func (e MultiExtractor) ExtractToken(req *http.Request) (string, error) {
|
||||
// loop over header names and return the first one that contains data
|
||||
for _, extractor := range e {
|
||||
if tok, err := extractor.ExtractToken(req); tok != "" {
|
||||
return tok, nil
|
||||
} else if err != ErrNoTokenInRequest {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", ErrNoTokenInRequest
|
||||
}
|
||||
|
||||
// Wrap an Extractor in this to post-process the value before it's handed off.
|
||||
// See AuthorizationHeaderExtractor for an example
|
||||
type PostExtractionFilter struct {
|
||||
Extractor
|
||||
Filter func(string) (string, error)
|
||||
}
|
||||
|
||||
func (e *PostExtractionFilter) ExtractToken(req *http.Request) (string, error) {
|
||||
if tok, err := e.Extractor.ExtractToken(req); tok != "" {
|
||||
return e.Filter(tok)
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
28
vendor/github.com/dgrijalva/jwt-go/request/oauth2.go
generated
vendored
Normal file
28
vendor/github.com/dgrijalva/jwt-go/request/oauth2.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Strips 'Bearer ' prefix from bearer token string
|
||||
func stripBearerPrefixFromTokenString(tok string) (string, error) {
|
||||
// Should be a bearer token
|
||||
if len(tok) > 6 && strings.ToUpper(tok[0:7]) == "BEARER " {
|
||||
return tok[7:], nil
|
||||
}
|
||||
return tok, nil
|
||||
}
|
||||
|
||||
// Extract bearer token from Authorization header
|
||||
// Uses PostExtractionFilter to strip "Bearer " prefix from header
|
||||
var AuthorizationHeaderExtractor = &PostExtractionFilter{
|
||||
HeaderExtractor{"Authorization"},
|
||||
stripBearerPrefixFromTokenString,
|
||||
}
|
||||
|
||||
// Extractor for OAuth2 access tokens. Looks in 'Authorization'
|
||||
// header then 'access_token' argument for a token.
|
||||
var OAuth2Extractor = &MultiExtractor{
|
||||
AuthorizationHeaderExtractor,
|
||||
ArgumentExtractor{"access_token"},
|
||||
}
|
24
vendor/github.com/dgrijalva/jwt-go/request/request.go
generated
vendored
Normal file
24
vendor/github.com/dgrijalva/jwt-go/request/request.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Extract and parse a JWT token from an HTTP request.
|
||||
// This behaves the same as Parse, but accepts a request and an extractor
|
||||
// instead of a token string. The Extractor interface allows you to define
|
||||
// the logic for extracting a token. Several useful implementations are provided.
|
||||
func ParseFromRequest(req *http.Request, extractor Extractor, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) {
|
||||
return ParseFromRequestWithClaims(req, extractor, jwt.MapClaims{}, keyFunc)
|
||||
}
|
||||
|
||||
// ParseFromRequest but with custom Claims type
|
||||
func ParseFromRequestWithClaims(req *http.Request, extractor Extractor, claims jwt.Claims, keyFunc jwt.Keyfunc) (token *jwt.Token, err error) {
|
||||
// Extract token from request
|
||||
if tokStr, err := extractor.ExtractToken(req); err == nil {
|
||||
return jwt.ParseWithClaims(tokStr, claims, keyFunc)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
100
vendor/github.com/dgrijalva/jwt-go/rsa.go
generated
vendored
Normal file
100
vendor/github.com/dgrijalva/jwt-go/rsa.go
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
)
|
||||
|
||||
// Implements the RSA family of signing methods signing methods
|
||||
type SigningMethodRSA struct {
|
||||
Name string
|
||||
Hash crypto.Hash
|
||||
}
|
||||
|
||||
// Specific instances for RS256 and company
|
||||
var (
|
||||
SigningMethodRS256 *SigningMethodRSA
|
||||
SigningMethodRS384 *SigningMethodRSA
|
||||
SigningMethodRS512 *SigningMethodRSA
|
||||
)
|
||||
|
||||
func init() {
|
||||
// RS256
|
||||
SigningMethodRS256 = &SigningMethodRSA{"RS256", crypto.SHA256}
|
||||
RegisterSigningMethod(SigningMethodRS256.Alg(), func() SigningMethod {
|
||||
return SigningMethodRS256
|
||||
})
|
||||
|
||||
// RS384
|
||||
SigningMethodRS384 = &SigningMethodRSA{"RS384", crypto.SHA384}
|
||||
RegisterSigningMethod(SigningMethodRS384.Alg(), func() SigningMethod {
|
||||
return SigningMethodRS384
|
||||
})
|
||||
|
||||
// RS512
|
||||
SigningMethodRS512 = &SigningMethodRSA{"RS512", crypto.SHA512}
|
||||
RegisterSigningMethod(SigningMethodRS512.Alg(), func() SigningMethod {
|
||||
return SigningMethodRS512
|
||||
})
|
||||
}
|
||||
|
||||
func (m *SigningMethodRSA) Alg() string {
|
||||
return m.Name
|
||||
}
|
||||
|
||||
// Implements the Verify method from SigningMethod
|
||||
// For this signing method, must be an rsa.PublicKey structure.
|
||||
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error {
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = DecodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rsaKey *rsa.PublicKey
|
||||
var ok bool
|
||||
|
||||
if rsaKey, ok = key.(*rsa.PublicKey); !ok {
|
||||
return ErrInvalidKeyType
|
||||
}
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Verify the signature
|
||||
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod
|
||||
// For this signing method, must be an rsa.PrivateKey structure.
|
||||
func (m *SigningMethodRSA) Sign(signingString string, key interface{}) (string, error) {
|
||||
var rsaKey *rsa.PrivateKey
|
||||
var ok bool
|
||||
|
||||
// Validate type of key
|
||||
if rsaKey, ok = key.(*rsa.PrivateKey); !ok {
|
||||
return "", ErrInvalidKey
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return the encoded bytes
|
||||
if sigBytes, err := rsa.SignPKCS1v15(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil)); err == nil {
|
||||
return EncodeSegment(sigBytes), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
126
vendor/github.com/dgrijalva/jwt-go/rsa_pss.go
generated
vendored
Normal file
126
vendor/github.com/dgrijalva/jwt-go/rsa_pss.go
generated
vendored
Normal file
@ -0,0 +1,126 @@
|
||||
// +build go1.4
|
||||
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
)
|
||||
|
||||
// Implements the RSAPSS family of signing methods signing methods
|
||||
type SigningMethodRSAPSS struct {
|
||||
*SigningMethodRSA
|
||||
Options *rsa.PSSOptions
|
||||
}
|
||||
|
||||
// Specific instances for RS/PS and company
|
||||
var (
|
||||
SigningMethodPS256 *SigningMethodRSAPSS
|
||||
SigningMethodPS384 *SigningMethodRSAPSS
|
||||
SigningMethodPS512 *SigningMethodRSAPSS
|
||||
)
|
||||
|
||||
func init() {
|
||||
// PS256
|
||||
SigningMethodPS256 = &SigningMethodRSAPSS{
|
||||
&SigningMethodRSA{
|
||||
Name: "PS256",
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
&rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
}
|
||||
RegisterSigningMethod(SigningMethodPS256.Alg(), func() SigningMethod {
|
||||
return SigningMethodPS256
|
||||
})
|
||||
|
||||
// PS384
|
||||
SigningMethodPS384 = &SigningMethodRSAPSS{
|
||||
&SigningMethodRSA{
|
||||
Name: "PS384",
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
&rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
}
|
||||
RegisterSigningMethod(SigningMethodPS384.Alg(), func() SigningMethod {
|
||||
return SigningMethodPS384
|
||||
})
|
||||
|
||||
// PS512
|
||||
SigningMethodPS512 = &SigningMethodRSAPSS{
|
||||
&SigningMethodRSA{
|
||||
Name: "PS512",
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
&rsa.PSSOptions{
|
||||
SaltLength: rsa.PSSSaltLengthAuto,
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
}
|
||||
RegisterSigningMethod(SigningMethodPS512.Alg(), func() SigningMethod {
|
||||
return SigningMethodPS512
|
||||
})
|
||||
}
|
||||
|
||||
// Implements the Verify method from SigningMethod
|
||||
// For this verify method, key must be an rsa.PublicKey struct
|
||||
func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error {
|
||||
var err error
|
||||
|
||||
// Decode the signature
|
||||
var sig []byte
|
||||
if sig, err = DecodeSegment(signature); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var rsaKey *rsa.PublicKey
|
||||
switch k := key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
rsaKey = k
|
||||
default:
|
||||
return ErrInvalidKey
|
||||
}
|
||||
|
||||
// Create hasher
|
||||
if !m.Hash.Available() {
|
||||
return ErrHashUnavailable
|
||||
}
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, m.Options)
|
||||
}
|
||||
|
||||
// Implements the Sign method from SigningMethod
|
||||
// For this signing method, key must be an rsa.PrivateKey struct
|
||||
func (m *SigningMethodRSAPSS) Sign(signingString string, key interface{}) (string, error) {
|
||||
var rsaKey *rsa.PrivateKey
|
||||
|
||||
switch k := key.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
rsaKey = k
|
||||
default:
|
||||
return "", ErrInvalidKeyType
|
||||
}
|
||||
|
||||
// Create the hasher
|
||||
if !m.Hash.Available() {
|
||||
return "", ErrHashUnavailable
|
||||
}
|
||||
|
||||
hasher := m.Hash.New()
|
||||
hasher.Write([]byte(signingString))
|
||||
|
||||
// Sign the string and return the encoded bytes
|
||||
if sigBytes, err := rsa.SignPSS(rand.Reader, rsaKey, m.Hash, hasher.Sum(nil), m.Options); err == nil {
|
||||
return EncodeSegment(sigBytes), nil
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
}
|
69
vendor/github.com/dgrijalva/jwt-go/rsa_utils.go
generated
vendored
Normal file
69
vendor/github.com/dgrijalva/jwt-go/rsa_utils.go
generated
vendored
Normal file
@ -0,0 +1,69 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrKeyMustBePEMEncoded = errors.New("Invalid Key: Key must be PEM encoded PKCS1 or PKCS8 private key")
|
||||
ErrNotRSAPrivateKey = errors.New("Key is not a valid RSA private key")
|
||||
ErrNotRSAPublicKey = errors.New("Key is not a valid RSA public key")
|
||||
)
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 private key
|
||||
func ParseRSAPrivateKeyFromPEM(key []byte) (*rsa.PrivateKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKCS1PrivateKey(block.Bytes); err != nil {
|
||||
if parsedKey, err = x509.ParsePKCS8PrivateKey(block.Bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PrivateKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PrivateKey); !ok {
|
||||
return nil, ErrNotRSAPrivateKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
||||
|
||||
// Parse PEM encoded PKCS1 or PKCS8 public key
|
||||
func ParseRSAPublicKeyFromPEM(key []byte) (*rsa.PublicKey, error) {
|
||||
var err error
|
||||
|
||||
// Parse PEM block
|
||||
var block *pem.Block
|
||||
if block, _ = pem.Decode(key); block == nil {
|
||||
return nil, ErrKeyMustBePEMEncoded
|
||||
}
|
||||
|
||||
// Parse the key
|
||||
var parsedKey interface{}
|
||||
if parsedKey, err = x509.ParsePKIXPublicKey(block.Bytes); err != nil {
|
||||
if cert, err := x509.ParseCertificate(block.Bytes); err == nil {
|
||||
parsedKey = cert.PublicKey
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var pkey *rsa.PublicKey
|
||||
var ok bool
|
||||
if pkey, ok = parsedKey.(*rsa.PublicKey); !ok {
|
||||
return nil, ErrNotRSAPublicKey
|
||||
}
|
||||
|
||||
return pkey, nil
|
||||
}
|
35
vendor/github.com/dgrijalva/jwt-go/signing_method.go
generated
vendored
Normal file
35
vendor/github.com/dgrijalva/jwt-go/signing_method.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
var signingMethods = map[string]func() SigningMethod{}
|
||||
var signingMethodLock = new(sync.RWMutex)
|
||||
|
||||
// Implement SigningMethod to add new methods for signing or verifying tokens.
|
||||
type SigningMethod interface {
|
||||
Verify(signingString, signature string, key interface{}) error // Returns nil if signature is valid
|
||||
Sign(signingString string, key interface{}) (string, error) // Returns encoded signature or error
|
||||
Alg() string // returns the alg identifier for this method (example: 'HS256')
|
||||
}
|
||||
|
||||
// Register the "alg" name and a factory function for signing method.
|
||||
// This is typically done during init() in the method's implementation
|
||||
func RegisterSigningMethod(alg string, f func() SigningMethod) {
|
||||
signingMethodLock.Lock()
|
||||
defer signingMethodLock.Unlock()
|
||||
|
||||
signingMethods[alg] = f
|
||||
}
|
||||
|
||||
// Get a signing method from an "alg" string
|
||||
func GetSigningMethod(alg string) (method SigningMethod) {
|
||||
signingMethodLock.RLock()
|
||||
defer signingMethodLock.RUnlock()
|
||||
|
||||
if methodF, ok := signingMethods[alg]; ok {
|
||||
method = methodF()
|
||||
}
|
||||
return
|
||||
}
|
42
vendor/github.com/dgrijalva/jwt-go/test/helpers.go
generated
vendored
Normal file
42
vendor/github.com/dgrijalva/jwt-go/test/helpers.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func LoadRSAPrivateKeyFromDisk(location string) *rsa.PrivateKey {
|
||||
keyData, e := ioutil.ReadFile(location)
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
key, e := jwt.ParseRSAPrivateKeyFromPEM(keyData)
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func LoadRSAPublicKeyFromDisk(location string) *rsa.PublicKey {
|
||||
keyData, e := ioutil.ReadFile(location)
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
key, e := jwt.ParseRSAPublicKeyFromPEM(keyData)
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func MakeSampleToken(c jwt.Claims, key interface{}) string {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, c)
|
||||
s, e := token.SignedString(key)
|
||||
|
||||
if e != nil {
|
||||
panic(e.Error())
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
108
vendor/github.com/dgrijalva/jwt-go/token.go
generated
vendored
Normal file
108
vendor/github.com/dgrijalva/jwt-go/token.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TimeFunc provides the current time when parsing token to validate "exp" claim (expiration time).
|
||||
// You can override it to use another time value. This is useful for testing or if your
|
||||
// server uses a different time zone than your tokens.
|
||||
var TimeFunc = time.Now
|
||||
|
||||
// Parse methods use this callback function to supply
|
||||
// the key for verification. The function receives the parsed,
|
||||
// but unverified Token. This allows you to use properties in the
|
||||
// Header of the token (such as `kid`) to identify which key to use.
|
||||
type Keyfunc func(*Token) (interface{}, error)
|
||||
|
||||
// A JWT Token. Different fields will be used depending on whether you're
|
||||
// creating or parsing/verifying a token.
|
||||
type Token struct {
|
||||
Raw string // The raw token. Populated when you Parse a token
|
||||
Method SigningMethod // The signing method used or to be used
|
||||
Header map[string]interface{} // The first segment of the token
|
||||
Claims Claims // The second segment of the token
|
||||
Signature string // The third segment of the token. Populated when you Parse a token
|
||||
Valid bool // Is the token valid? Populated when you Parse/Verify a token
|
||||
}
|
||||
|
||||
// Create a new Token. Takes a signing method
|
||||
func New(method SigningMethod) *Token {
|
||||
return NewWithClaims(method, MapClaims{})
|
||||
}
|
||||
|
||||
func NewWithClaims(method SigningMethod, claims Claims) *Token {
|
||||
return &Token{
|
||||
Header: map[string]interface{}{
|
||||
"typ": "JWT",
|
||||
"alg": method.Alg(),
|
||||
},
|
||||
Claims: claims,
|
||||
Method: method,
|
||||
}
|
||||
}
|
||||
|
||||
// Get the complete, signed token
|
||||
func (t *Token) SignedString(key interface{}) (string, error) {
|
||||
var sig, sstr string
|
||||
var err error
|
||||
if sstr, err = t.SigningString(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if sig, err = t.Method.Sign(sstr, key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Join([]string{sstr, sig}, "."), nil
|
||||
}
|
||||
|
||||
// Generate the signing string. This is the
|
||||
// most expensive part of the whole deal. Unless you
|
||||
// need this for something special, just go straight for
|
||||
// the SignedString.
|
||||
func (t *Token) SigningString() (string, error) {
|
||||
var err error
|
||||
parts := make([]string, 2)
|
||||
for i, _ := range parts {
|
||||
var jsonValue []byte
|
||||
if i == 0 {
|
||||
if jsonValue, err = json.Marshal(t.Header); err != nil {
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
if jsonValue, err = json.Marshal(t.Claims); err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
parts[i] = EncodeSegment(jsonValue)
|
||||
}
|
||||
return strings.Join(parts, "."), nil
|
||||
}
|
||||
|
||||
// Parse, validate, and return a token.
|
||||
// keyFunc will receive the parsed token and should return the key for validating.
|
||||
// If everything is kosher, err will be nil
|
||||
func Parse(tokenString string, keyFunc Keyfunc) (*Token, error) {
|
||||
return new(Parser).Parse(tokenString, keyFunc)
|
||||
}
|
||||
|
||||
func ParseWithClaims(tokenString string, claims Claims, keyFunc Keyfunc) (*Token, error) {
|
||||
return new(Parser).ParseWithClaims(tokenString, claims, keyFunc)
|
||||
}
|
||||
|
||||
// Encode JWT specific base64url encoding with padding stripped
|
||||
func EncodeSegment(seg []byte) string {
|
||||
return strings.TrimRight(base64.URLEncoding.EncodeToString(seg), "=")
|
||||
}
|
||||
|
||||
// Decode JWT specific base64url encoding with padding stripped
|
||||
func DecodeSegment(seg string) ([]byte, error) {
|
||||
if l := len(seg) % 4; l > 0 {
|
||||
seg += strings.Repeat("=", 4-l)
|
||||
}
|
||||
|
||||
return base64.URLEncoding.DecodeString(seg)
|
||||
}
|
21
vendor/github.com/facebookgo/clock/LICENSE
generated
vendored
Normal file
21
vendor/github.com/facebookgo/clock/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Ben Johnson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
363
vendor/github.com/facebookgo/clock/clock.go
generated
vendored
Normal file
363
vendor/github.com/facebookgo/clock/clock.go
generated
vendored
Normal file
@ -0,0 +1,363 @@
|
||||
package clock
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Clock represents an interface to the functions in the standard library time
|
||||
// package. Two implementations are available in the clock package. The first
|
||||
// is a real-time clock which simply wraps the time package's functions. The
|
||||
// second is a mock clock which will only make forward progress when
|
||||
// programmatically adjusted.
|
||||
type Clock interface {
|
||||
After(d time.Duration) <-chan time.Time
|
||||
AfterFunc(d time.Duration, f func()) *Timer
|
||||
Now() time.Time
|
||||
Sleep(d time.Duration)
|
||||
Tick(d time.Duration) <-chan time.Time
|
||||
Ticker(d time.Duration) *Ticker
|
||||
Timer(d time.Duration) *Timer
|
||||
}
|
||||
|
||||
// New returns an instance of a real-time clock.
|
||||
func New() Clock {
|
||||
return &clock{}
|
||||
}
|
||||
|
||||
// clock implements a real-time clock by simply wrapping the time package functions.
|
||||
type clock struct{}
|
||||
|
||||
func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
||||
|
||||
func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
|
||||
return &Timer{timer: time.AfterFunc(d, f)}
|
||||
}
|
||||
|
||||
func (c *clock) Now() time.Time { return time.Now() }
|
||||
|
||||
func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }
|
||||
|
||||
func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }
|
||||
|
||||
func (c *clock) Ticker(d time.Duration) *Ticker {
|
||||
t := time.NewTicker(d)
|
||||
return &Ticker{C: t.C, ticker: t}
|
||||
}
|
||||
|
||||
func (c *clock) Timer(d time.Duration) *Timer {
|
||||
t := time.NewTimer(d)
|
||||
return &Timer{C: t.C, timer: t}
|
||||
}
|
||||
|
||||
// Mock represents a mock clock that only moves forward programmically.
|
||||
// It can be preferable to a real-time clock when testing time-based functionality.
|
||||
type Mock struct {
|
||||
mu sync.Mutex
|
||||
now time.Time // current time
|
||||
timers clockTimers // tickers & timers
|
||||
|
||||
calls Calls
|
||||
waiting []waiting
|
||||
callsMutex sync.Mutex
|
||||
}
|
||||
|
||||
// NewMock returns an instance of a mock clock.
|
||||
// The current time of the mock clock on initialization is the Unix epoch.
|
||||
func NewMock() *Mock {
|
||||
return &Mock{now: time.Unix(0, 0)}
|
||||
}
|
||||
|
||||
// Add moves the current time of the mock clock forward by the duration.
|
||||
// This should only be called from a single goroutine at a time.
|
||||
func (m *Mock) Add(d time.Duration) {
|
||||
// Calculate the final current time.
|
||||
t := m.now.Add(d)
|
||||
|
||||
// Continue to execute timers until there are no more before the new time.
|
||||
for {
|
||||
if !m.runNextTimer(t) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that we end with the new time.
|
||||
m.mu.Lock()
|
||||
m.now = t
|
||||
m.mu.Unlock()
|
||||
|
||||
// Give a small buffer to make sure the other goroutines get handled.
|
||||
gosched()
|
||||
}
|
||||
|
||||
// runNextTimer executes the next timer in chronological order and moves the
|
||||
// current time to the timer's next tick time. The next time is not executed if
|
||||
// it's next time if after the max time. Returns true if a timer is executed.
|
||||
func (m *Mock) runNextTimer(max time.Time) bool {
|
||||
m.mu.Lock()
|
||||
|
||||
// Sort timers by time.
|
||||
sort.Sort(m.timers)
|
||||
|
||||
// If we have no more timers then exit.
|
||||
if len(m.timers) == 0 {
|
||||
m.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// Retrieve next timer. Exit if next tick is after new time.
|
||||
t := m.timers[0]
|
||||
if t.Next().After(max) {
|
||||
m.mu.Unlock()
|
||||
return false
|
||||
}
|
||||
|
||||
// Move "now" forward and unlock clock.
|
||||
m.now = t.Next()
|
||||
m.mu.Unlock()
|
||||
|
||||
// Execute timer.
|
||||
t.Tick(m.now)
|
||||
return true
|
||||
}
|
||||
|
||||
// After waits for the duration to elapse and then sends the current time on the returned channel.
|
||||
func (m *Mock) After(d time.Duration) <-chan time.Time {
|
||||
defer m.inc(&m.calls.After)
|
||||
return m.Timer(d).C
|
||||
}
|
||||
|
||||
// AfterFunc waits for the duration to elapse and then executes a function.
|
||||
// A Timer is returned that can be stopped.
|
||||
func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
|
||||
defer m.inc(&m.calls.AfterFunc)
|
||||
t := m.Timer(d)
|
||||
t.C = nil
|
||||
t.fn = f
|
||||
return t
|
||||
}
|
||||
|
||||
// Now returns the current wall time on the mock clock.
|
||||
func (m *Mock) Now() time.Time {
|
||||
defer m.inc(&m.calls.Now)
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
return m.now
|
||||
}
|
||||
|
||||
// Sleep pauses the goroutine for the given duration on the mock clock.
|
||||
// The clock must be moved forward in a separate goroutine.
|
||||
func (m *Mock) Sleep(d time.Duration) {
|
||||
defer m.inc(&m.calls.Sleep)
|
||||
<-m.After(d)
|
||||
}
|
||||
|
||||
// Tick is a convenience function for Ticker().
|
||||
// It will return a ticker channel that cannot be stopped.
|
||||
func (m *Mock) Tick(d time.Duration) <-chan time.Time {
|
||||
defer m.inc(&m.calls.Tick)
|
||||
return m.Ticker(d).C
|
||||
}
|
||||
|
||||
// Ticker creates a new instance of Ticker.
|
||||
func (m *Mock) Ticker(d time.Duration) *Ticker {
|
||||
defer m.inc(&m.calls.Ticker)
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
ch := make(chan time.Time)
|
||||
t := &Ticker{
|
||||
C: ch,
|
||||
c: ch,
|
||||
mock: m,
|
||||
d: d,
|
||||
next: m.now.Add(d),
|
||||
}
|
||||
m.timers = append(m.timers, (*internalTicker)(t))
|
||||
return t
|
||||
}
|
||||
|
||||
// Timer creates a new instance of Timer.
|
||||
func (m *Mock) Timer(d time.Duration) *Timer {
|
||||
defer m.inc(&m.calls.Timer)
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
ch := make(chan time.Time)
|
||||
t := &Timer{
|
||||
C: ch,
|
||||
c: ch,
|
||||
mock: m,
|
||||
next: m.now.Add(d),
|
||||
}
|
||||
m.timers = append(m.timers, (*internalTimer)(t))
|
||||
return t
|
||||
}
|
||||
|
||||
func (m *Mock) removeClockTimer(t clockTimer) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for i, timer := range m.timers {
|
||||
if timer == t {
|
||||
copy(m.timers[i:], m.timers[i+1:])
|
||||
m.timers[len(m.timers)-1] = nil
|
||||
m.timers = m.timers[:len(m.timers)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
sort.Sort(m.timers)
|
||||
}
|
||||
|
||||
func (m *Mock) inc(addr *uint32) {
|
||||
m.callsMutex.Lock()
|
||||
defer m.callsMutex.Unlock()
|
||||
*addr++
|
||||
var newWaiting []waiting
|
||||
for _, w := range m.waiting {
|
||||
if m.calls.atLeast(w.expected) {
|
||||
close(w.done)
|
||||
continue
|
||||
}
|
||||
newWaiting = append(newWaiting, w)
|
||||
}
|
||||
m.waiting = newWaiting
|
||||
}
|
||||
|
||||
// Wait waits for at least the relevant calls before returning. The expected
|
||||
// Calls are always over the lifetime of the Mock. Values in the Calls struct
|
||||
// are used as the minimum number of calls, this allows you to wait for only
|
||||
// the calls you care about.
|
||||
func (m *Mock) Wait(s Calls) {
|
||||
m.callsMutex.Lock()
|
||||
if m.calls.atLeast(s) {
|
||||
m.callsMutex.Unlock()
|
||||
return
|
||||
}
|
||||
done := make(chan struct{})
|
||||
m.waiting = append(m.waiting, waiting{expected: s, done: done})
|
||||
m.callsMutex.Unlock()
|
||||
<-done
|
||||
}
|
||||
|
||||
// clockTimer represents an object with an associated start time.
|
||||
type clockTimer interface {
|
||||
Next() time.Time
|
||||
Tick(time.Time)
|
||||
}
|
||||
|
||||
// clockTimers represents a list of sortable timers.
|
||||
type clockTimers []clockTimer
|
||||
|
||||
func (a clockTimers) Len() int { return len(a) }
|
||||
func (a clockTimers) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }
|
||||
|
||||
// Timer represents a single event.
|
||||
// The current time will be sent on C, unless the timer was created by AfterFunc.
|
||||
type Timer struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
timer *time.Timer // realtime impl, if set
|
||||
next time.Time // next tick time
|
||||
mock *Mock // mock clock, if set
|
||||
fn func() // AfterFunc function, if set
|
||||
}
|
||||
|
||||
// Stop turns off the ticker.
|
||||
func (t *Timer) Stop() {
|
||||
if t.timer != nil {
|
||||
t.timer.Stop()
|
||||
} else {
|
||||
t.mock.removeClockTimer((*internalTimer)(t))
|
||||
}
|
||||
}
|
||||
|
||||
type internalTimer Timer
|
||||
|
||||
func (t *internalTimer) Next() time.Time { return t.next }
|
||||
func (t *internalTimer) Tick(now time.Time) {
|
||||
if t.fn != nil {
|
||||
t.fn()
|
||||
} else {
|
||||
t.c <- now
|
||||
}
|
||||
t.mock.removeClockTimer((*internalTimer)(t))
|
||||
gosched()
|
||||
}
|
||||
|
||||
// Ticker holds a channel that receives "ticks" at regular intervals.
|
||||
type Ticker struct {
|
||||
C <-chan time.Time
|
||||
c chan time.Time
|
||||
ticker *time.Ticker // realtime impl, if set
|
||||
next time.Time // next tick time
|
||||
mock *Mock // mock clock, if set
|
||||
d time.Duration // time between ticks
|
||||
}
|
||||
|
||||
// Stop turns off the ticker.
|
||||
func (t *Ticker) Stop() {
|
||||
if t.ticker != nil {
|
||||
t.ticker.Stop()
|
||||
} else {
|
||||
t.mock.removeClockTimer((*internalTicker)(t))
|
||||
}
|
||||
}
|
||||
|
||||
type internalTicker Ticker
|
||||
|
||||
func (t *internalTicker) Next() time.Time { return t.next }
|
||||
func (t *internalTicker) Tick(now time.Time) {
|
||||
select {
|
||||
case t.c <- now:
|
||||
case <-time.After(1 * time.Millisecond):
|
||||
}
|
||||
t.next = now.Add(t.d)
|
||||
gosched()
|
||||
}
|
||||
|
||||
// Sleep momentarily so that other goroutines can process.
|
||||
func gosched() { runtime.Gosched() }
|
||||
|
||||
// Calls keeps track of the count of calls for each of the methods on the Clock
|
||||
// interface.
|
||||
type Calls struct {
|
||||
After uint32
|
||||
AfterFunc uint32
|
||||
Now uint32
|
||||
Sleep uint32
|
||||
Tick uint32
|
||||
Ticker uint32
|
||||
Timer uint32
|
||||
}
|
||||
|
||||
// atLeast returns true if at least the number of calls in o have been made.
|
||||
func (c Calls) atLeast(o Calls) bool {
|
||||
if c.After < o.After {
|
||||
return false
|
||||
}
|
||||
if c.AfterFunc < o.AfterFunc {
|
||||
return false
|
||||
}
|
||||
if c.Now < o.Now {
|
||||
return false
|
||||
}
|
||||
if c.Sleep < o.Sleep {
|
||||
return false
|
||||
}
|
||||
if c.Tick < o.Tick {
|
||||
return false
|
||||
}
|
||||
if c.Ticker < o.Ticker {
|
||||
return false
|
||||
}
|
||||
if c.Timer < o.Timer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type waiting struct {
|
||||
expected Calls
|
||||
done chan struct{}
|
||||
}
|
190
vendor/github.com/facebookgo/grace/gracehttp/http.go
generated
vendored
Normal file
190
vendor/github.com/facebookgo/grace/gracehttp/http.go
generated
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
// Package gracehttp provides easy to use graceful restart
|
||||
// functionality for HTTP server.
|
||||
package gracehttp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/facebookgo/grace/gracenet"
|
||||
"github.com/facebookgo/httpdown"
|
||||
)
|
||||
|
||||
var (
|
||||
logger *log.Logger
|
||||
didInherit = os.Getenv("LISTEN_FDS") != ""
|
||||
ppid = os.Getppid()
|
||||
)
|
||||
|
||||
// An app contains one or more servers and associated configuration.
|
||||
type app struct {
|
||||
servers []*http.Server
|
||||
http *httpdown.HTTP
|
||||
net *gracenet.Net
|
||||
listeners []net.Listener
|
||||
sds []httpdown.Server
|
||||
errors chan error
|
||||
}
|
||||
|
||||
func newApp(servers []*http.Server) *app {
|
||||
return &app{
|
||||
servers: servers,
|
||||
http: &httpdown.HTTP{},
|
||||
net: &gracenet.Net{},
|
||||
listeners: make([]net.Listener, 0, len(servers)),
|
||||
sds: make([]httpdown.Server, 0, len(servers)),
|
||||
|
||||
// 2x num servers for possible Close or Stop errors + 1 for possible
|
||||
// StartProcess error.
|
||||
errors: make(chan error, 1+(len(servers)*2)),
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) listen() error {
|
||||
for _, s := range a.servers {
|
||||
// TODO: default addresses
|
||||
l, err := a.net.Listen("tcp", s.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.TLSConfig != nil {
|
||||
l = tls.NewListener(l, s.TLSConfig)
|
||||
}
|
||||
a.listeners = append(a.listeners, l)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *app) serve() {
|
||||
for i, s := range a.servers {
|
||||
a.sds = append(a.sds, a.http.Serve(s, a.listeners[i]))
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) wait() {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(a.sds) * 2) // Wait & Stop
|
||||
go a.signalHandler(&wg)
|
||||
for _, s := range a.sds {
|
||||
go func(s httpdown.Server) {
|
||||
defer wg.Done()
|
||||
if err := s.Wait(); err != nil {
|
||||
a.errors <- err
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func (a *app) term(wg *sync.WaitGroup) {
|
||||
for _, s := range a.sds {
|
||||
go func(s httpdown.Server) {
|
||||
defer wg.Done()
|
||||
if err := s.Stop(); err != nil {
|
||||
a.errors <- err
|
||||
}
|
||||
}(s)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *app) signalHandler(wg *sync.WaitGroup) {
|
||||
ch := make(chan os.Signal, 10)
|
||||
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
|
||||
for {
|
||||
sig := <-ch
|
||||
switch sig {
|
||||
case syscall.SIGINT, syscall.SIGTERM:
|
||||
// this ensures a subsequent INT/TERM will trigger standard go behaviour of
|
||||
// terminating.
|
||||
signal.Stop(ch)
|
||||
a.term(wg)
|
||||
return
|
||||
case syscall.SIGUSR2:
|
||||
// we only return here if there's an error, otherwise the new process
|
||||
// will send us a TERM when it's ready to trigger the actual shutdown.
|
||||
if _, err := a.net.StartProcess(); err != nil {
|
||||
a.errors <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serve will serve the given http.Servers and will monitor for signals
|
||||
// allowing for graceful termination (SIGTERM) or restart (SIGUSR2).
|
||||
func Serve(servers ...*http.Server) error {
|
||||
a := newApp(servers)
|
||||
|
||||
// Acquire Listeners
|
||||
if err := a.listen(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Some useful logging.
|
||||
if logger != nil {
|
||||
if didInherit {
|
||||
if ppid == 1 {
|
||||
logger.Printf("Listening on init activated %s", pprintAddr(a.listeners))
|
||||
} else {
|
||||
const msg = "Graceful handoff of %s with new pid %d and old pid %d"
|
||||
logger.Printf(msg, pprintAddr(a.listeners), os.Getpid(), ppid)
|
||||
}
|
||||
} else {
|
||||
const msg = "Serving %s with pid %d"
|
||||
logger.Printf(msg, pprintAddr(a.listeners), os.Getpid())
|
||||
}
|
||||
}
|
||||
|
||||
// Start serving.
|
||||
a.serve()
|
||||
|
||||
// Close the parent if we inherited and it wasn't init that started us.
|
||||
if didInherit && ppid != 1 {
|
||||
if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
|
||||
return fmt.Errorf("failed to close parent: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
waitdone := make(chan struct{})
|
||||
go func() {
|
||||
defer close(waitdone)
|
||||
a.wait()
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-a.errors:
|
||||
if err == nil {
|
||||
panic("unexpected nil error")
|
||||
}
|
||||
return err
|
||||
case <-waitdone:
|
||||
if logger != nil {
|
||||
logger.Printf("Exiting pid %d.", os.Getpid())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Used for pretty printing addresses.
|
||||
func pprintAddr(listeners []net.Listener) []byte {
|
||||
var out bytes.Buffer
|
||||
for i, l := range listeners {
|
||||
if i != 0 {
|
||||
fmt.Fprint(&out, ", ")
|
||||
}
|
||||
fmt.Fprint(&out, l.Addr())
|
||||
}
|
||||
return out.Bytes()
|
||||
}
|
||||
|
||||
// SetLogger sets logger to be able to grab some useful logs
|
||||
func SetLogger(l *log.Logger) {
|
||||
logger = l
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user