mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-27 18:09:26 +00:00
Compare commits
7 Commits
Author | SHA1 | Date | |
---|---|---|---|
d9ff0b72fa | |||
8c83eb03c7 | |||
e28649b592 | |||
e4e822ef6a | |||
69d6f4b2da | |||
f7e22983a5 | |||
cac9fb838c |
20
.github/ISSUE_TEMPLATE.md
vendored
20
.github/ISSUE_TEMPLATE.md
vendored
@ -1,20 +0,0 @@
|
||||
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 -x -ldflags "-X main.githash=$(git log --pretty=format:'%h' -n 1)" -o /bin/matterbridge \
|
||||
&& go build -o /bin/matterbridge \
|
||||
&& rm -rf /go \
|
||||
&& apk del --purge git go gcc musl-dev
|
||||
|
201
README.md
201
README.md
@ -1,50 +1,54 @@
|
||||
# matterbridge
|
||||
[](https://gitter.im/42wim/matterbridge) [](https://webchat.freenode.net/?channels=matterbridgechat) [](https://discord.gg/AkKPtrQ) [](https://riot.im/app/#/room/#matterbridge:matrix.org)
|
||||
Simple bridge between mattermost, IRC, XMPP, Gitter, Slack and Discord
|
||||
|
||||

|
||||
|
||||
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.
|
||||
* 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.
|
||||
* 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).
|
||||
|
||||
# Requirements
|
||||
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
|
||||
Accounts to one of the supported bridges
|
||||
* [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)
|
||||
* [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)
|
||||
|
||||
# Installing
|
||||
## Binaries
|
||||
## Docker
|
||||
Create your matterbridge.toml file locally eg in ```/tmp/matterbridge.toml```
|
||||
```
|
||||
docker run -ti -v /tmp/matterbridge.toml:/matterbridge.toml 42wim/matterbridge
|
||||
```
|
||||
|
||||
## binaries
|
||||
Binaries can be found [here] (https://github.com/42wim/matterbridge/releases/)
|
||||
* Latest release [v0.10.3](https://github.com/42wim/matterbridge/releases/latest)
|
||||
* For use with mattermost 3.5.0+ [v0.8.1](https://github.com/42wim/matterircd/releases/tag/v0.8.1)
|
||||
* For use with mattermost 3.3.0 - 3.4.0 [v0.7.1](https://github.com/42wim/matterircd/releases/tag/v0.7.1)
|
||||
* 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)
|
||||
|
||||
## Building
|
||||
## Compatibility
|
||||
### Mattermost
|
||||
* Matterbridge v0.8.1 works with mattermost 3.5.0+ [3.5.0 release](https://github.com/mattermost/platform/releases/tag/v3.5.0)
|
||||
* Matterbridge v0.7.1 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
|
||||
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)
|
||||
|
||||
```
|
||||
@ -59,74 +63,10 @@ $ ls bin/
|
||||
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]
|
||||
[slack.test]
|
||||
useAPI=true
|
||||
Token="yourslacktoken"
|
||||
PrefixMessagesWithNick=true
|
||||
|
||||
[discord]
|
||||
[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
|
||||
2) Edit matterbridge.toml with the settings for your environment.
|
||||
3) Now you can run matterbridge. (```./matterbridge```)
|
||||
|
||||
(Matterbridge will only look for the config file in your current directory, if it isn't there specify -conf "/path/toyour/matterbridge.toml")
|
||||
## 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.
|
||||
|
||||
```
|
||||
Usage of ./matterbridge:
|
||||
@ -134,46 +74,39 @@ Usage of ./matterbridge:
|
||||
config file (default "matterbridge.toml")
|
||||
-debug
|
||||
enable debug
|
||||
-gops
|
||||
enable gops agent
|
||||
-version
|
||||
show version
|
||||
```
|
||||
|
||||
## Docker
|
||||
Create your matterbridge.toml file locally eg in ```/tmp/matterbridge.toml```
|
||||
```
|
||||
docker run -ti -v /tmp/matterbridge.toml:/matterbridge.toml 42wim/matterbridge
|
||||
```
|
||||
## config
|
||||
### matterbridge
|
||||
matterbridge looks for matterbridge.toml in current directory. (use -conf to specify another file)
|
||||
|
||||
# Changelog
|
||||
See [changelog.md](https://github.com/42wim/matterbridge/blob/master/changelog.md)
|
||||
Look at [matterbridge.toml.sample] (https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for an example.
|
||||
|
||||
# FAQ
|
||||
### mattermost
|
||||
#### webhooks version
|
||||
You'll have to configure the incoming and outgoing webhooks.
|
||||
|
||||
Please look at [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for more information first.
|
||||
* 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)
|
||||
|
||||
## Mattermost doesn't show the IRC nicks
|
||||
* 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
|
||||
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 API version you'll need to:
|
||||
If you're running the plus 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
|
||||
* gops - https://github.com/google/gops
|
||||
* 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
|
||||
|
||||
|
@ -1,91 +0,0 @@
|
||||
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,116 +1,45 @@
|
||||
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 Bridger interface {
|
||||
type Bridge interface {
|
||||
Send(msg config.Message) error
|
||||
Name() string
|
||||
Connect() error
|
||||
FullOrigin() string
|
||||
Origin() string
|
||||
Protocol() string
|
||||
JoinChannel(channel string) error
|
||||
Disconnect() error
|
||||
}
|
||||
|
||||
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)
|
||||
func New(cfg *config.Config, bridge *config.Bridge, c chan config.Message) Bridge {
|
||||
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":
|
||||
b.Config = cfg.Mattermost[name]
|
||||
b.Bridger = bmattermost.New(cfg.Mattermost[name], bridge.Account, c)
|
||||
return bmattermost.New(cfg.Mattermost[name], name, c)
|
||||
case "irc":
|
||||
b.Config = cfg.IRC[name]
|
||||
b.Bridger = birc.New(cfg.IRC[name], bridge.Account, c)
|
||||
return birc.New(cfg.IRC[name], name, c)
|
||||
case "gitter":
|
||||
b.Config = cfg.Gitter[name]
|
||||
b.Bridger = bgitter.New(cfg.Gitter[name], bridge.Account, c)
|
||||
return bgitter.New(cfg.Gitter[name], name, c)
|
||||
case "slack":
|
||||
b.Config = cfg.Slack[name]
|
||||
b.Bridger = bslack.New(cfg.Slack[name], bridge.Account, c)
|
||||
return bslack.New(cfg.Slack[name], name, c)
|
||||
case "xmpp":
|
||||
b.Config = cfg.Xmpp[name]
|
||||
b.Bridger = bxmpp.New(cfg.Xmpp[name], bridge.Account, c)
|
||||
return bxmpp.New(cfg.Xmpp[name], name, c)
|
||||
case "discord":
|
||||
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 bdiscord.New(cfg.Discord[name], name, c)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -6,32 +6,24 @@ import (
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
EVENT_JOIN_LEAVE = "join_leave"
|
||||
EVENT_FAILURE = "failure"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Text string
|
||||
Channel string
|
||||
Username string
|
||||
Avatar string
|
||||
Account string
|
||||
Event string
|
||||
Protocol string
|
||||
Timestamp time.Time
|
||||
Text string
|
||||
Channel string
|
||||
Username string
|
||||
Origin string
|
||||
FullOrigin string
|
||||
Protocol string
|
||||
Avatar string
|
||||
}
|
||||
|
||||
type Protocol struct {
|
||||
BindAddress string // mattermost, slack
|
||||
Buffer int // api
|
||||
IconURL string // mattermost, slack
|
||||
IgnoreNicks string // all protocols
|
||||
Jid string // xmpp
|
||||
Login string // mattermost, matrix
|
||||
Login string // mattermost
|
||||
Muc string // xmpp
|
||||
Name string // all protocols
|
||||
Nick string // all protocols
|
||||
@ -40,32 +32,26 @@ type Protocol struct {
|
||||
NickServPassword string // IRC
|
||||
NicksPerRow int // mattermost, slack
|
||||
NoTLS bool // mattermost
|
||||
Password string // IRC,mattermost,XMPP,matrix
|
||||
Password string // IRC,mattermost,XMPP
|
||||
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, matrix
|
||||
URL string // mattermost, slack
|
||||
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 {
|
||||
@ -73,7 +59,6 @@ type Gateway struct {
|
||||
Enable bool
|
||||
In []Bridge
|
||||
Out []Bridge
|
||||
InOut []Bridge
|
||||
}
|
||||
|
||||
type SameChannelGateway struct {
|
||||
@ -84,17 +69,12 @@ 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
|
||||
}
|
||||
@ -146,11 +126,16 @@ 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}", name, -1)
|
||||
iconURL = strings.Replace(iconURL, "{PROTOCOL}", protocol, -1)
|
||||
iconURL = strings.Replace(iconURL, "{BRIDGE}", msg.Origin, -1)
|
||||
iconURL = strings.Replace(iconURL, "{PROTOCOL}", msg.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,20 +5,17 @@ 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
|
||||
Account string
|
||||
Channels []*discordgo.Channel
|
||||
Nick string
|
||||
UseChannelID bool
|
||||
userMemberMap map[string]*discordgo.Member
|
||||
guildID string
|
||||
sync.RWMutex
|
||||
c *discordgo.Session
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
protocol string
|
||||
origin string
|
||||
Channels []*discordgo.Channel
|
||||
Nick string
|
||||
UseChannelID bool
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -28,21 +25,18 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *bdiscord {
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *bdiscord {
|
||||
b := &bdiscord{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.Account = account
|
||||
b.userMemberMap = make(map[string]*discordgo.Member)
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
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)
|
||||
@ -50,7 +44,6 @@ 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)
|
||||
@ -70,7 +63,6 @@ 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
|
||||
@ -80,8 +72,8 @@ func (b *bdiscord) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *bdiscord) Disconnect() error {
|
||||
return nil
|
||||
func (b *bdiscord) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *bdiscord) JoinChannel(channel string) error {
|
||||
@ -92,6 +84,18 @@ 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)
|
||||
@ -99,7 +103,8 @@ func (b *bdiscord) Send(msg config.Message) error {
|
||||
flog.Errorf("Could not find channelID for %v", msg.Channel)
|
||||
return nil
|
||||
}
|
||||
b.c.ChannelMessageSend(channelID, msg.Username+msg.Text)
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
b.c.ChannelMessageSend(channelID, nick+msg.Text)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -116,50 +121,13 @@ 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.Account)
|
||||
flog.Debugf("Sending message from %s on %s to gateway", m.Author.Username, b.FullOrigin())
|
||||
channelName := b.getChannelName(m.ChannelID)
|
||||
if b.UseChannelID {
|
||||
channelName = "ID:" + m.ChannelID
|
||||
}
|
||||
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
|
||||
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"}
|
||||
}
|
||||
|
||||
func (b *bdiscord) getChannelID(name string) string {
|
||||
@ -183,15 +151,3 @@ 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 (
|
||||
"fmt"
|
||||
"github.com/42wim/go-gitter"
|
||||
"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
|
||||
Account string
|
||||
Users []gitter.User
|
||||
Rooms []gitter.Room
|
||||
c *gitter.Gitter
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
protocol string
|
||||
origin string
|
||||
Users []gitter.User
|
||||
Rooms []gitter.Room
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -24,11 +24,12 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bgitter {
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bgitter {
|
||||
b := &Bgitter{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.Account = account
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
return b
|
||||
}
|
||||
|
||||
@ -46,21 +47,16 @@ func (b *Bgitter) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bgitter) Disconnect() error {
|
||||
return nil
|
||||
|
||||
func (b *Bgitter) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bgitter) JoinChannel(channel string) error {
|
||||
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 := channel
|
||||
roomID := b.getRoomID(room)
|
||||
if roomID == "" {
|
||||
return nil
|
||||
}
|
||||
room, err := b.c.GetRoom(roomID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
b.Rooms = append(b.Rooms, *room)
|
||||
user, err := b.c.GetUser()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -75,23 +71,36 @@ func (b *Bgitter) JoinChannel(channel string) error {
|
||||
go b.c.Listen(stream)
|
||||
|
||||
go func(stream *gitter.Stream, room string) {
|
||||
for event := range stream.Event {
|
||||
for {
|
||||
event := <-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.Account)
|
||||
flog.Debugf("Sending message from %s on %s to gateway", ev.Message.From.Username, b.FullOrigin())
|
||||
b.Remote <- config.Message{Username: ev.Message.From.Username, Text: ev.Message.Text, Channel: room,
|
||||
Account: b.Account, Avatar: b.getAvatar(ev.Message.From.Username)}
|
||||
Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin(), Avatar: b.getAvatar(ev.Message.From.Username)}
|
||||
}
|
||||
case *gitter.GitterConnectionClosed:
|
||||
flog.Errorf("connection with gitter closed for room %s", room)
|
||||
}
|
||||
}
|
||||
}(stream, room.Name)
|
||||
}(stream, room)
|
||||
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)
|
||||
@ -99,8 +108,9 @@ 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, msg.Username+msg.Text+" ")
|
||||
return b.c.SendMessage(roomID, nick+msg.Text+" ")
|
||||
}
|
||||
|
||||
func (b *Bgitter) getRoomID(channel string) string {
|
||||
|
@ -19,10 +19,11 @@ 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
|
||||
@ -32,13 +33,14 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Birc {
|
||||
func New(cfg config.Protocol, origin 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.Account = account
|
||||
b.origin = origin
|
||||
b.protocol = protocol
|
||||
b.connected = make(chan struct{})
|
||||
if b.Config.MessageDelay == 0 {
|
||||
b.Config.MessageDelay = 1300
|
||||
@ -46,6 +48,7 @@ func New(cfg config.Protocol, account 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
|
||||
}
|
||||
|
||||
@ -60,7 +63,6 @@ func (b *Birc) Command(msg *config.Message) string {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -91,10 +93,8 @@ func (b *Birc) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Birc) Disconnect() error {
|
||||
b.i.Disconnect()
|
||||
close(b.Local)
|
||||
return nil
|
||||
func (b *Birc) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Birc) JoinChannel(channel string) error {
|
||||
@ -102,21 +102,34 @@ 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.Account == b.Account {
|
||||
if msg.FullOrigin == b.FullOrigin() {
|
||||
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: msg.Username, Channel: msg.Channel}
|
||||
b.Local <- config.Message{Text: text, Username: nick, Channel: msg.Channel}
|
||||
} else {
|
||||
flog.Debugf("flooding, dropping message (queue at %d)", len(b.Local))
|
||||
}
|
||||
@ -140,12 +153,12 @@ 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, Account: b.Account}
|
||||
Channel: channel, Origin: b.origin, Protocol: b.protocol, FullOrigin: b.FullOrigin()}
|
||||
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, Account: b.Account}
|
||||
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.names[channel] = nil
|
||||
b.i.ClearCallback(ircm.RPL_NAMREPLY)
|
||||
b.i.ClearCallback(ircm.RPL_ENDOFNAMES)
|
||||
@ -164,28 +177,11 @@ func (b *Birc) handleNewConnection(event *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)
|
||||
@ -220,8 +216,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.Account)
|
||||
b.Remote <- config.Message{Username: event.Nick, Text: msg, Channel: event.Arguments[0], Account: b.Account}
|
||||
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()}
|
||||
}
|
||||
|
||||
func (b *Birc) handleTopicWhoTime(event *irc.Event) {
|
||||
|
@ -1,117 +0,0 @@
|
||||
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,11 +26,12 @@ type MMMessage struct {
|
||||
type Bmattermost struct {
|
||||
MMhook
|
||||
MMapi
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
name string
|
||||
TeamId string
|
||||
Account string
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
name string
|
||||
origin string
|
||||
protocol string
|
||||
TeamId string
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -40,11 +41,13 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bmattermost {
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bmattermost {
|
||||
b := &Bmattermost{}
|
||||
b.Config = &cfg
|
||||
b.origin = origin
|
||||
b.Remote = c
|
||||
b.Account = account
|
||||
b.protocol = "mattermost"
|
||||
b.name = cfg.Name
|
||||
b.mmMap = make(map[string]string)
|
||||
return b
|
||||
}
|
||||
@ -77,8 +80,8 @@ func (b *Bmattermost) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bmattermost) Disconnect() error {
|
||||
return nil
|
||||
func (b *Bmattermost) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bmattermost) JoinChannel(channel string) error {
|
||||
@ -89,9 +92,21 @@ 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 := msg.Username
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
message := msg.Text
|
||||
channel := msg.Channel
|
||||
|
||||
@ -129,8 +144,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.Account)
|
||||
b.Remote <- config.Message{Text: message.Text, Username: message.Username, Channel: message.Channel, Account: b.Account}
|
||||
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()}
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,14 +153,14 @@ func (b *Bmattermost) handleMatterClient(mchan chan *MMMessage) {
|
||||
for message := range b.mc.MessageChan {
|
||||
// do not post our own messages back to irc
|
||||
// only listen to message from our team
|
||||
if message.Raw.Event == "posted" && b.mc.User.Username != message.Username && message.Raw.Data["team_id"].(string) == b.TeamId {
|
||||
if message.Raw.Event == "posted" && b.mc.User.Username != message.Username && message.Raw.TeamId == b.TeamId {
|
||||
flog.Debugf("Receiving from matterclient %#v", message)
|
||||
m := &MMMessage{}
|
||||
m.Username = message.Username
|
||||
m.Channel = message.Channel
|
||||
m.Text = message.Text
|
||||
if len(message.Post.FileIds) > 0 {
|
||||
for _, link := range b.mc.GetPublicLinks(message.Post.FileIds) {
|
||||
if len(message.Post.Filenames) > 0 {
|
||||
for _, link := range b.mc.GetPublicLinks(message.Post.Filenames) {
|
||||
m.Text = m.Text + "\n" + link
|
||||
}
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
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,7 +6,6 @@ import (
|
||||
"github.com/42wim/matterbridge/matterhook"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/nlopes/slack"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@ -26,7 +25,8 @@ type Bslack struct {
|
||||
Plus bool
|
||||
Remote chan config.Message
|
||||
Users []slack.User
|
||||
Account string
|
||||
protocol string
|
||||
origin string
|
||||
si *slack.Info
|
||||
channels []slack.Channel
|
||||
}
|
||||
@ -38,11 +38,12 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bslack {
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bslack {
|
||||
b := &Bslack{}
|
||||
b.Config = &cfg
|
||||
b.Remote = c
|
||||
b.Account = account
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
return b
|
||||
}
|
||||
|
||||
@ -65,18 +66,13 @@ func (b *Bslack) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bslack) Disconnect() error {
|
||||
return nil
|
||||
|
||||
func (b *Bslack) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bslack) JoinChannel(channel string) error {
|
||||
// we can only join channels using the API
|
||||
if b.Config.UseAPI {
|
||||
if strings.HasPrefix(b.Config.Token, "xoxb") {
|
||||
// TODO check if bot has already joined channel
|
||||
return nil
|
||||
}
|
||||
_, err := b.sc.JoinChannel(channel)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -85,12 +81,24 @@ 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.Account == b.Account {
|
||||
if msg.FullOrigin == b.FullOrigin() {
|
||||
return nil
|
||||
}
|
||||
nick := msg.Username
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
message := msg.Text
|
||||
channel := msg.Channel
|
||||
if b.Config.PrefixMessagesWithNick {
|
||||
@ -146,26 +154,14 @@ 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.Account, name)
|
||||
return nil, fmt.Errorf("%s: channel %s not found (no channels found)", b.FullOrigin(), name)
|
||||
}
|
||||
for _, channel := range b.channels {
|
||||
if channel.Name == name {
|
||||
return &channel, nil
|
||||
}
|
||||
}
|
||||
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)
|
||||
return nil, fmt.Errorf("%s: channel %s not found", b.FullOrigin(), name)
|
||||
}
|
||||
|
||||
func (b *Bslack) handleSlack() {
|
||||
@ -180,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 b.Config.UseAPI && message.Username == b.si.User.Name {
|
||||
if 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.Account)
|
||||
b.Remote <- config.Message{Text: text, Username: message.Username, Channel: message.Channel, Account: b.Account, Avatar: b.getAvatar(message.Username)}
|
||||
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)}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -199,8 +195,8 @@ func (b *Bslack) handleSlackClient(mchan chan *MMMessage) {
|
||||
// ignore first message
|
||||
if count > 0 {
|
||||
flog.Debugf("Receiving from slackclient %#v", ev)
|
||||
// use our own func because rtm.GetChannelInfo doesn't work for private channels
|
||||
channel, err := b.getChannelByID(ev.Channel)
|
||||
//ev.ReplyTo
|
||||
channel, err := b.rtm.GetChannelInfo(ev.Channel)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -213,26 +209,15 @@ 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:
|
||||
@ -247,29 +232,7 @@ 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
|
||||
}
|
||||
|
@ -1,64 +0,0 @@
|
||||
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))
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
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,7 +1,6 @@
|
||||
package bxmpp
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/mattn/go-xmpp"
|
||||
@ -11,11 +10,12 @@ import (
|
||||
)
|
||||
|
||||
type Bxmpp struct {
|
||||
xc *xmpp.Client
|
||||
xmppMap map[string]string
|
||||
Config *config.Protocol
|
||||
Remote chan config.Message
|
||||
Account string
|
||||
xc *xmpp.Client
|
||||
xmppMap map[string]string
|
||||
Config *config.Protocol
|
||||
origin string
|
||||
protocol string
|
||||
Remote chan config.Message
|
||||
}
|
||||
|
||||
var flog *log.Entry
|
||||
@ -25,11 +25,12 @@ func init() {
|
||||
flog = log.WithFields(log.Fields{"module": protocol})
|
||||
}
|
||||
|
||||
func New(cfg config.Protocol, account string, c chan config.Message) *Bxmpp {
|
||||
func New(cfg config.Protocol, origin string, c chan config.Message) *Bxmpp {
|
||||
b := &Bxmpp{}
|
||||
b.xmppMap = make(map[string]string)
|
||||
b.Config = &cfg
|
||||
b.Account = account
|
||||
b.protocol = protocol
|
||||
b.origin = origin
|
||||
b.Remote = c
|
||||
return b
|
||||
}
|
||||
@ -47,8 +48,8 @@ func (b *Bxmpp) Connect() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Disconnect() error {
|
||||
return nil
|
||||
func (b *Bxmpp) FullOrigin() string {
|
||||
return b.protocol + "." + b.origin
|
||||
}
|
||||
|
||||
func (b *Bxmpp) JoinChannel(channel string) error {
|
||||
@ -56,24 +57,32 @@ 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)
|
||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
|
||||
nick := config.GetNick(&msg, b.Config)
|
||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: nick + 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,
|
||||
TLSConfig: tc,
|
||||
|
||||
Host: b.Config.Server,
|
||||
User: b.Config.Jid,
|
||||
Password: b.Config.Password,
|
||||
NoTLS: true,
|
||||
StartTLS: true,
|
||||
//StartTLS: false,
|
||||
Debug: true,
|
||||
Session: true,
|
||||
@ -88,27 +97,19 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
||||
return b.xc, err
|
||||
}
|
||||
|
||||
func (b *Bxmpp) xmppKeepAlive() chan bool {
|
||||
done := make(chan bool)
|
||||
func (b *Bxmpp) xmppKeepAlive() {
|
||||
go func() {
|
||||
ticker := time.NewTicker(90 * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
b.xc.PingC2S("", "")
|
||||
case <-done:
|
||||
return
|
||||
b.xc.Send(xmpp.Chat{})
|
||||
}
|
||||
}
|
||||
}()
|
||||
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 {
|
||||
@ -126,9 +127,9 @@ func (b *Bxmpp) handleXmpp() error {
|
||||
if len(s) == 2 {
|
||||
nick = s[1]
|
||||
}
|
||||
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}
|
||||
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()}
|
||||
}
|
||||
}
|
||||
case xmpp.Presence:
|
||||
|
87
changelog.md
87
changelog.md
@ -1,89 +1,3 @@
|
||||
# v0.10.3
|
||||
## Bugfix
|
||||
* slack: Allow bot tokens for now without warning (slack). Closes #140 (fixes user_is_bot message on channel join)
|
||||
|
||||
# v0.10.2
|
||||
## New features
|
||||
* general: gops agent added. Allows for more debugging. See #134
|
||||
* general: toml inline table support added for config file
|
||||
|
||||
## Bugfix
|
||||
* all: vendored libs updated
|
||||
|
||||
## Changes
|
||||
* general: add more informative messages on startup
|
||||
|
||||
# v0.10.1
|
||||
## Bugfix
|
||||
* gitter: Fix sending messages on new channel join.
|
||||
|
||||
# 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
|
||||
@ -133,7 +47,6 @@ 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,141 +7,92 @@ import (
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Gateway struct {
|
||||
*config.Config
|
||||
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
|
||||
MyConfig *config.Gateway
|
||||
Bridges []bridge.Bridge
|
||||
ChannelsOut map[string][]string
|
||||
ChannelsIn map[string][]string
|
||||
ignoreNicks map[string][]string
|
||||
Name string
|
||||
}
|
||||
|
||||
func New(cfg *config.Config, gateway *config.Gateway) *Gateway {
|
||||
func New(cfg *config.Config, gateway *config.Gateway) error {
|
||||
c := make(chan config.Message)
|
||||
gw := &Gateway{}
|
||||
gw.Name = gateway.Name
|
||||
gw.Config = cfg
|
||||
gw.MyConfig = gateway
|
||||
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 {
|
||||
if br.Account == cfg.Account {
|
||||
return nil
|
||||
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
|
||||
}
|
||||
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) 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)
|
||||
//TODO fix mapIgnores
|
||||
//gw.mapIgnores()
|
||||
exists = make(map[string]bool)
|
||||
for _, br := range gw.Bridges {
|
||||
err := br.Connect()
|
||||
if err != nil {
|
||||
return err
|
||||
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
|
||||
}
|
||||
}
|
||||
go gw.handleReceive()
|
||||
gw.handleReceive(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gw *Gateway) handleReceive() {
|
||||
func (gw *Gateway) handleReceive(c chan config.Message) {
|
||||
for {
|
||||
select {
|
||||
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)
|
||||
}
|
||||
case msg := <-c:
|
||||
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) getDestChannel(msg *config.Message, dest string) []string {
|
||||
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]
|
||||
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]
|
||||
for _, channel := range channels {
|
||||
if channel == msg.Channel {
|
||||
return gw.ChannelsOut[dest]
|
||||
@ -150,16 +101,15 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest string) []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
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 {
|
||||
func (gw *Gateway) handleMessage(msg config.Message, dest bridge.Bridge) {
|
||||
if gw.ignoreMessage(&msg) {
|
||||
return
|
||||
}
|
||||
originchannel := msg.Channel
|
||||
channels := gw.DestChannelFunc(&msg, dest.Account)
|
||||
channels := gw.getDestChannel(&msg, dest.FullOrigin())
|
||||
for _, channel := range channels {
|
||||
// do not send the message to the bridge we come from if also the channel is the same
|
||||
if msg.Account == dest.Account && channel == originchannel {
|
||||
if msg.FullOrigin == dest.FullOrigin() && channel == originchannel {
|
||||
continue
|
||||
}
|
||||
msg.Channel = channel
|
||||
@ -167,12 +117,7 @@ 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.Account, originchannel, dest.Account, channel)
|
||||
gw.modifyUsername(&msg, dest)
|
||||
// for api we need originchannel as channel
|
||||
if dest.Protocol == "api" {
|
||||
msg.Channel = originchannel
|
||||
}
|
||||
log.Debugf("Sending %#v from %s (%s) to %s (%s)", msg, msg.FullOrigin, originchannel, dest.FullOrigin(), channel)
|
||||
err := dest.Send(msg)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
@ -181,43 +126,26 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) {
|
||||
}
|
||||
|
||||
func (gw *Gateway) ignoreMessage(msg *config.Message) bool {
|
||||
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) {
|
||||
// should we discard messages ?
|
||||
for _, entry := range gw.ignoreNicks[msg.FullOrigin] {
|
||||
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.Name))
|
||||
protoCfg := val.Field(i).MapIndex(reflect.ValueOf(dest.Origin()))
|
||||
//config.SetNickFormat(msg, protoCfg.Interface().(config.Protocol))
|
||||
val.Field(i).SetMapIndex(reflect.ValueOf(dest.Name), protoCfg)
|
||||
val.Field(i).SetMapIndex(reflect.ValueOf(dest.Origin()), 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,49 +1,78 @@
|
||||
package samechannelgateway
|
||||
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/gateway"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
type SameChannelGateway struct {
|
||||
*config.Config
|
||||
MyConfig *config.SameChannelGateway
|
||||
Channels []string
|
||||
Name string
|
||||
MyConfig *config.SameChannelGateway
|
||||
Bridges []bridge.Bridge
|
||||
Channels []string
|
||||
ignoreNicks map[string][]string
|
||||
Name string
|
||||
}
|
||||
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
return gw.Start()
|
||||
gw.handleReceive(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sgw *SameChannelGateway) validChannel(channel string) bool {
|
||||
for _, c := range sgw.Channels {
|
||||
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
|
||||
}
|
||||
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 (gw *SameChannelGateway) validChannel(channel string) bool {
|
||||
for _, c := range gw.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{}
|
||||
}
|
||||
|
@ -1,108 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
287
matterbridge.conf.sample
Normal file
287
matterbridge.conf.sample
Normal file
@ -0,0 +1,287 @@
|
||||
#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
|
@ -7,13 +7,9 @@ import (
|
||||
"github.com/42wim/matterbridge/gateway"
|
||||
"github.com/42wim/matterbridge/gateway/samechannel"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/google/gops/agent"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "0.10.3"
|
||||
githash string
|
||||
)
|
||||
var version = "0.7.1"
|
||||
|
||||
func init() {
|
||||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
|
||||
@ -23,48 +19,42 @@ func main() {
|
||||
flagConfig := flag.String("conf", "matterbridge.toml", "config file")
|
||||
flagDebug := flag.Bool("debug", false, "enable debug")
|
||||
flagVersion := flag.Bool("version", false, "show version")
|
||||
flagGops := flag.Bool("gops", false, "enable gops agent")
|
||||
flag.Parse()
|
||||
if *flagGops {
|
||||
agent.Listen(&agent.Options{})
|
||||
defer agent.Close()
|
||||
}
|
||||
if *flagVersion {
|
||||
fmt.Printf("version: %s %s\n", version, githash)
|
||||
fmt.Println("version:", version)
|
||||
return
|
||||
}
|
||||
flag.Parse()
|
||||
if *flagDebug {
|
||||
log.Info("Enabling debug")
|
||||
log.Info("enabling debug")
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
log.Printf("Running version %s %s", version, githash)
|
||||
fmt.Println("running version", version)
|
||||
cfg := config.NewConfig(*flagConfig)
|
||||
for _, gw := range cfg.SameChannelGateway {
|
||||
if !gw.Enable {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
log.Printf("Started samechannel gateway %#v", gw.Name)
|
||||
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)
|
||||
}
|
||||
|
||||
for _, gw := range cfg.Gateway {
|
||||
if !gw.Enable {
|
||||
continue
|
||||
}
|
||||
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)
|
||||
}
|
||||
log.Printf("Started gateway %#v", gw.Name)
|
||||
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("Gateway(s) started succesfully. Now relaying messages")
|
||||
select {}
|
||||
}
|
||||
|
@ -13,10 +13,6 @@
|
||||
#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
|
||||
@ -41,6 +37,18 @@ 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)
|
||||
@ -52,22 +60,6 @@ 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
|
||||
###################################################################
|
||||
@ -97,70 +89,6 @@ 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
|
||||
@ -222,13 +150,9 @@ NoTLS=false
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
|
||||
#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
|
||||
#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
|
||||
@ -238,11 +162,6 @@ NicksPerRow=4
|
||||
#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
|
||||
@ -250,9 +169,17 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
||||
#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
|
||||
#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
|
||||
@ -270,10 +197,9 @@ ShowJoinPart=false
|
||||
#REQUIRED
|
||||
Token="Yourtokenhere"
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="ircspammer1 ircspammer2"
|
||||
#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.
|
||||
@ -282,10 +208,6 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
||||
#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
|
||||
###################################################################
|
||||
@ -298,15 +220,11 @@ ShowJoinPart=false
|
||||
#### 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
|
||||
@ -314,14 +232,11 @@ URL="https://hooks.slack.com/services/yourhook"
|
||||
#REQUIRED (unless useAPI=true)
|
||||
BindAddress="0.0.0.0:9999"
|
||||
|
||||
#### Settings for using slack API (RECOMMENDED)
|
||||
#### Settings for using slack API
|
||||
#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.
|
||||
#Use https://api.slack.com/custom-integrations/legacy-tokens
|
||||
#REQUIRED (when useAPI=true)
|
||||
Token="yourslacktoken"
|
||||
|
||||
@ -334,14 +249,6 @@ Token="yourslacktoken"
|
||||
#OPTIONAL
|
||||
IconURL="https://robohash.org/{NICK}.png?size=48x48"
|
||||
|
||||
#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
|
||||
|
||||
#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
|
||||
@ -350,11 +257,6 @@ NicksPerRow=4
|
||||
#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
|
||||
@ -362,9 +264,17 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
||||
#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
|
||||
#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"
|
||||
|
||||
###################################################################
|
||||
#discord section
|
||||
@ -378,51 +288,15 @@ ShowJoinPart=false
|
||||
#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
|
||||
#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.
|
||||
#The "Bot" tag needs to be added before the token
|
||||
#REQUIRED
|
||||
Token="Yourtokenhere"
|
||||
Token="Bot Yourtokenhere"
|
||||
|
||||
#REQUIRED
|
||||
Server="yourservername"
|
||||
|
||||
#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
|
||||
#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
|
||||
@ -432,145 +306,6 @@ 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
|
||||
@ -583,7 +318,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. You can then use [[gateway.inout]]
|
||||
#want bidirectional bridging.
|
||||
#
|
||||
|
||||
[[gateway]]
|
||||
@ -611,47 +346,21 @@ 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"
|
||||
|
||||
#OPTIONAL - only used for IRC protocol at the moment
|
||||
[gateway.in.options]
|
||||
#OPTIONAL - your irc channel key
|
||||
key="yourkey"
|
||||
[[gateway.in]]
|
||||
account="mattermost.work"
|
||||
channel="off-topic"
|
||||
|
||||
|
||||
#[[gateway.out]] specifies the account and channels we will sent messages to.
|
||||
[[gateway.out]]
|
||||
account="irc.freenode"
|
||||
channel="#testing"
|
||||
|
||||
#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]]
|
||||
[[gateway.out]]
|
||||
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,23 +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"
|
||||
|
||||
#simpler config possible since v0.10.2
|
||||
#[[gateway]]
|
||||
#name="gateway2"
|
||||
#enable=true
|
||||
#inout = [
|
||||
# { account="irc.freenode", channel="#testing", options={key="channelkey"}},
|
||||
# { account="mattermost.work", channel="off-topic" },
|
||||
#]
|
||||
|
@ -80,11 +80,6 @@ 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
|
||||
@ -103,7 +98,6 @@ 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"
|
||||
@ -131,7 +125,11 @@ func (m *MMClient) Login() error {
|
||||
if appErr != nil {
|
||||
d := b.Duration()
|
||||
m.log.Debug(appErr.DetailedError)
|
||||
if firstConnection {
|
||||
//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 appErr.Message == "" {
|
||||
return errors.New(appErr.DetailedError)
|
||||
}
|
||||
@ -159,11 +157,11 @@ func (m *MMClient) Login() error {
|
||||
m.Client.SetTeamId(m.Team.Id)
|
||||
|
||||
// setup websocket connection
|
||||
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX_V3 + "/users/websocket"
|
||||
wsurl := wsScheme + m.Credentials.Server + model.API_URL_SUFFIX + "/users/websocket"
|
||||
header := http.Header{}
|
||||
header.Set(model.HEADER_AUTH, "BEARER "+m.Client.AuthToken)
|
||||
|
||||
m.log.Debugf("WsClient: making connection: %s", wsurl)
|
||||
m.log.Debug("WsClient: making connection")
|
||||
for {
|
||||
wsDialer := &websocket.Dialer{Proxy: http.ProxyFromEnvironment, TLSClientConfig: &tls.Config{InsecureSkipVerify: m.SkipTLSVerify}}
|
||||
m.WsClient, _, err = wsDialer.Dial(wsurl, header)
|
||||
@ -266,7 +264,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
|
||||
}
|
||||
rmsg.Username = m.GetUser(data.UserId).Username
|
||||
rmsg.Channel = m.GetChannelName(data.ChannelId)
|
||||
rmsg.Team = m.GetTeamName(rmsg.Raw.Data["team_id"].(string))
|
||||
rmsg.Team = m.GetTeamName(rmsg.Raw.TeamId)
|
||||
// direct message
|
||||
if rmsg.Raw.Data["channel_type"] == "D" {
|
||||
rmsg.Channel = m.GetUser(data.UserId).Username
|
||||
@ -277,7 +275,7 @@ func (m *MMClient) parseActionPost(rmsg *Message) {
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateUsers() error {
|
||||
mmusers, err := m.Client.GetProfiles(0, 50000, "")
|
||||
mmusers, err := m.Client.GetProfilesForDirectMessageList(m.Team.Id)
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
@ -307,7 +305,7 @@ func (m *MMClient) GetChannelName(channelId string) string {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, t := range m.OtherTeams {
|
||||
for _, channel := range append(*t.Channels, *t.MoreChannels...) {
|
||||
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
|
||||
if channel.Id == channelId {
|
||||
return channel.Name
|
||||
}
|
||||
@ -324,7 +322,7 @@ func (m *MMClient) GetChannelId(name string, teamId string) string {
|
||||
}
|
||||
for _, t := range m.OtherTeams {
|
||||
if t.Id == teamId {
|
||||
for _, channel := range append(*t.Channels, *t.MoreChannels...) {
|
||||
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
|
||||
if channel.Name == name {
|
||||
return channel.Id
|
||||
}
|
||||
@ -338,7 +336,7 @@ func (m *MMClient) GetChannelHeader(channelId string) string {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, t := range m.OtherTeams {
|
||||
for _, channel := range append(*t.Channels, *t.MoreChannels...) {
|
||||
for _, channel := range append(t.Channels.Channels, t.MoreChannels.Channels...) {
|
||||
if channel.Id == channelId {
|
||||
return channel.Header
|
||||
}
|
||||
@ -356,7 +354,7 @@ func (m *MMClient) PostMessage(channelId string, text string) {
|
||||
func (m *MMClient) JoinChannel(channelId string) error {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
for _, c := range *m.Team.Channels {
|
||||
for _, c := range m.Team.Channels.Channels {
|
||||
if c.Id == channelId {
|
||||
m.log.Debug("Not joining ", channelId, " already joined.")
|
||||
return nil
|
||||
@ -399,7 +397,7 @@ func (m *MMClient) GetPublicLink(filename string) string {
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return res
|
||||
return res.Data.(string)
|
||||
}
|
||||
|
||||
func (m *MMClient) GetPublicLinks(filenames []string) []string {
|
||||
@ -409,7 +407,7 @@ func (m *MMClient) GetPublicLinks(filenames []string) []string {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
output = append(output, res)
|
||||
output = append(output, res.Data.(string))
|
||||
}
|
||||
return output
|
||||
}
|
||||
@ -434,17 +432,15 @@ func (m *MMClient) UpdateLastViewed(channelId string) {
|
||||
}
|
||||
|
||||
func (m *MMClient) UsernamesInChannel(channelId string) []string {
|
||||
res, err := m.Client.GetMyChannelMembers()
|
||||
ceiRes, err := m.Client.GetChannelExtraInfo(channelId, 5000, "")
|
||||
if err != nil {
|
||||
m.log.Errorf("UsernamesInChannel(%s) failed: %s", channelId, err)
|
||||
return []string{}
|
||||
}
|
||||
members := res.Data.(*model.ChannelMembers)
|
||||
extra := ceiRes.Data.(*model.ChannelExtra)
|
||||
result := []string{}
|
||||
for _, channel := range *members {
|
||||
if channel.ChannelId == channelId {
|
||||
result = append(result, m.GetUser(channel.UserId).Username)
|
||||
}
|
||||
for _, member := range extra.Members {
|
||||
result = append(result, member.Username)
|
||||
}
|
||||
return result
|
||||
}
|
||||
@ -471,16 +467,11 @@ 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, err := m.Client.GetChannels("")
|
||||
if err != nil {
|
||||
m.log.Debug("SendDirectMessage: Couldn't update channels")
|
||||
return
|
||||
}
|
||||
mmchannels, _ := m.Client.GetChannels("")
|
||||
m.Lock()
|
||||
m.Team.Channels = mmchannels.Data.(*model.ChannelList)
|
||||
m.Unlock()
|
||||
@ -509,10 +500,10 @@ func (m *MMClient) GetChannels() []*model.Channel {
|
||||
defer m.RUnlock()
|
||||
var channels []*model.Channel
|
||||
// our primary team channels first
|
||||
channels = append(channels, *m.Team.Channels...)
|
||||
channels = append(channels, m.Team.Channels.Channels...)
|
||||
for _, t := range m.OtherTeams {
|
||||
if t.Id != m.Team.Id {
|
||||
channels = append(channels, *t.Channels...)
|
||||
channels = append(channels, t.Channels.Channels...)
|
||||
}
|
||||
}
|
||||
return channels
|
||||
@ -524,7 +515,7 @@ func (m *MMClient) GetMoreChannels() []*model.Channel {
|
||||
defer m.RUnlock()
|
||||
var channels []*model.Channel
|
||||
for _, t := range m.OtherTeams {
|
||||
channels = append(channels, *t.MoreChannels...)
|
||||
channels = append(channels, t.MoreChannels.Channels...)
|
||||
}
|
||||
return channels
|
||||
}
|
||||
@ -535,8 +526,8 @@ func (m *MMClient) GetTeamFromChannel(channelId string) string {
|
||||
defer m.RUnlock()
|
||||
var channels []*model.Channel
|
||||
for _, t := range m.OtherTeams {
|
||||
channels = append(channels, *t.Channels...)
|
||||
channels = append(channels, *t.MoreChannels...)
|
||||
channels = append(channels, t.Channels.Channels...)
|
||||
channels = append(channels, t.MoreChannels.Channels...)
|
||||
for _, c := range channels {
|
||||
if c.Id == channelId {
|
||||
return t.Id
|
||||
@ -549,12 +540,12 @@ func (m *MMClient) GetTeamFromChannel(channelId string) string {
|
||||
func (m *MMClient) GetLastViewedAt(channelId string) int64 {
|
||||
m.RLock()
|
||||
defer m.RUnlock()
|
||||
res, err := m.Client.GetChannel(channelId, "")
|
||||
if err != nil {
|
||||
return model.GetMillis()
|
||||
for _, t := range m.OtherTeams {
|
||||
if _, ok := t.Channels.Members[channelId]; ok {
|
||||
return t.Channels.Members[channelId].LastViewedAt
|
||||
}
|
||||
}
|
||||
data := res.Data.(*model.ChannelData)
|
||||
return data.Member.LastViewedAt
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *MMClient) GetUsers() map[string]*model.User {
|
||||
@ -588,27 +579,6 @@ 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
|
||||
}
|
||||
@ -649,20 +619,11 @@ func (m *MMClient) initUser() error {
|
||||
//m.log.Debug("initUser(): loading all team data")
|
||||
for _, v := range initData.Teams {
|
||||
m.Client.SetTeamId(v.Id)
|
||||
mmusers, err := m.Client.GetProfiles(0, 50000, "")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
mmusers, _ := m.Client.GetProfiles(v.Id, "")
|
||||
t := &Team{Team: v, Users: mmusers.Data.(map[string]*model.User), Id: v.Id}
|
||||
mmchannels, err := m.Client.GetChannels("")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
mmchannels, _ := m.Client.GetChannels("")
|
||||
t.Channels = mmchannels.Data.(*model.ChannelList)
|
||||
mmchannels, err = m.Client.GetMoreChannels("")
|
||||
if err != nil {
|
||||
return errors.New(err.DetailedError)
|
||||
}
|
||||
mmchannels, _ = m.Client.GetMoreChannels("")
|
||||
t.MoreChannels = mmchannels.Data.(*model.ChannelList)
|
||||
m.OtherTeams = append(m.OtherTeams, t)
|
||||
if v.Name == m.Credentials.Team {
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// OMessage for mattermost incoming webhook. (send to mattermost)
|
||||
@ -83,14 +82,8 @@ 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 := srv.ListenAndServe(); err != nil {
|
||||
if err := http.ListenAndServe(c.BindAddress, mux); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
@ -127,6 +127,7 @@ func (gitter *Gitter) GetRooms() ([]Room, error) {
|
||||
|
||||
// GetUsersInRoom returns the users in the room with the passed id
|
||||
func (gitter *Gitter) GetUsersInRoom(roomID string) ([]User, error) {
|
||||
|
||||
var users []User
|
||||
response, err := gitter.get(gitter.config.apiBaseURL + "rooms/" + roomID + "/users")
|
||||
if err != nil {
|
||||
@ -258,45 +259,6 @@ func (gitter *Gitter) SetDebug(debug bool, logWriter io.Writer) {
|
||||
gitter.logWriter = logWriter
|
||||
}
|
||||
|
||||
// SearchRooms queries the Rooms resources of gitter API
|
||||
func (gitter *Gitter) SearchRooms(room string) ([]Room, error) {
|
||||
|
||||
var rooms struct {
|
||||
Results []Room `json:"results"`
|
||||
}
|
||||
|
||||
response, err := gitter.get(gitter.config.apiBaseURL + "rooms?q=" + room )
|
||||
|
||||
if err != nil {
|
||||
gitter.log(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(response, &rooms)
|
||||
if err != nil {
|
||||
gitter.log(err)
|
||||
return nil, err
|
||||
}
|
||||
return rooms.Results, nil
|
||||
}
|
||||
|
||||
// GetRoomId returns the room ID of a given URI
|
||||
func (gitter *Gitter) GetRoomId(uri string) (string, error) {
|
||||
|
||||
rooms, err := gitter.SearchRooms(uri)
|
||||
if err != nil {
|
||||
gitter.log(err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, element := range rooms {
|
||||
if element.URI == uri {
|
||||
return element.ID, nil
|
||||
}
|
||||
}
|
||||
return "", APIError{What: "Room not found."}
|
||||
}
|
||||
|
||||
// Pagination params
|
||||
type Pagination struct {
|
||||
|
@ -47,13 +47,13 @@ Loop:
|
||||
}
|
||||
break Loop
|
||||
}
|
||||
|
||||
|
||||
resp := stream.getResponse()
|
||||
if resp.StatusCode != 200 {
|
||||
gitter.log(fmt.Sprintf("Unexpected response code %v", resp.StatusCode))
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
//"The JSON stream returns messages as JSON objects that are delimited by carriage return (\r)" <- Not true crap it's (\n) only
|
||||
reader = bufio.NewReader(resp.Body)
|
||||
line, err := reader.ReadBytes('\n')
|
||||
@ -112,7 +112,6 @@ type Stream struct {
|
||||
|
||||
func (stream *Stream) destroy() {
|
||||
close(stream.Event)
|
||||
stream.streamConnection.currentRetries = 0
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
@ -136,8 +135,10 @@ func (stream *Stream) connect() {
|
||||
}
|
||||
|
||||
res, err := stream.gitter.getResponse(stream.url, stream)
|
||||
if err != nil || res.StatusCode != 200 {
|
||||
stream.gitter.log(fmt.Sprintf("Failed to get response, trying reconnect (Status code: %v)", res.StatusCode))
|
||||
if stream.streamConnection.canceled {
|
||||
// do nothing
|
||||
} else if err != nil || res.StatusCode != 200 {
|
||||
stream.gitter.log("Failed to get response, trying reconnect ")
|
||||
stream.gitter.log(err)
|
||||
|
||||
// sleep and wait
|
||||
@ -160,6 +161,9 @@ type streamConnection struct {
|
||||
// connection was closed
|
||||
closed bool
|
||||
|
||||
// canceled
|
||||
canceled bool
|
||||
|
||||
// wait time till next try
|
||||
wait time.Duration
|
||||
|
||||
@ -188,10 +192,13 @@ func (stream *Stream) Close() {
|
||||
stream.gitter.log("Stream connection close request")
|
||||
switch transport := stream.gitter.config.client.Transport.(type) {
|
||||
case *httpclient.Transport:
|
||||
stream.streamConnection.canceled = true
|
||||
transport.CancelRequest(conn.request)
|
||||
default:
|
||||
}
|
||||
|
||||
}
|
||||
conn.currentRetries = 0
|
||||
}
|
||||
|
||||
func (stream *Stream) isClosed() bool {
|
@ -199,3 +199,4 @@
|
||||
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.
|
||||
|
434
vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
generated
vendored
Normal file
434
vendor/github.com/42wim/matterbridge-plus/bridge/bridge.go
generated
vendored
Normal file
@ -0,0 +1,434 @@
|
||||
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
Normal file
68
vendor/github.com/42wim/matterbridge-plus/bridge/config.go
generated
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
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
Normal file
59
vendor/github.com/42wim/matterbridge-plus/bridge/helper.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
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
Normal file
202
vendor/github.com/42wim/matterbridge-plus/matterclient/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
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
Normal file
441
vendor/github.com/42wim/matterbridge-plus/matterclient/matterclient.go
generated
vendored
Normal file
@ -0,0 +1,441 @@
|
||||
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)
|
||||
}
|
2
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
2
vendor/github.com/BurntSushi/toml/doc.go
generated
vendored
@ -4,7 +4,7 @@ files via reflection. There is also support for delaying decoding with
|
||||
the Primitive type, and querying the set of keys in a TOML document with the
|
||||
MetaData type.
|
||||
|
||||
The specification implemented: https://github.com/toml-lang/toml
|
||||
The specification implemented: https://github.com/mojombo/toml
|
||||
|
||||
The sub-command github.com/BurntSushi/toml/cmd/tomlv can be used to verify
|
||||
whether a file is a valid TOML document. It can also be used to print the
|
||||
|
2
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
2
vendor/github.com/BurntSushi/toml/encode.go
generated
vendored
@ -241,7 +241,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||
func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
||||
panicIfInvalidKey(key)
|
||||
if len(key) == 1 {
|
||||
// Output an extra newline between top-level tables.
|
||||
// Output an extra new line between top-level tables.
|
||||
// (The newline isn't written if nothing else has been written though.)
|
||||
enc.newline()
|
||||
}
|
||||
|
259
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
259
vendor/github.com/BurntSushi/toml/lex.go
generated
vendored
@ -30,28 +30,24 @@ const (
|
||||
itemArrayTableEnd
|
||||
itemKeyStart
|
||||
itemCommentStart
|
||||
itemInlineTableStart
|
||||
itemInlineTableEnd
|
||||
)
|
||||
|
||||
const (
|
||||
eof = 0
|
||||
comma = ','
|
||||
tableStart = '['
|
||||
tableEnd = ']'
|
||||
arrayTableStart = '['
|
||||
arrayTableEnd = ']'
|
||||
tableSep = '.'
|
||||
keySep = '='
|
||||
arrayStart = '['
|
||||
arrayEnd = ']'
|
||||
commentStart = '#'
|
||||
stringStart = '"'
|
||||
stringEnd = '"'
|
||||
rawStringStart = '\''
|
||||
rawStringEnd = '\''
|
||||
inlineTableStart = '{'
|
||||
inlineTableEnd = '}'
|
||||
eof = 0
|
||||
tableStart = '['
|
||||
tableEnd = ']'
|
||||
arrayTableStart = '['
|
||||
arrayTableEnd = ']'
|
||||
tableSep = '.'
|
||||
keySep = '='
|
||||
arrayStart = '['
|
||||
arrayEnd = ']'
|
||||
arrayValTerm = ','
|
||||
commentStart = '#'
|
||||
stringStart = '"'
|
||||
stringEnd = '"'
|
||||
rawStringStart = '\''
|
||||
rawStringEnd = '\''
|
||||
)
|
||||
|
||||
type stateFn func(lx *lexer) stateFn
|
||||
@ -60,18 +56,11 @@ type lexer struct {
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
width int
|
||||
line int
|
||||
state stateFn
|
||||
items chan item
|
||||
|
||||
// Allow for backing up up to three runes.
|
||||
// This is necessary because TOML contains 3-rune tokens (""" and ''').
|
||||
prevWidths [3]int
|
||||
nprev int // how many of prevWidths are in use
|
||||
// If we emit an eof, we can still back up, but it is not OK to call
|
||||
// next again.
|
||||
atEOF bool
|
||||
|
||||
// A stack of state functions used to maintain context.
|
||||
// The idea is to reuse parts of the state machine in various places.
|
||||
// For example, values can appear at the top level or within arbitrarily
|
||||
@ -99,7 +88,7 @@ func (lx *lexer) nextItem() item {
|
||||
|
||||
func lex(input string) *lexer {
|
||||
lx := &lexer{
|
||||
input: input,
|
||||
input: input + "\n",
|
||||
state: lexTop,
|
||||
line: 1,
|
||||
items: make(chan item, 10),
|
||||
@ -114,7 +103,7 @@ func (lx *lexer) push(state stateFn) {
|
||||
|
||||
func (lx *lexer) pop() stateFn {
|
||||
if len(lx.stack) == 0 {
|
||||
return lx.errorf("BUG in lexer: no states to pop")
|
||||
return lx.errorf("BUG in lexer: no states to pop.")
|
||||
}
|
||||
last := lx.stack[len(lx.stack)-1]
|
||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
||||
@ -136,25 +125,16 @@ func (lx *lexer) emitTrim(typ itemType) {
|
||||
}
|
||||
|
||||
func (lx *lexer) next() (r rune) {
|
||||
if lx.atEOF {
|
||||
panic("next called after EOF")
|
||||
}
|
||||
if lx.pos >= len(lx.input) {
|
||||
lx.atEOF = true
|
||||
lx.width = 0
|
||||
return eof
|
||||
}
|
||||
|
||||
if lx.input[lx.pos] == '\n' {
|
||||
lx.line++
|
||||
}
|
||||
lx.prevWidths[2] = lx.prevWidths[1]
|
||||
lx.prevWidths[1] = lx.prevWidths[0]
|
||||
if lx.nprev < 3 {
|
||||
lx.nprev++
|
||||
}
|
||||
r, w := utf8.DecodeRuneInString(lx.input[lx.pos:])
|
||||
lx.prevWidths[0] = w
|
||||
lx.pos += w
|
||||
r, lx.width = utf8.DecodeRuneInString(lx.input[lx.pos:])
|
||||
lx.pos += lx.width
|
||||
return r
|
||||
}
|
||||
|
||||
@ -163,20 +143,9 @@ func (lx *lexer) ignore() {
|
||||
lx.start = lx.pos
|
||||
}
|
||||
|
||||
// backup steps back one rune. Can be called only twice between calls to next.
|
||||
// backup steps back one rune. Can be called only once per call of next.
|
||||
func (lx *lexer) backup() {
|
||||
if lx.atEOF {
|
||||
lx.atEOF = false
|
||||
return
|
||||
}
|
||||
if lx.nprev < 1 {
|
||||
panic("backed up too far")
|
||||
}
|
||||
w := lx.prevWidths[0]
|
||||
lx.prevWidths[0] = lx.prevWidths[1]
|
||||
lx.prevWidths[1] = lx.prevWidths[2]
|
||||
lx.nprev--
|
||||
lx.pos -= w
|
||||
lx.pos -= lx.width
|
||||
if lx.pos < len(lx.input) && lx.input[lx.pos] == '\n' {
|
||||
lx.line--
|
||||
}
|
||||
@ -213,7 +182,7 @@ func (lx *lexer) skip(pred func(rune) bool) {
|
||||
|
||||
// errorf stops all lexing by emitting an error and returning `nil`.
|
||||
// Note that any value that is a character is escaped if it's a special
|
||||
// character (newlines, tabs, etc.).
|
||||
// character (new lines, tabs, etc.).
|
||||
func (lx *lexer) errorf(format string, values ...interface{}) stateFn {
|
||||
lx.items <- item{
|
||||
itemError,
|
||||
@ -229,6 +198,7 @@ func lexTop(lx *lexer) stateFn {
|
||||
if isWhitespace(r) || isNL(r) {
|
||||
return lexSkip(lx, lexTop)
|
||||
}
|
||||
|
||||
switch r {
|
||||
case commentStart:
|
||||
lx.push(lexTop)
|
||||
@ -237,7 +207,7 @@ func lexTop(lx *lexer) stateFn {
|
||||
return lexTableStart
|
||||
case eof:
|
||||
if lx.pos > lx.start {
|
||||
return lx.errorf("unexpected EOF")
|
||||
return lx.errorf("Unexpected EOF.")
|
||||
}
|
||||
lx.emit(itemEOF)
|
||||
return nil
|
||||
@ -252,12 +222,12 @@ func lexTop(lx *lexer) stateFn {
|
||||
|
||||
// lexTopEnd is entered whenever a top-level item has been consumed. (A value
|
||||
// or a table.) It must see only whitespace, and will turn back to lexTop
|
||||
// upon a newline. If it sees EOF, it will quit the lexer successfully.
|
||||
// upon a new line. If it sees EOF, it will quit the lexer successfully.
|
||||
func lexTopEnd(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == commentStart:
|
||||
// a comment will read to a newline for us.
|
||||
// a comment will read to a new line for us.
|
||||
lx.push(lexTop)
|
||||
return lexCommentStart
|
||||
case isWhitespace(r):
|
||||
@ -266,11 +236,11 @@ func lexTopEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
return lexTop
|
||||
case r == eof:
|
||||
lx.emit(itemEOF)
|
||||
return nil
|
||||
lx.ignore()
|
||||
return lexTop
|
||||
}
|
||||
return lx.errorf("expected a top-level item to end with a newline, "+
|
||||
"comment, or EOF, but got %q instead", r)
|
||||
return lx.errorf("Expected a top-level item to end with a new line, "+
|
||||
"comment or EOF, but got %q instead.", r)
|
||||
}
|
||||
|
||||
// lexTable lexes the beginning of a table. Namely, it makes sure that
|
||||
@ -297,8 +267,8 @@ func lexTableEnd(lx *lexer) stateFn {
|
||||
|
||||
func lexArrayTableEnd(lx *lexer) stateFn {
|
||||
if r := lx.next(); r != arrayTableEnd {
|
||||
return lx.errorf("expected end of table array name delimiter %q, "+
|
||||
"but got %q instead", arrayTableEnd, r)
|
||||
return lx.errorf("Expected end of table array name delimiter %q, "+
|
||||
"but got %q instead.", arrayTableEnd, r)
|
||||
}
|
||||
lx.emit(itemArrayTableEnd)
|
||||
return lexTopEnd
|
||||
@ -308,11 +278,11 @@ func lexTableNameStart(lx *lexer) stateFn {
|
||||
lx.skip(isWhitespace)
|
||||
switch r := lx.peek(); {
|
||||
case r == tableEnd || r == eof:
|
||||
return lx.errorf("unexpected end of table name " +
|
||||
"(table names cannot be empty)")
|
||||
return lx.errorf("Unexpected end of table name. (Table names cannot " +
|
||||
"be empty.)")
|
||||
case r == tableSep:
|
||||
return lx.errorf("unexpected table separator " +
|
||||
"(table names cannot be empty)")
|
||||
return lx.errorf("Unexpected table separator. (Table names cannot " +
|
||||
"be empty.)")
|
||||
case r == stringStart || r == rawStringStart:
|
||||
lx.ignore()
|
||||
lx.push(lexTableNameEnd)
|
||||
@ -347,8 +317,8 @@ func lexTableNameEnd(lx *lexer) stateFn {
|
||||
case r == tableEnd:
|
||||
return lx.pop()
|
||||
default:
|
||||
return lx.errorf("expected '.' or ']' to end table name, "+
|
||||
"but got %q instead", r)
|
||||
return lx.errorf("Expected '.' or ']' to end table name, but got %q "+
|
||||
"instead.", r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -358,7 +328,7 @@ func lexKeyStart(lx *lexer) stateFn {
|
||||
r := lx.peek()
|
||||
switch {
|
||||
case r == keySep:
|
||||
return lx.errorf("unexpected key separator %q", keySep)
|
||||
return lx.errorf("Unexpected key separator %q.", keySep)
|
||||
case isWhitespace(r) || isNL(r):
|
||||
lx.next()
|
||||
return lexSkip(lx, lexKeyStart)
|
||||
@ -389,7 +359,7 @@ func lexBareKey(lx *lexer) stateFn {
|
||||
lx.emit(itemText)
|
||||
return lexKeyEnd
|
||||
default:
|
||||
return lx.errorf("bare keys cannot contain %q", r)
|
||||
return lx.errorf("Bare keys cannot contain %q.", r)
|
||||
}
|
||||
}
|
||||
|
||||
@ -402,7 +372,7 @@ func lexKeyEnd(lx *lexer) stateFn {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexKeyEnd)
|
||||
default:
|
||||
return lx.errorf("expected key separator %q, but got %q instead",
|
||||
return lx.errorf("Expected key separator %q, but got %q instead.",
|
||||
keySep, r)
|
||||
}
|
||||
}
|
||||
@ -411,8 +381,9 @@ func lexKeyEnd(lx *lexer) stateFn {
|
||||
// lexValue will ignore whitespace.
|
||||
// After a value is lexed, the last state on the next is popped and returned.
|
||||
func lexValue(lx *lexer) stateFn {
|
||||
// We allow whitespace to precede a value, but NOT newlines.
|
||||
// In array syntax, the array states are responsible for ignoring newlines.
|
||||
// We allow whitespace to precede a value, but NOT new lines.
|
||||
// In array syntax, the array states are responsible for ignoring new
|
||||
// lines.
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
@ -426,10 +397,6 @@ func lexValue(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.emit(itemArray)
|
||||
return lexArrayValue
|
||||
case inlineTableStart:
|
||||
lx.ignore()
|
||||
lx.emit(itemInlineTableStart)
|
||||
return lexInlineTableValue
|
||||
case stringStart:
|
||||
if lx.accept(stringStart) {
|
||||
if lx.accept(stringStart) {
|
||||
@ -453,7 +420,7 @@ func lexValue(lx *lexer) stateFn {
|
||||
case '+', '-':
|
||||
return lexNumberStart
|
||||
case '.': // special error case, be kind to users
|
||||
return lx.errorf("floats must start with a digit, not '.'")
|
||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||
}
|
||||
if unicode.IsLetter(r) {
|
||||
// Be permissive here; lexBool will give a nice error if the
|
||||
@ -463,11 +430,11 @@ func lexValue(lx *lexer) stateFn {
|
||||
lx.backup()
|
||||
return lexBool
|
||||
}
|
||||
return lx.errorf("expected value but found %q instead", r)
|
||||
return lx.errorf("Expected value but found %q instead.", r)
|
||||
}
|
||||
|
||||
// lexArrayValue consumes one value in an array. It assumes that '[' or ','
|
||||
// have already been consumed. All whitespace and newlines are ignored.
|
||||
// have already been consumed. All whitespace and new lines are ignored.
|
||||
func lexArrayValue(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
@ -476,11 +443,10 @@ func lexArrayValue(lx *lexer) stateFn {
|
||||
case r == commentStart:
|
||||
lx.push(lexArrayValue)
|
||||
return lexCommentStart
|
||||
case r == comma:
|
||||
return lx.errorf("unexpected comma")
|
||||
case r == arrayValTerm:
|
||||
return lx.errorf("Unexpected array value terminator %q.",
|
||||
arrayValTerm)
|
||||
case r == arrayEnd:
|
||||
// NOTE(caleb): The spec isn't clear about whether you can have
|
||||
// a trailing comma or not, so we'll allow it.
|
||||
return lexArrayEnd
|
||||
}
|
||||
|
||||
@ -489,9 +455,8 @@ func lexArrayValue(lx *lexer) stateFn {
|
||||
return lexValue
|
||||
}
|
||||
|
||||
// lexArrayValueEnd consumes everything between the end of an array value and
|
||||
// the next value (or the end of the array): it ignores whitespace and newlines
|
||||
// and expects either a ',' or a ']'.
|
||||
// lexArrayValueEnd consumes the cruft between values of an array. Namely,
|
||||
// it ignores whitespace and expects either a ',' or a ']'.
|
||||
func lexArrayValueEnd(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
@ -500,88 +465,31 @@ func lexArrayValueEnd(lx *lexer) stateFn {
|
||||
case r == commentStart:
|
||||
lx.push(lexArrayValueEnd)
|
||||
return lexCommentStart
|
||||
case r == comma:
|
||||
case r == arrayValTerm:
|
||||
lx.ignore()
|
||||
return lexArrayValue // move on to the next value
|
||||
case r == arrayEnd:
|
||||
return lexArrayEnd
|
||||
}
|
||||
return lx.errorf(
|
||||
"expected a comma or array terminator %q, but got %q instead",
|
||||
arrayEnd, r,
|
||||
)
|
||||
return lx.errorf("Expected an array value terminator %q or an array "+
|
||||
"terminator %q, but got %q instead.", arrayValTerm, arrayEnd, r)
|
||||
}
|
||||
|
||||
// lexArrayEnd finishes the lexing of an array.
|
||||
// It assumes that a ']' has just been consumed.
|
||||
// lexArrayEnd finishes the lexing of an array. It assumes that a ']' has
|
||||
// just been consumed.
|
||||
func lexArrayEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.emit(itemArrayEnd)
|
||||
return lx.pop()
|
||||
}
|
||||
|
||||
// lexInlineTableValue consumes one key/value pair in an inline table.
|
||||
// It assumes that '{' or ',' have already been consumed. Whitespace is ignored.
|
||||
func lexInlineTableValue(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
case isNL(r):
|
||||
return lx.errorf("newlines not allowed within inline tables")
|
||||
case r == commentStart:
|
||||
lx.push(lexInlineTableValue)
|
||||
return lexCommentStart
|
||||
case r == comma:
|
||||
return lx.errorf("unexpected comma")
|
||||
case r == inlineTableEnd:
|
||||
return lexInlineTableEnd
|
||||
}
|
||||
lx.backup()
|
||||
lx.push(lexInlineTableValueEnd)
|
||||
return lexKeyStart
|
||||
}
|
||||
|
||||
// lexInlineTableValueEnd consumes everything between the end of an inline table
|
||||
// key/value pair and the next pair (or the end of the table):
|
||||
// it ignores whitespace and expects either a ',' or a '}'.
|
||||
func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
case isNL(r):
|
||||
return lx.errorf("newlines not allowed within inline tables")
|
||||
case r == commentStart:
|
||||
lx.push(lexInlineTableValueEnd)
|
||||
return lexCommentStart
|
||||
case r == comma:
|
||||
lx.ignore()
|
||||
return lexInlineTableValue
|
||||
case r == inlineTableEnd:
|
||||
return lexInlineTableEnd
|
||||
}
|
||||
return lx.errorf("expected a comma or an inline table terminator %q, "+
|
||||
"but got %q instead", inlineTableEnd, r)
|
||||
}
|
||||
|
||||
// lexInlineTableEnd finishes the lexing of an inline table.
|
||||
// It assumes that a '}' has just been consumed.
|
||||
func lexInlineTableEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.emit(itemInlineTableEnd)
|
||||
return lx.pop()
|
||||
}
|
||||
|
||||
// lexString consumes the inner contents of a string. It assumes that the
|
||||
// beginning '"' has already been consumed and ignored.
|
||||
func lexString(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case isNL(r):
|
||||
return lx.errorf("strings cannot contain newlines")
|
||||
return lx.errorf("Strings cannot contain new lines.")
|
||||
case r == '\\':
|
||||
lx.push(lexString)
|
||||
return lexStringEscape
|
||||
@ -598,12 +506,11 @@ func lexString(lx *lexer) stateFn {
|
||||
// lexMultilineString consumes the inner contents of a string. It assumes that
|
||||
// the beginning '"""' has already been consumed and ignored.
|
||||
func lexMultilineString(lx *lexer) stateFn {
|
||||
switch lx.next() {
|
||||
case eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case '\\':
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == '\\':
|
||||
return lexMultilineStringEscape
|
||||
case stringEnd:
|
||||
case r == stringEnd:
|
||||
if lx.accept(stringEnd) {
|
||||
if lx.accept(stringEnd) {
|
||||
lx.backup()
|
||||
@ -627,10 +534,8 @@ func lexMultilineString(lx *lexer) stateFn {
|
||||
func lexRawString(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case isNL(r):
|
||||
return lx.errorf("strings cannot contain newlines")
|
||||
return lx.errorf("Strings cannot contain new lines.")
|
||||
case r == rawStringEnd:
|
||||
lx.backup()
|
||||
lx.emit(itemRawString)
|
||||
@ -642,13 +547,12 @@ func lexRawString(lx *lexer) stateFn {
|
||||
}
|
||||
|
||||
// lexMultilineRawString consumes a raw string. Nothing can be escaped in such
|
||||
// a string. It assumes that the beginning "'''" has already been consumed and
|
||||
// a string. It assumes that the beginning "'" has already been consumed and
|
||||
// ignored.
|
||||
func lexMultilineRawString(lx *lexer) stateFn {
|
||||
switch lx.next() {
|
||||
case eof:
|
||||
return lx.errorf("unexpected EOF")
|
||||
case rawStringEnd:
|
||||
r := lx.next()
|
||||
switch {
|
||||
case r == rawStringEnd:
|
||||
if lx.accept(rawStringEnd) {
|
||||
if lx.accept(rawStringEnd) {
|
||||
lx.backup()
|
||||
@ -701,9 +605,10 @@ func lexStringEscape(lx *lexer) stateFn {
|
||||
case 'U':
|
||||
return lexLongUnicodeEscape
|
||||
}
|
||||
return lx.errorf("invalid escape character %q; only the following "+
|
||||
return lx.errorf("Invalid escape character %q. Only the following "+
|
||||
"escape characters are allowed: "+
|
||||
`\b, \t, \n, \f, \r, \", \\, \uXXXX, and \UXXXXXXXX`, r)
|
||||
"\\b, \\t, \\n, \\f, \\r, \\\", \\/, \\\\, "+
|
||||
"\\uXXXX and \\UXXXXXXXX.", r)
|
||||
}
|
||||
|
||||
func lexShortUnicodeEscape(lx *lexer) stateFn {
|
||||
@ -711,8 +616,8 @@ func lexShortUnicodeEscape(lx *lexer) stateFn {
|
||||
for i := 0; i < 4; i++ {
|
||||
r = lx.next()
|
||||
if !isHexadecimal(r) {
|
||||
return lx.errorf(`expected four hexadecimal digits after '\u', `+
|
||||
"but got %q instead", lx.current())
|
||||
return lx.errorf("Expected four hexadecimal digits after '\\u', "+
|
||||
"but got '%s' instead.", lx.current())
|
||||
}
|
||||
}
|
||||
return lx.pop()
|
||||
@ -723,8 +628,8 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
|
||||
for i := 0; i < 8; i++ {
|
||||
r = lx.next()
|
||||
if !isHexadecimal(r) {
|
||||
return lx.errorf(`expected eight hexadecimal digits after '\U', `+
|
||||
"but got %q instead", lx.current())
|
||||
return lx.errorf("Expected eight hexadecimal digits after '\\U', "+
|
||||
"but got '%s' instead.", lx.current())
|
||||
}
|
||||
}
|
||||
return lx.pop()
|
||||
@ -742,9 +647,9 @@ func lexNumberOrDateStart(lx *lexer) stateFn {
|
||||
case 'e', 'E':
|
||||
return lexFloat
|
||||
case '.':
|
||||
return lx.errorf("floats must start with a digit, not '.'")
|
||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||
}
|
||||
return lx.errorf("expected a digit but got %q", r)
|
||||
return lx.errorf("Expected a digit but got %q.", r)
|
||||
}
|
||||
|
||||
// lexNumberOrDate consumes either an integer, float or datetime.
|
||||
@ -792,9 +697,9 @@ func lexNumberStart(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
if !isDigit(r) {
|
||||
if r == '.' {
|
||||
return lx.errorf("floats must start with a digit, not '.'")
|
||||
return lx.errorf("Floats must start with a digit, not '.'.")
|
||||
}
|
||||
return lx.errorf("expected a digit but got %q", r)
|
||||
return lx.errorf("Expected a digit but got %q.", r)
|
||||
}
|
||||
return lexNumber
|
||||
}
|
||||
@ -852,7 +757,7 @@ func lexBool(lx *lexer) stateFn {
|
||||
lx.emit(itemBool)
|
||||
return lx.pop()
|
||||
}
|
||||
return lx.errorf("expected value but found %q instead", s)
|
||||
return lx.errorf("Expected value but found %q instead.", s)
|
||||
}
|
||||
|
||||
// lexCommentStart begins the lexing of a comment. It will emit
|
||||
@ -864,7 +769,7 @@ func lexCommentStart(lx *lexer) stateFn {
|
||||
}
|
||||
|
||||
// lexComment lexes an entire comment. It assumes that '#' has been consumed.
|
||||
// It will consume *up to* the first newline character, and pass control
|
||||
// It will consume *up to* the first new line character, and pass control
|
||||
// back to the last state on the stack.
|
||||
func lexComment(lx *lexer) stateFn {
|
||||
r := lx.peek()
|
||||
|
35
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
35
vendor/github.com/BurntSushi/toml/parse.go
generated
vendored
@ -269,41 +269,6 @@ func (p *parser) value(it item) (interface{}, tomlType) {
|
||||
types = append(types, typ)
|
||||
}
|
||||
return array, p.typeOfArray(types)
|
||||
case itemInlineTableStart:
|
||||
var (
|
||||
hash = make(map[string]interface{})
|
||||
outerContext = p.context
|
||||
outerKey = p.currentKey
|
||||
)
|
||||
|
||||
p.context = append(p.context, p.currentKey)
|
||||
p.currentKey = ""
|
||||
for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
|
||||
if it.typ != itemKeyStart {
|
||||
p.bug("Expected key start but instead found %q, around line %d",
|
||||
it.val, p.approxLine)
|
||||
}
|
||||
if it.typ == itemCommentStart {
|
||||
p.expect(itemText)
|
||||
continue
|
||||
}
|
||||
|
||||
// retrieve key
|
||||
k := p.next()
|
||||
p.approxLine = k.line
|
||||
kname := p.keyString(k)
|
||||
|
||||
// retrieve value
|
||||
p.currentKey = kname
|
||||
val, typ := p.value(p.next())
|
||||
// make sure we keep metadata up to date
|
||||
p.setType(kname, typ)
|
||||
p.ordered = append(p.ordered, p.context.add(p.currentKey))
|
||||
hash[kname] = val
|
||||
}
|
||||
p.context = outerContext
|
||||
p.currentKey = outerKey
|
||||
return hash, tomlHash
|
||||
}
|
||||
p.bug("Unexpected value type: %s", it.typ)
|
||||
panic("unreachable")
|
||||
|
22
vendor/github.com/GeertJohan/go.rice/LICENSE
generated
vendored
22
vendor/github.com/GeertJohan/go.rice/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
||||
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
138
vendor/github.com/GeertJohan/go.rice/appended.go
generated
vendored
@ -1,138 +0,0 @@
|
||||
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
337
vendor/github.com/GeertJohan/go.rice/box.go
generated
vendored
@ -1,337 +0,0 @@
|
||||
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
39
vendor/github.com/GeertJohan/go.rice/config.go
generated
vendored
@ -1,39 +0,0 @@
|
||||
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
4
vendor/github.com/GeertJohan/go.rice/debug.go
generated
vendored
@ -1,4 +0,0 @@
|
||||
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
90
vendor/github.com/GeertJohan/go.rice/embedded.go
generated
vendored
@ -1,90 +0,0 @@
|
||||
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
80
vendor/github.com/GeertJohan/go.rice/embedded/embedded.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
// 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
69
vendor/github.com/GeertJohan/go.rice/example/example.go
generated
vendored
@ -1,69 +0,0 @@
|
||||
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
144
vendor/github.com/GeertJohan/go.rice/file.go
generated
vendored
@ -1,144 +0,0 @@
|
||||
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
21
vendor/github.com/GeertJohan/go.rice/http.go
generated
vendored
@ -1,21 +0,0 @@
|
||||
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
172
vendor/github.com/GeertJohan/go.rice/rice/append.go
generated
vendored
@ -1,172 +0,0 @@
|
||||
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
33
vendor/github.com/GeertJohan/go.rice/rice/clean.go
generated
vendored
@ -1,33 +0,0 @@
|
||||
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
158
vendor/github.com/GeertJohan/go.rice/rice/embed-go.go
generated
vendored
@ -1,158 +0,0 @@
|
||||
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
204
vendor/github.com/GeertJohan/go.rice/rice/embed-syso.go
generated
vendored
@ -1,204 +0,0 @@
|
||||
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
150
vendor/github.com/GeertJohan/go.rice/rice/find.go
generated
vendored
@ -1,150 +0,0 @@
|
||||
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
80
vendor/github.com/GeertJohan/go.rice/rice/flags.go
generated
vendored
@ -1,80 +0,0 @@
|
||||
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
14
vendor/github.com/GeertJohan/go.rice/rice/identifier.go
generated
vendored
@ -1,14 +0,0 @@
|
||||
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
68
vendor/github.com/GeertJohan/go.rice/rice/main.go
generated
vendored
@ -1,68 +0,0 @@
|
||||
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
98
vendor/github.com/GeertJohan/go.rice/rice/templates.go
generated
vendored
@ -1,98 +0,0 @@
|
||||
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
22
vendor/github.com/GeertJohan/go.rice/rice/util.go
generated
vendored
@ -1,22 +0,0 @@
|
||||
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
42
vendor/github.com/GeertJohan/go.rice/rice/writecoff.go
generated
vendored
@ -1,42 +0,0 @@
|
||||
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
19
vendor/github.com/GeertJohan/go.rice/sort.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
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
252
vendor/github.com/GeertJohan/go.rice/virtual.go
generated
vendored
@ -1,252 +0,0 @@
|
||||
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
122
vendor/github.com/GeertJohan/go.rice/walk.go
generated
vendored
@ -1,122 +0,0 @@
|
||||
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
|
||||
|
||||
}
|
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
64
vendor/github.com/Sirupsen/logrus/alt_exit.go
generated
vendored
@ -1,64 +0,0 @@
|
||||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://github.com/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// 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.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
57
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
57
vendor/github.com/Sirupsen/logrus/entry.go
generated
vendored
@ -3,21 +3,11 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bufferPool *sync.Pool
|
||||
|
||||
func init() {
|
||||
bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the key when adding errors using WithError.
|
||||
var ErrorKey = "error"
|
||||
|
||||
@ -39,9 +29,6 @@ type Entry struct {
|
||||
|
||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
|
||||
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
@ -52,15 +39,21 @@ func NewEntry(logger *Logger) *Entry {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
return bytes.NewBuffer(serialized), err
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
reader, err := entry.Reader()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
|
||||
return reader.String(), err
|
||||
}
|
||||
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
@ -88,7 +81,6 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
entry.Time = time.Now()
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
@ -98,23 +90,20 @@ func (entry Entry) log(level Level, msg string) {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
entry.Buffer = nil
|
||||
|
||||
reader, err := entry.Reader()
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
} else {
|
||||
entry.Logger.mu.Lock()
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
|
||||
_, err = io.Copy(entry.Logger.Out, reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
@ -161,7 +150,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
@ -209,7 +198,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
@ -256,7 +245,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
9
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
9
vendor/github.com/Sirupsen/logrus/examples/basic/basic.go
generated
vendored
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
// "os"
|
||||
)
|
||||
|
||||
var log = logrus.New()
|
||||
@ -10,14 +9,6 @@ var log = logrus.New()
|
||||
func init() {
|
||||
log.Formatter = new(logrus.JSONFormatter)
|
||||
log.Formatter = new(logrus.TextFormatter) // default
|
||||
|
||||
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY, 0666)
|
||||
// if err == nil {
|
||||
// log.Out = file
|
||||
// } else {
|
||||
// log.Info("Failed to log to file, using default stderr")
|
||||
// }
|
||||
|
||||
log.Level = logrus.DebugLevel
|
||||
}
|
||||
|
||||
|
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/formatter.go
generated
vendored
@ -31,15 +31,18 @@ type Formatter interface {
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields) {
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
}
|
||||
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
}
|
||||
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
}
|
||||
}
|
||||
|
61
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
Normal file
61
vendor/github.com/Sirupsen/logrus/formatters/logstash/logstash.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package logstash
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Formatter generates json in logstash format.
|
||||
// Logstash site: http://logstash.net/
|
||||
type LogstashFormatter struct {
|
||||
Type string // if not empty use for logstash type field.
|
||||
|
||||
// TimestampFormat sets the format used for timestamps.
|
||||
TimestampFormat string
|
||||
}
|
||||
|
||||
func (f *LogstashFormatter) Format(entry *logrus.Entry) ([]byte, error) {
|
||||
fields := make(logrus.Fields)
|
||||
for k, v := range entry.Data {
|
||||
fields[k] = v
|
||||
}
|
||||
|
||||
fields["@version"] = 1
|
||||
|
||||
if f.TimestampFormat == "" {
|
||||
f.TimestampFormat = logrus.DefaultTimestampFormat
|
||||
}
|
||||
|
||||
fields["@timestamp"] = entry.Time.Format(f.TimestampFormat)
|
||||
|
||||
// set message field
|
||||
v, ok := entry.Data["message"]
|
||||
if ok {
|
||||
fields["fields.message"] = v
|
||||
}
|
||||
fields["message"] = entry.Message
|
||||
|
||||
// set level field
|
||||
v, ok = entry.Data["level"]
|
||||
if ok {
|
||||
fields["fields.level"] = v
|
||||
}
|
||||
fields["level"] = entry.Level.String()
|
||||
|
||||
// set type field
|
||||
if f.Type != "" {
|
||||
v, ok = entry.Data["type"]
|
||||
if ok {
|
||||
fields["fields.type"] = v
|
||||
}
|
||||
fields["type"] = f.Type
|
||||
}
|
||||
|
||||
serialized, err := json.Marshal(fields)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to marshal fields to JSON, %v", err)
|
||||
}
|
||||
return append(serialized, '\n'), nil
|
||||
}
|
39
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
39
vendor/github.com/Sirupsen/logrus/json_formatter.go
generated
vendored
@ -5,40 +5,9 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type fieldKey string
|
||||
type FieldMap map[fieldKey]string
|
||||
|
||||
const (
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
)
|
||||
|
||||
func (f FieldMap) resolve(key fieldKey) string {
|
||||
if k, ok := f[key]; ok {
|
||||
return k
|
||||
}
|
||||
|
||||
return string(key)
|
||||
}
|
||||
|
||||
type JSONFormatter struct {
|
||||
// TimestampFormat sets the format used for marshaling timestamps.
|
||||
TimestampFormat string
|
||||
|
||||
// DisableTimestamp allows disabling automatic timestamps in output
|
||||
DisableTimestamp bool
|
||||
|
||||
// FieldMap allows users to customize the names of keys for various fields.
|
||||
// As an example:
|
||||
// formatter := &JSONFormatter{
|
||||
// FieldMap: FieldMap{
|
||||
// FieldKeyTime: "@timestamp",
|
||||
// FieldKeyLevel: "@level",
|
||||
// FieldKeyLevel: "@message",
|
||||
// },
|
||||
// }
|
||||
FieldMap FieldMap
|
||||
}
|
||||
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
@ -60,11 +29,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
timestampFormat = DefaultTimestampFormat
|
||||
}
|
||||
|
||||
if !f.DisableTimestamp {
|
||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||
data["time"] = entry.Time.Format(timestampFormat)
|
||||
data["msg"] = entry.Message
|
||||
data["level"] = entry.Level.String()
|
||||
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
162
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
162
vendor/github.com/Sirupsen/logrus/logger.go
generated
vendored
@ -26,31 +26,8 @@ type Logger struct {
|
||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged. `logrus.Debug` is useful in
|
||||
Level Level
|
||||
// Used to sync writing to the log. Locking is enabled by Default
|
||||
mu MutexWrap
|
||||
// Reusable empty entry
|
||||
entryPool sync.Pool
|
||||
}
|
||||
|
||||
type MutexWrap struct {
|
||||
lock sync.Mutex
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Lock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Unlock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Disable() {
|
||||
mw.disabled = true
|
||||
// Used to sync writing to the log.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
@ -74,235 +51,162 @@ func New() *Logger {
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) newEntry() *Entry {
|
||||
entry, ok := logger.entryPool.Get().(*Entry)
|
||||
if ok {
|
||||
return entry
|
||||
}
|
||||
return NewEntry(logger)
|
||||
}
|
||||
|
||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||
logger.entryPool.Put(entry)
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that it doesn't log until you call
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithField(key, value)
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithFields(fields)
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func (logger *Logger) WithError(err error) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
return NewEntry(logger).WithError(err)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Debug(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Info(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Info(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warn(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warn(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Error(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Fatal(args...)
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Panic(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Debugln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Infoln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Println(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Errorln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Panicln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
//When file is opened with appending mode, it's safe to
|
||||
//write concurrently to a file (within 4k message on Linux).
|
||||
//In these cases user can choose to disable the lock.
|
||||
func (logger *Logger) SetNoLock() {
|
||||
logger.mu.Disable()
|
||||
}
|
||||
|
10
vendor/github.com/Sirupsen/logrus/terminal_appengine.go
generated
vendored
10
vendor/github.com/Sirupsen/logrus/terminal_appengine.go
generated
vendored
@ -1,10 +0,0 @@
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "io"
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
return true
|
||||
}
|
1
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
1
vendor/github.com/Sirupsen/logrus/terminal_bsd.go
generated
vendored
@ -1,5 +1,4 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
2
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
2
vendor/github.com/Sirupsen/logrus/terminal_linux.go
generated
vendored
@ -3,8 +3,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
15
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
15
vendor/github.com/Sirupsen/logrus/terminal_notwindows.go
generated
vendored
@ -4,25 +4,18 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stderr
|
||||
var termios Termios
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
||||
|
14
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
14
vendor/github.com/Sirupsen/logrus/terminal_solaris.go
generated
vendored
@ -1,21 +1,15 @@
|
||||
// +build solaris,!appengine
|
||||
// +build solaris
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
|
||||
return err == nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
func IsTerminal() bool {
|
||||
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
18
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
18
vendor/github.com/Sirupsen/logrus/terminal_windows.go
generated
vendored
@ -3,13 +3,11 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows,!appengine
|
||||
// +build windows
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
@ -21,13 +19,9 @@ var (
|
||||
)
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stderr
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
||||
|
76
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
76
vendor/github.com/Sirupsen/logrus/text_formatter.go
generated
vendored
@ -3,9 +3,9 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -20,10 +20,16 @@ const (
|
||||
|
||||
var (
|
||||
baseTimestamp time.Time
|
||||
isTerminal bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
baseTimestamp = time.Now()
|
||||
isTerminal = IsTerminal()
|
||||
}
|
||||
|
||||
func miniTS() int {
|
||||
return int(time.Since(baseTimestamp) / time.Second)
|
||||
}
|
||||
|
||||
type TextFormatter struct {
|
||||
@ -48,32 +54,10 @@ type TextFormatter struct {
|
||||
// that log extremely frequently and don't use the JSON formatter this may not
|
||||
// be desired.
|
||||
DisableSorting bool
|
||||
|
||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||
QuoteEmptyFields bool
|
||||
|
||||
// QuoteCharacter can be set to the override the default quoting character "
|
||||
// with something else. For example: ', or `.
|
||||
QuoteCharacter string
|
||||
|
||||
// Whether the logger's out is to a terminal
|
||||
isTerminal bool
|
||||
|
||||
sync.Once
|
||||
}
|
||||
|
||||
func (f *TextFormatter) init(entry *Entry) {
|
||||
if len(f.QuoteCharacter) == 0 {
|
||||
f.QuoteCharacter = "\""
|
||||
}
|
||||
if entry.Logger != nil {
|
||||
f.isTerminal = IsTerminal(entry.Logger.Out)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var b *bytes.Buffer
|
||||
keys := make([]string, 0, len(entry.Data))
|
||||
var keys []string = make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
@ -81,17 +65,13 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
if !f.DisableSorting {
|
||||
sort.Strings(keys)
|
||||
}
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
prefixFieldClashes(entry.Data)
|
||||
|
||||
f.Do(func() { f.init(entry) })
|
||||
|
||||
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
|
||||
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
|
||||
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
|
||||
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
@ -131,59 +111,51 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
|
||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||
|
||||
if f.DisableTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
|
||||
} else if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
|
||||
if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||
} else {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||
}
|
||||
for _, k := range keys {
|
||||
v := entry.Data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||
if f.QuoteEmptyFields && len(text) == 0 {
|
||||
return true
|
||||
}
|
||||
func needsQuoting(text string) bool {
|
||||
for _, ch := range text {
|
||||
if !((ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.') {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||
|
||||
b.WriteString(key)
|
||||
b.WriteByte('=')
|
||||
f.appendValue(b, value)
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if !f.needsQuoting(value) {
|
||||
if needsQuoting(value) {
|
||||
b.WriteString(value)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
case error:
|
||||
errmsg := value.Error()
|
||||
if !f.needsQuoting(errmsg) {
|
||||
if needsQuoting(errmsg) {
|
||||
b.WriteString(errmsg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(b, value)
|
||||
}
|
||||
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
39
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
39
vendor/github.com/Sirupsen/logrus/writer.go
generated
vendored
@ -7,52 +7,21 @@ import (
|
||||
)
|
||||
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
return NewEntry(logger).WriterLevel(level)
|
||||
}
|
||||
|
||||
func (entry *Entry) Writer() *io.PipeWriter {
|
||||
return entry.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
var printFunc func(args ...interface{})
|
||||
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
printFunc = entry.Debug
|
||||
case InfoLevel:
|
||||
printFunc = entry.Info
|
||||
case WarnLevel:
|
||||
printFunc = entry.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = entry.Error
|
||||
case FatalLevel:
|
||||
printFunc = entry.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = entry.Panic
|
||||
default:
|
||||
printFunc = entry.Print
|
||||
}
|
||||
|
||||
go entry.writerScanner(reader, printFunc)
|
||||
go logger.writerScanner(reader)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
printFunc(scanner.Text())
|
||||
logger.Print(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
entry.Errorf("Error while reading from Writer: %s", err)
|
||||
logger.Errorf("Error while reading from Writer: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
}
|
||||
|
144
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
144
vendor/github.com/bwmarrin/discordgo/discord.go
generated
vendored
@ -13,10 +13,13 @@
|
||||
// Package discordgo provides Discord binding for Go
|
||||
package discordgo
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// VERSION of Discordgo, follows Symantic Versioning. (http://semver.org/)
|
||||
const VERSION = "0.15.0"
|
||||
const VERSION = "0.13.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
|
||||
@ -24,8 +27,6 @@ const VERSION = "0.15.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
|
||||
@ -36,13 +37,11 @@ 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.
|
||||
@ -123,3 +122,136 @@ 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,7 +24,6 @@ var (
|
||||
EndpointChannels = EndpointAPI + "channels/"
|
||||
EndpointUsers = EndpointAPI + "users/"
|
||||
EndpointGateway = EndpointAPI + "gateway"
|
||||
EndpointWebhooks = EndpointAPI + "webhooks/"
|
||||
|
||||
EndpointAuth = EndpointAPI + "auth/"
|
||||
EndpointLogin = EndpointAuth + "login"
|
||||
@ -62,7 +61,6 @@ 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" }
|
||||
@ -75,7 +73,6 @@ 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" }
|
||||
@ -89,21 +86,6 @@ 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
238
vendor/github.com/bwmarrin/discordgo/event.go
generated
vendored
@ -1,238 +0,0 @@
|
||||
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
977
vendor/github.com/bwmarrin/discordgo/eventhandlers.go
generated
vendored
@ -1,977 +0,0 @@
|
||||
// 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))
|
||||
}
|
315
vendor/github.com/bwmarrin/discordgo/events.go
generated
vendored
315
vendor/github.com/bwmarrin/discordgo/events.go
generated
vendored
@ -1,238 +1,159 @@
|
||||
package discordgo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
// 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{},
|
||||
}
|
||||
|
||||
// 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.
|
||||
// Connect is an empty struct for an event.
|
||||
type Connect struct{}
|
||||
|
||||
// Disconnect is the data for a Disconnect event.
|
||||
// This is a sythetic event and is not dispatched by Discord.
|
||||
// Disconnect is an empty struct for an event.
|
||||
type Disconnect struct{}
|
||||
|
||||
// RateLimit is the data for a RateLimit event.
|
||||
// This is a sythetic event and is not dispatched by Discord.
|
||||
// RateLimit is a struct for the RateLimited event
|
||||
type RateLimit struct {
|
||||
*TooManyRequests
|
||||
URL string
|
||||
}
|
||||
|
||||
// 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:"-"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// ChannelCreate is the data for a ChannelCreate event.
|
||||
type ChannelCreate struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// ChannelUpdate is the data for a ChannelUpdate event.
|
||||
type ChannelUpdate struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// ChannelDelete is the data for a ChannelDelete event.
|
||||
type ChannelDelete struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// 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 the data for a GuildUpdate event.
|
||||
type GuildUpdate struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildDelete is the data for a GuildDelete event.
|
||||
type GuildDelete struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildBanAdd is the data for a GuildBanAdd event.
|
||||
type GuildBanAdd struct {
|
||||
User *User `json:"user"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// GuildBanRemove is the data for a GuildBanRemove event.
|
||||
type GuildBanRemove struct {
|
||||
User *User `json:"user"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// GuildMemberAdd is the data for a GuildMemberAdd event.
|
||||
type GuildMemberAdd struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildMemberUpdate is the data for a GuildMemberUpdate event.
|
||||
type GuildMemberUpdate struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildMemberRemove is the data for a GuildMemberRemove event.
|
||||
type GuildMemberRemove struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildRoleCreate is the data for a GuildRoleCreate event.
|
||||
type GuildRoleCreate struct {
|
||||
*GuildRole
|
||||
}
|
||||
|
||||
// GuildRoleUpdate is the data for a GuildRoleUpdate event.
|
||||
type GuildRoleUpdate struct {
|
||||
*GuildRole
|
||||
}
|
||||
|
||||
// A GuildRoleDelete is the data for a GuildRoleDelete event.
|
||||
type GuildRoleDelete struct {
|
||||
RoleID string `json:"role_id"`
|
||||
GuildID string `json:"guild_id"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
// MessageCreate is a wrapper struct for an event.
|
||||
type MessageCreate struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// MessageUpdate is the data for a MessageUpdate event.
|
||||
// MessageUpdate is a wrapper struct for an event.
|
||||
type MessageUpdate struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// MessageDelete is the data for a MessageDelete event.
|
||||
// MessageDelete is a wrapper struct for an event.
|
||||
type MessageDelete struct {
|
||||
*Message
|
||||
}
|
||||
|
||||
// MessageReactionAdd is the data for a MessageReactionAdd event.
|
||||
type MessageReactionAdd struct {
|
||||
*MessageReaction
|
||||
// ChannelCreate is a wrapper struct for an event.
|
||||
type ChannelCreate struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// MessageReactionRemove is the data for a MessageReactionRemove event.
|
||||
type MessageReactionRemove struct {
|
||||
*MessageReaction
|
||||
// ChannelUpdate is a wrapper struct for an event.
|
||||
type ChannelUpdate struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// PresencesReplace is the data for a PresencesReplace event.
|
||||
// ChannelDelete is a wrapper struct for an event.
|
||||
type ChannelDelete struct {
|
||||
*Channel
|
||||
}
|
||||
|
||||
// GuildCreate is a wrapper struct for an event.
|
||||
type GuildCreate struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildUpdate is a wrapper struct for an event.
|
||||
type GuildUpdate struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildDelete is a wrapper struct for an event.
|
||||
type GuildDelete struct {
|
||||
*Guild
|
||||
}
|
||||
|
||||
// GuildBanAdd is a wrapper struct for an event.
|
||||
type GuildBanAdd struct {
|
||||
*GuildBan
|
||||
}
|
||||
|
||||
// GuildBanRemove is a wrapper struct for an event.
|
||||
type GuildBanRemove struct {
|
||||
*GuildBan
|
||||
}
|
||||
|
||||
// GuildMemberAdd is a wrapper struct for an event.
|
||||
type GuildMemberAdd struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildMemberUpdate is a wrapper struct for an event.
|
||||
type GuildMemberUpdate struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildMemberRemove is a wrapper struct for an event.
|
||||
type GuildMemberRemove struct {
|
||||
*Member
|
||||
}
|
||||
|
||||
// GuildRoleCreate is a wrapper struct for an event.
|
||||
type GuildRoleCreate struct {
|
||||
*GuildRole
|
||||
}
|
||||
|
||||
// GuildRoleUpdate is a wrapper struct for an event.
|
||||
type GuildRoleUpdate struct {
|
||||
*GuildRole
|
||||
}
|
||||
|
||||
// PresencesReplace is an array of Presences for an 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"`
|
||||
// VoiceStateUpdate is a wrapper struct for an event.
|
||||
type VoiceStateUpdate struct {
|
||||
*VoiceState
|
||||
}
|
||||
|
||||
// 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.
|
||||
// UserUpdate is a wrapper struct for an event.
|
||||
type UserUpdate struct {
|
||||
*User
|
||||
}
|
||||
|
||||
// UserSettingsUpdate is the data for a UserSettingsUpdate event.
|
||||
// UserSettingsUpdate is a map for an event.
|
||||
type UserSettingsUpdate map[string]interface{}
|
||||
|
||||
// UserGuildSettingsUpdate is the data for a UserGuildSettingsUpdate event.
|
||||
// UserGuildSettingsUpdate is a map for an 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", "", "Bot Token")
|
||||
flag.StringVar(&token, "t", "", "Account Token")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + token)
|
||||
// Create a new Discord session using the provided token.
|
||||
dg, err := discordgo.New(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 {
|
||||
if event.Guild.Unavailable != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@ -131,10 +131,6 @@ 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,19 +10,24 @@ import (
|
||||
|
||||
// Variables used for command line parameters
|
||||
var (
|
||||
Token string
|
||||
Email string
|
||||
Password string
|
||||
Token string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
flag.StringVar(&Token, "t", "", "Bot Token")
|
||||
flag.StringVar(&Email, "e", "", "Account Email")
|
||||
flag.StringVar(&Password, "p", "", "Account Password")
|
||||
flag.StringVar(&Token, "t", "", "Account Token")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + Token)
|
||||
// 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)
|
||||
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,20 +9,24 @@ import (
|
||||
|
||||
// Variables used for command line parameters
|
||||
var (
|
||||
Token string
|
||||
BotID string
|
||||
Email string
|
||||
Password string
|
||||
Token string
|
||||
BotID string
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
flag.StringVar(&Token, "t", "", "Bot Token")
|
||||
flag.StringVar(&Email, "e", "", "Account Email")
|
||||
flag.StringVar(&Password, "p", "", "Account Password")
|
||||
flag.StringVar(&Token, "t", "", "Account Token")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
// Create a new Discord session using the provided bot token.
|
||||
dg, err := discordgo.New("Bot " + Token)
|
||||
// Create a new Discord session using the provided login information.
|
||||
dg, err := discordgo.New(Email, Password, 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 Timestamp `json:"timestamp"`
|
||||
EditedTimestamp Timestamp `json:"edited_timestamp"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
EditedTimestamp string `json:"edited_timestamp"`
|
||||
MentionRoles []string `json:"mention_roles"`
|
||||
Tts bool `json:"tts"`
|
||||
MentionEveryone bool `json:"mention_everyone"`
|
||||
@ -28,7 +28,6 @@ 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.
|
||||
@ -42,80 +41,31 @@ 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,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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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,14 +21,13 @@ 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.RequestWithBucketID("GET", EndpointApplication(appID), nil, EndpointApplication(""))
|
||||
body, err := s.Request("GET", EndpointApplication(appID), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -40,7 +39,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.RequestWithBucketID("GET", EndpointApplications, nil, EndpointApplications)
|
||||
body, err := s.Request("GET", EndpointApplications, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -60,7 +59,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.RequestWithBucketID("POST", EndpointApplications, data, EndpointApplications)
|
||||
body, err := s.Request("POST", EndpointApplications, data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -79,7 +78,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.RequestWithBucketID("PUT", EndpointApplication(appID), data, EndpointApplication(""))
|
||||
body, err := s.Request("PUT", EndpointApplication(appID), data)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -92,7 +91,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.RequestWithBucketID("DELETE", EndpointApplication(appID), nil, EndpointApplication(""))
|
||||
_, err = s.Request("DELETE", EndpointApplication(appID), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@ -111,7 +110,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.RequestWithBucketID("POST", EndpointApplicationsBot(appID), nil, EndpointApplicationsBot(""))
|
||||
body, err := s.Request("POST", EndpointApplicationsBot(appID), nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
157
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
157
vendor/github.com/bwmarrin/discordgo/ratelimit.go
generated
vendored
@ -1,157 +0,0 @@
|
||||
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,6 +55,33 @@ 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 {
|
||||
@ -67,30 +94,20 @@ 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 {
|
||||
// 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 {
|
||||
// If this guild already exists with data, don't stomp on props.
|
||||
if g.Unavailable != nil && !*g.Unavailable {
|
||||
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
|
||||
}
|
||||
@ -308,12 +325,8 @@ func (s *State) ChannelAdd(channel *Channel) error {
|
||||
|
||||
// If the channel exists, replace it
|
||||
if c, ok := s.channelMap[channel.ID]; ok {
|
||||
if channel.Messages == nil {
|
||||
channel.Messages = c.Messages
|
||||
}
|
||||
if channel.PermissionOverwrites == nil {
|
||||
channel.PermissionOverwrites = c.PermissionOverwrites
|
||||
}
|
||||
channel.Messages = c.Messages
|
||||
channel.PermissionOverwrites = c.PermissionOverwrites
|
||||
|
||||
*c = *channel
|
||||
return nil
|
||||
@ -498,12 +511,6 @@ 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
|
||||
}
|
||||
@ -595,63 +602,18 @@ 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:
|
||||
@ -723,9 +685,6 @@ 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 {
|
||||
@ -747,13 +706,6 @@ 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 {
|
||||
@ -763,7 +715,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
|
||||
}
|
||||
}
|
||||
|
||||
if apermissions&PermissionAdministrator > 0 {
|
||||
if apermissions&PermissionManageRoles > 0 {
|
||||
apermissions |= PermissionAll
|
||||
}
|
||||
|
||||
@ -786,7 +738,7 @@ func (s *State) UserChannelPermissions(userID, channelID string) (apermissions i
|
||||
}
|
||||
}
|
||||
|
||||
if apermissions&PermissionAdministrator > 0 {
|
||||
if apermissions&PermissionManageRoles > 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"
|
||||
"strconv"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -53,9 +53,6 @@ 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
|
||||
@ -73,10 +70,13 @@ type Session struct {
|
||||
// StateEnabled is true.
|
||||
State *State
|
||||
|
||||
// Event handlers
|
||||
handlersMu sync.RWMutex
|
||||
handlers map[string][]*eventHandlerInstance
|
||||
onceHandlers map[string][]*eventHandlerInstance
|
||||
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
|
||||
|
||||
// The websocket connection.
|
||||
wsConn *websocket.Conn
|
||||
@ -85,7 +85,9 @@ type Session struct {
|
||||
listening chan interface{}
|
||||
|
||||
// used to deal with rate limits
|
||||
ratelimiter *RateLimiter
|
||||
// may switch to slices later
|
||||
// TODO: performance test map vs slices
|
||||
rateLimit rateLimitMutex
|
||||
|
||||
// sequence tracks the current gateway api websocket sequence number
|
||||
sequence int
|
||||
@ -106,6 +108,12 @@ 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"`
|
||||
@ -129,17 +137,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 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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// A Channel holds all data related to an individual Discord channel.
|
||||
@ -175,17 +183,6 @@ 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
|
||||
|
||||
@ -207,10 +204,9 @@ type Guild struct {
|
||||
AfkChannelID string `json:"afk_channel_id"`
|
||||
EmbedChannelID string `json:"embed_channel_id"`
|
||||
OwnerID string `json:"owner_id"`
|
||||
JoinedAt Timestamp `json:"joined_at"`
|
||||
JoinedAt string `json:"joined_at"` // make this a timestamp
|
||||
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"` // ??
|
||||
@ -221,16 +217,7 @@ type Guild struct {
|
||||
Presences []*Presence `json:"presences"`
|
||||
Channels []*Channel `json:"channels"`
|
||||
VoiceStates []*VoiceState `json:"voice_states"`
|
||||
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"`
|
||||
Unavailable *bool `json:"unavailable"`
|
||||
}
|
||||
|
||||
// A GuildParams stores all the data needed to update discord guild settings
|
||||
@ -245,7 +232,6 @@ 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"`
|
||||
@ -267,11 +253,9 @@ 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 Status `json:"status"`
|
||||
Game *Game `json:"game"`
|
||||
Nick string `json:"nick"`
|
||||
Roles []string `json:"roles"`
|
||||
User *User `json:"user"`
|
||||
Status string `json:"status"`
|
||||
Game *Game `json:"game"`
|
||||
}
|
||||
|
||||
// A Game struct holds the name of the "playing .." game for a user
|
||||
@ -281,38 +265,6 @@ 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"`
|
||||
@ -339,35 +291,21 @@ 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"`
|
||||
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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
@ -375,6 +313,32 @@ 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"`
|
||||
@ -397,21 +361,54 @@ type ReadState struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
// An Ack is used to ack messages
|
||||
type Ack struct {
|
||||
Token string `json:"token"`
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// A GuildRole stores data for guild roles.
|
||||
// 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.
|
||||
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 {
|
||||
Reason string `json:"reason"`
|
||||
User *User `json:"user"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// A GuildIntegration stores data for a guild integration.
|
||||
@ -467,41 +464,6 @@ 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)
|
||||
@ -512,7 +474,6 @@ const (
|
||||
PermissionAttachFiles
|
||||
PermissionReadMessageHistory
|
||||
PermissionMentionEveryone
|
||||
PermissionUseExternalEmojis
|
||||
)
|
||||
|
||||
// Constants for the different bit offsets of voice permissions
|
||||
@ -525,21 +486,12 @@ 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
|
||||
PermissionAdministrator
|
||||
PermissionManageRoles
|
||||
PermissionManageChannels
|
||||
PermissionManageServer
|
||||
|
||||
|
123
vendor/github.com/bwmarrin/discordgo/tools/cmd/eventhandlers/main.go
generated
vendored
123
vendor/github.com/bwmarrin/discordgo/tools/cmd/eventhandlers/main.go
generated
vendored
@ -1,123 +0,0 @@
|
||||
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
58
vendor/github.com/bwmarrin/discordgo/types.go
generated
vendored
@ -1,58 +0,0 @@
|
||||
// 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(LogDebug, "unknown voice operation, %d, %s", e.Operation, string(e.RawData))
|
||||
v.log(LogError, "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 through 20 to grab the IP address
|
||||
// Loop over position 4 though 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,7 +17,9 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
@ -45,17 +47,6 @@ 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
|
||||
@ -120,8 +111,9 @@ func (s *Session) Open() (err error) {
|
||||
|
||||
s.Unlock()
|
||||
|
||||
s.initialize()
|
||||
s.log(LogInformational, "emit connect event")
|
||||
s.handleEvent(connectEventType, &Connect{})
|
||||
s.handle(&Connect{})
|
||||
|
||||
s.log(LogInformational, "exiting")
|
||||
return
|
||||
@ -277,44 +269,6 @@ 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.
|
||||
//
|
||||
@ -407,12 +361,16 @@ 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 handlers.
|
||||
if eh, ok := registeredInterfaceProviders[e.Type]; ok {
|
||||
e.Struct = eh.New()
|
||||
// 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()
|
||||
|
||||
// Attempt to unmarshal our event.
|
||||
if err = json.Unmarshal(e.RawData, e.Struct); err != nil {
|
||||
if err = json.Unmarshal(e.RawData, i); err != nil {
|
||||
s.log(LogError, "error unmarshalling %s event, %s", e.Type, err)
|
||||
}
|
||||
|
||||
@ -423,19 +381,30 @@ 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.
|
||||
s.handleEvent(e.Type, e.Struct)
|
||||
go s.handle(i)
|
||||
|
||||
} else {
|
||||
s.log(LogWarning, "unknown event: Op: %d, Seq: %d, Type: %s, Data: %s", e.Operation, e.Sequence, e.Type, string(e.RawData))
|
||||
}
|
||||
|
||||
// For legacy reasons, we send the raw event also, this could be useful for handling unknown events.
|
||||
s.handleEvent(eventEventType, e)
|
||||
// Emit event to the OnEvent handler
|
||||
e.Struct = i
|
||||
go s.handle(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"`
|
||||
@ -492,7 +461,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(st *VoiceStateUpdate) {
|
||||
func (s *Session) onVoiceStateUpdate(se *Session, st *VoiceStateUpdate) {
|
||||
|
||||
// If we don't have a connection for the channel, don't bother
|
||||
if st.ChannelID == "" {
|
||||
@ -505,13 +474,22 @@ func (s *Session) onVoiceStateUpdate(st *VoiceStateUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
// We only care about events that are about us.
|
||||
if s.State.User.ID != st.UserID {
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
|
||||
// Store the SessionID for later use.
|
||||
voice.UserID = st.UserID
|
||||
voice.UserID = self.ID // TODO: Review
|
||||
voice.sessionID = st.SessionID
|
||||
}
|
||||
|
||||
@ -520,7 +498,7 @@ func (s *Session) onVoiceStateUpdate(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(st *VoiceServerUpdate) {
|
||||
func (s *Session) onVoiceServerUpdate(se *Session, st *VoiceServerUpdate) {
|
||||
|
||||
s.log(LogInformational, "called")
|
||||
|
||||
@ -677,7 +655,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(LogInformational, "error closing websocket, %s", err)
|
||||
s.log(LogError, "error closing websocket, %s", err)
|
||||
}
|
||||
|
||||
// TODO: Wait for Discord to actually close the connection.
|
||||
@ -686,7 +664,7 @@ func (s *Session) Close() (err error) {
|
||||
s.log(LogInformational, "closing gateway websocket")
|
||||
err = s.wsConn.Close()
|
||||
if err != nil {
|
||||
s.log(LogInformational, "error closing websocket, %s", err)
|
||||
s.log(LogError, "error closing websocket, %s", err)
|
||||
}
|
||||
|
||||
s.wsConn = nil
|
||||
@ -695,7 +673,7 @@ func (s *Session) Close() (err error) {
|
||||
s.Unlock()
|
||||
|
||||
s.log(LogInformational, "emit disconnect event")
|
||||
s.handleEvent(disconnectEventType, &Disconnect{})
|
||||
s.handle(&Disconnect{})
|
||||
|
||||
return
|
||||
}
|
||||
|
21
vendor/github.com/daaku/go.zipexe/license
generated
vendored
21
vendor/github.com/daaku/go.zipexe/license
generated
vendored
@ -1,21 +0,0 @@
|
||||
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.
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user