mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-27 00:39:26 +00:00
Compare commits
21 Commits
Author | SHA1 | Date | |
---|---|---|---|
bb38a61f3b | |||
c447647af9 | |||
1de64f3f61 | |||
59e55cfbd5 | |||
788d3b32ac | |||
1d414cf2fd | |||
cc3c168162 | |||
1ee6837f0e | |||
27dcea7c5b | |||
dcda7f7b8c | |||
e0cbb69a4f | |||
7ec95f786d | |||
1efe40add5 | |||
cbd73ee313 | |||
34227a7a39 | |||
71cb9b2d1d | |||
cd4c9b194f | |||
98762a0235 | |||
2fd1fd9573 | |||
aff3964078 | |||
2778580397 |
@ -54,7 +54,7 @@ See https://github.com/42wim/matterbridge/wiki
|
|||||||
|
|
||||||
# Installing
|
# Installing
|
||||||
## Binaries
|
## Binaries
|
||||||
* Latest stable release [v1.4.1](https://github.com/42wim/matterbridge/releases/latest)
|
* Latest stable release [v1.5.0](https://github.com/42wim/matterbridge/releases/latest)
|
||||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
@ -175,7 +175,7 @@ Matterbridge wouldn't exist without these libraries:
|
|||||||
* echo - https://github.com/labstack/echo
|
* echo - https://github.com/labstack/echo
|
||||||
* gitter - https://github.com/sromku/go-gitter
|
* gitter - https://github.com/sromku/go-gitter
|
||||||
* gops - https://github.com/google/gops
|
* gops - https://github.com/google/gops
|
||||||
* irc - https://github.com/thoj/go-ircevent
|
* irc - https://github.com/lrstanley/girc
|
||||||
* mattermost - https://github.com/mattermost/platform
|
* mattermost - https://github.com/mattermost/platform
|
||||||
* matrix - https://github.com/matrix-org/gomatrix
|
* matrix - https://github.com/matrix-org/gomatrix
|
||||||
* slack - https://github.com/nlopes/slack
|
* slack - https://github.com/nlopes/slack
|
||||||
|
@ -36,6 +36,7 @@ type FileInfo struct {
|
|||||||
Name string
|
Name string
|
||||||
Data *[]byte
|
Data *[]byte
|
||||||
Comment string
|
Comment string
|
||||||
|
URL string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelInfo struct {
|
type ChannelInfo struct {
|
||||||
@ -59,41 +60,46 @@ type Protocol struct {
|
|||||||
IgnoreMessages string // all protocols
|
IgnoreMessages string // all protocols
|
||||||
Jid string // xmpp
|
Jid string // xmpp
|
||||||
Login string // mattermost, matrix
|
Login string // mattermost, matrix
|
||||||
Muc string // xmpp
|
MediaServerDownload string
|
||||||
Name string // all protocols
|
MediaServerUpload string
|
||||||
Nick string // all protocols
|
MessageDelay int // IRC, time in millisecond to wait between messages
|
||||||
NickFormatter string // mattermost, slack
|
MessageFormat string // telegram
|
||||||
NickServNick string // IRC
|
MessageLength int // IRC, max length of a message allowed
|
||||||
NickServUsername string // IRC
|
MessageQueue int // IRC, size of message queue for flood control
|
||||||
NickServPassword string // IRC
|
MessageSplit bool // IRC, split long messages with newlines on MessageLength instead of clipping
|
||||||
NicksPerRow int // mattermost, slack
|
Muc string // xmpp
|
||||||
NoHomeServerSuffix bool // matrix
|
Name string // all protocols
|
||||||
NoTLS bool // mattermost
|
Nick string // all protocols
|
||||||
Password string // IRC,mattermost,XMPP,matrix
|
NickFormatter string // mattermost, slack
|
||||||
PrefixMessagesWithNick bool // mattemost, slack
|
NickServNick string // IRC
|
||||||
Protocol string //all protocols
|
NickServUsername string // IRC
|
||||||
MessageQueue int // IRC, size of message queue for flood control
|
NickServPassword string // IRC
|
||||||
MessageDelay int // IRC, time in millisecond to wait between messages
|
NicksPerRow int // mattermost, slack
|
||||||
MessageLength int // IRC, max length of a message allowed
|
NoHomeServerSuffix bool // matrix
|
||||||
MessageFormat string // telegram
|
NoTLS bool // mattermost
|
||||||
RemoteNickFormat string // all protocols
|
Password string // IRC,mattermost,XMPP,matrix
|
||||||
Server string // IRC,mattermost,XMPP,discord
|
PrefixMessagesWithNick bool // mattemost, slack
|
||||||
ShowJoinPart bool // all protocols
|
Protocol string // all protocols
|
||||||
ShowEmbeds bool // discord
|
ReplaceMessages [][]string // all protocols
|
||||||
SkipTLSVerify bool // IRC, mattermost
|
ReplaceNicks [][]string // all protocols
|
||||||
StripNick bool // all protocols
|
RemoteNickFormat string // all protocols
|
||||||
Team string // mattermost
|
Server string // IRC,mattermost,XMPP,discord
|
||||||
Token string // gitter, slack, discord, api
|
ShowJoinPart bool // all protocols
|
||||||
URL string // mattermost, slack // DEPRECATED
|
ShowEmbeds bool // discord
|
||||||
UseAPI bool // mattermost, slack
|
SkipTLSVerify bool // IRC, mattermost
|
||||||
UseSASL bool // IRC
|
StripNick bool // all protocols
|
||||||
UseTLS bool // IRC
|
Team string // mattermost
|
||||||
UseFirstName bool // telegram
|
Token string // gitter, slack, discord, api
|
||||||
UseUserName bool // discord
|
URL string // mattermost, slack // DEPRECATED
|
||||||
UseInsecureURL bool // telegram
|
UseAPI bool // mattermost, slack
|
||||||
WebhookBindAddress string // mattermost, slack
|
UseSASL bool // IRC
|
||||||
WebhookURL string // mattermost, slack
|
UseTLS bool // IRC
|
||||||
WebhookUse string // mattermost, slack, discord
|
UseFirstName bool // telegram
|
||||||
|
UseUserName bool // discord
|
||||||
|
UseInsecureURL bool // telegram
|
||||||
|
WebhookBindAddress string // mattermost, slack
|
||||||
|
WebhookURL string // mattermost, slack
|
||||||
|
WebhookUse string // mattermost, slack, discord
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelOptions struct {
|
type ChannelOptions struct {
|
||||||
|
@ -125,6 +125,23 @@ func (b *Bgitter) Send(msg config.Message) (string, error) {
|
|||||||
}
|
}
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.Extra != nil {
|
||||||
|
if len(msg.Extra["file"]) > 0 {
|
||||||
|
for _, f := range msg.Extra["file"] {
|
||||||
|
fi := f.(config.FileInfo)
|
||||||
|
if fi.URL != "" {
|
||||||
|
msg.Text = fi.URL
|
||||||
|
}
|
||||||
|
_, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
|
resp, err := b.c.SendMessage(roomID, msg.Username+msg.Text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -26,3 +26,15 @@ func DownloadFile(url string) (*[]byte, error) {
|
|||||||
resp.Body.Close()
|
resp.Body.Close()
|
||||||
return &data, nil
|
return &data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func SplitStringLength(input string, length int) string {
|
||||||
|
a := []rune(input)
|
||||||
|
str := ""
|
||||||
|
for i, r := range a {
|
||||||
|
str = str + string(r)
|
||||||
|
if i > 0 && (i+1)%length == 0 {
|
||||||
|
str += "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
"github.com/lrstanley/girc"
|
"github.com/lrstanley/girc"
|
||||||
"github.com/paulrosania/go-charset/charset"
|
"github.com/paulrosania/go-charset/charset"
|
||||||
@ -178,9 +179,27 @@ func (b *Birc) Send(msg config.Message) (string, error) {
|
|||||||
msg.Text = buf.String()
|
msg.Text = buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.Extra != nil {
|
||||||
|
if len(msg.Extra["file"]) > 0 {
|
||||||
|
for _, f := range msg.Extra["file"] {
|
||||||
|
fi := f.(config.FileInfo)
|
||||||
|
if fi.URL != "" {
|
||||||
|
msg.Text = fi.URL
|
||||||
|
}
|
||||||
|
b.Local <- config.Message{Text: msg.Text, Username: msg.Username, Channel: msg.Channel, Event: msg.Event}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// split long messages on messageLength, to avoid clipped messages #281
|
||||||
|
if b.Config.MessageSplit {
|
||||||
|
msg.Text = helper.SplitStringLength(msg.Text, b.Config.MessageLength)
|
||||||
|
}
|
||||||
for _, text := range strings.Split(msg.Text, "\n") {
|
for _, text := range strings.Split(msg.Text, "\n") {
|
||||||
|
input := []rune(text)
|
||||||
if len(text) > b.Config.MessageLength {
|
if len(text) > b.Config.MessageLength {
|
||||||
text = text[:b.Config.MessageLength] + " <message clipped>"
|
text = string(input[:b.Config.MessageLength]) + " <message clipped>"
|
||||||
}
|
}
|
||||||
if len(b.Local) < b.Config.MessageQueue {
|
if len(b.Local) < b.Config.MessageQueue {
|
||||||
if len(b.Local) == b.Config.MessageQueue-1 {
|
if len(b.Local) == b.Config.MessageQueue-1 {
|
||||||
@ -309,11 +328,10 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
|||||||
rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
|
rmsg := config.Message{Username: event.Source.Name, Channel: event.Params[0], Account: b.Account, UserID: event.Source.Ident + "@" + event.Source.Host}
|
||||||
flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event)
|
flog.Debugf("handlePrivMsg() %s %s %#v", event.Source.Name, event.Trailing, event)
|
||||||
msg := ""
|
msg := ""
|
||||||
if event.Command == "CTCP_ACTION" {
|
if event.IsAction() {
|
||||||
// msg = event.Source.Name + " "
|
|
||||||
rmsg.Event = config.EVENT_USER_ACTION
|
rmsg.Event = config.EVENT_USER_ACTION
|
||||||
}
|
}
|
||||||
msg += event.Trailing
|
msg += event.StripAction()
|
||||||
// strip IRC colors
|
// strip IRC colors
|
||||||
re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
|
re := regexp.MustCompile(`[[:cntrl:]](?:\d{1,2}(?:,\d{1,2})?)?`)
|
||||||
msg = re.ReplaceAllString(msg, "")
|
msg = re.ReplaceAllString(msg, "")
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package bmatrix
|
package bmatrix
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"mime"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
matrix "github.com/matrix-org/gomatrix"
|
matrix "github.com/matrix-org/gomatrix"
|
||||||
)
|
)
|
||||||
@ -87,6 +91,43 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
|
|||||||
matrix.TextMessage{"m.emote", msg.Username + msg.Text})
|
matrix.TextMessage{"m.emote", msg.Username + msg.Text})
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if msg.Extra != nil {
|
||||||
|
// check if we have files to upload (from slack, telegram or mattermost)
|
||||||
|
if len(msg.Extra["file"]) > 0 {
|
||||||
|
for _, f := range msg.Extra["file"] {
|
||||||
|
fi := f.(config.FileInfo)
|
||||||
|
content := bytes.NewReader(*fi.Data)
|
||||||
|
sp := strings.Split(fi.Name, ".")
|
||||||
|
mtype := mime.TypeByExtension("." + sp[len(sp)-1])
|
||||||
|
if strings.Contains(mtype, "image") ||
|
||||||
|
strings.Contains(mtype, "video") {
|
||||||
|
flog.Debugf("uploading file: %s %s", fi.Name, mtype)
|
||||||
|
res, err := b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
|
||||||
|
if err != nil {
|
||||||
|
flog.Errorf("file upload failed: %#v", err)
|
||||||
|
}
|
||||||
|
if strings.Contains(mtype, "video") {
|
||||||
|
flog.Debugf("sendVideo %s", res.ContentURI)
|
||||||
|
_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI)
|
||||||
|
if err != nil {
|
||||||
|
flog.Errorf("sendVideo failed: %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.Contains(mtype, "image") {
|
||||||
|
flog.Debugf("sendImage %s", res.ContentURI)
|
||||||
|
_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)
|
||||||
|
if err != nil {
|
||||||
|
flog.Errorf("sendImage failed: %#v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
flog.Debugf("result: %#v", res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b.mc.SendText(channel, msg.Username+msg.Text)
|
b.mc.SendText(channel, msg.Username+msg.Text)
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
@ -104,7 +145,13 @@ func (b *Bmatrix) getRoomID(channel string) string {
|
|||||||
func (b *Bmatrix) handlematrix() error {
|
func (b *Bmatrix) handlematrix() error {
|
||||||
syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
|
syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
|
||||||
syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
|
syncer.OnEventType("m.room.message", func(ev *matrix.Event) {
|
||||||
if (ev.Content["msgtype"].(string) == "m.text" || ev.Content["msgtype"].(string) == "m.notice" || ev.Content["msgtype"].(string) == "m.emote") && ev.Sender != b.UserID {
|
flog.Debugf("Received: %#v", ev)
|
||||||
|
if (ev.Content["msgtype"].(string) == "m.text" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.notice" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.emote" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.file" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.image" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.video") && ev.Sender != b.UserID {
|
||||||
b.RLock()
|
b.RLock()
|
||||||
channel, ok := b.RoomMap[ev.RoomID]
|
channel, ok := b.RoomMap[ev.RoomID]
|
||||||
b.RUnlock()
|
b.RUnlock()
|
||||||
@ -121,10 +168,31 @@ func (b *Bmatrix) handlematrix() error {
|
|||||||
if ev.Content["msgtype"].(string) == "m.emote" {
|
if ev.Content["msgtype"].(string) == "m.emote" {
|
||||||
rmsg.Event = config.EVENT_USER_ACTION
|
rmsg.Event = config.EVENT_USER_ACTION
|
||||||
}
|
}
|
||||||
|
if ev.Content["msgtype"].(string) == "m.image" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.video" ||
|
||||||
|
ev.Content["msgtype"].(string) == "m.file" {
|
||||||
|
flog.Debugf("ev: %#v", ev)
|
||||||
|
rmsg.Extra = make(map[string][]interface{})
|
||||||
|
url := ev.Content["url"].(string)
|
||||||
|
url = strings.Replace(url, "mxc://", b.Config.Server+"/_matrix/media/v1/download/", -1)
|
||||||
|
info := ev.Content["info"].(map[string]interface{})
|
||||||
|
size := info["size"].(float64)
|
||||||
|
name := ev.Content["body"].(string)
|
||||||
|
flog.Debugf("trying to download %#v with size %#v", name, size)
|
||||||
|
if size <= 1000000 {
|
||||||
|
data, err := helper.DownloadFile(url)
|
||||||
|
if err != nil {
|
||||||
|
flog.Errorf("download %s failed %#v", url, err)
|
||||||
|
} else {
|
||||||
|
flog.Debugf("download OK %#v %#v %#v", name, len(*data), len(url))
|
||||||
|
rmsg.Extra["file"] = append(rmsg.Extra["file"], config.FileInfo{Name: name, Data: data})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rmsg.Text = ""
|
||||||
|
}
|
||||||
flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
|
flog.Debugf("Sending message from %s on %s to gateway", ev.Sender, b.Account)
|
||||||
b.Remote <- rmsg
|
b.Remote <- rmsg
|
||||||
}
|
}
|
||||||
flog.Debugf("Received: %#v", ev)
|
|
||||||
})
|
})
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
|
@ -229,6 +229,9 @@ func (b *Btelegram) handleDownload(file interface{}, msg *config.Message) {
|
|||||||
url = b.getFileDirectURL(v.FileID)
|
url = b.getFileDirectURL(v.FileID)
|
||||||
urlPart := strings.Split(url, "/")
|
urlPart := strings.Split(url, "/")
|
||||||
name = urlPart[len(urlPart)-1]
|
name = urlPart[len(urlPart)-1]
|
||||||
|
if !strings.HasSuffix(name, ".webp") {
|
||||||
|
name = name + ".webp"
|
||||||
|
}
|
||||||
text = " " + url
|
text = " " + url
|
||||||
fileid = v.FileID
|
fileid = v.FileID
|
||||||
case *tgbotapi.Video:
|
case *tgbotapi.Video:
|
||||||
|
@ -85,6 +85,19 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
|
|||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
flog.Debugf("Receiving %#v", msg)
|
flog.Debugf("Receiving %#v", msg)
|
||||||
|
if msg.Extra != nil {
|
||||||
|
if len(msg.Extra["file"]) > 0 {
|
||||||
|
for _, f := range msg.Extra["file"] {
|
||||||
|
fi := f.(config.FileInfo)
|
||||||
|
if fi.URL != "" {
|
||||||
|
msg.Text = fi.URL
|
||||||
|
}
|
||||||
|
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
|
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.Config.Muc, Text: msg.Username + msg.Text})
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
14
changelog.md
14
changelog.md
@ -1,3 +1,17 @@
|
|||||||
|
# v1.5.0
|
||||||
|
## New features
|
||||||
|
* general: remote mediaserver support. See MediaServerDownload and MediaServerUpload in matterbridge.toml.sample
|
||||||
|
more information on https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
|
||||||
|
* general: Add support for ReplaceNicks using regexp to replace nicks. Closes #269 (see matterbridge.toml.sample)
|
||||||
|
* general: Add support for ReplaceMessages using regexp to replace messages. #269 (see matterbridge.toml.sample)
|
||||||
|
* irc: Add MessageSplit option to split messages on MessageLength (irc). Closes #281
|
||||||
|
* matrix: Add support for uploading images/video (matrix). Closes #302
|
||||||
|
* matrix: Add support for uploaded images/video (matrix)
|
||||||
|
|
||||||
|
## Bugfix
|
||||||
|
* telegram: Add webp extension to stickers if necessary (telegram)
|
||||||
|
* mattermost: Break when re-login fails (mattermost)
|
||||||
|
|
||||||
# v1.4.1
|
# v1.4.1
|
||||||
## Bugfix
|
## Bugfix
|
||||||
* telegram: fix issue with uploading for images/documents/stickers
|
* telegram: fix issue with uploading for images/documents/stickers
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
package gateway
|
package gateway
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/42wim/matterbridge/bridge"
|
"github.com/42wim/matterbridge/bridge"
|
||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
log "github.com/Sirupsen/logrus"
|
log "github.com/Sirupsen/logrus"
|
||||||
// "github.com/davecgh/go-spew/spew"
|
// "github.com/davecgh/go-spew/spew"
|
||||||
|
"crypto/sha1"
|
||||||
"github.com/hashicorp/golang-lru"
|
"github.com/hashicorp/golang-lru"
|
||||||
"github.com/peterhellberg/emojilib"
|
"github.com/peterhellberg/emojilib"
|
||||||
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -155,7 +158,8 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
|
|||||||
if dest.Protocol != "discord" &&
|
if dest.Protocol != "discord" &&
|
||||||
dest.Protocol != "slack" &&
|
dest.Protocol != "slack" &&
|
||||||
dest.Protocol != "mattermost" &&
|
dest.Protocol != "mattermost" &&
|
||||||
dest.Protocol != "telegram" {
|
dest.Protocol != "telegram" &&
|
||||||
|
dest.Protocol != "matrix" {
|
||||||
if msg.Text == "" {
|
if msg.Text == "" {
|
||||||
return brMsgIDs
|
return brMsgIDs
|
||||||
}
|
}
|
||||||
@ -254,6 +258,20 @@ func (gw *Gateway) modifyUsername(msg config.Message, dest *bridge.Bridge) strin
|
|||||||
if nick == "" {
|
if nick == "" {
|
||||||
nick = gw.Config.General.RemoteNickFormat
|
nick = gw.Config.General.RemoteNickFormat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loop to replace nicks
|
||||||
|
for _, outer := range br.Config.ReplaceNicks {
|
||||||
|
search := outer[0]
|
||||||
|
replace := outer[1]
|
||||||
|
// TODO move compile to bridge init somewhere
|
||||||
|
re, err := regexp.Compile(search)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("regexp in %s failed: %s", msg.Account, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
msg.Username = re.ReplaceAllString(msg.Username, replace)
|
||||||
|
}
|
||||||
|
|
||||||
if len(msg.Username) > 0 {
|
if len(msg.Username) > 0 {
|
||||||
// fix utf-8 issue #193
|
// fix utf-8 issue #193
|
||||||
i := 0
|
i := 0
|
||||||
@ -287,9 +305,50 @@ func (gw *Gateway) modifyAvatar(msg config.Message, dest *bridge.Bridge) string
|
|||||||
func (gw *Gateway) modifyMessage(msg *config.Message) {
|
func (gw *Gateway) modifyMessage(msg *config.Message) {
|
||||||
// replace :emoji: to unicode
|
// replace :emoji: to unicode
|
||||||
msg.Text = emojilib.Replace(msg.Text)
|
msg.Text = emojilib.Replace(msg.Text)
|
||||||
|
br := gw.Bridges[msg.Account]
|
||||||
|
// loop to replace messages
|
||||||
|
for _, outer := range br.Config.ReplaceMessages {
|
||||||
|
search := outer[0]
|
||||||
|
replace := outer[1]
|
||||||
|
// TODO move compile to bridge init somewhere
|
||||||
|
re, err := regexp.Compile(search)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("regexp in %s failed: %s", msg.Account, err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
msg.Text = re.ReplaceAllString(msg.Text, replace)
|
||||||
|
}
|
||||||
msg.Gateway = gw.Name
|
msg.Gateway = gw.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) handleFiles(msg *config.Message) {
|
||||||
|
if msg.Extra == nil || gw.Config.General.MediaServerUpload == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(msg.Extra["file"]) > 0 {
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: time.Second * 5,
|
||||||
|
}
|
||||||
|
for i, f := range msg.Extra["file"] {
|
||||||
|
fi := f.(config.FileInfo)
|
||||||
|
sha1sum := fmt.Sprintf("%x", sha1.Sum(*fi.Data))
|
||||||
|
reader := bytes.NewReader(*fi.Data)
|
||||||
|
url := gw.Config.General.MediaServerUpload + "/" + sha1sum + "/" + fi.Name
|
||||||
|
durl := gw.Config.General.MediaServerDownload + "/" + sha1sum + "/" + fi.Name
|
||||||
|
extra := msg.Extra["file"][i].(config.FileInfo)
|
||||||
|
extra.URL = durl
|
||||||
|
msg.Extra["file"][i] = extra
|
||||||
|
req, _ := http.NewRequest("PUT", url, reader)
|
||||||
|
req.Header.Set("Content-Type", "binary/octet-stream")
|
||||||
|
_, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("mediaserver upload failed: %#v", err)
|
||||||
|
}
|
||||||
|
log.Debugf("mediaserver download URL = %s", durl)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getChannelID(msg config.Message) string {
|
func getChannelID(msg config.Message) string {
|
||||||
return msg.Channel + msg.Account
|
return msg.Channel + msg.Account
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,7 @@ func (r *Router) handleReceive() {
|
|||||||
if !gw.ignoreMessage(&msg) {
|
if !gw.ignoreMessage(&msg) {
|
||||||
msg.Timestamp = time.Now()
|
msg.Timestamp = time.Now()
|
||||||
gw.modifyMessage(&msg)
|
gw.modifyMessage(&msg)
|
||||||
|
gw.handleFiles(&msg)
|
||||||
for _, br := range gw.Bridges {
|
for _, br := range gw.Bridges {
|
||||||
msgIDs = append(msgIDs, gw.handleMessage(msg, br)...)
|
msgIDs = append(msgIDs, gw.handleMessage(msg, br)...)
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
version = "1.4.1"
|
version = "1.5.1"
|
||||||
githash string
|
githash string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,6 +80,11 @@ MessageQueue=30
|
|||||||
#OPTIONAL (default 400)
|
#OPTIONAL (default 400)
|
||||||
MessageLength=400
|
MessageLength=400
|
||||||
|
|
||||||
|
#Split messages on MessageLength instead of showing the <message clipped>
|
||||||
|
#WARNING: this could lead to flooding
|
||||||
|
#OPTIONAL (default false)
|
||||||
|
MessageSplit=false
|
||||||
|
|
||||||
#Nicks you want to ignore.
|
#Nicks you want to ignore.
|
||||||
#Messages from those users will not be sent to other bridges.
|
#Messages from those users will not be sent to other bridges.
|
||||||
#OPTIONAL
|
#OPTIONAL
|
||||||
@ -91,6 +96,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -154,6 +176,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#Messages you want to replace.
|
||||||
|
#It replaces outgoing messages from the bridge.
|
||||||
|
#So you need to place it by the sending bridge definition.
|
||||||
|
#Regular expressions supported
|
||||||
|
#Some examples:
|
||||||
|
#This replaces cat => dog and sleep => awake
|
||||||
|
#ReplaceMessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#This Replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#ReplaceMessages=[ ["[0-9]","number"] ]
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#Nicks you want to replace.
|
||||||
|
#See ReplaceMessages for syntaxA
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -208,6 +247,23 @@ IgnoreNicks="spammer1 spammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -322,6 +378,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -366,6 +439,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -457,6 +547,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -525,6 +632,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -592,6 +716,23 @@ IgnoreNicks="spammer1 spammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -660,6 +801,23 @@ IgnoreNicks="ircspammer1 ircspammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -720,6 +878,23 @@ IgnoreNicks="spammer1 spammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -774,6 +949,23 @@ IgnoreNicks="spammer1 spammer2"
|
|||||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
#OPTIONAL (example below ignores messages starting with ~~ or messages containing badword
|
||||||
IgnoreMessages="^~~ badword"
|
IgnoreMessages="^~~ badword"
|
||||||
|
|
||||||
|
#messages you want to replace.
|
||||||
|
#it replaces outgoing messages from the bridge.
|
||||||
|
#so you need to place it by the sending bridge definition.
|
||||||
|
#regular expressions supported
|
||||||
|
#some examples:
|
||||||
|
#this replaces cat => dog and sleep => awake
|
||||||
|
#replacemessages=[ ["cat","dog"], ["sleep","awake"] ]
|
||||||
|
#this replaces every number with number. 123 => numbernumbernumber
|
||||||
|
#replacemessages=[ ["[0-9]","number"] ]
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceMessages=[ ["cat","dog"] ]
|
||||||
|
|
||||||
|
#nicks you want to replace.
|
||||||
|
#see replacemessages for syntaxa
|
||||||
|
#optional (default empty)
|
||||||
|
ReplaceNicks=[ ["user--","user"] ]
|
||||||
|
|
||||||
#RemoteNickFormat defines how remote users appear on this bridge
|
#RemoteNickFormat defines how remote users appear on this bridge
|
||||||
#The string "{NICK}" (case sensitive) will be replaced by the actual nick / username.
|
#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 "{BRIDGE}" (case sensitive) will be replaced by the sending bridge
|
||||||
@ -838,6 +1030,21 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
StripNick=false
|
StripNick=false
|
||||||
|
|
||||||
|
|
||||||
|
#MediaServerUpload and MediaServerDownload are used for uploading images/files/video to
|
||||||
|
#a remote "mediaserver" (a webserver like caddy for example).
|
||||||
|
#When configured images/files uploaded on bridges like mattermost,slack, telegram will be downloaded
|
||||||
|
#and uploaded again to MediaServerUpload URL
|
||||||
|
#The MediaServerDownload will be used so that bridges without native uploading support:
|
||||||
|
#gitter, irc and xmpp will be shown links to the files on MediaServerDownload
|
||||||
|
#
|
||||||
|
#More information https://github.com/42wim/matterbridge/wiki/Mediaserver-setup-%5Badvanced%5D
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
MediaServerUpload="https://user:pass@yourserver.com/upload"
|
||||||
|
#OPTIONAL (default empty)
|
||||||
|
MediaServerDownload="https://youserver.com/download"
|
||||||
|
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#Gateway configuration
|
#Gateway configuration
|
||||||
###################################################################
|
###################################################################
|
||||||
|
@ -817,9 +817,14 @@ func (m *MMClient) StatusLoop() {
|
|||||||
backoff = time.Second * 60
|
backoff = time.Second * 60
|
||||||
case <-time.After(time.Second * 5):
|
case <-time.After(time.Second * 5):
|
||||||
if retries > 3 {
|
if retries > 3 {
|
||||||
|
m.log.Debug("StatusLoop() timeout")
|
||||||
m.Logout()
|
m.Logout()
|
||||||
m.WsQuit = false
|
m.WsQuit = false
|
||||||
m.Login()
|
err := m.Login()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Login failed: %#v", err)
|
||||||
|
break
|
||||||
|
}
|
||||||
if m.OnWsConnect != nil {
|
if m.OnWsConnect != nil {
|
||||||
m.OnWsConnect()
|
m.OnWsConnect()
|
||||||
}
|
}
|
||||||
|
80
vendor/github.com/lrstanley/girc/builtin.go
generated
vendored
80
vendor/github.com/lrstanley/girc/builtin.go
generated
vendored
@ -16,64 +16,62 @@ func (c *Client) registerBuiltins() {
|
|||||||
c.Handlers.mu.Lock()
|
c.Handlers.mu.Lock()
|
||||||
|
|
||||||
// Built-in things that should always be supported.
|
// Built-in things that should always be supported.
|
||||||
c.Handlers.register(true, RPL_WELCOME, HandlerFunc(func(c *Client, e Event) {
|
c.Handlers.register(true, true, RPL_WELCOME, HandlerFunc(handleConnect))
|
||||||
go handleConnect(c, e)
|
c.Handlers.register(true, false, PING, HandlerFunc(handlePING))
|
||||||
}))
|
c.Handlers.register(true, false, PONG, HandlerFunc(handlePONG))
|
||||||
c.Handlers.register(true, PING, HandlerFunc(handlePING))
|
|
||||||
c.Handlers.register(true, PONG, HandlerFunc(handlePONG))
|
|
||||||
|
|
||||||
if !c.Config.disableTracking {
|
if !c.Config.disableTracking {
|
||||||
// Joins/parts/anything that may add/remove/rename users.
|
// Joins/parts/anything that may add/remove/rename users.
|
||||||
c.Handlers.register(true, JOIN, HandlerFunc(handleJOIN))
|
c.Handlers.register(true, false, JOIN, HandlerFunc(handleJOIN))
|
||||||
c.Handlers.register(true, PART, HandlerFunc(handlePART))
|
c.Handlers.register(true, false, PART, HandlerFunc(handlePART))
|
||||||
c.Handlers.register(true, KICK, HandlerFunc(handleKICK))
|
c.Handlers.register(true, false, KICK, HandlerFunc(handleKICK))
|
||||||
c.Handlers.register(true, QUIT, HandlerFunc(handleQUIT))
|
c.Handlers.register(true, false, QUIT, HandlerFunc(handleQUIT))
|
||||||
c.Handlers.register(true, NICK, HandlerFunc(handleNICK))
|
c.Handlers.register(true, false, NICK, HandlerFunc(handleNICK))
|
||||||
c.Handlers.register(true, RPL_NAMREPLY, HandlerFunc(handleNAMES))
|
c.Handlers.register(true, false, RPL_NAMREPLY, HandlerFunc(handleNAMES))
|
||||||
|
|
||||||
// Modes.
|
// Modes.
|
||||||
c.Handlers.register(true, MODE, HandlerFunc(handleMODE))
|
c.Handlers.register(true, false, MODE, HandlerFunc(handleMODE))
|
||||||
c.Handlers.register(true, RPL_CHANNELMODEIS, HandlerFunc(handleMODE))
|
c.Handlers.register(true, false, RPL_CHANNELMODEIS, HandlerFunc(handleMODE))
|
||||||
|
|
||||||
// WHO/WHOX responses.
|
// WHO/WHOX responses.
|
||||||
c.Handlers.register(true, RPL_WHOREPLY, HandlerFunc(handleWHO))
|
c.Handlers.register(true, false, RPL_WHOREPLY, HandlerFunc(handleWHO))
|
||||||
c.Handlers.register(true, RPL_WHOSPCRPL, HandlerFunc(handleWHO))
|
c.Handlers.register(true, false, RPL_WHOSPCRPL, HandlerFunc(handleWHO))
|
||||||
|
|
||||||
// Other misc. useful stuff.
|
// Other misc. useful stuff.
|
||||||
c.Handlers.register(true, TOPIC, HandlerFunc(handleTOPIC))
|
c.Handlers.register(true, false, TOPIC, HandlerFunc(handleTOPIC))
|
||||||
c.Handlers.register(true, RPL_TOPIC, HandlerFunc(handleTOPIC))
|
c.Handlers.register(true, false, RPL_TOPIC, HandlerFunc(handleTOPIC))
|
||||||
c.Handlers.register(true, RPL_MYINFO, HandlerFunc(handleMYINFO))
|
c.Handlers.register(true, false, RPL_MYINFO, HandlerFunc(handleMYINFO))
|
||||||
c.Handlers.register(true, RPL_ISUPPORT, HandlerFunc(handleISUPPORT))
|
c.Handlers.register(true, false, RPL_ISUPPORT, HandlerFunc(handleISUPPORT))
|
||||||
c.Handlers.register(true, RPL_MOTDSTART, HandlerFunc(handleMOTD))
|
c.Handlers.register(true, false, RPL_MOTDSTART, HandlerFunc(handleMOTD))
|
||||||
c.Handlers.register(true, RPL_MOTD, HandlerFunc(handleMOTD))
|
c.Handlers.register(true, false, RPL_MOTD, HandlerFunc(handleMOTD))
|
||||||
|
|
||||||
// Keep users lastactive times up to date.
|
// Keep users lastactive times up to date.
|
||||||
c.Handlers.register(true, PRIVMSG, HandlerFunc(updateLastActive))
|
c.Handlers.register(true, false, PRIVMSG, HandlerFunc(updateLastActive))
|
||||||
c.Handlers.register(true, NOTICE, HandlerFunc(updateLastActive))
|
c.Handlers.register(true, false, NOTICE, HandlerFunc(updateLastActive))
|
||||||
c.Handlers.register(true, TOPIC, HandlerFunc(updateLastActive))
|
c.Handlers.register(true, false, TOPIC, HandlerFunc(updateLastActive))
|
||||||
c.Handlers.register(true, KICK, HandlerFunc(updateLastActive))
|
c.Handlers.register(true, false, KICK, HandlerFunc(updateLastActive))
|
||||||
|
|
||||||
// CAP IRCv3-specific tracking and functionality.
|
// CAP IRCv3-specific tracking and functionality.
|
||||||
c.Handlers.register(true, CAP, HandlerFunc(handleCAP))
|
c.Handlers.register(true, false, CAP, HandlerFunc(handleCAP))
|
||||||
c.Handlers.register(true, CAP_CHGHOST, HandlerFunc(handleCHGHOST))
|
c.Handlers.register(true, false, CAP_CHGHOST, HandlerFunc(handleCHGHOST))
|
||||||
c.Handlers.register(true, CAP_AWAY, HandlerFunc(handleAWAY))
|
c.Handlers.register(true, false, CAP_AWAY, HandlerFunc(handleAWAY))
|
||||||
c.Handlers.register(true, CAP_ACCOUNT, HandlerFunc(handleACCOUNT))
|
c.Handlers.register(true, false, CAP_ACCOUNT, HandlerFunc(handleACCOUNT))
|
||||||
c.Handlers.register(true, ALL_EVENTS, HandlerFunc(handleTags))
|
c.Handlers.register(true, false, ALL_EVENTS, HandlerFunc(handleTags))
|
||||||
|
|
||||||
// SASL IRCv3 support.
|
// SASL IRCv3 support.
|
||||||
c.Handlers.register(true, AUTHENTICATE, HandlerFunc(handleSASL))
|
c.Handlers.register(true, false, AUTHENTICATE, HandlerFunc(handleSASL))
|
||||||
c.Handlers.register(true, RPL_SASLSUCCESS, HandlerFunc(handleSASL))
|
c.Handlers.register(true, false, RPL_SASLSUCCESS, HandlerFunc(handleSASL))
|
||||||
c.Handlers.register(true, RPL_NICKLOCKED, HandlerFunc(handleSASLError))
|
c.Handlers.register(true, false, RPL_NICKLOCKED, HandlerFunc(handleSASLError))
|
||||||
c.Handlers.register(true, ERR_SASLFAIL, HandlerFunc(handleSASLError))
|
c.Handlers.register(true, false, ERR_SASLFAIL, HandlerFunc(handleSASLError))
|
||||||
c.Handlers.register(true, ERR_SASLTOOLONG, HandlerFunc(handleSASLError))
|
c.Handlers.register(true, false, ERR_SASLTOOLONG, HandlerFunc(handleSASLError))
|
||||||
c.Handlers.register(true, ERR_SASLABORTED, HandlerFunc(handleSASLError))
|
c.Handlers.register(true, false, ERR_SASLABORTED, HandlerFunc(handleSASLError))
|
||||||
c.Handlers.register(true, RPL_SASLMECHS, HandlerFunc(handleSASLError))
|
c.Handlers.register(true, false, RPL_SASLMECHS, HandlerFunc(handleSASLError))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nickname collisions.
|
// Nickname collisions.
|
||||||
c.Handlers.register(true, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler))
|
c.Handlers.register(true, false, ERR_NICKNAMEINUSE, HandlerFunc(nickCollisionHandler))
|
||||||
c.Handlers.register(true, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler))
|
c.Handlers.register(true, false, ERR_NICKCOLLISION, HandlerFunc(nickCollisionHandler))
|
||||||
c.Handlers.register(true, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler))
|
c.Handlers.register(true, false, ERR_UNAVAILRESOURCE, HandlerFunc(nickCollisionHandler))
|
||||||
|
|
||||||
c.Handlers.mu.Unlock()
|
c.Handlers.mu.Unlock()
|
||||||
}
|
}
|
||||||
@ -389,7 +387,7 @@ func handleISUPPORT(c *Client, e Event) {
|
|||||||
c.state.Lock()
|
c.state.Lock()
|
||||||
// Skip the first parameter, as it's our nickname.
|
// Skip the first parameter, as it's our nickname.
|
||||||
for i := 1; i < len(e.Params); i++ {
|
for i := 1; i < len(e.Params); i++ {
|
||||||
j := strings.IndexByte(e.Params[i], 0x3D) // =
|
j := strings.IndexByte(e.Params[i], '=')
|
||||||
|
|
||||||
if j < 1 || (j+1) == len(e.Params[i]) {
|
if j < 1 || (j+1) == len(e.Params[i]) {
|
||||||
c.state.serverOptions[e.Params[i]] = ""
|
c.state.serverOptions[e.Params[i]] = ""
|
||||||
|
16
vendor/github.com/lrstanley/girc/cap.go
generated
vendored
16
vendor/github.com/lrstanley/girc/cap.go
generated
vendored
@ -136,7 +136,7 @@ func handleCAP(c *Client, e Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let them know which ones we'd like to enable.
|
// Let them know which ones we'd like to enable.
|
||||||
c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " ")})
|
c.write(&Event{Command: CAP, Params: []string{CAP_REQ}, Trailing: strings.Join(c.state.tmpCap, " "), EmptyTrailing: true})
|
||||||
|
|
||||||
// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests
|
// Re-initialize the tmpCap, so if we get multiple 'CAP LS' requests
|
||||||
// due to cap-notify, we can re-evaluate what we can support.
|
// due to cap-notify, we can re-evaluate what we can support.
|
||||||
@ -375,11 +375,11 @@ func handleTags(c *Client, e Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
prefixTag byte = 0x40 // @
|
prefixTag byte = '@'
|
||||||
prefixTagValue byte = 0x3D // =
|
prefixTagValue byte = '='
|
||||||
prefixUserTag byte = 0x2B // +
|
prefixUserTag byte = '+'
|
||||||
tagSeparator byte = 0x3B // ;
|
tagSeparator byte = ';'
|
||||||
maxTagLength int = 511 // 510 + @ and " " (space), though space usually not included.
|
maxTagLength int = 511 // 510 + @ and " " (space), though space usually not included.
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tags represents the key-value pairs in IRCv3 message tags. The map contains
|
// Tags represents the key-value pairs in IRCv3 message tags. The map contains
|
||||||
@ -618,7 +618,7 @@ func validTag(name string) bool {
|
|||||||
|
|
||||||
for i := 0; i < len(name); i++ {
|
for i := 0; i < len(name); i++ {
|
||||||
// A-Z, a-z, 0-9, -/._
|
// A-Z, a-z, 0-9, -/._
|
||||||
if (name[i] < 0x41 || name[i] > 0x5A) && (name[i] < 0x61 || name[i] > 0x7A) && (name[i] < 0x2D || name[i] > 0x39) && name[i] != 0x5F {
|
if (name[i] < 'A' || name[i] > 'Z') && (name[i] < 'a' || name[i] > 'z') && (name[i] < '-' || name[i] > '9') && name[i] != '_' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,7 +631,7 @@ func validTag(name string) bool {
|
|||||||
func validTagValue(value string) bool {
|
func validTagValue(value string) bool {
|
||||||
for i := 0; i < len(value); i++ {
|
for i := 0; i < len(value); i++ {
|
||||||
// Don't allow any invisible chars within the tag, or semicolons.
|
// Don't allow any invisible chars within the tag, or semicolons.
|
||||||
if value[i] < 0x21 || value[i] > 0x7E || value[i] == 0x3B {
|
if value[i] < '!' || value[i] > '~' || value[i] == ';' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
105
vendor/github.com/lrstanley/girc/client.go
generated
vendored
105
vendor/github.com/lrstanley/girc/client.go
generated
vendored
@ -191,18 +191,6 @@ func (conf *Config) isValid() error {
|
|||||||
// connected.
|
// connected.
|
||||||
var ErrNotConnected = errors.New("client is not connected to server")
|
var ErrNotConnected = errors.New("client is not connected to server")
|
||||||
|
|
||||||
// ErrDisconnected is called when Config.Retries is less than 1, and we
|
|
||||||
// non-intentionally disconnected from the server.
|
|
||||||
var ErrDisconnected = errors.New("unexpectedly disconnected")
|
|
||||||
|
|
||||||
// ErrInvalidTarget should be returned if the target which you are
|
|
||||||
// attempting to send an event to is invalid or doesn't match RFC spec.
|
|
||||||
type ErrInvalidTarget struct {
|
|
||||||
Target string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *ErrInvalidTarget) Error() string { return "invalid target: " + e.Target }
|
|
||||||
|
|
||||||
// New creates a new IRC client with the specified server, name and config.
|
// New creates a new IRC client with the specified server, name and config.
|
||||||
func New(config Config) *Client {
|
func New(config Config) *Client {
|
||||||
c := &Client{
|
c := &Client{
|
||||||
@ -253,6 +241,37 @@ func (c *Client) String() string {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TLSConnectionState returns the TLS connection state from tls.Conn{}, which
|
||||||
|
// is useful to return needed TLS fingerprint info, certificates, verify cert
|
||||||
|
// expiration dates, etc. Will only return an error if the underlying
|
||||||
|
// connection wasn't established using TLS (see ErrConnNotTLS), or if the
|
||||||
|
// client isn't connected.
|
||||||
|
func (c *Client) TLSConnectionState() (*tls.ConnectionState, error) {
|
||||||
|
c.mu.RLock()
|
||||||
|
defer c.mu.RUnlock()
|
||||||
|
if c.conn == nil {
|
||||||
|
return nil, ErrNotConnected
|
||||||
|
}
|
||||||
|
|
||||||
|
c.conn.mu.RLock()
|
||||||
|
defer c.conn.mu.RUnlock()
|
||||||
|
|
||||||
|
if !c.conn.connected {
|
||||||
|
return nil, ErrNotConnected
|
||||||
|
}
|
||||||
|
|
||||||
|
if tlsConn, ok := c.conn.sock.(*tls.Conn); ok {
|
||||||
|
cs := tlsConn.ConnectionState()
|
||||||
|
return &cs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, ErrConnNotTLS
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrConnNotTLS is returned when Client.TLSConnectionState() is called, and
|
||||||
|
// the connection to the server wasn't made with TLS.
|
||||||
|
var ErrConnNotTLS = errors.New("underlying connection is not tls")
|
||||||
|
|
||||||
// Close closes the network connection to the server, and sends a STOPPED
|
// Close closes the network connection to the server, and sends a STOPPED
|
||||||
// event. This should cause Connect() to return with nil. This should be
|
// event. This should cause Connect() to return with nil. This should be
|
||||||
// safe to call multiple times. See Connect()'s documentation on how
|
// safe to call multiple times. See Connect()'s documentation on how
|
||||||
@ -387,7 +406,7 @@ func (c *Client) ConnSince() (since *time.Duration, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IsConnected returns true if the client is connected to the server.
|
// IsConnected returns true if the client is connected to the server.
|
||||||
func (c *Client) IsConnected() (connected bool) {
|
func (c *Client) IsConnected() bool {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
if c.conn == nil {
|
if c.conn == nil {
|
||||||
c.mu.RUnlock()
|
c.mu.RUnlock()
|
||||||
@ -395,7 +414,7 @@ func (c *Client) IsConnected() (connected bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.conn.mu.RLock()
|
c.conn.mu.RLock()
|
||||||
connected = c.conn.connected
|
connected := c.conn.connected
|
||||||
c.conn.mu.RUnlock()
|
c.conn.mu.RUnlock()
|
||||||
c.mu.RUnlock()
|
c.mu.RUnlock()
|
||||||
|
|
||||||
@ -445,9 +464,9 @@ func (c *Client) GetHost() string {
|
|||||||
return c.state.host
|
return c.state.host
|
||||||
}
|
}
|
||||||
|
|
||||||
// Channels returns the active list of channels that the client is in.
|
// ChannelList returns the active list of channel names that the client is in.
|
||||||
// Panics if tracking is disabled.
|
// Panics if tracking is disabled.
|
||||||
func (c *Client) Channels() []string {
|
func (c *Client) ChannelList() []string {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
c.state.RLock()
|
c.state.RLock()
|
||||||
@ -463,9 +482,26 @@ func (c *Client) Channels() []string {
|
|||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
|
|
||||||
// Users returns the active list of users that the client is tracking across
|
// Channels returns the active channels that the client is in. Panics if
|
||||||
// all files. Panics if tracking is disabled.
|
// tracking is disabled.
|
||||||
func (c *Client) Users() []string {
|
func (c *Client) Channels() []*Channel {
|
||||||
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
|
c.state.RLock()
|
||||||
|
channels := make([]*Channel, len(c.state.channels))
|
||||||
|
var i int
|
||||||
|
for channel := range c.state.channels {
|
||||||
|
channels[i] = c.state.channels[channel].Copy()
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
c.state.RUnlock()
|
||||||
|
|
||||||
|
return channels
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserList returns the active list of nicknames that the client is tracking
|
||||||
|
// across all networks. Panics if tracking is disabled.
|
||||||
|
func (c *Client) UserList() []string {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
c.state.RLock()
|
c.state.RLock()
|
||||||
@ -481,6 +517,23 @@ func (c *Client) Users() []string {
|
|||||||
return users
|
return users
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Users returns the active users that the client is tracking across all
|
||||||
|
// networks. Panics if tracking is disabled.
|
||||||
|
func (c *Client) Users() []*User {
|
||||||
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
|
c.state.RLock()
|
||||||
|
users := make([]*User, len(c.state.users))
|
||||||
|
var i int
|
||||||
|
for user := range c.state.users {
|
||||||
|
users[i] = c.state.users[user].Copy()
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
c.state.RUnlock()
|
||||||
|
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
// LookupChannel looks up a given channel in state. If the channel doesn't
|
// LookupChannel looks up a given channel in state. If the channel doesn't
|
||||||
// exist, nil is returned. Panics if tracking is disabled.
|
// exist, nil is returned. Panics if tracking is disabled.
|
||||||
func (c *Client) LookupChannel(name string) *Channel {
|
func (c *Client) LookupChannel(name string) *Channel {
|
||||||
@ -562,30 +615,30 @@ func (c *Client) NetworkName() (name string) {
|
|||||||
// supplied this information during connection. May be empty if the server
|
// supplied this information during connection. May be empty if the server
|
||||||
// does not support RPL_MYINFO. Will panic if used when tracking has been
|
// does not support RPL_MYINFO. Will panic if used when tracking has been
|
||||||
// disabled.
|
// disabled.
|
||||||
func (c *Client) ServerVersion() (version string) {
|
func (c *Client) ServerVersion() string {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
version, _ = c.GetServerOption("VERSION")
|
version, _ := c.GetServerOption("VERSION")
|
||||||
|
|
||||||
return version
|
return version
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServerMOTD returns the servers message of the day, if the server has sent
|
// ServerMOTD returns the servers message of the day, if the server has sent
|
||||||
// it upon connect. Will panic if used when tracking has been disabled.
|
// it upon connect. Will panic if used when tracking has been disabled.
|
||||||
func (c *Client) ServerMOTD() (motd string) {
|
func (c *Client) ServerMOTD() string {
|
||||||
c.panicIfNotTracking()
|
c.panicIfNotTracking()
|
||||||
|
|
||||||
c.state.RLock()
|
c.state.RLock()
|
||||||
motd = c.state.motd
|
motd := c.state.motd
|
||||||
c.state.RUnlock()
|
c.state.RUnlock()
|
||||||
|
|
||||||
return motd
|
return motd
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lag is the latency between the server and the client. This is measured by
|
// Latency is the latency between the server and the client. This is measured
|
||||||
// determining the difference in time between when we ping the server, and
|
// by determining the difference in time between when we ping the server, and
|
||||||
// when we receive a pong.
|
// when we receive a pong.
|
||||||
func (c *Client) Lag() time.Duration {
|
func (c *Client) Latency() time.Duration {
|
||||||
c.mu.RLock()
|
c.mu.RLock()
|
||||||
c.conn.mu.RLock()
|
c.conn.mu.RLock()
|
||||||
delta := c.conn.lastPong.Sub(c.conn.lastPing)
|
delta := c.conn.lastPong.Sub(c.conn.lastPing)
|
||||||
|
10
vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
generated
vendored
10
vendor/github.com/lrstanley/girc/cmdhandler/cmd.go
generated
vendored
@ -12,8 +12,9 @@ import (
|
|||||||
|
|
||||||
// Input is a wrapper for events, based around private messages.
|
// Input is a wrapper for events, based around private messages.
|
||||||
type Input struct {
|
type Input struct {
|
||||||
Origin *girc.Event
|
Origin *girc.Event
|
||||||
Args []string
|
Args []string
|
||||||
|
RawArgs string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command is an IRC command, supporting aliases, help documentation and easy
|
// Command is an IRC command, supporting aliases, help documentation and easy
|
||||||
@ -189,8 +190,9 @@ func (ch *CmdHandler) Execute(client *girc.Client, event girc.Event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
in := &Input{
|
in := &Input{
|
||||||
Origin: &event,
|
Origin: &event,
|
||||||
Args: args,
|
Args: args,
|
||||||
|
RawArgs: parsed[2],
|
||||||
}
|
}
|
||||||
|
|
||||||
go cmd.Fn(client, in)
|
go cmd.Fn(client, in)
|
||||||
|
261
vendor/github.com/lrstanley/girc/commands.go
generated
vendored
261
vendor/github.com/lrstanley/girc/commands.go
generated
vendored
@ -16,18 +16,13 @@ type Commands struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Nick changes the client nickname.
|
// Nick changes the client nickname.
|
||||||
func (cmd *Commands) Nick(name string) error {
|
func (cmd *Commands) Nick(name string) {
|
||||||
if !IsValidNick(name) {
|
|
||||||
return &ErrInvalidTarget{Target: name}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: NICK, Params: []string{name}})
|
cmd.c.Send(&Event{Command: NICK, Params: []string{name}})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join attempts to enter a list of IRC channels, at bulk if possible to
|
// Join attempts to enter a list of IRC channels, at bulk if possible to
|
||||||
// prevent sending extensive JOIN commands.
|
// prevent sending extensive JOIN commands.
|
||||||
func (cmd *Commands) Join(channels ...string) error {
|
func (cmd *Commands) Join(channels ...string) {
|
||||||
// We can join multiple channels at once, however we need to ensure that
|
// We can join multiple channels at once, however we need to ensure that
|
||||||
// we are not exceeding the line length. (see maxLength)
|
// we are not exceeding the line length. (see maxLength)
|
||||||
max := maxLength - len(JOIN) - 1
|
max := maxLength - len(JOIN) - 1
|
||||||
@ -35,10 +30,6 @@ func (cmd *Commands) Join(channels ...string) error {
|
|||||||
var buffer string
|
var buffer string
|
||||||
|
|
||||||
for i := 0; i < len(channels); i++ {
|
for i := 0; i < len(channels); i++ {
|
||||||
if !IsValidChannel(channels[i]) {
|
|
||||||
return &ErrInvalidTarget{Target: channels[i]}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(buffer+","+channels[i]) > max {
|
if len(buffer+","+channels[i]) > max {
|
||||||
cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
||||||
buffer = ""
|
buffer = ""
|
||||||
@ -53,91 +44,74 @@ func (cmd *Commands) Join(channels ...string) error {
|
|||||||
|
|
||||||
if i == len(channels)-1 {
|
if i == len(channels)-1 {
|
||||||
cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
cmd.c.Send(&Event{Command: JOIN, Params: []string{buffer}})
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// JoinKey attempts to enter an IRC channel with a password.
|
// JoinKey attempts to enter an IRC channel with a password.
|
||||||
func (cmd *Commands) JoinKey(channel, password string) error {
|
func (cmd *Commands) JoinKey(channel, password string) {
|
||||||
if !IsValidChannel(channel) {
|
|
||||||
return &ErrInvalidTarget{Target: channel}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
|
cmd.c.Send(&Event{Command: JOIN, Params: []string{channel, password}})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part leaves an IRC channel.
|
// Part leaves an IRC channel.
|
||||||
func (cmd *Commands) Part(channel, message string) error {
|
func (cmd *Commands) Part(channels ...string) {
|
||||||
if !IsValidChannel(channel) {
|
for i := 0; i < len(channels); i++ {
|
||||||
return &ErrInvalidTarget{Target: channel}
|
cmd.c.Send(&Event{Command: PART, Params: []string{channels[i]}})
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PartMessage leaves an IRC channel with a specified leave message.
|
// PartMessage leaves an IRC channel with a specified leave message.
|
||||||
func (cmd *Commands) PartMessage(channel, message string) error {
|
func (cmd *Commands) PartMessage(channel, message string) {
|
||||||
if !IsValidChannel(channel) {
|
cmd.c.Send(&Event{Command: PART, Params: []string{channel}, Trailing: message, EmptyTrailing: true})
|
||||||
return &ErrInvalidTarget{Target: channel}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: JOIN, Params: []string{channel}, Trailing: message})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCTCP sends a CTCP request to target. Note that this method uses
|
// SendCTCP sends a CTCP request to target. Note that this method uses
|
||||||
// PRIVMSG specifically.
|
// PRIVMSG specifically. ctcpType is the CTCP command, e.g. "FINGER", "TIME",
|
||||||
func (cmd *Commands) SendCTCP(target, ctcpType, message string) error {
|
// "VERSION", etc.
|
||||||
|
func (cmd *Commands) SendCTCP(target, ctcpType, message string) {
|
||||||
out := encodeCTCPRaw(ctcpType, message)
|
out := encodeCTCPRaw(ctcpType, message)
|
||||||
if out == "" {
|
if out == "" {
|
||||||
return errors.New("invalid CTCP")
|
panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.Message(target, out)
|
cmd.Message(target, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCTCPf sends a CTCP request to target using a specific format. Note that
|
// SendCTCPf sends a CTCP request to target using a specific format. Note that
|
||||||
// this method uses PRIVMSG specifically.
|
// this method uses PRIVMSG specifically. ctcpType is the CTCP command, e.g.
|
||||||
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) error {
|
// "FINGER", "TIME", "VERSION", etc.
|
||||||
return cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
|
func (cmd *Commands) SendCTCPf(target, ctcpType, format string, a ...interface{}) {
|
||||||
|
cmd.SendCTCP(target, ctcpType, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCTCPReplyf sends a CTCP response to target using a specific format.
|
// SendCTCPReplyf sends a CTCP response to target using a specific format.
|
||||||
// Note that this method uses NOTICE specifically.
|
// Note that this method uses NOTICE specifically. ctcpType is the CTCP
|
||||||
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) error {
|
// command, e.g. "FINGER", "TIME", "VERSION", etc.
|
||||||
return cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
|
func (cmd *Commands) SendCTCPReplyf(target, ctcpType, format string, a ...interface{}) {
|
||||||
|
cmd.SendCTCPReply(target, ctcpType, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendCTCPReply sends a CTCP response to target. Note that this method uses
|
// SendCTCPReply sends a CTCP response to target. Note that this method uses
|
||||||
// NOTICE specifically.
|
// NOTICE specifically.
|
||||||
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) error {
|
func (cmd *Commands) SendCTCPReply(target, ctcpType, message string) {
|
||||||
out := encodeCTCPRaw(ctcpType, message)
|
out := encodeCTCPRaw(ctcpType, message)
|
||||||
if out == "" {
|
if out == "" {
|
||||||
return errors.New("invalid CTCP")
|
panic(fmt.Sprintf("invalid CTCP: %s -> %s: %s", target, ctcpType, message))
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.Notice(target, out)
|
cmd.Notice(target, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Message sends a PRIVMSG to target (either channel, service, or user).
|
// Message sends a PRIVMSG to target (either channel, service, or user).
|
||||||
func (cmd *Commands) Message(target, message string) error {
|
func (cmd *Commands) Message(target, message string) {
|
||||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message, EmptyTrailing: true})
|
||||||
return &ErrInvalidTarget{Target: target}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: PRIVMSG, Params: []string{target}, Trailing: message})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Messagef sends a formated PRIVMSG to target (either channel, service, or
|
// Messagef sends a formated PRIVMSG to target (either channel, service, or
|
||||||
// user).
|
// user).
|
||||||
func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
|
func (cmd *Commands) Messagef(target, format string, a ...interface{}) {
|
||||||
return cmd.Message(target, fmt.Sprintf(format, a...))
|
cmd.Message(target, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrInvalidSource is returned when a method needs to know the origin of an
|
// ErrInvalidSource is returned when a method needs to know the origin of an
|
||||||
@ -146,94 +120,95 @@ func (cmd *Commands) Messagef(target, format string, a ...interface{}) error {
|
|||||||
var ErrInvalidSource = errors.New("event has nil or invalid source address")
|
var ErrInvalidSource = errors.New("event has nil or invalid source address")
|
||||||
|
|
||||||
// Reply sends a reply to channel or user, based on where the supplied event
|
// Reply sends a reply to channel or user, based on where the supplied event
|
||||||
// originated from. See also ReplyTo().
|
// originated from. See also ReplyTo(). Panics if the incoming event has no
|
||||||
func (cmd *Commands) Reply(event Event, message string) error {
|
// source.
|
||||||
|
func (cmd *Commands) Reply(event Event, message string) {
|
||||||
if event.Source == nil {
|
if event.Source == nil {
|
||||||
return ErrInvalidSource
|
panic(ErrInvalidSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
|
if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
|
||||||
return cmd.Message(event.Params[0], message)
|
cmd.Message(event.Params[0], message)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.Message(event.Source.Name, message)
|
cmd.Message(event.Source.Name, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replyf sends a reply to channel or user with a format string, based on
|
// Replyf sends a reply to channel or user with a format string, based on
|
||||||
// where the supplied event originated from. See also ReplyTof().
|
// where the supplied event originated from. See also ReplyTof(). Panics if
|
||||||
func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) error {
|
// the incoming event has no source.
|
||||||
return cmd.Reply(event, fmt.Sprintf(format, a...))
|
func (cmd *Commands) Replyf(event Event, format string, a ...interface{}) {
|
||||||
|
cmd.Reply(event, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyTo sends a reply to a channel or user, based on where the supplied
|
// ReplyTo sends a reply to a channel or user, based on where the supplied
|
||||||
// event originated from. ReplyTo(), when originating from a channel will
|
// event originated from. ReplyTo(), when originating from a channel will
|
||||||
// default to replying with "<user>, <message>". See also Reply().
|
// default to replying with "<user>, <message>". See also Reply(). Panics if
|
||||||
func (cmd *Commands) ReplyTo(event Event, message string) error {
|
// the incoming event has no source.
|
||||||
|
func (cmd *Commands) ReplyTo(event Event, message string) {
|
||||||
if event.Source == nil {
|
if event.Source == nil {
|
||||||
return ErrInvalidSource
|
panic(ErrInvalidSource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
|
if len(event.Params) > 0 && IsValidChannel(event.Params[0]) {
|
||||||
return cmd.Message(event.Params[0], event.Source.Name+", "+message)
|
cmd.Message(event.Params[0], event.Source.Name+", "+message)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return cmd.Message(event.Source.Name, message)
|
cmd.Message(event.Source.Name, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReplyTof sends a reply to a channel or user with a format string, based
|
// ReplyTof sends a reply to a channel or user with a format string, based
|
||||||
// on where the supplied event originated from. ReplyTo(), when originating
|
// on where the supplied event originated from. ReplyTo(), when originating
|
||||||
// from a channel will default to replying with "<user>, <message>". See
|
// from a channel will default to replying with "<user>, <message>". See
|
||||||
// also Replyf().
|
// also Replyf(). Panics if the incoming event has no source.
|
||||||
func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) error {
|
func (cmd *Commands) ReplyTof(event Event, format string, a ...interface{}) {
|
||||||
return cmd.ReplyTo(event, fmt.Sprintf(format, a...))
|
cmd.ReplyTo(event, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action sends a PRIVMSG ACTION (/me) to target (either channel, service,
|
// Action sends a PRIVMSG ACTION (/me) to target (either channel, service,
|
||||||
// or user).
|
// or user).
|
||||||
func (cmd *Commands) Action(target, message string) error {
|
func (cmd *Commands) Action(target, message string) {
|
||||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
|
||||||
return &ErrInvalidTarget{Target: target}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{
|
cmd.c.Send(&Event{
|
||||||
Command: PRIVMSG,
|
Command: PRIVMSG,
|
||||||
Params: []string{target},
|
Params: []string{target},
|
||||||
Trailing: fmt.Sprintf("\001ACTION %s\001", message),
|
Trailing: fmt.Sprintf("\001ACTION %s\001", message),
|
||||||
})
|
})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel,
|
// Actionf sends a formated PRIVMSG ACTION (/me) to target (either channel,
|
||||||
// service, or user).
|
// service, or user).
|
||||||
func (cmd *Commands) Actionf(target, format string, a ...interface{}) error {
|
func (cmd *Commands) Actionf(target, format string, a ...interface{}) {
|
||||||
return cmd.Action(target, fmt.Sprintf(format, a...))
|
cmd.Action(target, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notice sends a NOTICE to target (either channel, service, or user).
|
// Notice sends a NOTICE to target (either channel, service, or user).
|
||||||
func (cmd *Commands) Notice(target, message string) error {
|
func (cmd *Commands) Notice(target, message string) {
|
||||||
if !IsValidNick(target) && !IsValidChannel(target) {
|
cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message, EmptyTrailing: true})
|
||||||
return &ErrInvalidTarget{Target: target}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: NOTICE, Params: []string{target}, Trailing: message})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Noticef sends a formated NOTICE to target (either channel, service, or
|
// Noticef sends a formated NOTICE to target (either channel, service, or
|
||||||
// user).
|
// user).
|
||||||
func (cmd *Commands) Noticef(target, format string, a ...interface{}) error {
|
func (cmd *Commands) Noticef(target, format string, a ...interface{}) {
|
||||||
return cmd.Notice(target, fmt.Sprintf(format, a...))
|
cmd.Notice(target, fmt.Sprintf(format, a...))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRaw sends a raw string back to the server, without carriage returns
|
// SendRaw sends a raw string (or multiple) to the server, without carriage
|
||||||
// or newlines.
|
// returns or newlines. Returns an error if one of the raw strings cannot be
|
||||||
func (cmd *Commands) SendRaw(raw string) error {
|
// properly parsed.
|
||||||
e := ParseEvent(raw)
|
func (cmd *Commands) SendRaw(raw ...string) error {
|
||||||
if e == nil {
|
var event *Event
|
||||||
return errors.New("invalid event: " + raw)
|
|
||||||
|
for i := 0; i < len(raw); i++ {
|
||||||
|
event = ParseEvent(raw[i])
|
||||||
|
if event == nil {
|
||||||
|
return errors.New("invalid event: " + raw[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.c.Send(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.c.Send(e)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,31 +221,26 @@ func (cmd *Commands) SendRawf(format string, a ...interface{}) error {
|
|||||||
// Topic sets the topic of channel to message. Does not verify the length
|
// Topic sets the topic of channel to message. Does not verify the length
|
||||||
// of the topic.
|
// of the topic.
|
||||||
func (cmd *Commands) Topic(channel, message string) {
|
func (cmd *Commands) Topic(channel, message string) {
|
||||||
cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message})
|
cmd.c.Send(&Event{Command: TOPIC, Params: []string{channel}, Trailing: message, EmptyTrailing: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Who sends a WHO query to the server, which will attempt WHOX by default.
|
// Who sends a WHO query to the server, which will attempt WHOX by default.
|
||||||
// See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This
|
// See http://faerion.sourceforge.net/doc/irc/whox.var for more details. This
|
||||||
// sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with
|
// sends "%tcuhnr,2" per default. Do not use "1" as this will conflict with
|
||||||
// girc's builtin tracking functionality.
|
// girc's builtin tracking functionality.
|
||||||
func (cmd *Commands) Who(target string) error {
|
func (cmd *Commands) Who(users ...string) {
|
||||||
if !IsValidNick(target) && !IsValidChannel(target) && !IsValidUser(target) {
|
for i := 0; i < len(users); i++ {
|
||||||
return &ErrInvalidTarget{Target: target}
|
cmd.c.Send(&Event{Command: WHO, Params: []string{users[i], "%tcuhnr,2"}})
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: WHO, Params: []string{target, "%tcuhnr,2"}})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whois sends a WHOIS query to the server, targeted at a specific user.
|
// Whois sends a WHOIS query to the server, targeted at a specific user (or
|
||||||
// as WHOIS is a bit slower, you may want to use WHO for brief user info.
|
// set of users). As WHOIS is a bit slower, you may want to use WHO for brief
|
||||||
func (cmd *Commands) Whois(nick string) error {
|
// user info.
|
||||||
if !IsValidNick(nick) {
|
func (cmd *Commands) Whois(users ...string) {
|
||||||
return &ErrInvalidTarget{Target: nick}
|
for i := 0; i < len(users); i++ {
|
||||||
|
cmd.c.Send(&Event{Command: WHOIS, Params: []string{users[i]}})
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: WHOIS, Params: []string{nick}})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ping sends a PING query to the server, with a specific identifier that
|
// Ping sends a PING query to the server, with a specific identifier that
|
||||||
@ -294,36 +264,40 @@ func (cmd *Commands) Oper(user, pass string) {
|
|||||||
// Kick sends a KICK query to the server, attempting to kick nick from
|
// Kick sends a KICK query to the server, attempting to kick nick from
|
||||||
// channel, with reason. If reason is blank, one will not be sent to the
|
// channel, with reason. If reason is blank, one will not be sent to the
|
||||||
// server.
|
// server.
|
||||||
func (cmd *Commands) Kick(channel, nick, reason string) error {
|
func (cmd *Commands) Kick(channel, user, reason string) {
|
||||||
if !IsValidChannel(channel) {
|
|
||||||
return &ErrInvalidTarget{Target: channel}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !IsValidNick(nick) {
|
|
||||||
return &ErrInvalidTarget{Target: nick}
|
|
||||||
}
|
|
||||||
|
|
||||||
if reason != "" {
|
if reason != "" {
|
||||||
cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}, Trailing: reason})
|
cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}, Trailing: reason, EmptyTrailing: true})
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: KICK, Params: []string{channel, nick}})
|
cmd.c.Send(&Event{Command: KICK, Params: []string{channel, user}})
|
||||||
return nil
|
}
|
||||||
|
|
||||||
|
// Ban adds the +b mode on the given mask on a channel.
|
||||||
|
func (cmd *Commands) Ban(channel, mask string) {
|
||||||
|
cmd.Mode(channel, "+b", mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unban removes the +b mode on the given mask on a channel.
|
||||||
|
func (cmd *Commands) Unban(channel, mask string) {
|
||||||
|
cmd.Mode(channel, "-b", mask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode sends a mode change to the server which should be applied to target
|
||||||
|
// (usually a channel or user), along with a set of modes (generally "+m",
|
||||||
|
// "+mmmm", or "-m", where "m" is the mode you want to change). Params is only
|
||||||
|
// needed if the mode change requires a parameter (ban or invite-only exclude.)
|
||||||
|
func (cmd *Commands) Mode(target, modes string, params ...string) {
|
||||||
|
out := []string{target, modes}
|
||||||
|
out = append(out, params...)
|
||||||
|
|
||||||
|
cmd.c.Send(&Event{Command: MODE, Params: out})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invite sends a INVITE query to the server, to invite nick to channel.
|
// Invite sends a INVITE query to the server, to invite nick to channel.
|
||||||
func (cmd *Commands) Invite(channel, nick string) error {
|
func (cmd *Commands) Invite(channel string, users ...string) {
|
||||||
if !IsValidChannel(channel) {
|
for i := 0; i < len(users); i++ {
|
||||||
return &ErrInvalidTarget{Target: channel}
|
cmd.c.Send(&Event{Command: INVITE, Params: []string{users[i], channel}})
|
||||||
}
|
}
|
||||||
|
|
||||||
if !IsValidNick(nick) {
|
|
||||||
return &ErrInvalidTarget{Target: nick}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: INVITE, Params: []string{nick, channel}})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Away sends a AWAY query to the server, suggesting that the client is no
|
// Away sends a AWAY query to the server, suggesting that the client is no
|
||||||
@ -348,10 +322,10 @@ func (cmd *Commands) Back() {
|
|||||||
// Supports multiple channels at once, in hopes it will reduce extensive
|
// Supports multiple channels at once, in hopes it will reduce extensive
|
||||||
// LIST queries to the server. Supply no channels to run a list against the
|
// LIST queries to the server. Supply no channels to run a list against the
|
||||||
// entire server (warning, that may mean LOTS of channels!)
|
// entire server (warning, that may mean LOTS of channels!)
|
||||||
func (cmd *Commands) List(channels ...string) error {
|
func (cmd *Commands) List(channels ...string) {
|
||||||
if len(channels) == 0 {
|
if len(channels) == 0 {
|
||||||
cmd.c.Send(&Event{Command: LIST})
|
cmd.c.Send(&Event{Command: LIST})
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can LIST multiple channels at once, however we need to ensure that
|
// We can LIST multiple channels at once, however we need to ensure that
|
||||||
@ -361,10 +335,6 @@ func (cmd *Commands) List(channels ...string) error {
|
|||||||
var buffer string
|
var buffer string
|
||||||
|
|
||||||
for i := 0; i < len(channels); i++ {
|
for i := 0; i < len(channels); i++ {
|
||||||
if !IsValidChannel(channels[i]) {
|
|
||||||
return &ErrInvalidTarget{Target: channels[i]}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(buffer+","+channels[i]) > max {
|
if len(buffer+","+channels[i]) > max {
|
||||||
cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
||||||
buffer = ""
|
buffer = ""
|
||||||
@ -379,20 +349,13 @@ func (cmd *Commands) List(channels ...string) error {
|
|||||||
|
|
||||||
if i == len(channels)-1 {
|
if i == len(channels)-1 {
|
||||||
cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
cmd.c.Send(&Event{Command: LIST, Params: []string{buffer}})
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whowas sends a WHOWAS query to the server. amount is the amount of results
|
// Whowas sends a WHOWAS query to the server. amount is the amount of results
|
||||||
// you want back.
|
// you want back.
|
||||||
func (cmd *Commands) Whowas(nick string, amount int) error {
|
func (cmd *Commands) Whowas(user string, amount int) {
|
||||||
if !IsValidNick(nick) {
|
cmd.c.Send(&Event{Command: WHOWAS, Params: []string{user, string(amount)}})
|
||||||
return &ErrInvalidTarget{Target: nick}
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd.c.Send(&Event{Command: WHOWAS, Params: []string{nick, string(amount)}})
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
6
vendor/github.com/lrstanley/girc/ctcp.go
generated
vendored
6
vendor/github.com/lrstanley/girc/ctcp.go
generated
vendored
@ -58,7 +58,7 @@ func decodeCTCP(e *Event) *CTCPEvent {
|
|||||||
if s < 0 {
|
if s < 0 {
|
||||||
for i := 0; i < len(text); i++ {
|
for i := 0; i < len(text); i++ {
|
||||||
// Check for A-Z, 0-9.
|
// Check for A-Z, 0-9.
|
||||||
if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
|
if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ func decodeCTCP(e *Event) *CTCPEvent {
|
|||||||
// Loop through checking the tag first.
|
// Loop through checking the tag first.
|
||||||
for i := 0; i < s; i++ {
|
for i := 0; i < s; i++ {
|
||||||
// Check for A-Z, 0-9.
|
// Check for A-Z, 0-9.
|
||||||
if (text[i] < 0x41 || text[i] > 0x5A) && (text[i] < 0x30 || text[i] > 0x39) {
|
if (text[i] < 'A' || text[i] > 'Z') && (text[i] < '0' || text[i] > '9') {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ func (c *CTCP) parseCMD(cmd string) string {
|
|||||||
|
|
||||||
for i := 0; i < len(cmd); i++ {
|
for i := 0; i < len(cmd); i++ {
|
||||||
// Check for A-Z, 0-9.
|
// Check for A-Z, 0-9.
|
||||||
if (cmd[i] < 0x41 || cmd[i] > 0x5A) && (cmd[i] < 0x30 || cmd[i] > 0x39) {
|
if (cmd[i] < 'A' || cmd[i] > 'Z') && (cmd[i] < '0' || cmd[i] > '9') {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
vendor/github.com/lrstanley/girc/event.go
generated
vendored
12
vendor/github.com/lrstanley/girc/event.go
generated
vendored
@ -11,8 +11,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
eventSpace byte = 0x20 // Separator.
|
eventSpace byte = ' ' // Separator.
|
||||||
maxLength = 510 // Maximum length is 510 (2 for line endings).
|
maxLength = 510 // Maximum length is 510 (2 for line endings).
|
||||||
)
|
)
|
||||||
|
|
||||||
// cutCRFunc is used to trim CR characters from prefixes/messages.
|
// cutCRFunc is used to trim CR characters from prefixes/messages.
|
||||||
@ -256,7 +256,7 @@ func (e *Event) Bytes() []byte {
|
|||||||
|
|
||||||
// Strip newlines and carriage returns.
|
// Strip newlines and carriage returns.
|
||||||
for i := 0; i < len(out); i++ {
|
for i := 0; i < len(out); i++ {
|
||||||
if out[i] == 0x0A || out[i] == 0x0D {
|
if out[i] == '\n' || out[i] == '\r' {
|
||||||
out = append(out[:i], out[i+1:]...)
|
out = append(out[:i], out[i+1:]...)
|
||||||
i-- // Decrease the index so we can pick up where we left off.
|
i-- // Decrease the index so we can pick up where we left off.
|
||||||
}
|
}
|
||||||
@ -432,9 +432,9 @@ func (e *Event) StripAction() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
messagePrefix byte = 0x3A // ":" -- prefix or last argument
|
messagePrefix byte = ':' // Prefix or last argument.
|
||||||
prefixIdent byte = 0x21 // "!" -- username
|
prefixIdent byte = '!' // Username.
|
||||||
prefixHost byte = 0x40 // "@" -- hostname
|
prefixHost byte = '@' // Hostname.
|
||||||
)
|
)
|
||||||
|
|
||||||
// Source represents the sender of an IRC event, see RFC1459 section 2.3.1.
|
// Source represents the sender of an IRC event, see RFC1459 section 2.3.1.
|
||||||
|
46
vendor/github.com/lrstanley/girc/format.go
generated
vendored
46
vendor/github.com/lrstanley/girc/format.go
generated
vendored
@ -12,8 +12,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
fmtOpenChar = 0x7B // {
|
fmtOpenChar = '{'
|
||||||
fmtCloseChar = 0x7D // }
|
fmtCloseChar = '}'
|
||||||
)
|
)
|
||||||
|
|
||||||
var fmtColors = map[string]int{
|
var fmtColors = map[string]int{
|
||||||
@ -113,7 +113,7 @@ func Fmt(text string) string {
|
|||||||
|
|
||||||
if last > -1 {
|
if last > -1 {
|
||||||
// A-Z, a-z, and ","
|
// A-Z, a-z, and ","
|
||||||
if text[i] != 0x2c && (text[i] <= 0x41 || text[i] >= 0x5a) && (text[i] <= 0x61 || text[i] >= 0x7a) {
|
if text[i] != ',' && (text[i] <= 'A' || text[i] >= 'Z') && (text[i] <= 'a' || text[i] >= 'z') {
|
||||||
last = -1
|
last = -1
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -127,10 +127,10 @@ func Fmt(text string) string {
|
|||||||
// See Fmt() for more information.
|
// See Fmt() for more information.
|
||||||
func TrimFmt(text string) string {
|
func TrimFmt(text string) string {
|
||||||
for color := range fmtColors {
|
for color := range fmtColors {
|
||||||
text = strings.Replace(text, "{"+color+"}", "", -1)
|
text = strings.Replace(text, string(fmtOpenChar)+color+string(fmtCloseChar), "", -1)
|
||||||
}
|
}
|
||||||
for code := range fmtCodes {
|
for code := range fmtCodes {
|
||||||
text = strings.Replace(text, "{"+code+"}", "", -1)
|
text = strings.Replace(text, string(fmtOpenChar)+code+string(fmtCloseChar), "", -1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return text
|
return text
|
||||||
@ -175,9 +175,10 @@ func IsValidChannel(channel string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// #, +, !<channelid>, or &
|
// #, +, !<channelid>, ~, or &
|
||||||
// Including "*" in the prefix list, as this is commonly used (e.g. ZNC)
|
// Including "*" and "~" in the prefix list, as these are commonly used
|
||||||
if bytes.IndexByte([]byte{0x21, 0x23, 0x26, 0x2A, 0x2B}, channel[0]) == -1 {
|
// (e.g. ZNC.)
|
||||||
|
if bytes.IndexByte([]byte{'!', '#', '&', '*', '~', '+'}, channel[0]) == -1 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,14 +187,14 @@ func IsValidChannel(channel string) bool {
|
|||||||
// 1 (prefix) + 5 (id) + 1 (+, channel name)
|
// 1 (prefix) + 5 (id) + 1 (+, channel name)
|
||||||
// On some networks, this may be extended with ISUPPORT capabilities,
|
// On some networks, this may be extended with ISUPPORT capabilities,
|
||||||
// however this is extremely uncommon.
|
// however this is extremely uncommon.
|
||||||
if channel[0] == 0x21 {
|
if channel[0] == '!' {
|
||||||
if len(channel) < 7 {
|
if len(channel) < 7 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for valid ID
|
// check for valid ID
|
||||||
for i := 1; i < 6; i++ {
|
for i := 1; i < 6; i++ {
|
||||||
if (channel[i] < 0x30 || channel[i] > 0x39) && (channel[i] < 0x41 || channel[i] > 0x5A) {
|
if (channel[i] < '0' || channel[i] > '9') && (channel[i] < 'A' || channel[i] > 'Z') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,17 +223,15 @@ func IsValidNick(nick string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
nick = ToRFC1459(nick)
|
|
||||||
|
|
||||||
// Check the first index. Some characters aren't allowed for the first
|
// Check the first index. Some characters aren't allowed for the first
|
||||||
// index of an IRC nickname.
|
// index of an IRC nickname.
|
||||||
if nick[0] < 0x41 || nick[0] > 0x7D {
|
if (nick[0] < 'A' || nick[0] > '}') && nick[0] != '?' {
|
||||||
// a-z, A-Z, and _\[]{}^|
|
// a-z, A-Z, '_\[]{}^|', and '?' in the case of znc.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i < len(nick); i++ {
|
for i := 1; i < len(nick); i++ {
|
||||||
if (nick[i] < 0x41 || nick[i] > 0x7D) && (nick[i] < 0x30 || nick[i] > 0x39) && nick[i] != 0x2D {
|
if (nick[i] < 'A' || nick[i] > '}') && (nick[i] < '0' || nick[i] > '9') && nick[i] != '-' {
|
||||||
// a-z, A-Z, 0-9, -, and _\[]{}^|
|
// a-z, A-Z, 0-9, -, and _\[]{}^|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -261,10 +260,8 @@ func IsValidUser(name string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
name = ToRFC1459(name)
|
|
||||||
|
|
||||||
// "~" is prepended (commonly) if there was no ident server response.
|
// "~" is prepended (commonly) if there was no ident server response.
|
||||||
if name[0] == 0x7E {
|
if name[0] == '~' {
|
||||||
// Means name only contained "~".
|
// Means name only contained "~".
|
||||||
if len(name) < 2 {
|
if len(name) < 2 {
|
||||||
return false
|
return false
|
||||||
@ -274,12 +271,12 @@ func IsValidUser(name string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check to see if the first index is alphanumeric.
|
// Check to see if the first index is alphanumeric.
|
||||||
if (name[0] < 0x41 || name[0] > 0x4A) && (name[0] < 0x61 || name[0] > 0x7A) && (name[0] < 0x30 || name[0] > 0x39) {
|
if (name[0] < 'A' || name[0] > 'J') && (name[0] < 'a' || name[0] > 'z') && (name[0] < '0' || name[0] > '9') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 1; i < len(name); i++ {
|
for i := 1; i < len(name); i++ {
|
||||||
if (name[i] < 0x41 || name[i] > 0x7D) && (name[i] < 0x30 || name[i] > 0x39) && name[i] != 0x2D && name[i] != 0x2E {
|
if (name[i] < 'A' || name[i] > '}') && (name[i] < '0' || name[i] > '9') && name[i] != '-' && name[i] != '.' {
|
||||||
// a-z, A-Z, 0-9, -, and _\[]{}^|
|
// a-z, A-Z, 0-9, -, and _\[]{}^|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -290,8 +287,13 @@ func IsValidUser(name string) bool {
|
|||||||
|
|
||||||
// ToRFC1459 converts a string to the stripped down conversion within RFC
|
// ToRFC1459 converts a string to the stripped down conversion within RFC
|
||||||
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
|
// 1459. This will do things like replace an "A" with an "a", "[]" with "{}",
|
||||||
// and so forth. Useful to compare two nicknames or channels.
|
// and so forth. Useful to compare two nicknames or channels. Note that this
|
||||||
func ToRFC1459(input string) (out string) {
|
// should not be used to normalize nicknames or similar, as this may convert
|
||||||
|
// valid input characters to non-rfc-valid characters. As such, it's main use
|
||||||
|
// is for comparing two nicks.
|
||||||
|
func ToRFC1459(input string) string {
|
||||||
|
var out string
|
||||||
|
|
||||||
for i := 0; i < len(input); i++ {
|
for i := 0; i < len(input); i++ {
|
||||||
if input[i] >= 65 && input[i] <= 94 {
|
if input[i] >= 65 && input[i] <= 94 {
|
||||||
out += string(rune(input[i]) + 32)
|
out += string(rune(input[i]) + 32)
|
||||||
|
131
vendor/github.com/lrstanley/girc/handler.go
generated
vendored
131
vendor/github.com/lrstanley/girc/handler.go
generated
vendored
@ -29,11 +29,12 @@ func (c *Client) RunHandlers(event *Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular wildcard handlers.
|
// Background handlers first.
|
||||||
c.Handlers.exec(ALL_EVENTS, c, event.Copy())
|
c.Handlers.exec(ALL_EVENTS, true, c, event.Copy())
|
||||||
|
c.Handlers.exec(event.Command, true, c, event.Copy())
|
||||||
|
|
||||||
// Then regular handlers.
|
c.Handlers.exec(ALL_EVENTS, false, c, event.Copy())
|
||||||
c.Handlers.exec(event.Command, c, event.Copy())
|
c.Handlers.exec(event.Command, false, c, event.Copy())
|
||||||
|
|
||||||
// Check if it's a CTCP.
|
// Check if it's a CTCP.
|
||||||
if ctcp := decodeCTCP(event.Copy()); ctcp != nil {
|
if ctcp := decodeCTCP(event.Copy()); ctcp != nil {
|
||||||
@ -144,7 +145,7 @@ func (c *Caller) cuid(cmd string, n int) (cuid, uid string) {
|
|||||||
// cuidToID allows easy mapping between a generated cuid and the caller
|
// cuidToID allows easy mapping between a generated cuid and the caller
|
||||||
// external/internal handler maps.
|
// external/internal handler maps.
|
||||||
func (c *Caller) cuidToID(input string) (cmd, uid string) {
|
func (c *Caller) cuidToID(input string) (cmd, uid string) {
|
||||||
i := strings.IndexByte(input, 0x3A)
|
i := strings.IndexByte(input, ':')
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
@ -160,9 +161,9 @@ type execStack struct {
|
|||||||
// exec executes all handlers pertaining to specified event. Internal first,
|
// exec executes all handlers pertaining to specified event. Internal first,
|
||||||
// then external.
|
// then external.
|
||||||
//
|
//
|
||||||
// Please note that there is no specific order/priority for which the
|
// Please note that there is no specific order/priority for which the handlers
|
||||||
// handler types themselves or the handlers are executed.
|
// are executed.
|
||||||
func (c *Caller) exec(command string, client *Client, event *Event) {
|
func (c *Caller) exec(command string, bg bool, client *Client, event *Event) {
|
||||||
// Build a stack of handlers which can be executed concurrently.
|
// Build a stack of handlers which can be executed concurrently.
|
||||||
var stack []execStack
|
var stack []execStack
|
||||||
|
|
||||||
@ -170,13 +171,21 @@ func (c *Caller) exec(command string, client *Client, event *Event) {
|
|||||||
// Get internal handlers first.
|
// Get internal handlers first.
|
||||||
if _, ok := c.internal[command]; ok {
|
if _, ok := c.internal[command]; ok {
|
||||||
for cuid := range c.internal[command] {
|
for cuid := range c.internal[command] {
|
||||||
|
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
stack = append(stack, execStack{c.internal[command][cuid], cuid})
|
stack = append(stack, execStack{c.internal[command][cuid], cuid})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aaand then external handlers.
|
// Then external handlers.
|
||||||
if _, ok := c.external[command]; ok {
|
if _, ok := c.external[command]; ok {
|
||||||
for cuid := range c.external[command] {
|
for cuid := range c.external[command] {
|
||||||
|
if (strings.HasSuffix(cuid, ":bg") && !bg) || (!strings.HasSuffix(cuid, ":bg") && bg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
stack = append(stack, execStack{c.external[command][cuid], cuid})
|
stack = append(stack, execStack{c.external[command][cuid], cuid})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -189,18 +198,29 @@ func (c *Caller) exec(command string, client *Client, event *Event) {
|
|||||||
wg.Add(len(stack))
|
wg.Add(len(stack))
|
||||||
for i := 0; i < len(stack); i++ {
|
for i := 0; i < len(stack); i++ {
|
||||||
go func(index int) {
|
go func(index int) {
|
||||||
c.debug.Printf("executing handler %s for event %s (%d of %d)", stack[index].cuid, command, index+1, len(stack))
|
defer wg.Done()
|
||||||
|
c.debug.Printf("[%d/%d] exec %s => %s", index+1, len(stack), stack[index].cuid, command)
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
// If they want to catch any panics, add to defer stack.
|
if bg {
|
||||||
|
go func() {
|
||||||
|
if client.Config.RecoverFunc != nil {
|
||||||
|
defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
stack[index].Execute(client, *event)
|
||||||
|
c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start))
|
||||||
|
}()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if client.Config.RecoverFunc != nil {
|
if client.Config.RecoverFunc != nil {
|
||||||
defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
|
defer recoverHandlerPanic(client, event, stack[index].cuid, 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
stack[index].Execute(client, *event)
|
stack[index].Execute(client, *event)
|
||||||
|
c.debug.Printf("[%d/%d] done %s == %s", index+1, len(stack), stack[index].cuid, time.Since(start))
|
||||||
c.debug.Printf("execution of %s took %s (%d of %d)", stack[index].cuid, time.Since(start), index+1, len(stack))
|
|
||||||
wg.Done()
|
|
||||||
}(i)
|
}(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -281,9 +301,9 @@ func (c *Caller) remove(cuid string) (success bool) {
|
|||||||
|
|
||||||
// sregister is much like Caller.register(), except that it safely locks
|
// sregister is much like Caller.register(), except that it safely locks
|
||||||
// the Caller mutex.
|
// the Caller mutex.
|
||||||
func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid string) {
|
func (c *Caller) sregister(internal, bg bool, cmd string, handler Handler) (cuid string) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
cuid = c.register(internal, cmd, handler)
|
cuid = c.register(internal, bg, cmd, handler)
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
|
|
||||||
return cuid
|
return cuid
|
||||||
@ -291,30 +311,34 @@ func (c *Caller) sregister(internal bool, cmd string, handler Handler) (cuid str
|
|||||||
|
|
||||||
// register will register a handler in the internal tracker. Unsafe (you
|
// register will register a handler in the internal tracker. Unsafe (you
|
||||||
// must lock c.mu yourself!)
|
// must lock c.mu yourself!)
|
||||||
func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid string) {
|
func (c *Caller) register(internal, bg bool, cmd string, handler Handler) (cuid string) {
|
||||||
var uid string
|
var uid string
|
||||||
|
|
||||||
cmd = strings.ToUpper(cmd)
|
cmd = strings.ToUpper(cmd)
|
||||||
|
|
||||||
|
cuid, uid = c.cuid(cmd, 20)
|
||||||
|
if bg {
|
||||||
|
uid += ":bg"
|
||||||
|
cuid += ":bg"
|
||||||
|
}
|
||||||
|
|
||||||
if internal {
|
if internal {
|
||||||
if _, ok := c.internal[cmd]; !ok {
|
if _, ok := c.internal[cmd]; !ok {
|
||||||
c.internal[cmd] = map[string]Handler{}
|
c.internal[cmd] = map[string]Handler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cuid, uid = c.cuid(cmd, 20)
|
|
||||||
c.internal[cmd][uid] = handler
|
c.internal[cmd][uid] = handler
|
||||||
} else {
|
} else {
|
||||||
if _, ok := c.external[cmd]; !ok {
|
if _, ok := c.external[cmd]; !ok {
|
||||||
c.external[cmd] = map[string]Handler{}
|
c.external[cmd] = map[string]Handler{}
|
||||||
}
|
}
|
||||||
|
|
||||||
cuid, uid = c.cuid(cmd, 20)
|
|
||||||
c.external[cmd][uid] = handler
|
c.external[cmd][uid] = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
_, file, line, _ := runtime.Caller(3)
|
_, file, line, _ := runtime.Caller(3)
|
||||||
|
|
||||||
c.debug.Printf("registering handler for %q with cuid %q (internal: %t) from: %s:%d", cmd, cuid, internal, file, line)
|
c.debug.Printf("reg %q => %s [int:%t bg:%t] %s:%d", uid, cmd, internal, bg, file, line)
|
||||||
|
|
||||||
return cuid
|
return cuid
|
||||||
}
|
}
|
||||||
@ -323,31 +347,20 @@ func (c *Caller) register(internal bool, cmd string, handler Handler) (cuid stri
|
|||||||
// given event. cuid is the handler uid which can be used to remove the
|
// given event. cuid is the handler uid which can be used to remove the
|
||||||
// handler with Caller.Remove().
|
// handler with Caller.Remove().
|
||||||
func (c *Caller) AddHandler(cmd string, handler Handler) (cuid string) {
|
func (c *Caller) AddHandler(cmd string, handler Handler) (cuid string) {
|
||||||
return c.sregister(false, cmd, handler)
|
return c.sregister(false, false, cmd, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add registers the handler function for the given event. cuid is the
|
// Add registers the handler function for the given event. cuid is the
|
||||||
// handler uid which can be used to remove the handler with Caller.Remove().
|
// handler uid which can be used to remove the handler with Caller.Remove().
|
||||||
func (c *Caller) Add(cmd string, handler func(client *Client, event Event)) (cuid string) {
|
func (c *Caller) Add(cmd string, handler func(client *Client, event Event)) (cuid string) {
|
||||||
return c.sregister(false, cmd, HandlerFunc(handler))
|
return c.sregister(false, false, cmd, HandlerFunc(handler))
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddBg registers the handler function for the given event and executes it
|
// AddBg registers the handler function for the given event and executes it
|
||||||
// in a go-routine. cuid is the handler uid which can be used to remove the
|
// in a go-routine. cuid is the handler uid which can be used to remove the
|
||||||
// handler with Caller.Remove().
|
// handler with Caller.Remove().
|
||||||
func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (cuid string) {
|
func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (cuid string) {
|
||||||
return c.sregister(false, cmd, HandlerFunc(func(client *Client, event Event) {
|
return c.sregister(false, true, cmd, HandlerFunc(handler))
|
||||||
// Setting up background-based handlers this way allows us to get
|
|
||||||
// clean call stacks for use with panic recovery.
|
|
||||||
go func() {
|
|
||||||
// If they want to catch any panics, add to defer stack.
|
|
||||||
if client.Config.RecoverFunc != nil {
|
|
||||||
defer recoverHandlerPanic(client, &event, "goroutine", 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
handler(client, event)
|
|
||||||
}()
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddTmp adds a "temporary" handler, which is good for one-time or few-time
|
// AddTmp adds a "temporary" handler, which is good for one-time or few-time
|
||||||
@ -361,47 +374,37 @@ func (c *Caller) AddBg(cmd string, handler func(client *Client, event Event)) (c
|
|||||||
//
|
//
|
||||||
// Additionally, AddTmp has a useful option, deadline. When set to greater
|
// Additionally, AddTmp has a useful option, deadline. When set to greater
|
||||||
// than 0, deadline will be the amount of time that passes before the handler
|
// than 0, deadline will be the amount of time that passes before the handler
|
||||||
// is removed from the stack, regardless if the handler returns true or not.
|
// is removed from the stack, regardless of if the handler returns true or not.
|
||||||
// This is useful in that it ensures that the handler is cleaned up if the
|
// This is useful in that it ensures that the handler is cleaned up if the
|
||||||
// server does not respond appropriately, or takes too long to respond.
|
// server does not respond appropriately, or takes too long to respond.
|
||||||
//
|
//
|
||||||
// Note that handlers supplied with AddTmp are executed in a goroutine to
|
// Note that handlers supplied with AddTmp are executed in a goroutine to
|
||||||
// ensure that they are not blocking other handlers. Additionally, use cuid
|
// ensure that they are not blocking other handlers. However, if you are
|
||||||
// with Caller.Remove() to prematurely remove the handler from the stack,
|
// creating a temporary handler from another handler, it should be a
|
||||||
// bypassing the timeout or waiting for the handler to return that it wants
|
// background handler.
|
||||||
// to be removed from the stack.
|
//
|
||||||
|
// Use cuid with Caller.Remove() to prematurely remove the handler from the
|
||||||
|
// stack, bypassing the timeout or waiting for the handler to return that it
|
||||||
|
// wants to be removed from the stack.
|
||||||
func (c *Caller) AddTmp(cmd string, deadline time.Duration, handler func(client *Client, event Event) bool) (cuid string, done chan struct{}) {
|
func (c *Caller) AddTmp(cmd string, deadline time.Duration, handler func(client *Client, event Event) bool) (cuid string, done chan struct{}) {
|
||||||
var uid string
|
|
||||||
cuid, uid = c.cuid(cmd, 20)
|
|
||||||
|
|
||||||
done = make(chan struct{})
|
done = make(chan struct{})
|
||||||
|
|
||||||
c.mu.Lock()
|
cuid = c.sregister(false, true, cmd, HandlerFunc(func(client *Client, event Event) {
|
||||||
if _, ok := c.external[cmd]; !ok {
|
remove := handler(client, event)
|
||||||
c.external[cmd] = map[string]Handler{}
|
if remove {
|
||||||
}
|
if ok := c.Remove(cuid); ok {
|
||||||
c.external[cmd][uid] = HandlerFunc(func(client *Client, event Event) {
|
close(done)
|
||||||
// Setting up background-based handlers this way allows us to get
|
|
||||||
// clean call stacks for use with panic recovery.
|
|
||||||
go func() {
|
|
||||||
// If they want to catch any panics, add to defer stack.
|
|
||||||
if client.Config.RecoverFunc != nil {
|
|
||||||
defer recoverHandlerPanic(client, &event, "tmp-goroutine", 3)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
remove := handler(client, event)
|
}))
|
||||||
if remove {
|
|
||||||
if ok := c.Remove(cuid); ok {
|
|
||||||
close(done)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
})
|
|
||||||
c.mu.Unlock()
|
|
||||||
|
|
||||||
if deadline > 0 {
|
if deadline > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
<-time.After(deadline)
|
select {
|
||||||
|
case <-time.After(deadline):
|
||||||
|
case <-done:
|
||||||
|
}
|
||||||
|
|
||||||
if ok := c.Remove(cuid); ok {
|
if ok := c.Remove(cuid); ok {
|
||||||
close(done)
|
close(done)
|
||||||
}
|
}
|
||||||
|
10
vendor/github.com/lrstanley/girc/modes.go
generated
vendored
10
vendor/github.com/lrstanley/girc/modes.go
generated
vendored
@ -206,11 +206,11 @@ func (c *CModes) Parse(flags string, args []string) (out []CMode) {
|
|||||||
var argCount int
|
var argCount int
|
||||||
|
|
||||||
for i := 0; i < len(flags); i++ {
|
for i := 0; i < len(flags); i++ {
|
||||||
if flags[i] == 0x2B {
|
if flags[i] == '+' {
|
||||||
add = true
|
add = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if flags[i] == 0x2D {
|
if flags[i] == '-' {
|
||||||
add = false
|
add = false
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -265,7 +265,7 @@ func IsValidChannelMode(raw string) bool {
|
|||||||
|
|
||||||
for i := 0; i < len(raw); i++ {
|
for i := 0; i < len(raw); i++ {
|
||||||
// Allowed are: ",", A-Z and a-z.
|
// Allowed are: ",", A-Z and a-z.
|
||||||
if raw[i] != 0x2C && (raw[i] < 0x41 || raw[i] > 0x5A) && (raw[i] < 0x61 || raw[i] > 0x7A) {
|
if raw[i] != ',' && (raw[i] < 'A' || raw[i] > 'Z') && (raw[i] < 'a' || raw[i] > 'z') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -279,7 +279,7 @@ func isValidUserPrefix(raw string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if raw[0] != 0x28 { // (.
|
if raw[0] != '(' {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ func isValidUserPrefix(raw string) bool {
|
|||||||
|
|
||||||
// Skip the first one as we know it's (.
|
// Skip the first one as we know it's (.
|
||||||
for i := 1; i < len(raw); i++ {
|
for i := 1; i < len(raw); i++ {
|
||||||
if raw[i] == 0x29 { // ).
|
if raw[i] == ')' {
|
||||||
passedKeys = true
|
passedKeys = true
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
90
vendor/github.com/matrix-org/gomatrix/client.go
generated
vendored
90
vendor/github.com/matrix-org/gomatrix/client.go
generated
vendored
@ -79,7 +79,7 @@ func (cli *Client) BuildBaseURL(urlPath ...string) string {
|
|||||||
return hsURL.String()
|
return hsURL.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildURLWithQuery builds a URL with query paramters in addition to the Client's homeserver/prefix/access_token set already.
|
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix/access_token set already.
|
||||||
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
|
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
|
||||||
u, _ := url.Parse(cli.BuildURL(urlPath...))
|
u, _ := url.Parse(cli.BuildURL(urlPath...))
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
@ -387,6 +387,20 @@ func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
|
||||||
|
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
|
||||||
|
urlPath := cli.BuildURL("profile", mxid, "displayname")
|
||||||
|
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
|
||||||
|
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
|
||||||
|
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
|
||||||
|
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
|
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
|
||||||
func (cli *Client) SetDisplayName(displayName string) (err error) {
|
func (cli *Client) SetDisplayName(displayName string) (err error) {
|
||||||
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
|
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
|
||||||
@ -450,6 +464,35 @@ func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
|
|||||||
TextMessage{"m.text", text})
|
TextMessage{"m.text", text})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendImage sends an m.room.message event into the given room with a msgtype of m.image
|
||||||
|
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
|
||||||
|
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
|
||||||
|
return cli.SendMessageEvent(roomID, "m.room.message",
|
||||||
|
ImageMessage{
|
||||||
|
MsgType: "m.image",
|
||||||
|
Body: body,
|
||||||
|
URL: url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
|
||||||
|
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
|
||||||
|
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
|
||||||
|
return cli.SendMessageEvent(roomID, "m.room.message",
|
||||||
|
VideoMessage{
|
||||||
|
MsgType: "m.video",
|
||||||
|
Body: body,
|
||||||
|
URL: url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
|
||||||
|
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
|
||||||
|
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
|
||||||
|
return cli.SendMessageEvent(roomID, "m.room.message",
|
||||||
|
TextMessage{"m.notice", text})
|
||||||
|
}
|
||||||
|
|
||||||
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
|
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
|
||||||
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
|
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
|
||||||
txnID := txnID()
|
txnID := txnID()
|
||||||
@ -518,6 +561,14 @@ func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanU
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
|
||||||
|
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
|
||||||
|
req := ReqTyping{Typing: typing, Timeout: timeout}
|
||||||
|
u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
|
||||||
|
_, err = cli.MakeRequest("PUT", u, req, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
|
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
|
||||||
// the HTTP response body, or return an error.
|
// the HTTP response body, or return an error.
|
||||||
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
|
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
|
||||||
@ -556,8 +607,15 @@ func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, co
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if res.StatusCode != 200 {
|
if res.StatusCode != 200 {
|
||||||
|
contents, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, HTTPError{
|
||||||
|
Message: "Upload request failed - Failed to read response body: " + err.Error(),
|
||||||
|
Code: res.StatusCode,
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil, HTTPError{
|
return nil, HTTPError{
|
||||||
Message: "Upload request failed",
|
Message: "Upload request failed: " + string(contents),
|
||||||
Code: res.StatusCode,
|
Code: res.StatusCode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -588,6 +646,34 @@ func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Messages returns a list of message and state events for a room. It uses
|
||||||
|
// pagination query parameters to paginate history in the room.
|
||||||
|
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
|
||||||
|
func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
|
||||||
|
query := map[string]string{
|
||||||
|
"from": from,
|
||||||
|
"dir": string(dir),
|
||||||
|
}
|
||||||
|
if to != "" {
|
||||||
|
query["to"] = to
|
||||||
|
}
|
||||||
|
if limit != 0 {
|
||||||
|
query["limit"] = strconv.Itoa(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
|
||||||
|
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TurnServer returns turn server details and credentials for the client to use when initiating calls.
|
||||||
|
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
|
||||||
|
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
|
||||||
|
urlPath := cli.BuildURL("voip", "turnServer")
|
||||||
|
_, err = cli.MakeRequest("GET", urlPath, nil, &resp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func txnID() string {
|
func txnID() string {
|
||||||
return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
|
return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
|
||||||
}
|
}
|
||||||
|
43
vendor/github.com/matrix-org/gomatrix/events.go
generated
vendored
43
vendor/github.com/matrix-org/gomatrix/events.go
generated
vendored
@ -7,13 +7,13 @@ import (
|
|||||||
|
|
||||||
// Event represents a single Matrix event.
|
// Event represents a single Matrix event.
|
||||||
type Event struct {
|
type Event struct {
|
||||||
StateKey string `json:"state_key"` // The state key for the event. Only present on State Events.
|
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
|
||||||
Sender string `json:"sender"` // The user ID of the sender of the event
|
Sender string `json:"sender"` // The user ID of the sender of the event
|
||||||
Type string `json:"type"` // The event type
|
Type string `json:"type"` // The event type
|
||||||
Timestamp int `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
|
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
|
||||||
ID string `json:"event_id"` // The unique ID of this event
|
ID string `json:"event_id"` // The unique ID of this event
|
||||||
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
|
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
|
||||||
Content map[string]interface{} `json:"content"` // The JSON content of the event.
|
Content map[string]interface{} `json:"content"` // The JSON content of the event.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Body returns the value of the "body" key in the event content if it is
|
// Body returns the value of the "body" key in the event content if it is
|
||||||
@ -44,12 +44,31 @@ type TextMessage struct {
|
|||||||
Body string `json:"body"`
|
Body string `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageInfo contains info about an image
|
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
|
||||||
type ImageInfo struct {
|
type ImageInfo struct {
|
||||||
Height uint `json:"h"`
|
Height uint `json:"h,omitempty"`
|
||||||
Width uint `json:"w"`
|
Width uint `json:"w,omitempty"`
|
||||||
Mimetype string `json:"mimetype"`
|
Mimetype string `json:"mimetype,omitempty"`
|
||||||
Size uint `json:"size"`
|
Size uint `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
|
||||||
|
type VideoInfo struct {
|
||||||
|
Mimetype string `json:"mimetype,omitempty"`
|
||||||
|
ThumbnailInfo ImageInfo `json:"thumbnail_info"`
|
||||||
|
ThumbnailURL string `json:"thumbnail_url,omitempty"`
|
||||||
|
Height uint `json:"h,omitempty"`
|
||||||
|
Width uint `json:"w,omitempty"`
|
||||||
|
Duration uint `json:"duration,omitempty"`
|
||||||
|
Size uint `json:"size,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
|
||||||
|
type VideoMessage struct {
|
||||||
|
MsgType string `json:"msgtype"`
|
||||||
|
Body string `json:"body"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Info VideoInfo `json:"info"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ImageMessage is an m.image event
|
// ImageMessage is an m.image event
|
||||||
|
43
vendor/github.com/matrix-org/gomatrix/filter.go
generated
vendored
Normal file
43
vendor/github.com/matrix-org/gomatrix/filter.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// Copyright 2017 Jan Christian Grünhage
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
package gomatrix
|
||||||
|
|
||||||
|
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
|
||||||
|
//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
|
||||||
|
type Filter struct {
|
||||||
|
AccountData FilterPart `json:"account_data,omitempty"`
|
||||||
|
EventFields []string `json:"event_fields,omitempty"`
|
||||||
|
EventFormat string `json:"event_format,omitempty"`
|
||||||
|
Presence FilterPart `json:"presence,omitempty"`
|
||||||
|
Room struct {
|
||||||
|
AccountData FilterPart `json:"account_data,omitempty"`
|
||||||
|
Ephemeral FilterPart `json:"ephemeral,omitempty"`
|
||||||
|
IncludeLeave bool `json:"include_leave,omitempty"`
|
||||||
|
NotRooms []string `json:"not_rooms,omitempty"`
|
||||||
|
Rooms []string `json:"rooms,omitempty"`
|
||||||
|
State FilterPart `json:"state,omitempty"`
|
||||||
|
Timeline FilterPart `json:"timeline,omitempty"`
|
||||||
|
} `json:"room,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FilterPart struct {
|
||||||
|
NotRooms []string `json:"not_rooms,omitempty"`
|
||||||
|
Rooms []string `json:"rooms,omitempty"`
|
||||||
|
Limit *int `json:"limit,omitempty"`
|
||||||
|
NotSenders []string `json:"not_senders,omitempty"`
|
||||||
|
NotTypes []string `json:"not_types,omitempty"`
|
||||||
|
Senders []string `json:"senders,omitempty"`
|
||||||
|
Types []string `json:"types,omitempty"`
|
||||||
|
}
|
6
vendor/github.com/matrix-org/gomatrix/requests.go
generated
vendored
6
vendor/github.com/matrix-org/gomatrix/requests.go
generated
vendored
@ -70,3 +70,9 @@ type ReqBanUser struct {
|
|||||||
type ReqUnbanUser struct {
|
type ReqUnbanUser struct {
|
||||||
UserID string `json:"user_id"`
|
UserID string `json:"user_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
|
||||||
|
type ReqTyping struct {
|
||||||
|
Typing bool `json:"typing"`
|
||||||
|
Timeout int64 `json:"timeout"`
|
||||||
|
}
|
||||||
|
32
vendor/github.com/matrix-org/gomatrix/responses.go
generated
vendored
32
vendor/github.com/matrix-org/gomatrix/responses.go
generated
vendored
@ -45,6 +45,9 @@ type RespBanUser struct{}
|
|||||||
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
|
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
|
||||||
type RespUnbanUser struct{}
|
type RespUnbanUser struct{}
|
||||||
|
|
||||||
|
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
|
||||||
|
type RespTyping struct{}
|
||||||
|
|
||||||
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
|
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
|
||||||
type RespJoinedRooms struct {
|
type RespJoinedRooms struct {
|
||||||
JoinedRooms []string `json:"joined_rooms"`
|
JoinedRooms []string `json:"joined_rooms"`
|
||||||
@ -58,6 +61,13 @@ type RespJoinedMembers struct {
|
|||||||
} `json:"joined"`
|
} `json:"joined"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
|
||||||
|
type RespMessages struct {
|
||||||
|
Start string `json:"start"`
|
||||||
|
Chunk []Event `json:"chunk"`
|
||||||
|
End string `json:"end"`
|
||||||
|
}
|
||||||
|
|
||||||
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
|
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
|
||||||
type RespSendEvent struct {
|
type RespSendEvent struct {
|
||||||
EventID string `json:"event_id"`
|
EventID string `json:"event_id"`
|
||||||
@ -90,6 +100,11 @@ func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
|
||||||
|
type RespUserDisplayName struct {
|
||||||
|
DisplayName string `json:"displayname"`
|
||||||
|
}
|
||||||
|
|
||||||
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
|
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
|
||||||
type RespRegister struct {
|
type RespRegister struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
@ -125,6 +140,16 @@ type RespSync struct {
|
|||||||
Events []Event `json:"events"`
|
Events []Event `json:"events"`
|
||||||
} `json:"presence"`
|
} `json:"presence"`
|
||||||
Rooms struct {
|
Rooms struct {
|
||||||
|
Leave map[string]struct {
|
||||||
|
State struct {
|
||||||
|
Events []Event `json:"events"`
|
||||||
|
} `json:"state"`
|
||||||
|
Timeline struct {
|
||||||
|
Events []Event `json:"events"`
|
||||||
|
Limited bool `json:"limited"`
|
||||||
|
PrevBatch string `json:"prev_batch"`
|
||||||
|
} `json:"timeline"`
|
||||||
|
} `json:"leave"`
|
||||||
Join map[string]struct {
|
Join map[string]struct {
|
||||||
State struct {
|
State struct {
|
||||||
Events []Event `json:"events"`
|
Events []Event `json:"events"`
|
||||||
@ -142,3 +167,10 @@ type RespSync struct {
|
|||||||
} `json:"invite"`
|
} `json:"invite"`
|
||||||
} `json:"rooms"`
|
} `json:"rooms"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RespTurnServer struct {
|
||||||
|
Username string `json:"username"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
TTL int `json:"ttl"`
|
||||||
|
URIs []string `json:"uris"`
|
||||||
|
}
|
||||||
|
2
vendor/github.com/matrix-org/gomatrix/room.go
generated
vendored
2
vendor/github.com/matrix-org/gomatrix/room.go
generated
vendored
@ -13,7 +13,7 @@ func (room Room) UpdateState(event *Event) {
|
|||||||
if !exists {
|
if !exists {
|
||||||
room.State[event.Type] = make(map[string]*Event)
|
room.State[event.Type] = make(map[string]*Event)
|
||||||
}
|
}
|
||||||
room.State[event.Type][event.StateKey] = event
|
room.State[event.Type][*event.StateKey] = event
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
|
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
|
||||||
|
12
vendor/github.com/matrix-org/gomatrix/sync.go
generated
vendored
12
vendor/github.com/matrix-org/gomatrix/sync.go
generated
vendored
@ -73,6 +73,16 @@ func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error)
|
|||||||
s.notifyListeners(&event)
|
s.notifyListeners(&event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for roomID, roomData := range res.Rooms.Leave {
|
||||||
|
room := s.getOrCreateRoom(roomID)
|
||||||
|
for _, event := range roomData.Timeline.Events {
|
||||||
|
if event.StateKey != nil {
|
||||||
|
event.RoomID = roomID
|
||||||
|
room.UpdateState(&event)
|
||||||
|
s.notifyListeners(&event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +112,7 @@ func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool
|
|||||||
for roomID, roomData := range resp.Rooms.Join {
|
for roomID, roomData := range resp.Rooms.Join {
|
||||||
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
|
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
|
||||||
e := roomData.Timeline.Events[i]
|
e := roomData.Timeline.Events[i]
|
||||||
if e.Type == "m.room.member" && e.StateKey == s.UserID {
|
if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
|
||||||
m := e.Content["membership"]
|
m := e.Content["membership"]
|
||||||
mship, ok := m.(string)
|
mship, ok := m.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
4
vendor/manifest
vendored
4
vendor/manifest
vendored
@ -282,7 +282,7 @@
|
|||||||
"importpath": "github.com/lrstanley/girc",
|
"importpath": "github.com/lrstanley/girc",
|
||||||
"repository": "https://github.com/lrstanley/girc",
|
"repository": "https://github.com/lrstanley/girc",
|
||||||
"vcs": "git",
|
"vcs": "git",
|
||||||
"revision": "055075db54ebd311be5946efb3f62502846089ff",
|
"revision": "5dff93b5453c1b2ac8382c9a38881635f47bba0e",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"notests": true
|
"notests": true
|
||||||
},
|
},
|
||||||
@ -290,7 +290,7 @@
|
|||||||
"importpath": "github.com/matrix-org/gomatrix",
|
"importpath": "github.com/matrix-org/gomatrix",
|
||||||
"repository": "https://github.com/matrix-org/gomatrix",
|
"repository": "https://github.com/matrix-org/gomatrix",
|
||||||
"vcs": "git",
|
"vcs": "git",
|
||||||
"revision": "812dcb5515581023371efaa6a82750d997f50d57",
|
"revision": "a7fc80c8060c2544fe5d4dae465b584f8e9b4e27",
|
||||||
"branch": "master",
|
"branch": "master",
|
||||||
"notests": true
|
"notests": true
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user