forked from lug/matterbridge
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f06e9b5605 | ||
|
|
7a3bb0e55c | ||
|
|
6e8f535e8b | ||
|
|
5619a75b05 | ||
|
|
53dfb78215 | ||
|
|
8e97cbab1e | ||
|
|
ce7b749fd5 | ||
|
|
6617bd6609 | ||
|
|
e610fb3201 | ||
|
|
40f1d35415 | ||
|
|
b79bf7d414 | ||
|
|
3724cc3a15 | ||
|
|
3418e8c9af | ||
|
|
9619dff334 | ||
|
|
1b2feb19e5 | ||
|
|
1829dc3d9f | ||
|
|
bd0e81f5a0 | ||
|
|
f04d360ee2 | ||
|
|
92f27281fa | ||
|
|
65781b9316 | ||
|
|
9be0be0316 | ||
|
|
9f5f004725 | ||
|
|
fed77cccf3 | ||
|
|
9b520dfb78 | ||
|
|
8ad2be10b2 | ||
|
|
2d277a15f5 | ||
|
|
d60468bb05 | ||
|
|
82d6210464 | ||
|
|
ff198042d2 | ||
|
|
6b47e29583 | ||
|
|
380c38674c | ||
|
|
3c14a0891e | ||
|
|
8513a07416 | ||
|
|
220485a849 | ||
|
|
4db34b0506 | ||
|
|
5677c912a8 | ||
|
|
7a24de15e4 | ||
|
|
99d9ea283a | ||
|
|
dac92a0e0a | ||
|
|
a25efb16f3 |
@@ -13,6 +13,7 @@ notifications:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- /.*/
|
||||
|
||||
jobs:
|
||||
include:
|
||||
|
||||
15
README.md
15
README.md
@@ -50,9 +50,10 @@
|
||||
* [Screenshots](https://github.com/42wim/matterbridge/wiki/)
|
||||
* [Installing](#installing)
|
||||
* [Binaries](#binaries)
|
||||
* [Building](#building)
|
||||
* [Building](#building)
|
||||
* [Configuration](#configuration)
|
||||
* [Howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config)
|
||||
* [Settings](#settings)
|
||||
* [Examples](#examples)
|
||||
* [Running](#running)
|
||||
* [Docker](#docker)
|
||||
@@ -81,7 +82,6 @@
|
||||
* [Slack](https://slack.com)
|
||||
* [Discord](https://discordapp.com)
|
||||
* [Telegram](https://telegram.org)
|
||||
* [Hipchat](https://www.hipchat.com)
|
||||
* [Rocket.chat](https://rocket.chat)
|
||||
* [Matrix](https://matrix.org)
|
||||
* [Steam](https://store.steampowered.com/)
|
||||
@@ -97,7 +97,7 @@
|
||||
* [Discourse](https://github.com/DeclanHoare/matterbabble)
|
||||
|
||||
### API
|
||||
The API is very basic at the moment.
|
||||
The API is basic at the moment.
|
||||
More info and examples on the [wiki](https://github.com/42wim/matterbridge/wiki/Api).
|
||||
|
||||
Used by the projects below. Feel free to make a PR to add your project to this list.
|
||||
@@ -119,7 +119,7 @@ Questions or want to test on your favorite platform? Join below:
|
||||
* [Slack][mb-slack]
|
||||
* [Mattermost][mb-mattermost]
|
||||
* [Rocket.Chat][mb-rocketchat]
|
||||
* [XMPP][mb-xmpp]
|
||||
* [XMPP][mb-xmpp] (matterbridge@conference.jabber.de)
|
||||
* [Twitch][mb-twitch]
|
||||
* [Zulip][mb-zulip]
|
||||
* [Telegram][mb-telegram]
|
||||
@@ -129,13 +129,13 @@ See https://github.com/42wim/matterbridge/wiki
|
||||
|
||||
## Installing
|
||||
### Binaries
|
||||
* Latest stable release [v1.14.2](https://github.com/42wim/matterbridge/releases/latest)
|
||||
* Latest stable release [v1.15.0](https://github.com/42wim/matterbridge/releases/latest)
|
||||
* Development releases (follows master) can be downloaded [here](https://dl.bintray.com/42wim/nightly/)
|
||||
|
||||
### Packages
|
||||
* [Overview](https://repology.org/metapackage/matterbridge/versions)
|
||||
|
||||
### Building
|
||||
## Building
|
||||
Go 1.9+ is required. Make sure you have [Go](https://golang.org/doc/install) properly installed, including setting up your [GOPATH](https://golang.org/doc/code.html#GOPATH).
|
||||
|
||||
After Go is setup, download matterbridge to your $GOPATH directory.
|
||||
@@ -156,6 +156,9 @@ matterbridge
|
||||
### Basic configuration
|
||||
See [howto](https://github.com/42wim/matterbridge/wiki/How-to-create-your-config) for a step by step walkthrough for creating your configuration.
|
||||
|
||||
### Settings
|
||||
All possible [settings](https://github.com/42wim/matterbridge/wiki/Settings) for each bridge.
|
||||
|
||||
### Advanced configuration
|
||||
* [matterbridge.toml.sample](https://github.com/42wim/matterbridge/blob/master/matterbridge.toml.sample) for documentation and an example.
|
||||
|
||||
|
||||
@@ -120,7 +120,7 @@ type Protocol struct {
|
||||
ReplaceMessages [][]string // all protocols
|
||||
ReplaceNicks [][]string // all protocols
|
||||
RemoteNickFormat string // all protocols
|
||||
RunCommands []string // irc
|
||||
RunCommands []string // IRC
|
||||
Server string // IRC,mattermost,XMPP,discord
|
||||
ShowJoinPart bool // all protocols
|
||||
ShowTopicChange bool // slack
|
||||
@@ -141,6 +141,7 @@ type Protocol struct {
|
||||
UseFirstName bool // telegram
|
||||
UseUserName bool // discord
|
||||
UseInsecureURL bool // telegram
|
||||
VerboseJoinPart bool // IRC
|
||||
WebhookBindAddress string // mattermost, slack
|
||||
WebhookURL string // mattermost, slack
|
||||
}
|
||||
@@ -166,6 +167,13 @@ type Gateway struct {
|
||||
InOut []Bridge
|
||||
}
|
||||
|
||||
type Tengo struct {
|
||||
InMessage string
|
||||
Message string
|
||||
RemoteNickFormat string
|
||||
OutMessage string
|
||||
}
|
||||
|
||||
type SameChannelGateway struct {
|
||||
Name string
|
||||
Enable bool
|
||||
@@ -190,6 +198,7 @@ type BridgeValues struct {
|
||||
WhatsApp map[string]Protocol // TODO is this struct used? Search for "SlackLegacy" for example didn't return any results
|
||||
Zulip map[string]Protocol
|
||||
General Protocol
|
||||
Tengo Tengo
|
||||
Gateway []Gateway
|
||||
SameChannelGateway []SameChannelGateway
|
||||
}
|
||||
@@ -245,12 +254,12 @@ func newConfigFromString(logger *logrus.Entry, input []byte) *config {
|
||||
viper.AutomaticEnv()
|
||||
|
||||
if err := viper.ReadConfig(bytes.NewBuffer(input)); err != nil {
|
||||
logger.Fatalf("Failed to parse the configuration: %#v", err)
|
||||
logger.Fatalf("Failed to parse the configuration: %s", err)
|
||||
}
|
||||
|
||||
cfg := &BridgeValues{}
|
||||
if err := viper.Unmarshal(cfg); err != nil {
|
||||
logger.Fatalf("Failed to load the configuration: %#v", err)
|
||||
logger.Fatalf("Failed to load the configuration: %s", err)
|
||||
}
|
||||
return &config{
|
||||
logger: logger,
|
||||
|
||||
@@ -36,7 +36,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat
|
||||
return
|
||||
}
|
||||
// if using webhooks, do not relay if it's ours
|
||||
if b.useWebhook() && m.Author.Bot { // && b.isWebhookID(m.Author.ID) {
|
||||
if b.useWebhook() && m.Author.Bot && b.isWebhookID(m.Author.ID) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -91,8 +90,13 @@ func (b *Birc) handleJoinPart(client *girc.Client, event girc.Event) {
|
||||
if b.GetBool("nosendjoinpart") {
|
||||
return
|
||||
}
|
||||
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||
msg := config.Message{Username: "system", Text: event.Source.Name + " " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||
if b.GetBool("verbosejoinpart") {
|
||||
b.Log.Debugf("<= Sending verbose JOIN_LEAVE event from %s to gateway", b.Account)
|
||||
msg = config.Message{Username: "system", Text: event.Source.Name + " (" + event.Source.Ident + "@" + event.Source.Host + ") " + strings.ToLower(event.Command) + "s", Channel: channel, Account: b.Account, Event: config.EventJoinLeave}
|
||||
} else {
|
||||
b.Log.Debugf("<= Sending JOIN_LEAVE event from %s to gateway", b.Account)
|
||||
}
|
||||
b.Log.Debugf("<= Message is %#v", msg)
|
||||
b.Remote <- msg
|
||||
return
|
||||
@@ -156,7 +160,10 @@ func (b *Birc) handleOtherAuth(client *girc.Client, event girc.Event) {
|
||||
b.handleNickServ()
|
||||
b.handleRunCommands()
|
||||
// we are now fully connected
|
||||
b.connected <- nil
|
||||
// only send on first connection
|
||||
if b.FirstConnection {
|
||||
b.connected <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
||||
@@ -174,10 +181,6 @@ func (b *Birc) handlePrivMsg(client *girc.Client, event girc.Event) {
|
||||
// strip action, we made an event if it was an action
|
||||
rmsg.Text += event.StripAction()
|
||||
|
||||
// strip IRC colors
|
||||
re := regexp.MustCompile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
|
||||
rmsg.Text = re.ReplaceAllString(rmsg.Text, "")
|
||||
|
||||
// start detecting the charset
|
||||
mycharset := b.GetString("Charset")
|
||||
if mycharset == "" {
|
||||
|
||||
@@ -186,6 +186,12 @@ func (b *Bmattermost) skipMessage(message *matterclient.Message) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Ignore non-post messages
|
||||
if message.Post == nil {
|
||||
b.Log.Debugf("ignoring nil message.Post: %#v", message)
|
||||
return true
|
||||
}
|
||||
|
||||
// Ignore messages sent from matterbridge
|
||||
if message.Post.Props != nil {
|
||||
if _, ok := message.Post.Props["matterbridge_"+b.uuid].(bool); ok {
|
||||
|
||||
@@ -121,6 +121,12 @@ func (b *Bmattermost) Send(msg config.Message) (string, error) {
|
||||
return msg.ID, b.mc.DeleteMessage(msg.ID)
|
||||
}
|
||||
|
||||
// Handle prefix hint for unthreaded messages.
|
||||
if msg.ParentID == "msg-parent-not-found" {
|
||||
msg.ParentID = ""
|
||||
msg.Text = fmt.Sprintf("[thread]: %s", msg.Text)
|
||||
}
|
||||
|
||||
// Upload a file if it exists
|
||||
if msg.Extra != nil {
|
||||
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||
|
||||
@@ -108,6 +108,11 @@ func (b *Brocketchat) Send(msg config.Message) (string, error) {
|
||||
msg.Channel = strings.TrimPrefix(msg.Channel, "#")
|
||||
channel := &models.Channel{ID: b.getChannelID(msg.Channel), Name: msg.Channel}
|
||||
|
||||
// Make a action /me of the message
|
||||
if msg.Event == config.EventUserAction {
|
||||
msg.Text = "_" + msg.Text + "_"
|
||||
}
|
||||
|
||||
// Delete message
|
||||
if msg.Event == config.EventMsgDelete {
|
||||
if msg.ID == "" {
|
||||
|
||||
@@ -22,20 +22,20 @@ func (b *Bslack) handleSlack() {
|
||||
time.Sleep(time.Second)
|
||||
b.Log.Debug("Start listening for Slack messages")
|
||||
for message := range messages {
|
||||
if message.Event != config.EventUserTyping {
|
||||
// don't do any action on deleted/typing messages
|
||||
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete {
|
||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||
// cleanup the message
|
||||
message.Text = b.replaceMention(message.Text)
|
||||
message.Text = b.replaceVariable(message.Text)
|
||||
message.Text = b.replaceChannel(message.Text)
|
||||
message.Text = b.replaceURL(message.Text)
|
||||
message.Text = html.UnescapeString(message.Text)
|
||||
|
||||
// Add the avatar
|
||||
message.Avatar = b.users.getAvatar(message.UserID)
|
||||
}
|
||||
|
||||
// cleanup the message
|
||||
message.Text = b.replaceMention(message.Text)
|
||||
message.Text = b.replaceVariable(message.Text)
|
||||
message.Text = b.replaceChannel(message.Text)
|
||||
message.Text = b.replaceURL(message.Text)
|
||||
message.Text = html.UnescapeString(message.Text)
|
||||
|
||||
// Add the avatar
|
||||
message.Avatar = b.users.getAvatar(message.UserID)
|
||||
|
||||
b.Log.Debugf("<= Message is %#v", message)
|
||||
b.Remote <- *message
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ type BLegacy struct {
|
||||
}
|
||||
|
||||
func NewLegacy(cfg *bridge.Config) bridge.Bridger {
|
||||
return &BLegacy{Bslack: newBridge(cfg)}
|
||||
b := &BLegacy{Bslack: newBridge(cfg)}
|
||||
b.legacy = true
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *BLegacy) Connect() error {
|
||||
|
||||
@@ -32,6 +32,7 @@ type Bslack struct {
|
||||
|
||||
channels *channels
|
||||
users *users
|
||||
legacy bool
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -151,6 +152,18 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// try to join a channel when in legacy
|
||||
if b.legacy {
|
||||
_, err := b.sc.JoinChannel(channel.Name)
|
||||
if err != nil {
|
||||
switch err.Error() {
|
||||
case "name_taken", "restricted_action":
|
||||
case "default":
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
b.channels.populateChannels(false)
|
||||
|
||||
channelInfo, err := b.channels.getChannel(channel.Name)
|
||||
@@ -163,7 +176,8 @@ func (b *Bslack) JoinChannel(channel config.ChannelInfo) error {
|
||||
channel.Name = channelInfo.Name
|
||||
}
|
||||
|
||||
if !channelInfo.IsMember {
|
||||
// we can't join a channel unless we are using legacy tokens #651
|
||||
if !channelInfo.IsMember && !b.legacy {
|
||||
return fmt.Errorf("slack integration that matterbridge is using is not member of channel '%s', please add it manually", channelInfo.Name)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -87,6 +87,11 @@ func (b *users) populateUser(userID string) {
|
||||
// in case the previous query failed for some reason.
|
||||
} else {
|
||||
b.usersSyncPoints[userID] = make(chan struct{})
|
||||
defer func() {
|
||||
// Wake up any waiting goroutines and remove the synchronization point.
|
||||
close(b.usersSyncPoints[userID])
|
||||
delete(b.usersSyncPoints, userID)
|
||||
}()
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -106,10 +111,6 @@ func (b *users) populateUser(userID string) {
|
||||
|
||||
// Register user information.
|
||||
b.users[userID] = user
|
||||
|
||||
// Wake up any waiting goroutines and remove the synchronization point.
|
||||
close(b.usersSyncPoints[userID])
|
||||
delete(b.usersSyncPoints, userID)
|
||||
}
|
||||
|
||||
func (b *users) populateUsers(wait bool) {
|
||||
|
||||
@@ -5,10 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
|
||||
whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -21,6 +18,10 @@ Check:
|
||||
|
||||
// HandleError received from WhatsApp
|
||||
func (b *Bwhatsapp) HandleError(err error) {
|
||||
// ignore received invalid data errors. https://github.com/42wim/matterbridge/issues/843
|
||||
if strings.Contains(err.Error(), "error processing data: received invalid data") {
|
||||
return
|
||||
}
|
||||
b.Log.Errorf("%v", err) // TODO implement proper handling? at least respond to different error types
|
||||
}
|
||||
|
||||
@@ -57,7 +58,7 @@ func (b *Bwhatsapp) HandleTextMessage(message whatsapp.TextMessage) {
|
||||
|
||||
// mentions comes as telephone numbers and we don't want to expose it to other bridges
|
||||
// replace it with something more meaninful to others
|
||||
mention := b.getSenderNotify(numberAndSuffix[0] + whatsappExt.NewUserSuffix)
|
||||
mention := b.getSenderNotify(numberAndSuffix[0] + "@s.whatsapp.net")
|
||||
if mention == "" {
|
||||
mention = "someone"
|
||||
}
|
||||
|
||||
@@ -2,13 +2,22 @@ package bwhatsapp
|
||||
|
||||
import (
|
||||
"encoding/gob"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
qrcodeTerminal "github.com/Baozisoftware/qrcode-terminal-go"
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
type ProfilePicInfo struct {
|
||||
URL string `json:"eurl"`
|
||||
Tag string `json:"tag"`
|
||||
|
||||
Status int16 `json:"status"`
|
||||
}
|
||||
|
||||
func qrFromTerminal(invert bool) chan string {
|
||||
qr := make(chan string)
|
||||
go func() {
|
||||
@@ -82,3 +91,17 @@ func (b *Bwhatsapp) getSenderNotify(senderJid string) string {
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (b *Bwhatsapp) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) {
|
||||
data, err := b.conn.GetProfilePicThumb(jid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get avatar: %v", err)
|
||||
}
|
||||
content := <-data
|
||||
info := &ProfilePicInfo{}
|
||||
err = json.Unmarshal([]byte(content), info)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to unmarshal avatar info: %v", err)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
|
||||
@@ -11,10 +11,7 @@ import (
|
||||
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
|
||||
whatsappExt "github.com/matterbridge/mautrix-whatsapp/whatsapp-ext"
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -29,10 +26,8 @@ type Bwhatsapp struct {
|
||||
*bridge.Config
|
||||
|
||||
// https://github.com/Rhymen/go-whatsapp/blob/c31092027237441cffba1b9cb148eadf7c83c3d2/session.go#L18-L21
|
||||
session *whatsapp.Session
|
||||
conn *whatsapp.Conn
|
||||
// https://github.com/tulir/mautrix-whatsapp/blob/master/whatsapp-ext/whatsapp.go
|
||||
connExt *whatsappExt.ExtendedConn
|
||||
session *whatsapp.Session
|
||||
conn *whatsapp.Conn
|
||||
startedAt uint64
|
||||
|
||||
users map[string]whatsapp.Contact
|
||||
@@ -74,8 +69,6 @@ func (b *Bwhatsapp) Connect() error {
|
||||
}
|
||||
|
||||
b.conn = conn
|
||||
b.connExt = whatsappExt.ExtendConn(b.conn)
|
||||
// TODO do we want to use it? b.connExt.SetClientName("Matterbridge WhatsApp bridge", "mb-wa")
|
||||
|
||||
b.conn.AddHandler(b)
|
||||
b.Log.Debugln("WhatsApp connection successful")
|
||||
@@ -89,7 +82,7 @@ func (b *Bwhatsapp) Connect() error {
|
||||
b.Log.Debugln("Restoring WhatsApp session..")
|
||||
|
||||
// https://github.com/Rhymen/go-whatsapp#restore
|
||||
session, err = b.conn.RestoreSession(session)
|
||||
session, err = b.conn.RestoreWithSession(session)
|
||||
if err != nil {
|
||||
// TODO return or continue to normal login?
|
||||
// restore session connection timed out (I couldn't get over it without logging in again)
|
||||
@@ -130,7 +123,7 @@ func (b *Bwhatsapp) Connect() error {
|
||||
b.Log.Debug("Getting user avatars..")
|
||||
|
||||
for jid := range b.users {
|
||||
info, err := b.connExt.GetProfilePicThumb(jid)
|
||||
info, err := b.GetProfilePicThumb(jid)
|
||||
if err != nil {
|
||||
b.Log.Warnf("Could not get profile photo of %s: %v", jid, err)
|
||||
|
||||
@@ -294,7 +287,7 @@ func (b *Bwhatsapp) Send(msg config.Message) (string, error) {
|
||||
}
|
||||
text.Info.Id = strings.ToUpper(hex.EncodeToString(bytes))
|
||||
|
||||
err := b.conn.Send(text)
|
||||
_, err := b.conn.Send(text)
|
||||
|
||||
return text.Info.Id, err
|
||||
}
|
||||
|
||||
@@ -14,50 +14,29 @@ import (
|
||||
)
|
||||
|
||||
type Bxmpp struct {
|
||||
xc *xmpp.Client
|
||||
xmppMap map[string]string
|
||||
*bridge.Config
|
||||
|
||||
startTime time.Time
|
||||
xc *xmpp.Client
|
||||
xmppMap map[string]string
|
||||
}
|
||||
|
||||
func New(cfg *bridge.Config) bridge.Bridger {
|
||||
b := &Bxmpp{Config: cfg}
|
||||
b.xmppMap = make(map[string]string)
|
||||
return b
|
||||
return &Bxmpp{
|
||||
Config: cfg,
|
||||
xmppMap: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bxmpp) Connect() error {
|
||||
var err error
|
||||
b.Log.Infof("Connecting %s", b.GetString("Server"))
|
||||
b.xc, err = b.createXMPP()
|
||||
if err != nil {
|
||||
if err := b.createXMPP(); err != nil {
|
||||
b.Log.Debugf("%#v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
b.Log.Info("Connection succeeded")
|
||||
go func() {
|
||||
initial := true
|
||||
bf := &backoff.Backoff{
|
||||
Min: time.Second,
|
||||
Max: 5 * time.Minute,
|
||||
Jitter: true,
|
||||
}
|
||||
for {
|
||||
if initial {
|
||||
b.handleXMPP()
|
||||
initial = false
|
||||
}
|
||||
d := bf.Duration()
|
||||
b.Log.Infof("Disconnected. Reconnecting in %s", d)
|
||||
time.Sleep(d)
|
||||
b.xc, err = b.createXMPP()
|
||||
if err == nil {
|
||||
b.Remote <- config.Message{Username: "system", Text: "rejoin", Channel: "", Account: b.Account, Event: config.EventRejoinChannels}
|
||||
b.handleXMPP()
|
||||
bf.Reset()
|
||||
}
|
||||
}
|
||||
}()
|
||||
go b.manageConnection()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -82,34 +61,48 @@ func (b *Bxmpp) Send(msg config.Message) (string, error) {
|
||||
}
|
||||
b.Log.Debugf("=> Receiving %#v", msg)
|
||||
|
||||
// Upload a file (in xmpp case send the upload URL because xmpp has no native upload support)
|
||||
// Upload a file (in XMPP case send the upload URL because XMPP has no native upload support).
|
||||
if msg.Extra != nil {
|
||||
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
|
||||
b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: rmsg.Channel + "@" + b.GetString("Muc"), Text: rmsg.Username + rmsg.Text})
|
||||
b.Log.Debugf("=> Sending attachement message %#v", rmsg)
|
||||
if _, err := b.xc.Send(xmpp.Chat{
|
||||
Type: "groupchat",
|
||||
Remote: rmsg.Channel + "@" + b.GetString("Muc"),
|
||||
Text: rmsg.Username + rmsg.Text,
|
||||
}); err != nil {
|
||||
b.Log.WithError(err).Error("Unable to send message with share URL.")
|
||||
}
|
||||
}
|
||||
if len(msg.Extra["file"]) > 0 {
|
||||
return b.handleUploadFile(&msg)
|
||||
return "", b.handleUploadFile(&msg)
|
||||
}
|
||||
}
|
||||
|
||||
var msgreplaceid string
|
||||
msgid := xid.New().String()
|
||||
var msgReplaceID string
|
||||
msgID := xid.New().String()
|
||||
if msg.ID != "" {
|
||||
msgid = msg.ID
|
||||
msgreplaceid = msg.ID
|
||||
msgID = msg.ID
|
||||
msgReplaceID = msg.ID
|
||||
}
|
||||
// Post normal message
|
||||
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text, ID: msgid, ReplaceID: msgreplaceid})
|
||||
if err != nil {
|
||||
// Post normal message.
|
||||
b.Log.Debugf("=> Sending message %#v", msg)
|
||||
if _, err := b.xc.Send(xmpp.Chat{
|
||||
Type: "groupchat",
|
||||
Remote: msg.Channel + "@" + b.GetString("Muc"),
|
||||
Text: msg.Username + msg.Text,
|
||||
ID: msgID,
|
||||
ReplaceID: msgReplaceID,
|
||||
}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return msgid, nil
|
||||
return msgID, nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
||||
tc := new(tls.Config)
|
||||
tc.InsecureSkipVerify = b.GetBool("SkipTLSVerify")
|
||||
tc.ServerName = strings.Split(b.GetString("Server"), ":")[0]
|
||||
func (b *Bxmpp) createXMPP() error {
|
||||
tc := &tls.Config{
|
||||
ServerName: strings.Split(b.GetString("Jid"), "@")[1],
|
||||
InsecureSkipVerify: b.GetBool("SkipTLSVerify"), // nolint: gosec
|
||||
}
|
||||
options := xmpp.Options{
|
||||
Host: b.GetString("Server"),
|
||||
User: b.GetString("Jid"),
|
||||
@@ -127,7 +120,51 @@ func (b *Bxmpp) createXMPP() (*xmpp.Client, error) {
|
||||
}
|
||||
var err error
|
||||
b.xc, err = options.NewClient()
|
||||
return b.xc, err
|
||||
return err
|
||||
}
|
||||
|
||||
func (b *Bxmpp) manageConnection() {
|
||||
initial := true
|
||||
bf := &backoff.Backoff{
|
||||
Min: time.Second,
|
||||
Max: 5 * time.Minute,
|
||||
Jitter: true,
|
||||
}
|
||||
|
||||
// Main connection loop. Each iteration corresponds to a successful
|
||||
// connection attempt and the subsequent handling of the connection.
|
||||
for {
|
||||
if initial {
|
||||
initial = false
|
||||
} else {
|
||||
b.Remote <- config.Message{
|
||||
Username: "system",
|
||||
Text: "rejoin",
|
||||
Channel: "",
|
||||
Account: b.Account,
|
||||
Event: config.EventRejoinChannels,
|
||||
}
|
||||
}
|
||||
|
||||
if err := b.handleXMPP(); err != nil {
|
||||
b.Log.WithError(err).Error("Disconnected.")
|
||||
}
|
||||
|
||||
// Reconnection loop using an exponential back-off strategy. We
|
||||
// only break out of the loop if we have successfully reconnected.
|
||||
for {
|
||||
d := bf.Duration()
|
||||
b.Log.Infof("Reconnecting in %s.", d)
|
||||
time.Sleep(d)
|
||||
|
||||
b.Log.Infof("Reconnecting now.")
|
||||
if err := b.createXMPP(); err == nil {
|
||||
bf.Reset()
|
||||
break
|
||||
}
|
||||
b.Log.Warn("Failed to reconnect.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Bxmpp) xmppKeepAlive() chan bool {
|
||||
@@ -139,8 +176,7 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
b.Log.Debugf("PING")
|
||||
err := b.xc.PingC2S("", "")
|
||||
if err != nil {
|
||||
if err := b.xc.PingC2S("", ""); err != nil {
|
||||
b.Log.Debugf("PING failed %#v", err)
|
||||
}
|
||||
case <-done:
|
||||
@@ -152,31 +188,35 @@ func (b *Bxmpp) xmppKeepAlive() chan bool {
|
||||
}
|
||||
|
||||
func (b *Bxmpp) handleXMPP() error {
|
||||
var ok bool
|
||||
var msgid string
|
||||
b.startTime = time.Now()
|
||||
|
||||
done := b.xmppKeepAlive()
|
||||
defer close(done)
|
||||
|
||||
for {
|
||||
m, err := b.xc.Recv()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch v := m.(type) {
|
||||
case xmpp.Chat:
|
||||
if v.Type == "groupchat" {
|
||||
b.Log.Debugf("== Receiving %#v", v)
|
||||
event := ""
|
||||
// skip invalid messages
|
||||
|
||||
// Skip invalid messages.
|
||||
if b.skipMessage(v) {
|
||||
continue
|
||||
}
|
||||
|
||||
var event string
|
||||
if strings.Contains(v.Text, "has set the subject to:") {
|
||||
event = config.EventTopicChange
|
||||
}
|
||||
msgid = v.ID
|
||||
|
||||
msgID := v.ID
|
||||
if v.ReplaceID != "" {
|
||||
msgid = v.ReplaceID
|
||||
msgID = v.ReplaceID
|
||||
}
|
||||
rmsg := config.Message{
|
||||
Username: b.parseNick(v.Remote),
|
||||
@@ -184,21 +224,23 @@ func (b *Bxmpp) handleXMPP() error {
|
||||
Channel: b.parseChannel(v.Remote),
|
||||
Account: b.Account,
|
||||
UserID: v.Remote,
|
||||
ID: msgid,
|
||||
ID: msgID,
|
||||
Event: event,
|
||||
}
|
||||
|
||||
// check if we have an action event
|
||||
// Check if we have an action event.
|
||||
var ok bool
|
||||
rmsg.Text, ok = b.replaceAction(rmsg.Text)
|
||||
if ok {
|
||||
rmsg.Event = config.EventUserAction
|
||||
}
|
||||
|
||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", rmsg.Username, b.Account)
|
||||
b.Log.Debugf("<= Message is %#v", rmsg)
|
||||
b.Remote <- rmsg
|
||||
}
|
||||
case xmpp.Presence:
|
||||
// do nothing
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,30 +253,41 @@ func (b *Bxmpp) replaceAction(text string) (string, bool) {
|
||||
}
|
||||
|
||||
// handleUploadFile handles native upload of files
|
||||
func (b *Bxmpp) handleUploadFile(msg *config.Message) (string, error) {
|
||||
var urldesc = ""
|
||||
func (b *Bxmpp) handleUploadFile(msg *config.Message) error {
|
||||
var urlDesc string
|
||||
|
||||
for _, f := range msg.Extra["file"] {
|
||||
fi := f.(config.FileInfo)
|
||||
if fi.Comment != "" {
|
||||
msg.Text += fi.Comment + ": "
|
||||
for _, file := range msg.Extra["file"] {
|
||||
fileInfo := file.(config.FileInfo)
|
||||
if fileInfo.Comment != "" {
|
||||
msg.Text += fileInfo.Comment + ": "
|
||||
}
|
||||
if fi.URL != "" {
|
||||
msg.Text = fi.URL
|
||||
if fi.Comment != "" {
|
||||
msg.Text = fi.Comment + ": " + fi.URL
|
||||
urldesc = fi.Comment
|
||||
if fileInfo.URL != "" {
|
||||
msg.Text = fileInfo.URL
|
||||
if fileInfo.Comment != "" {
|
||||
msg.Text = fileInfo.Comment + ": " + fileInfo.URL
|
||||
urlDesc = fileInfo.Comment
|
||||
}
|
||||
}
|
||||
_, err := b.xc.Send(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Text: msg.Username + msg.Text})
|
||||
if err != nil {
|
||||
return "", err
|
||||
if _, err := b.xc.Send(xmpp.Chat{
|
||||
Type: "groupchat",
|
||||
Remote: msg.Channel + "@" + b.GetString("Muc"),
|
||||
Text: msg.Username + msg.Text,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if fi.URL != "" {
|
||||
b.xc.SendOOB(xmpp.Chat{Type: "groupchat", Remote: msg.Channel + "@" + b.GetString("Muc"), Ooburl: fi.URL, Oobdesc: urldesc})
|
||||
|
||||
if fileInfo.URL != "" {
|
||||
if _, err := b.xc.SendOOB(xmpp.Chat{
|
||||
Type: "groupchat",
|
||||
Remote: msg.Channel + "@" + b.GetString("Muc"),
|
||||
Ooburl: fileInfo.URL,
|
||||
Oobdesc: urlDesc,
|
||||
}); err != nil {
|
||||
b.Log.WithError(err).Warn("Failed to send share URL.")
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Bxmpp) parseNick(remote string) string {
|
||||
@@ -279,6 +332,5 @@ func (b *Bxmpp) skipMessage(message xmpp.Chat) bool {
|
||||
}
|
||||
|
||||
// skip delayed messages
|
||||
t := time.Time{}
|
||||
return message.Stamp != t
|
||||
return !message.Stamp.IsZero() && time.Since(message.Stamp).Minutes() > 5
|
||||
}
|
||||
|
||||
43
changelog.md
43
changelog.md
@@ -1,3 +1,46 @@
|
||||
# v1.15.0
|
||||
## New features
|
||||
* Add scripting (tengo) support for every outgoing message (#806)
|
||||
See https://github.com/42wim/matterbridge/wiki/Settings#tengo and
|
||||
https://github.com/42wim/matterbridge/wiki/Settings#outmessage for more information
|
||||
* Add tengo support to RemoteNickFormat (#793)
|
||||
See https://github.com/42wim/matterbridge/wiki/Settings#remotenickformat-2
|
||||
* Deprecated `Message` under `[tengo]` to `InMessage`
|
||||
|
||||
## Enhancements
|
||||
* general: Forward only user-typing messages if supported by protocol (#832)
|
||||
* general: updated wiki with all possible settings: https://github.com/42wim/matterbridge/wiki/Settings
|
||||
* tengo: Add msg event to tengo
|
||||
* xmpp: Verify TLS against JID domain, not the host. (xmpp) (#834)
|
||||
* xmpp: Allow messages with timestamp (xmpp). Fixes #835 (#847)
|
||||
* irc: Add verbose IRC joins/parts (ident@host) (#805)
|
||||
See https://github.com/42wim/matterbridge/wiki/Settings#verbosejoinpart
|
||||
* rocketchat: Add useraction support (rocketchat). Closes #772 (#794)
|
||||
|
||||
## Bugfix
|
||||
* slack: Fix regression in autojoining with legacy tokens (slack). Fixes #651 (#848)
|
||||
* xmpp: Revert xmpp to orig behaviour. Closes #844
|
||||
* whatsapp: Update github.com/Rhymen/go-whatsapp vendor. Fixes #843
|
||||
* mattermost: Update channels of all teams (mattermost)
|
||||
|
||||
This release couldn't exist without the following contributors:
|
||||
@42wim, @Helcaraxan, @chotaire, @qaisjp, @dajohi, @kousu
|
||||
|
||||
# v1.14.4
|
||||
|
||||
## Bugfix
|
||||
* mattermost: Add Id to EditMessage (mattermost). Fixes #802
|
||||
* mattermost: Fix panic on nil message.Post (mattermost). Fixes #804
|
||||
* mattermost: Handle unthreaded messages (mattermost). Fixes #803
|
||||
* mattermost: Use paging in initUser and UpdateUsers (mattermost)
|
||||
* slack: Add lacking clean-up in Slack synchronisation (#811)
|
||||
* slack: Disable user lookups on delete messages (slack) (#812)
|
||||
|
||||
# v1.14.3
|
||||
|
||||
## Bugfix
|
||||
* irc: Fix deadlock on reconnect (irc). Closes #757
|
||||
|
||||
# v1.14.2
|
||||
|
||||
## Bugfix
|
||||
|
||||
7
contrib/outmessage-irccolors.tengo
Normal file
7
contrib/outmessage-irccolors.tengo
Normal file
@@ -0,0 +1,7 @@
|
||||
// See https://github.com/42wim/matterbridge/issues/798
|
||||
|
||||
// if we're not sending to an irc bridge we strip the IRC colors
|
||||
if outProtocol != "irc" {
|
||||
re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
|
||||
msgText=re.replace(msgText,"")
|
||||
}
|
||||
16
contrib/remotenickformat-zerowidth.tengo
Normal file
16
contrib/remotenickformat-zerowidth.tengo
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
This script will return the nick except with multi-character usernames
|
||||
containing a zero-width space between the first and second character letter.
|
||||
|
||||
Single character usernames will be left untouched.
|
||||
|
||||
This is useful to prevent remote users from nickalerting
|
||||
IRC users of the same name when the remote user speaks.
|
||||
|
||||
This result can be used in {TENGO} in RemoteNickFormat.
|
||||
*/
|
||||
|
||||
result = nick
|
||||
if len(nick) > 1 {
|
||||
result = string(nick[0]) + "" + nick[1:]
|
||||
}
|
||||
9
contrib/remotenickformat.tengo
Normal file
9
contrib/remotenickformat.tengo
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
This script will return the current time in kitchen format if the protocol (of the remote bridge) isn't irc
|
||||
See https://github.com/d5/tengo/blob/master/docs/stdlib-times.md
|
||||
This result can be used in {TENGO} in RemoteNickFormat
|
||||
*/
|
||||
times := import("times")
|
||||
if protocol != "irc" {
|
||||
result=times.time_format(times.now(),times.format_kitchen)
|
||||
}
|
||||
@@ -3,35 +3,41 @@ package bridgemap
|
||||
import (
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/api"
|
||||
"github.com/42wim/matterbridge/bridge/discord"
|
||||
"github.com/42wim/matterbridge/bridge/gitter"
|
||||
"github.com/42wim/matterbridge/bridge/irc"
|
||||
"github.com/42wim/matterbridge/bridge/matrix"
|
||||
"github.com/42wim/matterbridge/bridge/mattermost"
|
||||
"github.com/42wim/matterbridge/bridge/rocketchat"
|
||||
"github.com/42wim/matterbridge/bridge/slack"
|
||||
"github.com/42wim/matterbridge/bridge/sshchat"
|
||||
"github.com/42wim/matterbridge/bridge/steam"
|
||||
"github.com/42wim/matterbridge/bridge/telegram"
|
||||
"github.com/42wim/matterbridge/bridge/whatsapp"
|
||||
"github.com/42wim/matterbridge/bridge/xmpp"
|
||||
"github.com/42wim/matterbridge/bridge/zulip"
|
||||
bdiscord "github.com/42wim/matterbridge/bridge/discord"
|
||||
bgitter "github.com/42wim/matterbridge/bridge/gitter"
|
||||
birc "github.com/42wim/matterbridge/bridge/irc"
|
||||
bmatrix "github.com/42wim/matterbridge/bridge/matrix"
|
||||
bmattermost "github.com/42wim/matterbridge/bridge/mattermost"
|
||||
brocketchat "github.com/42wim/matterbridge/bridge/rocketchat"
|
||||
bslack "github.com/42wim/matterbridge/bridge/slack"
|
||||
bsshchat "github.com/42wim/matterbridge/bridge/sshchat"
|
||||
bsteam "github.com/42wim/matterbridge/bridge/steam"
|
||||
btelegram "github.com/42wim/matterbridge/bridge/telegram"
|
||||
bwhatsapp "github.com/42wim/matterbridge/bridge/whatsapp"
|
||||
bxmpp "github.com/42wim/matterbridge/bridge/xmpp"
|
||||
bzulip "github.com/42wim/matterbridge/bridge/zulip"
|
||||
)
|
||||
|
||||
var FullMap = map[string]bridge.Factory{
|
||||
"api": api.New,
|
||||
"discord": bdiscord.New,
|
||||
"gitter": bgitter.New,
|
||||
"irc": birc.New,
|
||||
"mattermost": bmattermost.New,
|
||||
"matrix": bmatrix.New,
|
||||
"rocketchat": brocketchat.New,
|
||||
"slack-legacy": bslack.NewLegacy,
|
||||
"slack": bslack.New,
|
||||
"sshchat": bsshchat.New,
|
||||
"steam": bsteam.New,
|
||||
"telegram": btelegram.New,
|
||||
"whatsapp": bwhatsapp.New,
|
||||
"xmpp": bxmpp.New,
|
||||
"zulip": bzulip.New,
|
||||
}
|
||||
var (
|
||||
FullMap = map[string]bridge.Factory{
|
||||
"api": api.New,
|
||||
"discord": bdiscord.New,
|
||||
"gitter": bgitter.New,
|
||||
"irc": birc.New,
|
||||
"mattermost": bmattermost.New,
|
||||
"matrix": bmatrix.New,
|
||||
"rocketchat": brocketchat.New,
|
||||
"slack-legacy": bslack.NewLegacy,
|
||||
"slack": bslack.New,
|
||||
"sshchat": bsshchat.New,
|
||||
"steam": bsteam.New,
|
||||
"telegram": btelegram.New,
|
||||
"whatsapp": bwhatsapp.New,
|
||||
"xmpp": bxmpp.New,
|
||||
"zulip": bzulip.New,
|
||||
}
|
||||
|
||||
UserTypingSupport = map[string]struct{}{
|
||||
"slack": {},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/internal"
|
||||
"github.com/d5/tengo/script"
|
||||
"github.com/d5/tengo/stdlib"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
@@ -331,6 +332,11 @@ func (gw *Gateway) modifyUsername(msg *config.Message, dest *bridge.Bridge) stri
|
||||
nick = strings.Replace(nick, "{LABEL}", br.GetString("Label"), -1)
|
||||
nick = strings.Replace(nick, "{NICK}", msg.Username, -1)
|
||||
nick = strings.Replace(nick, "{CHANNEL}", msg.Channel, -1)
|
||||
tengoNick, err := gw.modifyUsernameTengo(msg, br)
|
||||
if err != nil {
|
||||
gw.logger.Errorf("modifyUsernameTengo error: %s", err)
|
||||
}
|
||||
nick = strings.Replace(nick, "{TENGO}", tengoNick, -1) //nolint:gocritic
|
||||
return nick
|
||||
}
|
||||
|
||||
@@ -347,6 +353,9 @@ func (gw *Gateway) modifyMessage(msg *config.Message) {
|
||||
if err := modifyMessageTengo(gw.BridgeValues().General.TengoModifyMessage, msg); err != nil {
|
||||
gw.logger.Errorf("TengoModifyMessage failed: %s", err)
|
||||
}
|
||||
if err := modifyMessageTengo(gw.BridgeValues().Tengo.Message, msg); err != nil {
|
||||
gw.logger.Errorf("Tengo.Message failed: %s", err)
|
||||
}
|
||||
|
||||
// replace :emoji: to unicode
|
||||
msg.Text = emojilib.Replace(msg.Text)
|
||||
@@ -421,6 +430,11 @@ func (gw *Gateway) SendMessage(
|
||||
msg.ParentID = "msg-parent-not-found"
|
||||
}
|
||||
|
||||
err := gw.modifySendMessageTengo(rmsg, &msg, dest)
|
||||
if err != nil {
|
||||
gw.logger.Errorf("modifySendMessageTengo: %s", err)
|
||||
}
|
||||
|
||||
// if we are using mattermost plugin account, send messages to MattermostPlugin channel
|
||||
// that can be picked up by the mattermost matterbridge plugin
|
||||
if dest.Account == "mattermost.plugin" {
|
||||
@@ -503,3 +517,77 @@ func modifyMessageTengo(filename string, msg *config.Message) error {
|
||||
msg.Username = c.Get("msgUsername").String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gw *Gateway) modifyUsernameTengo(msg *config.Message, br *bridge.Bridge) (string, error) {
|
||||
filename := gw.BridgeValues().Tengo.RemoteNickFormat
|
||||
if filename == "" {
|
||||
return "", nil
|
||||
}
|
||||
res, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
s := script.New(res)
|
||||
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
||||
_ = s.Add("result", "")
|
||||
_ = s.Add("msgText", msg.Text)
|
||||
_ = s.Add("msgUsername", msg.Username)
|
||||
_ = s.Add("nick", msg.Username)
|
||||
_ = s.Add("msgAccount", msg.Account)
|
||||
_ = s.Add("msgChannel", msg.Channel)
|
||||
_ = s.Add("channel", msg.Channel)
|
||||
_ = s.Add("msgProtocol", msg.Protocol)
|
||||
_ = s.Add("remoteAccount", br.Account)
|
||||
_ = s.Add("protocol", br.Protocol)
|
||||
_ = s.Add("bridge", br.Name)
|
||||
_ = s.Add("gateway", gw.Name)
|
||||
c, err := s.Compile()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := c.Run(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return c.Get("result").String(), nil
|
||||
}
|
||||
|
||||
func (gw *Gateway) modifySendMessageTengo(origmsg *config.Message, msg *config.Message, br *bridge.Bridge) error {
|
||||
filename := gw.BridgeValues().Tengo.OutMessage
|
||||
var res []byte
|
||||
var err error
|
||||
if filename == "" {
|
||||
res, err = internal.Asset("tengo/outmessage.tengo")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
res, err = ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
s := script.New(res)
|
||||
s.SetImports(stdlib.GetModuleMap(stdlib.AllModuleNames()...))
|
||||
_ = s.Add("inAccount", origmsg.Account)
|
||||
_ = s.Add("inProtocol", origmsg.Protocol)
|
||||
_ = s.Add("inChannel", origmsg.Channel)
|
||||
_ = s.Add("inGateway", origmsg.Gateway)
|
||||
_ = s.Add("inEvent", origmsg.Event)
|
||||
_ = s.Add("outAccount", br.Account)
|
||||
_ = s.Add("outProtocol", br.Protocol)
|
||||
_ = s.Add("outChannel", msg.Channel)
|
||||
_ = s.Add("outGateway", gw.Name)
|
||||
_ = s.Add("outEvent", msg.Event)
|
||||
_ = s.Add("msgText", msg.Text)
|
||||
_ = s.Add("msgUsername", msg.Username)
|
||||
c, err := s.Compile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
msg.Text = c.Get("msgText").String()
|
||||
msg.Username = c.Get("msgUsername").String()
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/42wim/matterbridge/bridge"
|
||||
"github.com/42wim/matterbridge/bridge/config"
|
||||
"github.com/42wim/matterbridge/gateway/bridgemap"
|
||||
)
|
||||
|
||||
// handleEventFailure handles failures and reconnects bridges.
|
||||
@@ -190,6 +191,14 @@ func (gw *Gateway) ignoreEvent(event string, dest *bridge.Bridge) bool {
|
||||
func (gw *Gateway) handleMessage(rmsg *config.Message, dest *bridge.Bridge) []*BrMsgID {
|
||||
var brMsgIDs []*BrMsgID
|
||||
|
||||
// Not all bridges support "user is typing" indications so skip the message
|
||||
// if the targeted bridge does not support it.
|
||||
if rmsg.Event == config.EventUserTyping {
|
||||
if _, ok := bridgemap.UserTypingSupport[dest.Protocol]; !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// if we have an attached file, or other info
|
||||
if rmsg.Extra != nil && len(rmsg.Extra[config.EventFileFailureSize]) != 0 && rmsg.Text == "" {
|
||||
return brMsgIDs
|
||||
|
||||
8
go.mod
8
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
github.com/BurntSushi/toml v0.0.0-20170318202913-d94612f9fc14 // indirect
|
||||
github.com/Jeffail/gabs v1.1.1 // indirect
|
||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329
|
||||
github.com/Rhymen/go-whatsapp v0.0.2
|
||||
github.com/bwmarrin/discordgo v0.19.0
|
||||
github.com/d5/tengo v1.20.0
|
||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec
|
||||
@@ -27,12 +28,10 @@ require (
|
||||
github.com/lusis/go-slackbot v0.0.0-20180109053408-401027ccfef5 // indirect
|
||||
github.com/lusis/slack-test v0.0.0-20180109053238-3c758769bfa6 // indirect
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d
|
||||
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91
|
||||
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61
|
||||
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e
|
||||
github.com/mattermost/mattermost-server v5.5.0+incompatible
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
|
||||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474 // indirect
|
||||
@@ -45,7 +44,6 @@ require (
|
||||
github.com/paulrosania/go-charset v0.0.0-20151028000031-621bb39fcc83
|
||||
github.com/pborman/uuid v0.0.0-20160216163710-c55201b03606 // indirect
|
||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320
|
||||
github.com/pkg/errors v0.8.0 // indirect
|
||||
github.com/rs/xid v1.2.1
|
||||
github.com/russross/blackfriday v1.5.2
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca
|
||||
@@ -68,6 +66,10 @@ require (
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
go.uber.org/zap v1.9.1 // indirect
|
||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9
|
||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc // indirect
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/fsnotify.v1 v1.4.7 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
|
||||
|
||||
36
go.sum
36
go.sum
@@ -8,6 +8,13 @@ github.com/Jeffail/gabs v1.1.1 h1:V0uzR08Hj22EX8+8QMhyI9sX2hwRu+/RJhJUmnwda/E=
|
||||
github.com/Jeffail/gabs v1.1.1/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc=
|
||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329 h1:xZBoq249G9MSt+XuY7sVQzcfONJ6IQuwpCK+KAaOpnY=
|
||||
github.com/Philipp15b/go-steam v1.0.1-0.20180818081528-681bd9573329/go.mod h1:HuVM+sZFzumUdKPWiz+IlCMb4RdsKdT3T+nQBKL+sYg=
|
||||
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
|
||||
github.com/Rhymen/go-whatsapp v0.0.2 h1:MelwdquHuuNObBGV7CpXbky2aVdilx/CwiXMwZvS74U=
|
||||
github.com/Rhymen/go-whatsapp v0.0.2/go.mod h1:qf/2PQi82Okxw/igghu/oMGzTeUYuKBq1JNo3tdQyNg=
|
||||
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
|
||||
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
|
||||
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
|
||||
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
|
||||
github.com/alexcesaro/log v0.0.0-20150915221235-61e686294e58/go.mod h1:YNfsMyWSs+h+PaYkxGeMVmVCX75Zj/pqdjbu12ciCYE=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/bwmarrin/discordgo v0.19.0 h1:kMED/DB0NR1QhRcalb85w0Cu3Ep2OrGAqZH1R5awQiY=
|
||||
@@ -24,21 +31,19 @@ github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec h1:JEUiu7P9smN7zgX
|
||||
github.com/dfordsoft/golib v0.0.0-20180902042739-76ee6ab99bec/go.mod h1:UGa5M2Sz/Uh13AMse4+RELKCDw7kqgqlTjeGae+7vUY=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible h1:i64CCJcSqkRIkm5OSdZQjZq84/gJsk2zNwHWIRYWlKE=
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.5-0.20181225215658-ec221ba9ea45+incompatible/go.mod h1:qf9acutJ8cwBUhm1bqgz6Bei9/C/c93FPDljKWwsOgM=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/google/gops v0.3.5 h1:SIWvPLiYvy5vMwjxB3rVFTE4QBhUFj2KKWr3Xm7CKhw=
|
||||
github.com/google/gops v0.3.5/go.mod h1:pMQgrscwEK/aUSW1IFSaBPbJX82FPHWaSoJw1axQfD0=
|
||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbVkFXCXHz1QPhVXcHnQKAzBTPfQo=
|
||||
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/schema v1.0.2 h1:sAgNfOcNYvdDSrzGHVy9nzCQahG+qmsg+nE8dK85QRA=
|
||||
github.com/gorilla/schema v1.0.2/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
@@ -78,8 +83,6 @@ github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDe
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d h1:F+Sr+C0ojSlYQ37BLylQtSFmyQULe3jbAygcyXQ9mVs=
|
||||
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20190210153444-cc9d05784d5d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
|
||||
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b h1:cO6Z+yj4Ivq/ay/IxSrV90oSIW/SSXWLa+XHsiLKMrw=
|
||||
github.com/matterbridge/go-whatsapp v0.0.1-0.20190301204034-f2f1b29d441b/go.mod h1:dW19fYkkdUZsBAx7zv9fDh0n6NRqYIaKwB2JEBw8d0U=
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91 h1:KzDEcy8eDbTx881giW8a6llsAck3e2bJvMyKvh1IK+k=
|
||||
github.com/matterbridge/go-xmpp v0.0.0-20180529212104-cd19799fba91/go.mod h1:ECDRehsR9TYTKCAsRS8/wLeOk6UUqDydw47ln7wG41Q=
|
||||
github.com/matterbridge/gomatrix v0.0.0-20190102230110-6f9631ca6dea h1:kaADGqpK4gGO2BpzEyJrBxq2Jc57Rsar4i2EUxcACUc=
|
||||
@@ -88,8 +91,6 @@ github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18 h1:fLhwXtW
|
||||
github.com/matterbridge/gozulipbot v0.0.0-20190212232658-7aa251978a18/go.mod h1:yAjnZ34DuDyPHMPHHjOsTk/FefW4JJjoMMCGt/8uuQA=
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61 h1:R/MgM/eUyRBQx2FiH6JVmXck8PaAuKfe2M1tWIzW7nE=
|
||||
github.com/matterbridge/logrus-prefixed-formatter v0.0.0-20180806162718-01618749af61/go.mod h1:iXGEotOvwI1R1SjLxRc+BF5rUORTMtE0iMZBT2lxqAU=
|
||||
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e h1:1NqciL8sz+0UYeFrd/UQlL8tJPhFxOBmg+a94DN2sJU=
|
||||
github.com/matterbridge/mautrix-whatsapp v0.0.0-20190301210046-3539cf52ed6e/go.mod h1:DrIFGcFumRlEW5k3PJjWGKPd4+w37d3SwOxlh1ZAL+4=
|
||||
github.com/mattermost/mattermost-server v5.5.0+incompatible h1:0wcLGgYtd+YImtLDPf2AOfpBHxbU4suATx+6XKw1XbU=
|
||||
github.com/mattermost/mattermost-server v5.5.0+incompatible/go.mod h1:5L6MjAec+XXQwMIt791Ganu45GKsSiM+I0tLR9wUj8Y=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -98,7 +99,6 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
@@ -125,22 +125,20 @@ github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320 h1:YxcQy/DV+48NGv1lxx1vsWBzs6W1f1ogubkuCozxpX0=
|
||||
github.com/peterhellberg/emojilib v0.0.0-20190124112554-c18758d55320/go.mod h1:G7LufuPajuIvdt9OitkNt2qh0mmvD4bfRgRM7bhDIOA=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1 h1:Lx3BlDGFElJt4u/zKc9A3BuGYbQAGlEFyPuUA3jeMD0=
|
||||
github.com/shazow/rateio v0.0.0-20150116013248-e8e00881e5c1/go.mod h1:vt2jWY/3Qw1bIzle5thrJWucsLuuX9iUNnp20CqCciI=
|
||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296 h1:8RLq547MSVc6vhOuCl4Ca0TsAQknj6NX6ZLSZ3+xmio=
|
||||
github.com/shazow/ssh-chat v0.0.0-20190125184227-81d7e1686296/go.mod h1:1GLXsL4esywkpNId3v4QWuMf3THtWGitWvtQ/L3aSA4=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.3.0 h1:hI/7Q+DtNZ2kINb6qt/lS+IyXnHQe9e90POfeewL/ME=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
||||
@@ -199,22 +197,26 @@ golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190130090550-b01c7a725664/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f h1:qWFY9ZxP3tfI37wYIs/MnIAqK0vlXp1xnYEa5HxFSSY=
|
||||
golang.org/x/crypto v0.0.0-20190222235706-ffb98f73852f/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9 h1:+vH8qNweCrORN49012OX3h0oWEXO3p+rRnpAGQinddk=
|
||||
golang.org/x/image v0.0.0-20190220214146-31aff87c08e9/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc h1:Yx9JGxI1SBhVLFjpAkWMaO1TF+xyqtHLjZpvQboJGiM=
|
||||
golang.org/x/net v0.0.0-20190110200230-915654e7eabc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190129075346-302c3dd5f1cc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e h1:oF7qaQxUH6KzFdKN4ww7NpPdo53SZi4UlcksLrb2y/o=
|
||||
golang.org/x/sys v0.0.0-20190222171317-cd391775e71e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -226,7 +228,3 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
|
||||
maunium.net/go/maulogger/v2 v2.0.0/go.mod h1:Hbbkq3NV6jvJodByZu1mgEF3fpT7Kz9z0MjEZ3/BusI=
|
||||
maunium.net/go/mautrix v0.1.0-alpha.3/go.mod h1:GTVu6WDHR+98DKOrYetWsXorvUeKQV3jsSWO6ScbuFI=
|
||||
maunium.net/go/mautrix-appservice v0.1.0-alpha.3/go.mod h1:wOnWOIuprYad7ly12rHIo3JLCPh4jwvx1prVrAB9RhM=
|
||||
|
||||
288
internal/bindata.go
Normal file
288
internal/bindata.go
Normal file
@@ -0,0 +1,288 @@
|
||||
// Code generated by go-bindata. DO NOT EDIT.
|
||||
// sources:
|
||||
// tengo/outmessage.tengo
|
||||
|
||||
package internal
|
||||
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func bindataRead(data []byte, name string) ([]byte, error) {
|
||||
gz, err := gzip.NewReader(bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
_, err = io.Copy(&buf, gz)
|
||||
clErr := gz.Close()
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Read %q: %v", name, err)
|
||||
}
|
||||
if clErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
|
||||
type asset struct {
|
||||
bytes []byte
|
||||
info fileInfoEx
|
||||
}
|
||||
|
||||
type fileInfoEx interface {
|
||||
os.FileInfo
|
||||
MD5Checksum() string
|
||||
}
|
||||
|
||||
type bindataFileInfo struct {
|
||||
name string
|
||||
size int64
|
||||
mode os.FileMode
|
||||
modTime time.Time
|
||||
md5checksum string
|
||||
}
|
||||
|
||||
func (fi bindataFileInfo) Name() string {
|
||||
return fi.name
|
||||
}
|
||||
func (fi bindataFileInfo) Size() int64 {
|
||||
return fi.size
|
||||
}
|
||||
func (fi bindataFileInfo) Mode() os.FileMode {
|
||||
return fi.mode
|
||||
}
|
||||
func (fi bindataFileInfo) ModTime() time.Time {
|
||||
return fi.modTime
|
||||
}
|
||||
func (fi bindataFileInfo) MD5Checksum() string {
|
||||
return fi.md5checksum
|
||||
}
|
||||
func (fi bindataFileInfo) IsDir() bool {
|
||||
return false
|
||||
}
|
||||
func (fi bindataFileInfo) Sys() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
var _bindataTengoOutmessagetengo = []byte(
|
||||
"\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xc4\x91\x3d\x8f\xda\x40\x10\x86\xfb\xfd\x15\x13\x37\xb1\x2d\x07\xe7\xa3" +
|
||||
"\xb3\x64\x59\x11\x45\x94\x2e\x8a\x92\x0a\xd0\xb1\xac\x07\x33\xd2\x7a\xc7\x1a\x8f\x31\x88\xe3\xbf\x9f\xcc\x01\x47" +
|
||||
"\x7f\xc5\x75\xef\xae\x9e\x9d\x77\x1f\x4d\x9e\x9a\xbd\x15\xb2\x1b\x8f\x3d\xd8\xbd\x25\x3f\x45\x30\x82\xb6\xfe\xc2" +
|
||||
"\xc1\x1f\x0b\x43\xe1\xa7\x73\x3c\x04\xcd\x80\xc2\x1f\x61\x65\xc7\x7e\xca\xf3\x9d\x0d\x01\x2f\xf1\x97\x55\x1c\xed" +
|
||||
"\xd1\xf0\xa0\x77\x98\x07\x7d\xa3\x79\xd0\x3b\xce\x83\xde\xf8\xd7\x9e\x51\x48\xb1\x30\x6d\xdf\xfc\xc3\x83\x66\xd0" +
|
||||
"\xf6\xcd\xff\x1e\x25\xd8\x16\x4d\x9a\x1b\xa3\x78\x50\x28\x4a\xa0\xb6\x63\xd1\x38\x9a\xce\x51\x62\x4c\x9e\x43\xaf" +
|
||||
"\x42\x1d\x90\x38\x70\xec\x59\xfa\xe9\x8e\xb6\x30\xe2\x67\x41\x08\xac\xd0\x63\xa8\x29\x34\xa0\x0c\x36\x5c\xc0\x8d" +
|
||||
"\x50\xdd\x20\x8c\x78\x7d\xac\x3b\x84\xdf\x7f\xe7\xb7\x01\xb4\x7d\xd0\x84\xb2\x84\x88\xc4\x45\x70\x32\x00\x00\x82" +
|
||||
"\xd3\x3f\xa6\xfe\x99\xe0\x93\xe3\xb6\x23\x8f\xf1\x7a\x79\xf8\xfa\x23\xae\x8a\x65\x7d\xfa\x96\x7d\x3f\xc7\x55\x91" +
|
||||
"\x5d\x63\x52\x25\xd5\xf3\x62\x51\xb8\xa0\xe2\x8b\xd5\x6a\x9d\x5c\xc6\x5c\x4d\x4b\xc1\x99\x60\xe7\xad\xc3\xf8\x26" +
|
||||
"\x1f\x45\x89\x39\x9b\xf7\x6b\xe4\x29\x6d\x1f\x57\x00\x9f\x3e\xc6\x24\xcd\xcd\x4b\x00\x00\x00\xff\xff\x40\xb8\x54" +
|
||||
"\xb8\x64\x02\x00\x00")
|
||||
|
||||
func bindataTengoOutmessagetengoBytes() ([]byte, error) {
|
||||
return bindataRead(
|
||||
_bindataTengoOutmessagetengo,
|
||||
"tengo/outmessage.tengo",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
func bindataTengoOutmessagetengo() (*asset, error) {
|
||||
bytes, err := bindataTengoOutmessagetengoBytes()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
info := bindataFileInfo{
|
||||
name: "tengo/outmessage.tengo",
|
||||
size: 612,
|
||||
md5checksum: "",
|
||||
mode: os.FileMode(420),
|
||||
modTime: time.Unix(1555622139, 0),
|
||||
}
|
||||
|
||||
a := &asset{bytes: bytes, info: info}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Asset loads and returns the asset for the given name.
|
||||
// It returns an error if the asset could not be found or
|
||||
// could not be loaded.
|
||||
//
|
||||
func Asset(name string) ([]byte, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.bytes, nil
|
||||
}
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
//
|
||||
// MustAsset is like Asset but panics when Asset would return an error.
|
||||
// It simplifies safe initialization of global variables.
|
||||
// nolint: deadcode
|
||||
//
|
||||
func MustAsset(name string) []byte {
|
||||
a, err := Asset(name)
|
||||
if err != nil {
|
||||
panic("asset: Asset(" + name + "): " + err.Error())
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
||||
//
|
||||
// AssetInfo loads and returns the asset info for the given name.
|
||||
// It returns an error if the asset could not be found or could not be loaded.
|
||||
//
|
||||
func AssetInfo(name string) (os.FileInfo, error) {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
if f, ok := _bindata[cannonicalName]; ok {
|
||||
a, err := f()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
|
||||
}
|
||||
return a.info, nil
|
||||
}
|
||||
return nil, &os.PathError{Op: "open", Path: name, Err: os.ErrNotExist}
|
||||
}
|
||||
|
||||
//
|
||||
// AssetNames returns the names of the assets.
|
||||
// nolint: deadcode
|
||||
//
|
||||
func AssetNames() []string {
|
||||
names := make([]string, 0, len(_bindata))
|
||||
for name := range _bindata {
|
||||
names = append(names, name)
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
//
|
||||
// _bindata is a table, holding each asset generator, mapped to its name.
|
||||
//
|
||||
var _bindata = map[string]func() (*asset, error){
|
||||
"tengo/outmessage.tengo": bindataTengoOutmessagetengo,
|
||||
}
|
||||
|
||||
//
|
||||
// AssetDir returns the file names below a certain
|
||||
// directory embedded in the file by go-bindata.
|
||||
// For example if you run go-bindata on data/... and data contains the
|
||||
// following hierarchy:
|
||||
// data/
|
||||
// foo.txt
|
||||
// img/
|
||||
// a.png
|
||||
// b.png
|
||||
// then AssetDir("data") would return []string{"foo.txt", "img"}
|
||||
// AssetDir("data/img") would return []string{"a.png", "b.png"}
|
||||
// AssetDir("foo.txt") and AssetDir("notexist") would return an error
|
||||
// AssetDir("") will return []string{"data"}.
|
||||
//
|
||||
func AssetDir(name string) ([]string, error) {
|
||||
node := _bintree
|
||||
if len(name) != 0 {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
pathList := strings.Split(cannonicalName, "/")
|
||||
for _, p := range pathList {
|
||||
node = node.Children[p]
|
||||
if node == nil {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.Func != nil {
|
||||
return nil, &os.PathError{
|
||||
Op: "open",
|
||||
Path: name,
|
||||
Err: os.ErrNotExist,
|
||||
}
|
||||
}
|
||||
rv := make([]string, 0, len(node.Children))
|
||||
for childName := range node.Children {
|
||||
rv = append(rv, childName)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
|
||||
type bintree struct {
|
||||
Func func() (*asset, error)
|
||||
Children map[string]*bintree
|
||||
}
|
||||
|
||||
var _bintree = &bintree{Func: nil, Children: map[string]*bintree{
|
||||
"tengo": {Func: nil, Children: map[string]*bintree{
|
||||
"outmessage.tengo": {Func: bindataTengoOutmessagetengo, Children: map[string]*bintree{}},
|
||||
}},
|
||||
}}
|
||||
|
||||
// RestoreAsset restores an asset under the given directory
|
||||
func RestoreAsset(dir, name string) error {
|
||||
data, err := Asset(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
info, err := AssetInfo(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
|
||||
}
|
||||
|
||||
// RestoreAssets restores an asset under the given directory recursively
|
||||
func RestoreAssets(dir, name string) error {
|
||||
children, err := AssetDir(name)
|
||||
// File
|
||||
if err != nil {
|
||||
return RestoreAsset(dir, name)
|
||||
}
|
||||
// Dir
|
||||
for _, child := range children {
|
||||
err = RestoreAssets(dir, filepath.Join(name, child))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func _filePath(dir, name string) string {
|
||||
cannonicalName := strings.Replace(name, "\\", "/", -1)
|
||||
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
|
||||
}
|
||||
19
internal/tengo/outmessage.tengo
Normal file
19
internal/tengo/outmessage.tengo
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
variables available
|
||||
read-only:
|
||||
inAccount, inProtocol, inChannel, inGateway
|
||||
outAccount, outProtocol, outChannel, outGateway
|
||||
|
||||
read-write:
|
||||
msgText, msgUsername
|
||||
*/
|
||||
|
||||
text := import("text")
|
||||
|
||||
// start - strip irc colors
|
||||
// if we're not sending to an irc bridge we strip the IRC colors
|
||||
if inProtocol == "irc" {
|
||||
re := text.re_compile(`\x03(?:\d{1,2}(?:,\d{1,2})?)?|[[:cntrl:]]`)
|
||||
msgText=re.replace(msgText,"")
|
||||
}
|
||||
// end - strip irc colors
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
version = "1.14.2"
|
||||
version = "1.15.0"
|
||||
githash string
|
||||
|
||||
flagConfig = flag.String("conf", "matterbridge.toml", "config file")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#This is configuration for matterbridge.
|
||||
#WARNING: as this file contains credentials, be sure to set correct file permissions
|
||||
#See https://github.com/42wim/matterbridge/wiki/How-to-create-your-config for how to create your config
|
||||
#See https://github.com/42wim/matterbridge/wiki/Settings for all settings
|
||||
###################################################################
|
||||
#IRC section
|
||||
###################################################################
|
||||
@@ -27,7 +29,7 @@ UseTLS=false
|
||||
#OPTIONAL (default false)
|
||||
UseSASL=false
|
||||
|
||||
#Enable to not verify the certificate on your irc server. i
|
||||
#Enable to not verify the certificate on your irc server.
|
||||
#e.g. when using selfsigned certificates
|
||||
#OPTIONAL (default false)
|
||||
SkipTLSVerify=true
|
||||
@@ -155,6 +157,11 @@ RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
#Enable to show verbose users joins/parts (ident@host) from other bridges
|
||||
#Currently works for messages from the following bridges: irc
|
||||
#OPTIONAL (default false)
|
||||
VerboseJoinPart=false
|
||||
|
||||
#Do not send joins/parts to other bridges
|
||||
#Currently works for messages from the following bridges: irc, mattermost, slack
|
||||
#OPTIONAL (default false)
|
||||
@@ -270,98 +277,6 @@ StripNick=false
|
||||
#OPTIONAL (default false)
|
||||
ShowTopicChange=false
|
||||
|
||||
###################################################################
|
||||
#hipchat section
|
||||
###################################################################
|
||||
#Go to https://www.hipchat.com/account/xmpp this will show you the necessary data
|
||||
#to fill in the section below
|
||||
[xmpp.hipchat]
|
||||
#xmpp server to connect to.
|
||||
#REQUIRED
|
||||
Server="chat.hipchat.com:5222"
|
||||
|
||||
#Jabber ID
|
||||
#REQUIRED
|
||||
Jid="12345_12345@chat.hipchat.com"
|
||||
|
||||
#Password (your hipchat password)
|
||||
#REQUIRED
|
||||
Password="yourpass"
|
||||
|
||||
#Conference (MUC) domain
|
||||
#REQUIRED
|
||||
Muc="conf.hipchat.com"
|
||||
|
||||
#Room nickname
|
||||
#REQUIRED
|
||||
Nick="yourlogin"
|
||||
|
||||
## RELOADABLE SETTINGS
|
||||
## Settings below can be reloaded by editing the file
|
||||
|
||||
#Nicks you want to ignore.
|
||||
#Regular expressions supported
|
||||
#Messages from those users will not be sent to other bridges.
|
||||
#OPTIONAL
|
||||
IgnoreNicks="spammer1 spammer2"
|
||||
|
||||
#Messages you want to ignore.
|
||||
#Messages matching these regexp will be ignored and not sent to other bridges
|
||||
#See https://regex-golang.appspot.com/assets/html/index.html for more regex info
|
||||
#OPTIONAL (example below ignores messages starting with ~~ or messages containing 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"] ]
|
||||
|
||||
#Extractnicks is used to for example rewrite messages from other relaybots
|
||||
#See https://github.com/42wim/matterbridge/issues/713 and https://github.com/42wim/matterbridge/issues/466
|
||||
#some examples:
|
||||
#this replaces a message like "Relaybot: <relayeduser> something interesting" to "relayeduser: something interesting"
|
||||
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ] ]
|
||||
#you can use multiple entries for multiplebots
|
||||
#this also replaces a message like "otherbot: (relayeduser) something else" to "relayeduser: something else"
|
||||
#ExtractNicks=[ [ "Relaybot", "<(.*?)>\\s+" ],[ "otherbot","\\((.*?)\\)\\s+" ]
|
||||
#OPTIONAL (default empty)
|
||||
ExtractNicks=[ ["otherbot","<(.*?)>\\s+" ] ]
|
||||
|
||||
#extra label that can be used in the RemoteNickFormat
|
||||
#optional (default empty)
|
||||
Label=""
|
||||
|
||||
#RemoteNickFormat defines how remote users appear on this bridge
|
||||
#See [general] config section for default options
|
||||
RemoteNickFormat="[{PROTOCOL}/{BRIDGE}] <{NICK}> "
|
||||
|
||||
#Enable to show users joins/parts from other bridges
|
||||
#Currently works for messages from the following bridges: irc, mattermost, slack, discord
|
||||
#OPTIONAL (default false)
|
||||
ShowJoinPart=false
|
||||
|
||||
#StripNick only allows alphanumerical nicks. See https://github.com/42wim/matterbridge/issues/285
|
||||
#It will strip other characters from the nick
|
||||
#OPTIONAL (default false)
|
||||
StripNick=false
|
||||
|
||||
#Enable to show topic changes from other bridges
|
||||
#Only works hiding/show topic changes from slack bridge for now
|
||||
#OPTIONAL (default false)
|
||||
ShowTopicChange=false
|
||||
|
||||
###################################################################
|
||||
#mattermost section
|
||||
###################################################################
|
||||
@@ -1480,6 +1395,7 @@ RemoteNickFormat="{NICK}"
|
||||
#The string "{PROTOCOL}" (case sensitive) will be replaced by the protocol used by the bridge
|
||||
#The string "{GATEWAY}" (case sensitive) will be replaced by the origin gateway name that is replicating the message.
|
||||
#The string "{CHANNEL}" (case sensitive) will be replaced by the origin channel name used by the bridge
|
||||
#The string "{TENGO}" (case sensitive) will be replaced by the output of the RemoteNickFormat script under [tengo]
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="[{PROTOCOL}] <{NICK}> "
|
||||
|
||||
@@ -1529,8 +1445,14 @@ MediaDownloadBlacklist=[".html$",".htm$"]
|
||||
#OPTIONAL (default false)
|
||||
IgnoreFailureOnStart=false
|
||||
|
||||
###################################################################
|
||||
#Tengo configuration
|
||||
###################################################################
|
||||
#More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and
|
||||
#https://github.com/d5/tengo/blob/master/docs/stdlib.md
|
||||
|
||||
#TengoModifyMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
||||
[tengo]
|
||||
#InMessage allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
||||
#This script will receive every incoming message and can be used to modify the Username and the Text of that message.
|
||||
#The script will have the following global variables:
|
||||
#to modify: msgUsername and msgText
|
||||
@@ -1547,10 +1469,42 @@ IgnoreFailureOnStart=false
|
||||
# msgText="replaced by this"
|
||||
# msgUsername="fakeuser"
|
||||
#}
|
||||
#More information about tengo on: https://github.com/d5/tengo/blob/master/docs/tutorial.md and
|
||||
#https://github.com/d5/tengo/blob/master/docs/stdlib.md
|
||||
#OPTIONAL (default empty)
|
||||
TengoModifyMessage="example.tengo"
|
||||
InMessage="example.tengo"
|
||||
|
||||
#OutMessage allows you to specify the location of the script that
|
||||
#will be invoked on each message being sent to a bridge and can be used to modify the Username
|
||||
#and the Text of that message.
|
||||
#
|
||||
#The script will have the following global variables:
|
||||
#read-only:
|
||||
#inAccount, inProtocol, inChannel, inGateway, inEvent
|
||||
#outAccount, outProtocol, outChannel, outGateway, outEvent
|
||||
#
|
||||
#read-write:
|
||||
#msgText, msgUsername
|
||||
#
|
||||
#The script is reloaded on every message, so you can modify the script on the fly.
|
||||
#
|
||||
#The default script in https://github.com/42wim/matterbridge/tree/master/internal/tengo/outmessage.tengo
|
||||
#is compiled in and will be executed if no script is specified.
|
||||
#OPTIONAL (default empty)
|
||||
OutMessage="example.tengo"
|
||||
|
||||
|
||||
#RemoteNickFormat allows you to specify the location of a tengo (https://github.com/d5/tengo/) script.
|
||||
#The script will have the following global variables:
|
||||
#to modify: result
|
||||
#to read: channel, bridge, gateway, protocol, nick
|
||||
#
|
||||
#The result will be set in {TENGO} in the RemoteNickFormat key of every bridge where {TENGO} is specified
|
||||
#
|
||||
#The script is reloaded on every message, so you can modify the script on the fly.
|
||||
#
|
||||
#Example script can be found in https://github.com/42wim/matterbridge/tree/master/contrib/remotenickformat.tengo
|
||||
#
|
||||
#OPTIONAL (default empty)
|
||||
RemoteNickFormat="remotenickformat.tengo"
|
||||
|
||||
###################################################################
|
||||
#Gateway configuration
|
||||
|
||||
@@ -167,23 +167,42 @@ func (m *MMClient) JoinChannel(channelId string) error { //nolint:golint
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateChannelsTeam(teamID string) error {
|
||||
mmchannels, resp := m.Client.GetChannelsForTeamForUser(teamID, m.User.Id, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
}
|
||||
for idx, t := range m.OtherTeams {
|
||||
if t.Id == teamID {
|
||||
m.Lock()
|
||||
m.OtherTeams[idx].Channels = mmchannels
|
||||
m.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
mmchannels, resp = m.Client.GetPublicChannelsForTeam(teamID, 0, 5000, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
}
|
||||
for idx, t := range m.OtherTeams {
|
||||
if t.Id == teamID {
|
||||
m.Lock()
|
||||
m.OtherTeams[idx].MoreChannels = mmchannels
|
||||
m.Unlock()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateChannels() error {
|
||||
mmchannels, resp := m.Client.GetChannelsForTeamForUser(m.Team.Id, m.User.Id, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
if err := m.UpdateChannelsTeam(m.Team.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
m.Lock()
|
||||
m.Team.Channels = mmchannels
|
||||
m.Unlock()
|
||||
|
||||
mmchannels, resp = m.Client.GetPublicChannelsForTeam(m.Team.Id, 0, 5000, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
for _, t := range m.OtherTeams {
|
||||
if err := m.UpdateChannelsTeam(t.Id); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
m.Lock()
|
||||
m.Team.MoreChannels = mmchannels
|
||||
m.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -132,14 +132,25 @@ func (m *MMClient) initUser() error {
|
||||
return resp.Error
|
||||
}
|
||||
for _, team := range teams {
|
||||
mmusers, resp := m.Client.GetUsersInTeam(team.Id, 0, 50000, "")
|
||||
idx := 0
|
||||
max := 200
|
||||
usermap := make(map[string]*model.User)
|
||||
mmusers, resp := m.Client.GetUsersInTeam(team.Id, idx, max, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
}
|
||||
usermap := make(map[string]*model.User)
|
||||
for _, user := range mmusers {
|
||||
usermap[user.Id] = user
|
||||
for len(mmusers) > 0 {
|
||||
for _, user := range mmusers {
|
||||
usermap[user.Id] = user
|
||||
}
|
||||
mmusers, resp = m.Client.GetUsersInTeam(team.Id, idx, max, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
}
|
||||
idx++
|
||||
time.Sleep(time.Millisecond * 200)
|
||||
}
|
||||
m.logger.Infof("found %d users in team %s", len(usermap), team.Name)
|
||||
|
||||
t := &Team{Team: team, Users: usermap, Id: team.Id}
|
||||
|
||||
|
||||
@@ -220,7 +220,10 @@ func (m *MMClient) WsReceiver() {
|
||||
}
|
||||
}
|
||||
switch msg.Raw.Event {
|
||||
case model.WEBSOCKET_EVENT_USER_ADDED, model.WEBSOCKET_EVENT_USER_REMOVED:
|
||||
case model.WEBSOCKET_EVENT_USER_ADDED,
|
||||
model.WEBSOCKET_EVENT_USER_REMOVED,
|
||||
model.WEBSOCKET_EVENT_CHANNEL_CREATED,
|
||||
model.WEBSOCKET_EVENT_CHANNEL_DELETED:
|
||||
m.MessageChan <- msg
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ func (m *MMClient) DeleteMessage(postId string) error { //nolint:golint
|
||||
}
|
||||
|
||||
func (m *MMClient) EditMessage(postId string, text string) (string, error) { //nolint:golint
|
||||
post := &model.Post{Message: text}
|
||||
post := &model.Post{Message: text, Id: postId}
|
||||
res, resp := m.Client.UpdatePost(postId, post)
|
||||
if resp.Error != nil {
|
||||
return "", resp.Error
|
||||
|
||||
@@ -2,6 +2,7 @@ package matterclient
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/model"
|
||||
)
|
||||
@@ -99,15 +100,25 @@ func (m *MMClient) GetUsers() map[string]*model.User {
|
||||
}
|
||||
|
||||
func (m *MMClient) UpdateUsers() error {
|
||||
mmusers, resp := m.Client.GetUsers(0, 50000, "")
|
||||
idx := 0
|
||||
max := 200
|
||||
mmusers, resp := m.Client.GetUsers(idx, max, "")
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
}
|
||||
m.Lock()
|
||||
for _, user := range mmusers {
|
||||
m.Users[user.Id] = user
|
||||
for len(mmusers) > 0 {
|
||||
m.Lock()
|
||||
for _, user := range mmusers {
|
||||
m.Users[user.Id] = user
|
||||
}
|
||||
m.Unlock()
|
||||
mmusers, resp = m.Client.GetUsers(idx, max, "")
|
||||
time.Sleep(time.Millisecond * 300)
|
||||
if resp.Error != nil {
|
||||
return errors.New(resp.Error.DetailedError)
|
||||
}
|
||||
idx++
|
||||
}
|
||||
m.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
.idea/
|
||||
docs/
|
||||
build/
|
||||
@@ -3,7 +3,7 @@ Package rhymen/go-whatsapp implements the WhatsApp Web API to provide a clean in
|
||||
|
||||
## Installation
|
||||
```sh
|
||||
go get github.com/rhymen/go-whatsapp
|
||||
go get github.com/Rhymen/go-whatsapp
|
||||
```
|
||||
|
||||
## Usage
|
||||
@@ -30,7 +30,7 @@ The authentication process requires you to scan the qr code, that is send throug
|
||||
|
||||
### Restore
|
||||
```go
|
||||
newSess, err := wac.RestoreSession(sess)
|
||||
newSess, err := wac.RestoreWithSession(sess)
|
||||
```
|
||||
The restore function needs a valid session and returns the new session that was created.
|
||||
|
||||
@@ -2,7 +2,7 @@ package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/matterbridge/go-whatsapp/binary/token"
|
||||
"github.com/Rhymen/go-whatsapp/binary/token"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
@@ -2,7 +2,7 @@ package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/matterbridge/go-whatsapp/binary/token"
|
||||
"github.com/Rhymen/go-whatsapp/binary/token"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -2,7 +2,7 @@ package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
pb "github.com/matterbridge/go-whatsapp/binary/proto"
|
||||
pb "github.com/Rhymen/go-whatsapp/binary/proto"
|
||||
"github.com/golang/protobuf/proto"
|
||||
)
|
||||
|
||||
210
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
Normal file
210
vendor/github.com/Rhymen/go-whatsapp/conn.go
generated
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type metric byte
|
||||
|
||||
const (
|
||||
debugLog metric = iota + 1
|
||||
queryResume
|
||||
queryReceipt
|
||||
queryMedia
|
||||
queryChat
|
||||
queryContacts
|
||||
queryMessages
|
||||
presence
|
||||
presenceSubscribe
|
||||
group
|
||||
read
|
||||
chat
|
||||
received
|
||||
pic
|
||||
status
|
||||
message
|
||||
queryActions
|
||||
block
|
||||
queryGroup
|
||||
queryPreview
|
||||
queryEmoji
|
||||
queryMessageInfo
|
||||
spam
|
||||
querySearch
|
||||
queryIdentity
|
||||
queryUrl
|
||||
profile
|
||||
contact
|
||||
queryVcard
|
||||
queryStatus
|
||||
queryStatusUpdate
|
||||
privacyStatus
|
||||
queryLiveLocations
|
||||
liveLocation
|
||||
queryVname
|
||||
queryLabels
|
||||
call
|
||||
queryCall
|
||||
queryQuickReplies
|
||||
)
|
||||
|
||||
type flag byte
|
||||
|
||||
const (
|
||||
ignore flag = 1 << (7 - iota)
|
||||
ackRequest
|
||||
available
|
||||
notAvailable
|
||||
expires
|
||||
skipOffline
|
||||
)
|
||||
|
||||
/*
|
||||
Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
|
||||
It holds all necessary information to make the package work internally.
|
||||
*/
|
||||
type Conn struct {
|
||||
ws *websocketWrapper
|
||||
listener *listenerWrapper
|
||||
|
||||
connected bool
|
||||
loggedIn bool
|
||||
wg *sync.WaitGroup
|
||||
|
||||
session *Session
|
||||
sessionLock uint32
|
||||
handler []Handler
|
||||
msgCount int
|
||||
msgTimeout time.Duration
|
||||
Info *Info
|
||||
Store *Store
|
||||
ServerLastSeen time.Time
|
||||
|
||||
longClientName string
|
||||
shortClientName string
|
||||
}
|
||||
|
||||
type websocketWrapper struct {
|
||||
sync.Mutex
|
||||
conn *websocket.Conn
|
||||
close chan struct{}
|
||||
}
|
||||
|
||||
type listenerWrapper struct {
|
||||
sync.RWMutex
|
||||
m map[string]chan string
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
|
||||
The goroutine for handling incoming messages is started
|
||||
*/
|
||||
func NewConn(timeout time.Duration) (*Conn, error) {
|
||||
wac := &Conn{
|
||||
handler: make([]Handler, 0),
|
||||
msgCount: 0,
|
||||
msgTimeout: timeout,
|
||||
Store: newStore(),
|
||||
|
||||
longClientName: "github.com/rhymen/go-whatsapp",
|
||||
shortClientName: "go-whatsapp",
|
||||
}
|
||||
return wac, wac.connect()
|
||||
}
|
||||
|
||||
// connect should be guarded with wsWriteMutex
|
||||
func (wac *Conn) connect() (err error) {
|
||||
if wac.connected {
|
||||
return ErrAlreadyConnected
|
||||
}
|
||||
wac.connected = true
|
||||
defer func() { // set connected to false on error
|
||||
if err != nil {
|
||||
wac.connected = false
|
||||
}
|
||||
}()
|
||||
|
||||
dialer := &websocket.Dialer{
|
||||
ReadBufferSize: 25 * 1024 * 1024,
|
||||
WriteBufferSize: 10 * 1024 * 1024,
|
||||
HandshakeTimeout: wac.msgTimeout,
|
||||
}
|
||||
|
||||
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
|
||||
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "couldn't dial whatsapp web websocket")
|
||||
}
|
||||
|
||||
wsConn.SetCloseHandler(func(code int, text string) error {
|
||||
// from default CloseHandler
|
||||
message := websocket.FormatCloseMessage(code, "")
|
||||
err := wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
||||
|
||||
// our close handling
|
||||
_, _ = wac.Disconnect()
|
||||
wac.handle(&ErrConnectionClosed{Code: code, Text: text})
|
||||
return err
|
||||
})
|
||||
|
||||
wac.ws = &websocketWrapper{
|
||||
conn: wsConn,
|
||||
close: make(chan struct{}),
|
||||
}
|
||||
|
||||
wac.listener = &listenerWrapper{
|
||||
m: make(map[string]chan string),
|
||||
}
|
||||
|
||||
wac.wg = &sync.WaitGroup{}
|
||||
wac.wg.Add(2)
|
||||
go wac.readPump()
|
||||
go wac.keepAlive(20000, 60000)
|
||||
|
||||
wac.loggedIn = false
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) Disconnect() (Session, error) {
|
||||
if !wac.connected {
|
||||
return Session{}, ErrNotConnected
|
||||
}
|
||||
wac.connected = false
|
||||
wac.loggedIn = false
|
||||
|
||||
close(wac.ws.close) //signal close
|
||||
wac.wg.Wait() //wait for close
|
||||
|
||||
err := wac.ws.conn.Close()
|
||||
wac.ws = nil
|
||||
|
||||
if wac.session == nil {
|
||||
return Session{}, err
|
||||
}
|
||||
return *wac.session, err
|
||||
}
|
||||
|
||||
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
|
||||
defer wac.wg.Done()
|
||||
|
||||
for {
|
||||
err := wac.sendKeepAlive()
|
||||
if err != nil {
|
||||
wac.handle(errors.Wrap(err, "keepAlive failed"))
|
||||
//TODO: Consequences?
|
||||
}
|
||||
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
|
||||
select {
|
||||
case <-time.After(time.Duration(interval) * time.Millisecond):
|
||||
case <-wac.ws.close:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/matterbridge/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
@@ -18,21 +18,21 @@ const (
|
||||
)
|
||||
|
||||
//TODO: filename? WhatsApp uses Store.Contacts for these functions
|
||||
//TODO: functions probably shouldn't return a string, maybe build a struct / return json
|
||||
//TODO: check for further queries
|
||||
// functions probably shouldn't return a string, maybe build a struct / return json
|
||||
// check for further queries
|
||||
func (wac *Conn) GetProfilePicThumb(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "ProfilePicThumb", jid}
|
||||
return wac.write(data)
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) GetStatus(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "Status", jid}
|
||||
return wac.write(data)
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) SubscribePresence(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"action", "presence", "subscribe", jid}
|
||||
return wac.write(data)
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) Search(search string, count, page int) (*binary.Node, error) {
|
||||
@@ -84,7 +84,7 @@ func (wac *Conn) Presence(jid string, presence Presence) (<-chan string, error)
|
||||
|
||||
func (wac *Conn) Exist(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "exist", jid}
|
||||
return wac.write(data)
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) Emoji() (*binary.Node, error) {
|
||||
@@ -9,7 +9,6 @@ second stage "expands" this key into several additional pseudorandom keys (the o
|
||||
package hkdf
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
@@ -20,33 +19,29 @@ import (
|
||||
Expand expands a given key with the HKDF algorithm.
|
||||
*/
|
||||
func Expand(key []byte, length int, info string) ([]byte, error) {
|
||||
var h io.Reader
|
||||
if info == "" {
|
||||
keyBlock := hmac.New(sha256.New, key)
|
||||
var out, last []byte
|
||||
|
||||
var blockIndex byte = 1
|
||||
for i := 0; len(out) < length; i++ {
|
||||
keyBlock.Reset()
|
||||
//keyBlock.Write(append(append(last, []byte(info)...), blockIndex))
|
||||
keyBlock.Write(last)
|
||||
keyBlock.Write([]byte(info))
|
||||
keyBlock.Write([]byte{blockIndex})
|
||||
last = keyBlock.Sum(nil)
|
||||
blockIndex += 1
|
||||
out = append(out, last...)
|
||||
}
|
||||
return out[:length], nil
|
||||
/*
|
||||
Only used during initial login
|
||||
Pseudorandom Key is provided by server and has not to be created
|
||||
*/
|
||||
h = hkdf.Expand(sha256.New, key, []byte(info))
|
||||
} else {
|
||||
h := hkdf.New(sha256.New, key, nil, []byte(info))
|
||||
out := make([]byte, length)
|
||||
n, err := io.ReadAtLeast(h, out, length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != length {
|
||||
return nil, fmt.Errorf("new key to short")
|
||||
}
|
||||
|
||||
return out[:length], nil
|
||||
/*
|
||||
Used every other time
|
||||
Pseudorandom Key is created during kdf.New
|
||||
This is the normal that crypto/hkdf is used
|
||||
*/
|
||||
h = hkdf.New(sha256.New, key, nil, []byte(info))
|
||||
}
|
||||
out := make([]byte, length)
|
||||
n, err := io.ReadAtLeast(h, out, length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != length {
|
||||
return nil, fmt.Errorf("new key to short")
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
35
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
Normal file
35
vendor/github.com/Rhymen/go-whatsapp/errors.go
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAlreadyConnected = errors.New("already connected")
|
||||
ErrAlreadyLoggedIn = errors.New("already logged in")
|
||||
ErrInvalidSession = errors.New("invalid session")
|
||||
ErrLoginInProgress = errors.New("login or restore already running")
|
||||
ErrNotConnected = errors.New("not connected")
|
||||
ErrInvalidWsData = errors.New("received invalid data")
|
||||
ErrConnectionTimeout = errors.New("connection timed out")
|
||||
ErrMissingMessageTag = errors.New("no messageTag specified or to short")
|
||||
ErrInvalidHmac = errors.New("invalid hmac")
|
||||
)
|
||||
|
||||
type ErrConnectionFailed struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *ErrConnectionFailed) Error() string {
|
||||
return fmt.Sprintf("connection to WhatsApp servers failed: %v", e.Err)
|
||||
}
|
||||
|
||||
type ErrConnectionClosed struct {
|
||||
Code int
|
||||
Text string
|
||||
}
|
||||
|
||||
func (e *ErrConnectionClosed) Error() string {
|
||||
return fmt.Sprintf("server closed connection,code: %d,text: %s", e.Code, e.Text)
|
||||
}
|
||||
12
vendor/github.com/Rhymen/go-whatsapp/go.mod
generated
vendored
Normal file
12
vendor/github.com/Rhymen/go-whatsapp/go.mod
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
module github.com/Rhymen/go-whatsapp
|
||||
|
||||
require (
|
||||
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d // indirect
|
||||
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d // indirect
|
||||
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d // indirect
|
||||
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d // indirect
|
||||
github.com/golang/protobuf v1.3.0
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2
|
||||
)
|
||||
35
vendor/github.com/Rhymen/go-whatsapp/go.sum
generated
vendored
Normal file
35
vendor/github.com/Rhymen/go-whatsapp/go.sum
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f h1:2dk3eOnYllh+wUOuDhOoC2vUVoJF/5z478ryJ+wzEII=
|
||||
github.com/Baozisoftware/qrcode-terminal-go v0.0.0-20170407111555-c0650d8dff0f/go.mod h1:4a58ifQTEe2uwwsaqbh3i2un5/CBPg+At/qHpt18Tmk=
|
||||
github.com/Rhymen/go-whatsapp v0.0.0/go.mod h1:rdQr95g2C1xcOfM7QGOhza58HeI3I+tZ/bbluv7VazA=
|
||||
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d h1:m3wkrunHupL9XzzM+JZu1pgoDV1d9LFtD0gedNTHVDU=
|
||||
github.com/Rhymen/go-whatsapp/examples/echo v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:zgCiQtBtZ4P4gFWvwl9aashsdwOcbb/EHOGRmSzM8ME=
|
||||
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d h1:muQlzqfZxjptOBjPdv+UoxVMr8Y1rPx7VMGPJIAFc5w=
|
||||
github.com/Rhymen/go-whatsapp/examples/restoreSession v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:5sCUSpG616ZoSJhlt9iBNI/KXBqrVLcNUJqg7J9+8pU=
|
||||
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d h1:xP//3V77YvHd1cj2Z3ttuQWAvs5WmIwBbjKe/t0g/tM=
|
||||
github.com/Rhymen/go-whatsapp/examples/sendImage v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:RdiyhanVEGXTam+mZ3k6Y3VDCCvXYCwReOoxGozqhHw=
|
||||
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d h1:IRmRE0SPMByczwE2dhnTcVojje3w2TCSKwFrboLUbDg=
|
||||
github.com/Rhymen/go-whatsapp/examples/sendTextMessages v0.0.0-20190325075644-cc2581bbf24d/go.mod h1:suwzklatySS3Q0+NCxCDh5hYfgXdQUWU1DNcxwAxStM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk=
|
||||
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg=
|
||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
||||
github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw=
|
||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9 h1:lpEzuenPuO1XNTeikEmvqYFcU37GVLl8SRNblzyvGBE=
|
||||
github.com/skip2/go-qrcode v0.0.0-20190110000554-dc11ecdae0a9/go.mod h1:PLPIyL7ikehBD1OAjmKKiOEhbvWyHGaNDjquXMcYABo=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
func (wac *Conn) GetGroupMetaData(jid string) (<-chan string, error) {
|
||||
data := []interface{}{"query", "GroupMetadata", jid}
|
||||
return wac.write(data)
|
||||
return wac.writeJson(data)
|
||||
}
|
||||
|
||||
func (wac *Conn) CreateGroup(subject string, participants []string) (<-chan string, error) {
|
||||
@@ -41,7 +41,7 @@ func (wac *Conn) LeaveGroup(jid string) (<-chan string, error) {
|
||||
|
||||
func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
||||
request := []interface{}{"query", "inviteCode", jid}
|
||||
ch, err := wac.write(request)
|
||||
ch, err := wac.writeJson(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -63,3 +63,28 @@ func (wac *Conn) GroupInviteLink(jid string) (string, error) {
|
||||
|
||||
return response["code"].(string), nil
|
||||
}
|
||||
|
||||
func (wac *Conn) GroupAcceptInviteCode(code string) (jid string, err error) {
|
||||
request := []interface{}{"action", "invite", code}
|
||||
ch, err := wac.writeJson(request)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var response map[string]interface{}
|
||||
|
||||
select {
|
||||
case r := <-ch:
|
||||
if err := json.Unmarshal([]byte(r), &response); err != nil {
|
||||
return "", fmt.Errorf("error decoding response message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return "", fmt.Errorf("request timed out")
|
||||
}
|
||||
|
||||
if int(response["status"].(float64)) != 200 {
|
||||
return "", fmt.Errorf("request responded with %d", response["status"])
|
||||
}
|
||||
|
||||
return response["gid"].(string), nil
|
||||
}
|
||||
@@ -2,9 +2,11 @@ package whatsapp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/matterbridge/go-whatsapp/binary"
|
||||
"github.com/matterbridge/go-whatsapp/binary/proto"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -78,6 +80,22 @@ type RawMessageHandler interface {
|
||||
HandleRawMessage(message *proto.WebMessageInfo)
|
||||
}
|
||||
|
||||
/**
|
||||
The ContactListHandler interface needs to be implemented to applky custom actions to contact lists dispatched by the dispatcher.
|
||||
*/
|
||||
type ContactListHandler interface {
|
||||
Handler
|
||||
HandleContactList(contacts []Contact)
|
||||
}
|
||||
|
||||
/**
|
||||
The ChatListHandler interface needs to be implemented to apply custom actions to chat lists dispatched by the dispatcher.
|
||||
*/
|
||||
type ChatListHandler interface {
|
||||
Handler
|
||||
HandleChatList(contacts []Chat)
|
||||
}
|
||||
|
||||
/*
|
||||
AddHandler adds an handler to the list of handler that receive dispatched messages.
|
||||
The provided handler must at least implement the Handler interface. Additionally implemented
|
||||
@@ -88,6 +106,27 @@ func (wac *Conn) AddHandler(handler Handler) {
|
||||
wac.handler = append(wac.handler, handler)
|
||||
}
|
||||
|
||||
// RemoveHandler removes a handler from the list of handlers that receive dispatched messages.
|
||||
func (wac *Conn) RemoveHandler(handler Handler) bool {
|
||||
i := -1
|
||||
for k, v := range wac.handler {
|
||||
if v == handler {
|
||||
i = k
|
||||
break
|
||||
}
|
||||
}
|
||||
if i > -1 {
|
||||
wac.handler = append(wac.handler[:i], wac.handler[i+1:]...)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveHandlers empties the list of handlers that receive dispatched messages.
|
||||
func (wac *Conn) RemoveHandlers() {
|
||||
wac.handler = make([]Handler, 0)
|
||||
}
|
||||
|
||||
func (wac *Conn) handle(message interface{}) {
|
||||
switch m := message.(type) {
|
||||
case error:
|
||||
@@ -140,6 +179,62 @@ func (wac *Conn) handle(message interface{}) {
|
||||
|
||||
}
|
||||
|
||||
func (wac *Conn) handleContacts(contacts interface{}) {
|
||||
var contactList []Contact
|
||||
c, ok := contacts.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, contact := range c {
|
||||
contactNode, ok := contact.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
contactList = append(contactList, Contact{
|
||||
jid,
|
||||
contactNode.Attributes["notify"],
|
||||
contactNode.Attributes["name"],
|
||||
contactNode.Attributes["short"],
|
||||
})
|
||||
}
|
||||
for _, h := range wac.handler {
|
||||
if x, ok := h.(ContactListHandler); ok {
|
||||
go x.HandleContactList(contactList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) handleChats(chats interface{}) {
|
||||
var chatList []Chat
|
||||
c, ok := chats.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, chat := range c {
|
||||
chatNode, ok := chat.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
chatList = append(chatList, Chat{
|
||||
jid,
|
||||
chatNode.Attributes["name"],
|
||||
chatNode.Attributes["count"],
|
||||
chatNode.Attributes["t"],
|
||||
chatNode.Attributes["mute"],
|
||||
chatNode.Attributes["spam"],
|
||||
})
|
||||
}
|
||||
for _, h := range wac.handler {
|
||||
if x, ok := h.(ChatListHandler); ok {
|
||||
go x.HandleChatList(chatList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) dispatch(msg interface{}) {
|
||||
if msg == nil {
|
||||
return
|
||||
@@ -158,6 +253,10 @@ func (wac *Conn) dispatch(msg interface{}) {
|
||||
}
|
||||
} else if message.Description == "response" && message.Attributes["type"] == "contacts" {
|
||||
wac.updateContacts(message.Content)
|
||||
wac.handleContacts(message.Content)
|
||||
} else if message.Description == "response" && message.Attributes["type"] == "chat" {
|
||||
wac.updateChats(message.Content)
|
||||
wac.handleChats(message.Content)
|
||||
}
|
||||
case error:
|
||||
wac.handle(message)
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/matterbridge/go-whatsapp/crypto/cbc"
|
||||
"github.com/matterbridge/go-whatsapp/crypto/hkdf"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
@@ -133,7 +133,7 @@ func (wac *Conn) Upload(reader io.Reader, appInfo MediaType) (url string, mediaK
|
||||
}
|
||||
|
||||
uploadReq := []interface{}{"action", "encr_upload", filetype, base64.StdEncoding.EncodeToString(fileEncSha256)}
|
||||
ch, err := wac.write(uploadReq)
|
||||
ch, err := wac.writeJson(uploadReq)
|
||||
if err != nil {
|
||||
return "", nil, nil, nil, 0, err
|
||||
}
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/matterbridge/go-whatsapp/binary"
|
||||
"github.com/matterbridge/go-whatsapp/binary/proto"
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/binary/proto"
|
||||
"io"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
@@ -22,61 +22,77 @@ const (
|
||||
MediaDocument MediaType = "WhatsApp Document Keys"
|
||||
)
|
||||
|
||||
func (wac *Conn) Send(msg interface{}) error {
|
||||
var msgInfo MessageInfo
|
||||
|
||||
func (wac *Conn) Send(msg interface{}) (string, error) {
|
||||
var err error
|
||||
var ch <-chan string
|
||||
var msgProto *proto.WebMessageInfo
|
||||
|
||||
switch m := msg.(type) {
|
||||
case *proto.WebMessageInfo:
|
||||
ch, err = wac.sendProto(m)
|
||||
case TextMessage:
|
||||
ch, err = wac.sendProto(getTextProto(m))
|
||||
msgProto = getTextProto(m)
|
||||
msgInfo = getMessageInfo(msgProto)
|
||||
ch, err = wac.sendProto(msgProto)
|
||||
case ImageMessage:
|
||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaImage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("image upload failed: %v", err)
|
||||
return "ERROR", fmt.Errorf("image upload failed: %v", err)
|
||||
}
|
||||
ch, err = wac.sendProto(getImageProto(m))
|
||||
msgProto = getImageProto(m)
|
||||
msgInfo = getMessageInfo(msgProto)
|
||||
ch, err = wac.sendProto(msgProto)
|
||||
case VideoMessage:
|
||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaVideo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("video upload failed: %v", err)
|
||||
return "ERROR", fmt.Errorf("video upload failed: %v", err)
|
||||
}
|
||||
ch, err = wac.sendProto(getVideoProto(m))
|
||||
msgProto = getVideoProto(m)
|
||||
msgInfo = getMessageInfo(msgProto)
|
||||
ch, err = wac.sendProto(msgProto)
|
||||
case DocumentMessage:
|
||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaDocument)
|
||||
if err != nil {
|
||||
return fmt.Errorf("document upload failed: %v", err)
|
||||
return "ERROR", fmt.Errorf("document upload failed: %v", err)
|
||||
}
|
||||
ch, err = wac.sendProto(getDocumentProto(m))
|
||||
msgProto = getDocumentProto(m)
|
||||
msgInfo = getMessageInfo(msgProto)
|
||||
ch, err = wac.sendProto(msgProto)
|
||||
case AudioMessage:
|
||||
m.url, m.mediaKey, m.fileEncSha256, m.fileSha256, m.fileLength, err = wac.Upload(m.Content, MediaAudio)
|
||||
if err != nil {
|
||||
return fmt.Errorf("audio upload failed: %v", err)
|
||||
return "ERROR", fmt.Errorf("audio upload failed: %v", err)
|
||||
}
|
||||
ch, err = wac.sendProto(getAudioProto(m))
|
||||
msgProto = getAudioProto(m)
|
||||
msgInfo = getMessageInfo(msgProto)
|
||||
ch, err = wac.sendProto(msgProto)
|
||||
default:
|
||||
return fmt.Errorf("cannot match type %T, use message types declared in the package", msg)
|
||||
return "ERROR", fmt.Errorf("cannot match type %T, use message types declared in the package", msg)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not send proto: %v", err)
|
||||
return "ERROR", fmt.Errorf("could not send proto: %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case response := <-ch:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(response), &resp); err != nil {
|
||||
return fmt.Errorf("error decoding sending response: %v\n", err)
|
||||
return "ERROR", fmt.Errorf("error decoding sending response: %v\n", err)
|
||||
}
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
return fmt.Errorf("message sending responded with %d", resp["status"])
|
||||
return "ERROR", fmt.Errorf("message sending responded with %d", resp["status"])
|
||||
}
|
||||
if int(resp["status"].(float64)) == 200 {
|
||||
return msgInfo.Id, nil
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return fmt.Errorf("sending message timed out")
|
||||
return "ERROR", fmt.Errorf("sending message timed out")
|
||||
}
|
||||
|
||||
return nil
|
||||
return "ERROR", nil
|
||||
}
|
||||
|
||||
func (wac *Conn) sendProto(p *proto.WebMessageInfo) (<-chan string, error) {
|
||||
111
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
Normal file
111
vendor/github.com/Rhymen/go-whatsapp/read.go
generated
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (wac *Conn) readPump() {
|
||||
defer wac.wg.Done()
|
||||
|
||||
var readErr error
|
||||
var msgType int
|
||||
var reader io.Reader
|
||||
|
||||
for {
|
||||
readerFound := make(chan struct{})
|
||||
go func() {
|
||||
msgType, reader, readErr = wac.ws.conn.NextReader()
|
||||
close(readerFound)
|
||||
}()
|
||||
select {
|
||||
case <-readerFound:
|
||||
if readErr != nil {
|
||||
wac.handle(&ErrConnectionFailed{Err: readErr})
|
||||
_, _ = wac.Disconnect()
|
||||
return
|
||||
}
|
||||
msg, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
wac.handle(errors.Wrap(err, "error reading message from Reader"))
|
||||
continue
|
||||
}
|
||||
err = wac.processReadData(msgType, msg)
|
||||
if err != nil {
|
||||
wac.handle(errors.Wrap(err, "error processing data"))
|
||||
}
|
||||
case <-wac.ws.close:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) processReadData(msgType int, msg []byte) error {
|
||||
data := strings.SplitN(string(msg), ",", 2)
|
||||
|
||||
if data[0][0] == '!' { //Keep-Alive Timestamp
|
||||
data = append(data, data[0][1:]) //data[1]
|
||||
data[0] = "!"
|
||||
}
|
||||
|
||||
if len(data) != 2 || len(data[1]) == 0 {
|
||||
return ErrInvalidWsData
|
||||
}
|
||||
|
||||
wac.listener.RLock()
|
||||
listener, hasListener := wac.listener.m[data[0]]
|
||||
wac.listener.RUnlock()
|
||||
|
||||
if hasListener {
|
||||
// listener only exists for TextMessages query messages out of contact.go
|
||||
// If these binary query messages can be handled another way,
|
||||
// then the TextMessages, which are all JSON encoded, can directly
|
||||
// be unmarshalled. The listener chan could then be changed from type
|
||||
// chan string to something like chan map[string]interface{}. The unmarshalling
|
||||
// in several places, especially in session.go, would then be gone.
|
||||
listener <- data[1]
|
||||
|
||||
wac.listener.Lock()
|
||||
delete(wac.listener.m, data[0])
|
||||
wac.listener.Unlock()
|
||||
} else if msgType == websocket.BinaryMessage && wac.loggedIn {
|
||||
message, err := wac.decryptBinaryMessage([]byte(data[1]))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error decoding binary")
|
||||
}
|
||||
wac.dispatch(message)
|
||||
} else { //RAW json status updates
|
||||
wac.handle(string(data[1]))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
|
||||
//message validation
|
||||
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
||||
h2.Write([]byte(msg[32:]))
|
||||
if !hmac.Equal(h2.Sum(nil), msg[:32]) {
|
||||
return nil, ErrInvalidHmac
|
||||
}
|
||||
|
||||
// message decrypt
|
||||
d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "decrypting message with AES-CBC failed")
|
||||
}
|
||||
|
||||
// message unmarshal
|
||||
message, err := binary.Unmarshal(d)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not decode binary")
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
@@ -7,16 +7,20 @@ import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp/crypto/cbc"
|
||||
"github.com/matterbridge/go-whatsapp/crypto/curve25519"
|
||||
"github.com/matterbridge/go-whatsapp/crypto/hkdf"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/curve25519"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/hkdf"
|
||||
)
|
||||
|
||||
//represents the WhatsAppWeb client version
|
||||
var waVersion = []int{0, 3, 3324}
|
||||
|
||||
/*
|
||||
Session contains session individual information. To be able to resume the connection without scanning the qr code
|
||||
every time you should save the Session returned by Login and use RestoreSession the next time you want to login.
|
||||
every time you should save the Session returned by Login and use RestoreWithSession the next time you want to login.
|
||||
Every successful created connection returns a new Session. The Session(ClientToken, ServerToken) is altered after
|
||||
every re-login and should be saved every time.
|
||||
*/
|
||||
@@ -98,7 +102,7 @@ func (wac *Conn) SetClientName(long, short string) error {
|
||||
|
||||
/*
|
||||
Login is the function that creates a new whatsapp session and logs you in. If you do not want to scan the qr code
|
||||
every time, you should save the returned session and use RestoreSession the next time. Login takes a writable channel
|
||||
every time, you should save the returned session and use RestoreWithSession the next time. Login takes a writable channel
|
||||
as an parameter. This channel is used to push the data represented by the qr code back to the user. The received data
|
||||
should be displayed as an qr code in a way you prefer. To print a qr code to console you can use:
|
||||
github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
||||
@@ -121,7 +125,21 @@ github.com/Baozisoftware/qrcode-terminal-go Example login procedure:
|
||||
*/
|
||||
func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
||||
session := Session{}
|
||||
//Makes sure that only a single Login or Restore can happen at the same time
|
||||
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
||||
return session, ErrLoginInProgress
|
||||
}
|
||||
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
||||
|
||||
if wac.loggedIn {
|
||||
return session, ErrAlreadyLoggedIn
|
||||
}
|
||||
|
||||
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
||||
return session, err
|
||||
}
|
||||
|
||||
//logged in?!?
|
||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
||||
return session, fmt.Errorf("already logged in")
|
||||
}
|
||||
@@ -133,9 +151,8 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
||||
}
|
||||
|
||||
session.ClientId = base64.StdEncoding.EncodeToString(clientId)
|
||||
//oldVersion=8691
|
||||
login := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
||||
loginChan, err := wac.write(login)
|
||||
login := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
||||
loginChan, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
return session, fmt.Errorf("error writing login: %v\n", err)
|
||||
}
|
||||
@@ -160,14 +177,16 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
||||
}
|
||||
|
||||
//listener for Login response
|
||||
messageTag := "s1"
|
||||
wac.listener[messageTag] = make(chan string, 1)
|
||||
s1 := make(chan string, 1)
|
||||
wac.listener.Lock()
|
||||
wac.listener.m["s1"] = s1
|
||||
wac.listener.Unlock()
|
||||
|
||||
qrChan <- fmt.Sprintf("%v,%v,%v", ref, base64.StdEncoding.EncodeToString(pub[:]), session.ClientId)
|
||||
|
||||
var resp2 []interface{}
|
||||
select {
|
||||
case r1 := <-wac.listener[messageTag]:
|
||||
case r1 := <-s1:
|
||||
if err := json.Unmarshal([]byte(r1), &resp2); err != nil {
|
||||
return session, fmt.Errorf("error decoding qr code resp: %v", err)
|
||||
}
|
||||
@@ -226,90 +245,136 @@ func (wac *Conn) Login(qrChan chan<- string) (Session, error) {
|
||||
session.EncKey = keyDecrypted[:32]
|
||||
session.MacKey = keyDecrypted[32:64]
|
||||
wac.session = &session
|
||||
wac.loggedIn = true
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
//TODO: GoDoc
|
||||
/*
|
||||
RestoreSession is the function that restores a given session. It will try to reestablish the connection to the
|
||||
Basically the old RestoreSession functionality
|
||||
*/
|
||||
func (wac *Conn) RestoreWithSession(session Session) (_ Session, err error) {
|
||||
if wac.loggedIn {
|
||||
return Session{}, ErrAlreadyLoggedIn
|
||||
}
|
||||
old := wac.session
|
||||
defer func() {
|
||||
if err != nil {
|
||||
wac.session = old
|
||||
}
|
||||
}()
|
||||
wac.session = &session
|
||||
|
||||
if err = wac.Restore(); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, err
|
||||
}
|
||||
return *wac.session, nil
|
||||
}
|
||||
|
||||
/*//TODO: GoDoc
|
||||
RestoreWithSession is the function that restores a given session. It will try to reestablish the connection to the
|
||||
WhatsAppWeb servers with the provided session. If it succeeds it will return a new session. This new session has to be
|
||||
saved because the Client and Server-Token will change after every login. Logging in with old tokens is possible, but not
|
||||
suggested. If so, a challenge has to be resolved which is just another possible point of failure.
|
||||
*/
|
||||
func (wac *Conn) RestoreSession(session Session) (Session, error) {
|
||||
if wac.session != nil && (wac.session.EncKey != nil || wac.session.MacKey != nil) {
|
||||
return Session{}, fmt.Errorf("already logged in")
|
||||
func (wac *Conn) Restore() error {
|
||||
//Makes sure that only a single Login or Restore can happen at the same time
|
||||
if !atomic.CompareAndSwapUint32(&wac.sessionLock, 0, 1) {
|
||||
return ErrLoginInProgress
|
||||
}
|
||||
defer atomic.StoreUint32(&wac.sessionLock, 0)
|
||||
|
||||
if wac.session == nil {
|
||||
return ErrInvalidSession
|
||||
}
|
||||
|
||||
wac.session = &session
|
||||
if err := wac.connect(); err != nil && err != ErrAlreadyConnected {
|
||||
return err
|
||||
}
|
||||
|
||||
if wac.loggedIn {
|
||||
return ErrAlreadyLoggedIn
|
||||
}
|
||||
|
||||
//listener for Conn or challenge; s1 is not allowed to drop
|
||||
wac.listener["s1"] = make(chan string, 1)
|
||||
s1 := make(chan string, 1)
|
||||
wac.listener.Lock()
|
||||
wac.listener.m["s1"] = s1
|
||||
wac.listener.Unlock()
|
||||
|
||||
//admin init
|
||||
init := []interface{}{"admin", "init", []int{0, 3, 225}, []string{wac.longClientName, wac.shortClientName}, session.ClientId, true}
|
||||
initChan, err := wac.write(init)
|
||||
init := []interface{}{"admin", "init", waVersion, []string{wac.longClientName, wac.shortClientName}, wac.session.ClientId, true}
|
||||
initChan, err := wac.writeJson(init)
|
||||
if err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error writing admin init: %v\n", err)
|
||||
return fmt.Errorf("error writing admin init: %v\n", err)
|
||||
}
|
||||
|
||||
//admin login with takeover
|
||||
login := []interface{}{"admin", "login", session.ClientToken, session.ServerToken, session.ClientId, "takeover"}
|
||||
loginChan, err := wac.write(login)
|
||||
login := []interface{}{"admin", "login", wac.session.ClientToken, wac.session.ServerToken, wac.session.ClientId, "takeover"}
|
||||
loginChan, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error writing admin login: %v\n", err)
|
||||
return fmt.Errorf("error writing admin login: %v\n", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case r := <-initChan:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
}
|
||||
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("init responded with %d", resp["status"])
|
||||
return fmt.Errorf("init responded with %d", resp["status"])
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("restore session init timed out")
|
||||
return fmt.Errorf("restore session init timed out")
|
||||
}
|
||||
|
||||
//wait for s1
|
||||
var connResp []interface{}
|
||||
select {
|
||||
case r1 := <-wac.listener["s1"]:
|
||||
case r1 := <-s1:
|
||||
if err := json.Unmarshal([]byte(r1), &connResp); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error decoding s1 message: %v\n", err)
|
||||
return fmt.Errorf("error decoding s1 message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("restore session connection timed out")
|
||||
|
||||
//check for an error message
|
||||
select {
|
||||
case r := <-loginChan:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
}
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
return fmt.Errorf("admin login responded with %d", int(resp["status"].(float64)))
|
||||
}
|
||||
default:
|
||||
// not even an error message – assume timeout
|
||||
return fmt.Errorf("restore session connection timed out")
|
||||
}
|
||||
}
|
||||
|
||||
//check if challenge is present
|
||||
if len(connResp) == 2 && connResp[0] == "Cmd" && connResp[1].(map[string]interface{})["type"] == "challenge" {
|
||||
wac.listener["s2"] = make(chan string, 1)
|
||||
s2 := make(chan string, 1)
|
||||
wac.listener.Lock()
|
||||
wac.listener.m["s2"] = s2
|
||||
wac.listener.Unlock()
|
||||
|
||||
if err := wac.resolveChallenge(connResp[1].(map[string]interface{})["challenge"].(string)); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error resolving challenge: %v\n", err)
|
||||
return fmt.Errorf("error resolving challenge: %v\n", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case r := <-wac.listener["s2"]:
|
||||
case r := <-s2:
|
||||
if err := json.Unmarshal([]byte(r), &connResp); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error decoding s2 message: %v\n", err)
|
||||
return fmt.Errorf("error decoding s2 message: %v\n", err)
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("restore session challenge timed out")
|
||||
return fmt.Errorf("restore session challenge timed out")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,17 +383,14 @@ func (wac *Conn) RestoreSession(session Session) (Session, error) {
|
||||
case r := <-loginChan:
|
||||
var resp map[string]interface{}
|
||||
if err = json.Unmarshal([]byte(r), &resp); err != nil {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
return fmt.Errorf("error decoding login connResp: %v\n", err)
|
||||
}
|
||||
|
||||
if int(resp["status"].(float64)) != 200 {
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("admin login responded with %d", resp["status"])
|
||||
return fmt.Errorf("admin login responded with %d", resp["status"])
|
||||
}
|
||||
case <-time.After(wac.msgTimeout):
|
||||
wac.session = nil
|
||||
return Session{}, fmt.Errorf("restore session login timed out")
|
||||
return fmt.Errorf("restore session login timed out")
|
||||
}
|
||||
|
||||
info := connResp[1].(map[string]interface{})
|
||||
@@ -336,11 +398,12 @@ func (wac *Conn) RestoreSession(session Session) (Session, error) {
|
||||
wac.Info = newInfoFromReq(info)
|
||||
|
||||
//set new tokens
|
||||
session.ClientToken = info["clientToken"].(string)
|
||||
session.ServerToken = info["serverToken"].(string)
|
||||
session.Wid = info["wid"].(string)
|
||||
wac.session.ClientToken = info["clientToken"].(string)
|
||||
wac.session.ServerToken = info["serverToken"].(string)
|
||||
wac.session.Wid = info["wid"].(string)
|
||||
wac.loggedIn = true
|
||||
|
||||
return *wac.session, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) resolveChallenge(challenge string) error {
|
||||
@@ -353,7 +416,7 @@ func (wac *Conn) resolveChallenge(challenge string) error {
|
||||
h2.Write([]byte(decoded))
|
||||
|
||||
ch := []interface{}{"admin", "challenge", base64.StdEncoding.EncodeToString(h2.Sum(nil)), wac.session.ServerToken, wac.session.ClientId}
|
||||
challengeChan, err := wac.write(ch)
|
||||
challengeChan, err := wac.writeJson(ch)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing challenge: %v\n", err)
|
||||
}
|
||||
@@ -380,7 +443,7 @@ The session can not be resumed and will disappear on your phone in the WhatsAppW
|
||||
*/
|
||||
func (wac *Conn) Logout() error {
|
||||
login := []interface{}{"admin", "Conn", "disconnect"}
|
||||
_, err := wac.write(login)
|
||||
_, err := wac.writeJson(login)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing logout: %v\n", err)
|
||||
}
|
||||
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
Normal file
80
vendor/github.com/Rhymen/go-whatsapp/store.go
generated
vendored
Normal file
@@ -0,0 +1,80 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
Contacts map[string]Contact
|
||||
Chats map[string]Chat
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
Jid string
|
||||
Notify string
|
||||
Name string
|
||||
Short string
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
Jid string
|
||||
Name string
|
||||
Unread string
|
||||
LastMessageTime string
|
||||
IsMuted string
|
||||
IsMarkedSpam string
|
||||
}
|
||||
|
||||
func newStore() *Store {
|
||||
return &Store{
|
||||
make(map[string]Contact),
|
||||
make(map[string]Chat),
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) updateContacts(contacts interface{}) {
|
||||
c, ok := contacts.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, contact := range c {
|
||||
contactNode, ok := contact.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
wac.Store.Contacts[jid] = Contact{
|
||||
jid,
|
||||
contactNode.Attributes["notify"],
|
||||
contactNode.Attributes["name"],
|
||||
contactNode.Attributes["short"],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) updateChats(chats interface{}) {
|
||||
c, ok := chats.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, chat := range c {
|
||||
chatNode, ok := chat.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(chatNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
wac.Store.Chats[jid] = Chat{
|
||||
jid,
|
||||
chatNode.Attributes["name"],
|
||||
chatNode.Attributes["count"],
|
||||
chatNode.Attributes["t"],
|
||||
chatNode.Attributes["mute"],
|
||||
chatNode.Attributes["spam"],
|
||||
}
|
||||
}
|
||||
}
|
||||
125
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
Normal file
125
vendor/github.com/Rhymen/go-whatsapp/write.go
generated
vendored
Normal file
@@ -0,0 +1,125 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/Rhymen/go-whatsapp/binary"
|
||||
"github.com/Rhymen/go-whatsapp/crypto/cbc"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
//writeJson enqueues a json message into the writeChan
|
||||
func (wac *Conn) writeJson(data []interface{}) (<-chan string, error) {
|
||||
d, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts := time.Now().Unix()
|
||||
messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
bytes := []byte(fmt.Sprintf("%s,%s", messageTag, d))
|
||||
|
||||
ch, err := wac.write(websocket.TextMessage, messageTag, bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
wac.msgCount++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, messageTag string) (<-chan string, error) {
|
||||
if len(messageTag) < 2 {
|
||||
return nil, ErrMissingMessageTag
|
||||
}
|
||||
|
||||
data, err := wac.encryptBinaryMessage(node)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "encryptBinaryMessage(node) failed")
|
||||
}
|
||||
|
||||
bytes := []byte(messageTag + ",")
|
||||
bytes = append(bytes, byte(metric), byte(flag))
|
||||
bytes = append(bytes, data...)
|
||||
|
||||
ch, err := wac.write(websocket.BinaryMessage, messageTag, bytes)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to write message")
|
||||
}
|
||||
|
||||
wac.msgCount++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) sendKeepAlive() error {
|
||||
bytes := []byte("?,,")
|
||||
respChan, err := wac.write(websocket.TextMessage, "!", bytes)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error sending keepAlive")
|
||||
}
|
||||
|
||||
select {
|
||||
case resp := <-respChan:
|
||||
msecs, err := strconv.ParseInt(resp, 10, 64)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Error converting time string to uint")
|
||||
}
|
||||
wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
|
||||
|
||||
case <-time.After(wac.msgTimeout):
|
||||
return ErrConnectionTimeout
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (wac *Conn) write(messageType int, answerMessageTag string, data []byte) (<-chan string, error) {
|
||||
var ch chan string
|
||||
if answerMessageTag != "" {
|
||||
ch = make(chan string, 1)
|
||||
|
||||
wac.listener.Lock()
|
||||
wac.listener.m[answerMessageTag] = ch
|
||||
wac.listener.Unlock()
|
||||
}
|
||||
|
||||
wac.ws.Lock()
|
||||
err := wac.ws.conn.WriteMessage(messageType, data)
|
||||
wac.ws.Unlock()
|
||||
|
||||
if err != nil {
|
||||
if answerMessageTag != "" {
|
||||
wac.listener.Lock()
|
||||
delete(wac.listener.m, answerMessageTag)
|
||||
wac.listener.Unlock()
|
||||
}
|
||||
return nil, errors.Wrap(err, "error writing to websocket")
|
||||
}
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) encryptBinaryMessage(node binary.Node) (data []byte, err error) {
|
||||
b, err := binary.Marshal(node)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "binary node marshal failed")
|
||||
}
|
||||
|
||||
cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "encrypt failed")
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, wac.session.MacKey)
|
||||
h.Write(cipher)
|
||||
hash := h.Sum(nil)
|
||||
|
||||
data = append(data, hash[:32]...)
|
||||
data = append(data, cipher...)
|
||||
|
||||
return data, nil
|
||||
}
|
||||
1
vendor/github.com/golang/protobuf/proto/decode.go
generated
vendored
1
vendor/github.com/golang/protobuf/proto/decode.go
generated
vendored
@@ -186,7 +186,6 @@ func (p *Buffer) DecodeVarint() (x uint64, err error) {
|
||||
if b&0x80 == 0 {
|
||||
goto done
|
||||
}
|
||||
// x -= 0x80 << 63 // Always zero.
|
||||
|
||||
return 0, errOverflow
|
||||
|
||||
|
||||
63
vendor/github.com/golang/protobuf/proto/deprecated.go
generated
vendored
Normal file
63
vendor/github.com/golang/protobuf/proto/deprecated.go
generated
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
// Go support for Protocol Buffers - Google's data interchange format
|
||||
//
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// https://github.com/golang/protobuf
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package proto
|
||||
|
||||
import "errors"
|
||||
|
||||
// Deprecated: do not use.
|
||||
type Stats struct{ Emalloc, Dmalloc, Encode, Decode, Chit, Cmiss, Size uint64 }
|
||||
|
||||
// Deprecated: do not use.
|
||||
func GetStats() Stats { return Stats{} }
|
||||
|
||||
// Deprecated: do not use.
|
||||
func MarshalMessageSet(interface{}) ([]byte, error) {
|
||||
return nil, errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: do not use.
|
||||
func UnmarshalMessageSet([]byte, interface{}) error {
|
||||
return errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: do not use.
|
||||
func MarshalMessageSetJSON(interface{}) ([]byte, error) {
|
||||
return nil, errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: do not use.
|
||||
func UnmarshalMessageSetJSON([]byte, interface{}) error {
|
||||
return errors.New("proto: not implemented")
|
||||
}
|
||||
|
||||
// Deprecated: do not use.
|
||||
func RegisterMessageSetType(Message, int32, string) {}
|
||||
3
vendor/github.com/golang/protobuf/proto/equal.go
generated
vendored
3
vendor/github.com/golang/protobuf/proto/equal.go
generated
vendored
@@ -246,7 +246,8 @@ func equalExtMap(base reflect.Type, em1, em2 map[int32]Extension) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
m1, m2 := e1.value, e2.value
|
||||
m1 := extensionAsLegacyType(e1.value)
|
||||
m2 := extensionAsLegacyType(e2.value)
|
||||
|
||||
if m1 == nil && m2 == nil {
|
||||
// Both have only encoded form.
|
||||
|
||||
78
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
78
vendor/github.com/golang/protobuf/proto/extensions.go
generated
vendored
@@ -185,9 +185,25 @@ type Extension struct {
|
||||
// extension will have only enc set. When such an extension is
|
||||
// accessed using GetExtension (or GetExtensions) desc and value
|
||||
// will be set.
|
||||
desc *ExtensionDesc
|
||||
desc *ExtensionDesc
|
||||
|
||||
// value is a concrete value for the extension field. Let the type of
|
||||
// desc.ExtensionType be the "API type" and the type of Extension.value
|
||||
// be the "storage type". The API type and storage type are the same except:
|
||||
// * For scalars (except []byte), the API type uses *T,
|
||||
// while the storage type uses T.
|
||||
// * For repeated fields, the API type uses []T, while the storage type
|
||||
// uses *[]T.
|
||||
//
|
||||
// The reason for the divergence is so that the storage type more naturally
|
||||
// matches what is expected of when retrieving the values through the
|
||||
// protobuf reflection APIs.
|
||||
//
|
||||
// The value may only be populated if desc is also populated.
|
||||
value interface{}
|
||||
enc []byte
|
||||
|
||||
// enc is the raw bytes for the extension field.
|
||||
enc []byte
|
||||
}
|
||||
|
||||
// SetRawExtension is for testing only.
|
||||
@@ -334,7 +350,7 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
||||
// descriptors with the same field number.
|
||||
return nil, errors.New("proto: descriptor conflict")
|
||||
}
|
||||
return e.value, nil
|
||||
return extensionAsLegacyType(e.value), nil
|
||||
}
|
||||
|
||||
if extension.ExtensionType == nil {
|
||||
@@ -349,11 +365,11 @@ func GetExtension(pb Message, extension *ExtensionDesc) (interface{}, error) {
|
||||
|
||||
// Remember the decoded version and drop the encoded version.
|
||||
// That way it is safe to mutate what we return.
|
||||
e.value = v
|
||||
e.value = extensionAsStorageType(v)
|
||||
e.desc = extension
|
||||
e.enc = nil
|
||||
emap[extension.Field] = e
|
||||
return e.value, nil
|
||||
return extensionAsLegacyType(e.value), nil
|
||||
}
|
||||
|
||||
// defaultExtensionValue returns the default value for extension.
|
||||
@@ -488,7 +504,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
|
||||
}
|
||||
typ := reflect.TypeOf(extension.ExtensionType)
|
||||
if typ != reflect.TypeOf(value) {
|
||||
return errors.New("proto: bad extension value type")
|
||||
return fmt.Errorf("proto: bad extension value type. got: %T, want: %T", value, extension.ExtensionType)
|
||||
}
|
||||
// nil extension values need to be caught early, because the
|
||||
// encoder can't distinguish an ErrNil due to a nil extension
|
||||
@@ -500,7 +516,7 @@ func SetExtension(pb Message, extension *ExtensionDesc, value interface{}) error
|
||||
}
|
||||
|
||||
extmap := epb.extensionsWrite()
|
||||
extmap[extension.Field] = Extension{desc: extension, value: value}
|
||||
extmap[extension.Field] = Extension{desc: extension, value: extensionAsStorageType(value)}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -541,3 +557,51 @@ func RegisterExtension(desc *ExtensionDesc) {
|
||||
func RegisteredExtensions(pb Message) map[int32]*ExtensionDesc {
|
||||
return extensionMaps[reflect.TypeOf(pb).Elem()]
|
||||
}
|
||||
|
||||
// extensionAsLegacyType converts an value in the storage type as the API type.
|
||||
// See Extension.value.
|
||||
func extensionAsLegacyType(v interface{}) interface{} {
|
||||
switch rv := reflect.ValueOf(v); rv.Kind() {
|
||||
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
||||
// Represent primitive types as a pointer to the value.
|
||||
rv2 := reflect.New(rv.Type())
|
||||
rv2.Elem().Set(rv)
|
||||
v = rv2.Interface()
|
||||
case reflect.Ptr:
|
||||
// Represent slice types as the value itself.
|
||||
switch rv.Type().Elem().Kind() {
|
||||
case reflect.Slice:
|
||||
if rv.IsNil() {
|
||||
v = reflect.Zero(rv.Type().Elem()).Interface()
|
||||
} else {
|
||||
v = rv.Elem().Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// extensionAsStorageType converts an value in the API type as the storage type.
|
||||
// See Extension.value.
|
||||
func extensionAsStorageType(v interface{}) interface{} {
|
||||
switch rv := reflect.ValueOf(v); rv.Kind() {
|
||||
case reflect.Ptr:
|
||||
// Represent slice types as the value itself.
|
||||
switch rv.Type().Elem().Kind() {
|
||||
case reflect.Bool, reflect.Int32, reflect.Int64, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64, reflect.String:
|
||||
if rv.IsNil() {
|
||||
v = reflect.Zero(rv.Type().Elem()).Interface()
|
||||
} else {
|
||||
v = rv.Elem().Interface()
|
||||
}
|
||||
}
|
||||
case reflect.Slice:
|
||||
// Represent slice types as a pointer to the value.
|
||||
if rv.Type().Elem().Kind() != reflect.Uint8 {
|
||||
rv2 := reflect.New(rv.Type())
|
||||
rv2.Elem().Set(rv)
|
||||
v = rv2.Interface()
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
38
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
38
vendor/github.com/golang/protobuf/proto/lib.go
generated
vendored
@@ -341,26 +341,6 @@ type Message interface {
|
||||
ProtoMessage()
|
||||
}
|
||||
|
||||
// Stats records allocation details about the protocol buffer encoders
|
||||
// and decoders. Useful for tuning the library itself.
|
||||
type Stats struct {
|
||||
Emalloc uint64 // mallocs in encode
|
||||
Dmalloc uint64 // mallocs in decode
|
||||
Encode uint64 // number of encodes
|
||||
Decode uint64 // number of decodes
|
||||
Chit uint64 // number of cache hits
|
||||
Cmiss uint64 // number of cache misses
|
||||
Size uint64 // number of sizes
|
||||
}
|
||||
|
||||
// Set to true to enable stats collection.
|
||||
const collectStats = false
|
||||
|
||||
var stats Stats
|
||||
|
||||
// GetStats returns a copy of the global Stats structure.
|
||||
func GetStats() Stats { return stats }
|
||||
|
||||
// A Buffer is a buffer manager for marshaling and unmarshaling
|
||||
// protocol buffers. It may be reused between invocations to
|
||||
// reduce memory usage. It is not necessary to use a Buffer;
|
||||
@@ -960,13 +940,19 @@ func isProto3Zero(v reflect.Value) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
||||
// to assert that that code is compatible with this version of the proto package.
|
||||
const ProtoPackageIsVersion2 = true
|
||||
const (
|
||||
// ProtoPackageIsVersion3 is referenced from generated protocol buffer files
|
||||
// to assert that that code is compatible with this version of the proto package.
|
||||
ProtoPackageIsVersion3 = true
|
||||
|
||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
||||
// to assert that that code is compatible with this version of the proto package.
|
||||
const ProtoPackageIsVersion1 = true
|
||||
// ProtoPackageIsVersion2 is referenced from generated protocol buffer files
|
||||
// to assert that that code is compatible with this version of the proto package.
|
||||
ProtoPackageIsVersion2 = true
|
||||
|
||||
// ProtoPackageIsVersion1 is referenced from generated protocol buffer files
|
||||
// to assert that that code is compatible with this version of the proto package.
|
||||
ProtoPackageIsVersion1 = true
|
||||
)
|
||||
|
||||
// InternalMessageInfo is a type used internally by generated .pb.go files.
|
||||
// This type is not intended to be used by non-generated code.
|
||||
|
||||
137
vendor/github.com/golang/protobuf/proto/message_set.go
generated
vendored
137
vendor/github.com/golang/protobuf/proto/message_set.go
generated
vendored
@@ -36,13 +36,7 @@ package proto
|
||||
*/
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// errNoMessageTypeID occurs when a protocol buffer does not have a message type ID.
|
||||
@@ -145,46 +139,9 @@ func skipVarint(buf []byte) []byte {
|
||||
return buf[i+1:]
|
||||
}
|
||||
|
||||
// MarshalMessageSet encodes the extension map represented by m in the message set wire format.
|
||||
// It is called by generated Marshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func MarshalMessageSet(exts interface{}) ([]byte, error) {
|
||||
return marshalMessageSet(exts, false)
|
||||
}
|
||||
|
||||
// marshaMessageSet implements above function, with the opt to turn on / off deterministic during Marshal.
|
||||
func marshalMessageSet(exts interface{}, deterministic bool) ([]byte, error) {
|
||||
switch exts := exts.(type) {
|
||||
case *XXX_InternalExtensions:
|
||||
var u marshalInfo
|
||||
siz := u.sizeMessageSet(exts)
|
||||
b := make([]byte, 0, siz)
|
||||
return u.appendMessageSet(b, exts, deterministic)
|
||||
|
||||
case map[int32]Extension:
|
||||
// This is an old-style extension map.
|
||||
// Wrap it in a new-style XXX_InternalExtensions.
|
||||
ie := XXX_InternalExtensions{
|
||||
p: &struct {
|
||||
mu sync.Mutex
|
||||
extensionMap map[int32]Extension
|
||||
}{
|
||||
extensionMap: exts,
|
||||
},
|
||||
}
|
||||
|
||||
var u marshalInfo
|
||||
siz := u.sizeMessageSet(&ie)
|
||||
b := make([]byte, 0, siz)
|
||||
return u.appendMessageSet(b, &ie, deterministic)
|
||||
|
||||
default:
|
||||
return nil, errors.New("proto: not an extension map")
|
||||
}
|
||||
}
|
||||
|
||||
// UnmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||
// unmarshalMessageSet decodes the extension map encoded in buf in the message set wire format.
|
||||
// It is called by Unmarshal methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||
func unmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||
var m map[int32]Extension
|
||||
switch exts := exts.(type) {
|
||||
case *XXX_InternalExtensions:
|
||||
@@ -222,93 +179,3 @@ func UnmarshalMessageSet(buf []byte, exts interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalMessageSetJSON encodes the extension map represented by m in JSON format.
|
||||
// It is called by generated MarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func MarshalMessageSetJSON(exts interface{}) ([]byte, error) {
|
||||
var m map[int32]Extension
|
||||
switch exts := exts.(type) {
|
||||
case *XXX_InternalExtensions:
|
||||
var mu sync.Locker
|
||||
m, mu = exts.extensionsRead()
|
||||
if m != nil {
|
||||
// Keep the extensions map locked until we're done marshaling to prevent
|
||||
// races between marshaling and unmarshaling the lazily-{en,de}coded
|
||||
// values.
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
}
|
||||
case map[int32]Extension:
|
||||
m = exts
|
||||
default:
|
||||
return nil, errors.New("proto: not an extension map")
|
||||
}
|
||||
var b bytes.Buffer
|
||||
b.WriteByte('{')
|
||||
|
||||
// Process the map in key order for deterministic output.
|
||||
ids := make([]int32, 0, len(m))
|
||||
for id := range m {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
sort.Sort(int32Slice(ids)) // int32Slice defined in text.go
|
||||
|
||||
for i, id := range ids {
|
||||
ext := m[id]
|
||||
msd, ok := messageSetMap[id]
|
||||
if !ok {
|
||||
// Unknown type; we can't render it, so skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
if i > 0 && b.Len() > 1 {
|
||||
b.WriteByte(',')
|
||||
}
|
||||
|
||||
fmt.Fprintf(&b, `"[%s]":`, msd.name)
|
||||
|
||||
x := ext.value
|
||||
if x == nil {
|
||||
x = reflect.New(msd.t.Elem()).Interface()
|
||||
if err := Unmarshal(ext.enc, x.(Message)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d, err := json.Marshal(x)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
b.Write(d)
|
||||
}
|
||||
b.WriteByte('}')
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalMessageSetJSON decodes the extension map encoded in buf in JSON format.
|
||||
// It is called by generated UnmarshalJSON methods on protocol buffer messages with the message_set_wire_format option.
|
||||
func UnmarshalMessageSetJSON(buf []byte, exts interface{}) error {
|
||||
// Common-case fast path.
|
||||
if len(buf) == 0 || bytes.Equal(buf, []byte("{}")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// This is fairly tricky, and it's not clear that it is needed.
|
||||
return errors.New("TODO: UnmarshalMessageSetJSON not yet implemented")
|
||||
}
|
||||
|
||||
// A global registry of types that can be used in a MessageSet.
|
||||
|
||||
var messageSetMap = make(map[int32]messageSetDesc)
|
||||
|
||||
type messageSetDesc struct {
|
||||
t reflect.Type // pointer to struct
|
||||
name string
|
||||
}
|
||||
|
||||
// RegisterMessageSetType is called from the generated code.
|
||||
func RegisterMessageSetType(m Message, fieldNum int32, name string) {
|
||||
messageSetMap[fieldNum] = messageSetDesc{
|
||||
t: reflect.TypeOf(m),
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
5
vendor/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
5
vendor/github.com/golang/protobuf/proto/pointer_reflect.go
generated
vendored
@@ -79,10 +79,13 @@ func toPointer(i *Message) pointer {
|
||||
|
||||
// toAddrPointer converts an interface to a pointer that points to
|
||||
// the interface data.
|
||||
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
||||
func toAddrPointer(i *interface{}, isptr, deref bool) pointer {
|
||||
v := reflect.ValueOf(*i)
|
||||
u := reflect.New(v.Type())
|
||||
u.Elem().Set(v)
|
||||
if deref {
|
||||
u = u.Elem()
|
||||
}
|
||||
return pointer{v: u}
|
||||
}
|
||||
|
||||
|
||||
15
vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
15
vendor/github.com/golang/protobuf/proto/pointer_unsafe.go
generated
vendored
@@ -85,16 +85,21 @@ func toPointer(i *Message) pointer {
|
||||
|
||||
// toAddrPointer converts an interface to a pointer that points to
|
||||
// the interface data.
|
||||
func toAddrPointer(i *interface{}, isptr bool) pointer {
|
||||
func toAddrPointer(i *interface{}, isptr, deref bool) (p pointer) {
|
||||
// Super-tricky - read or get the address of data word of interface value.
|
||||
if isptr {
|
||||
// The interface is of pointer type, thus it is a direct interface.
|
||||
// The data word is the pointer data itself. We take its address.
|
||||
return pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
||||
p = pointer{p: unsafe.Pointer(uintptr(unsafe.Pointer(i)) + ptrSize)}
|
||||
} else {
|
||||
// The interface is not of pointer type. The data word is the pointer
|
||||
// to the data.
|
||||
p = pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||
}
|
||||
// The interface is not of pointer type. The data word is the pointer
|
||||
// to the data.
|
||||
return pointer{p: (*[2]unsafe.Pointer)(unsafe.Pointer(i))[1]}
|
||||
if deref {
|
||||
p.p = *(*unsafe.Pointer)(p.p)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// valToPointer converts v to a pointer. v must be of pointer type.
|
||||
|
||||
31
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
31
vendor/github.com/golang/protobuf/proto/properties.go
generated
vendored
@@ -334,9 +334,6 @@ func GetProperties(t reflect.Type) *StructProperties {
|
||||
sprop, ok := propertiesMap[t]
|
||||
propertiesMu.RUnlock()
|
||||
if ok {
|
||||
if collectStats {
|
||||
stats.Chit++
|
||||
}
|
||||
return sprop
|
||||
}
|
||||
|
||||
@@ -346,17 +343,20 @@ func GetProperties(t reflect.Type) *StructProperties {
|
||||
return sprop
|
||||
}
|
||||
|
||||
type (
|
||||
oneofFuncsIface interface {
|
||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
||||
}
|
||||
oneofWrappersIface interface {
|
||||
XXX_OneofWrappers() []interface{}
|
||||
}
|
||||
)
|
||||
|
||||
// getPropertiesLocked requires that propertiesMu is held.
|
||||
func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||
if prop, ok := propertiesMap[t]; ok {
|
||||
if collectStats {
|
||||
stats.Chit++
|
||||
}
|
||||
return prop
|
||||
}
|
||||
if collectStats {
|
||||
stats.Cmiss++
|
||||
}
|
||||
|
||||
prop := new(StructProperties)
|
||||
// in case of recursive protos, fill this in now.
|
||||
@@ -391,13 +391,14 @@ func getPropertiesLocked(t reflect.Type) *StructProperties {
|
||||
// Re-order prop.order.
|
||||
sort.Sort(prop)
|
||||
|
||||
type oneofMessage interface {
|
||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
||||
var oots []interface{}
|
||||
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
||||
case oneofFuncsIface:
|
||||
_, _, _, oots = m.XXX_OneofFuncs()
|
||||
case oneofWrappersIface:
|
||||
oots = m.XXX_OneofWrappers()
|
||||
}
|
||||
if om, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
||||
var oots []interface{}
|
||||
_, _, _, oots = om.XXX_OneofFuncs()
|
||||
|
||||
if len(oots) > 0 {
|
||||
// Interpret oneof metadata.
|
||||
prop.OneofTypes = make(map[string]*OneofProperties)
|
||||
for _, oot := range oots {
|
||||
|
||||
45
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
45
vendor/github.com/golang/protobuf/proto/table_marshal.go
generated
vendored
@@ -87,6 +87,7 @@ type marshalElemInfo struct {
|
||||
sizer sizer
|
||||
marshaler marshaler
|
||||
isptr bool // elem is pointer typed, thus interface of this type is a direct interface (extension only)
|
||||
deref bool // dereference the pointer before operating on it; implies isptr
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -320,8 +321,11 @@ func (u *marshalInfo) computeMarshalInfo() {
|
||||
|
||||
// get oneof implementers
|
||||
var oneofImplementers []interface{}
|
||||
if m, ok := reflect.Zero(reflect.PtrTo(t)).Interface().(oneofMessage); ok {
|
||||
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
||||
case oneofFuncsIface:
|
||||
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
|
||||
case oneofWrappersIface:
|
||||
oneofImplementers = m.XXX_OneofWrappers()
|
||||
}
|
||||
|
||||
n := t.NumField()
|
||||
@@ -407,13 +411,22 @@ func (u *marshalInfo) getExtElemInfo(desc *ExtensionDesc) *marshalElemInfo {
|
||||
panic("tag is not an integer")
|
||||
}
|
||||
wt := wiretype(tags[0])
|
||||
if t.Kind() == reflect.Ptr && t.Elem().Kind() != reflect.Struct {
|
||||
t = t.Elem()
|
||||
}
|
||||
sizer, marshaler := typeMarshaler(t, tags, false, false)
|
||||
var deref bool
|
||||
if t.Kind() == reflect.Slice && t.Elem().Kind() != reflect.Uint8 {
|
||||
t = reflect.PtrTo(t)
|
||||
deref = true
|
||||
}
|
||||
e = &marshalElemInfo{
|
||||
wiretag: uint64(tag)<<3 | wt,
|
||||
tagsize: SizeVarint(uint64(tag) << 3),
|
||||
sizer: sizer,
|
||||
marshaler: marshaler,
|
||||
isptr: t.Kind() == reflect.Ptr,
|
||||
deref: deref,
|
||||
}
|
||||
|
||||
// update cache
|
||||
@@ -448,7 +461,7 @@ func (fi *marshalFieldInfo) computeMarshalFieldInfo(f *reflect.StructField) {
|
||||
|
||||
func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofImplementers []interface{}) {
|
||||
fi.field = toField(f)
|
||||
fi.wiretag = 1<<31 - 1 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
|
||||
fi.wiretag = math.MaxInt32 // Use a large tag number, make oneofs sorted at the end. This tag will not appear on the wire.
|
||||
fi.isPointer = true
|
||||
fi.sizer, fi.marshaler = makeOneOfMarshaler(fi, f)
|
||||
fi.oneofElems = make(map[reflect.Type]*marshalElemInfo)
|
||||
@@ -476,10 +489,6 @@ func (fi *marshalFieldInfo) computeOneofFieldInfo(f *reflect.StructField, oneofI
|
||||
}
|
||||
}
|
||||
|
||||
type oneofMessage interface {
|
||||
XXX_OneofFuncs() (func(Message, *Buffer) error, func(Message, int, int, *Buffer) (bool, error), func(Message) int, []interface{})
|
||||
}
|
||||
|
||||
// wiretype returns the wire encoding of the type.
|
||||
func wiretype(encoding string) uint64 {
|
||||
switch encoding {
|
||||
@@ -2310,8 +2319,8 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
|
||||
for _, k := range m.MapKeys() {
|
||||
ki := k.Interface()
|
||||
vi := m.MapIndex(k).Interface()
|
||||
kaddr := toAddrPointer(&ki, false) // pointer to key
|
||||
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
|
||||
kaddr := toAddrPointer(&ki, false, false) // pointer to key
|
||||
vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value
|
||||
siz := keySizer(kaddr, 1) + valSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
||||
n += siz + SizeVarint(uint64(siz)) + tagsize
|
||||
}
|
||||
@@ -2329,8 +2338,8 @@ func makeMapMarshaler(f *reflect.StructField) (sizer, marshaler) {
|
||||
for _, k := range keys {
|
||||
ki := k.Interface()
|
||||
vi := m.MapIndex(k).Interface()
|
||||
kaddr := toAddrPointer(&ki, false) // pointer to key
|
||||
vaddr := toAddrPointer(&vi, valIsPtr) // pointer to value
|
||||
kaddr := toAddrPointer(&ki, false, false) // pointer to key
|
||||
vaddr := toAddrPointer(&vi, valIsPtr, false) // pointer to value
|
||||
b = appendVarint(b, tag)
|
||||
siz := keySizer(kaddr, 1) + valCachedSizer(vaddr, 1) // tag of key = 1 (size=1), tag of val = 2 (size=1)
|
||||
b = appendVarint(b, uint64(siz))
|
||||
@@ -2399,7 +2408,7 @@ func (u *marshalInfo) sizeExtensions(ext *XXX_InternalExtensions) int {
|
||||
// the last time this function was called.
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
n += ei.sizer(p, ei.tagsize)
|
||||
}
|
||||
mu.Unlock()
|
||||
@@ -2434,7 +2443,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
@@ -2465,7 +2474,7 @@ func (u *marshalInfo) appendExtensions(b []byte, ext *XXX_InternalExtensions, de
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
@@ -2510,7 +2519,7 @@ func (u *marshalInfo) sizeMessageSet(ext *XXX_InternalExtensions) int {
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
n += ei.sizer(p, 1) // message, tag = 3 (size=1)
|
||||
}
|
||||
mu.Unlock()
|
||||
@@ -2553,7 +2562,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
@@ -2591,7 +2600,7 @@ func (u *marshalInfo) appendMessageSet(b []byte, ext *XXX_InternalExtensions, de
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
b, err = ei.marshaler(b, p, 3<<3|WireBytes, deterministic)
|
||||
b = append(b, 1<<3|WireEndGroup)
|
||||
if !nerr.Merge(err) {
|
||||
@@ -2621,7 +2630,7 @@ func (u *marshalInfo) sizeV1Extensions(m map[int32]Extension) int {
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
n += ei.sizer(p, ei.tagsize)
|
||||
}
|
||||
return n
|
||||
@@ -2656,7 +2665,7 @@ func (u *marshalInfo) appendV1Extensions(b []byte, m map[int32]Extension, determ
|
||||
|
||||
ei := u.getExtElemInfo(e.desc)
|
||||
v := e.value
|
||||
p := toAddrPointer(&v, ei.isptr)
|
||||
p := toAddrPointer(&v, ei.isptr, ei.deref)
|
||||
b, err = ei.marshaler(b, p, ei.wiretag, deterministic)
|
||||
if !nerr.Merge(err) {
|
||||
return b, err
|
||||
|
||||
74
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
74
vendor/github.com/golang/protobuf/proto/table_unmarshal.go
generated
vendored
@@ -136,7 +136,7 @@ func (u *unmarshalInfo) unmarshal(m pointer, b []byte) error {
|
||||
u.computeUnmarshalInfo()
|
||||
}
|
||||
if u.isMessageSet {
|
||||
return UnmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
|
||||
return unmarshalMessageSet(b, m.offset(u.extensions).toExtensions())
|
||||
}
|
||||
var reqMask uint64 // bitmask of required fields we've seen.
|
||||
var errLater error
|
||||
@@ -362,46 +362,48 @@ func (u *unmarshalInfo) computeUnmarshalInfo() {
|
||||
}
|
||||
|
||||
// Find any types associated with oneof fields.
|
||||
// TODO: XXX_OneofFuncs returns more info than we need. Get rid of some of it?
|
||||
fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("XXX_OneofFuncs")
|
||||
if fn.IsValid() {
|
||||
res := fn.Call(nil)[3] // last return value from XXX_OneofFuncs: []interface{}
|
||||
for i := res.Len() - 1; i >= 0; i-- {
|
||||
v := res.Index(i) // interface{}
|
||||
tptr := reflect.ValueOf(v.Interface()).Type() // *Msg_X
|
||||
typ := tptr.Elem() // Msg_X
|
||||
var oneofImplementers []interface{}
|
||||
switch m := reflect.Zero(reflect.PtrTo(t)).Interface().(type) {
|
||||
case oneofFuncsIface:
|
||||
_, _, _, oneofImplementers = m.XXX_OneofFuncs()
|
||||
case oneofWrappersIface:
|
||||
oneofImplementers = m.XXX_OneofWrappers()
|
||||
}
|
||||
for _, v := range oneofImplementers {
|
||||
tptr := reflect.TypeOf(v) // *Msg_X
|
||||
typ := tptr.Elem() // Msg_X
|
||||
|
||||
f := typ.Field(0) // oneof implementers have one field
|
||||
baseUnmarshal := fieldUnmarshaler(&f)
|
||||
tags := strings.Split(f.Tag.Get("protobuf"), ",")
|
||||
fieldNum, err := strconv.Atoi(tags[1])
|
||||
if err != nil {
|
||||
panic("protobuf tag field not an integer: " + tags[1])
|
||||
}
|
||||
var name string
|
||||
for _, tag := range tags {
|
||||
if strings.HasPrefix(tag, "name=") {
|
||||
name = strings.TrimPrefix(tag, "name=")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find the oneof field that this struct implements.
|
||||
// Might take O(n^2) to process all of the oneofs, but who cares.
|
||||
for _, of := range oneofFields {
|
||||
if tptr.Implements(of.ityp) {
|
||||
// We have found the corresponding interface for this struct.
|
||||
// That lets us know where this struct should be stored
|
||||
// when we encounter it during unmarshaling.
|
||||
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
|
||||
u.setTag(fieldNum, of.field, unmarshal, 0, name)
|
||||
}
|
||||
f := typ.Field(0) // oneof implementers have one field
|
||||
baseUnmarshal := fieldUnmarshaler(&f)
|
||||
tags := strings.Split(f.Tag.Get("protobuf"), ",")
|
||||
fieldNum, err := strconv.Atoi(tags[1])
|
||||
if err != nil {
|
||||
panic("protobuf tag field not an integer: " + tags[1])
|
||||
}
|
||||
var name string
|
||||
for _, tag := range tags {
|
||||
if strings.HasPrefix(tag, "name=") {
|
||||
name = strings.TrimPrefix(tag, "name=")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Find the oneof field that this struct implements.
|
||||
// Might take O(n^2) to process all of the oneofs, but who cares.
|
||||
for _, of := range oneofFields {
|
||||
if tptr.Implements(of.ityp) {
|
||||
// We have found the corresponding interface for this struct.
|
||||
// That lets us know where this struct should be stored
|
||||
// when we encounter it during unmarshaling.
|
||||
unmarshal := makeUnmarshalOneof(typ, of.ityp, baseUnmarshal)
|
||||
u.setTag(fieldNum, of.field, unmarshal, 0, name)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Get extension ranges, if any.
|
||||
fn = reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
|
||||
fn := reflect.Zero(reflect.PtrTo(t)).MethodByName("ExtensionRangeArray")
|
||||
if fn.IsValid() {
|
||||
if !u.extensions.IsValid() && !u.oldExtensions.IsValid() {
|
||||
panic("a message with extensions, but no extensions field in " + t.Name())
|
||||
@@ -1948,7 +1950,7 @@ func encodeVarint(b []byte, x uint64) []byte {
|
||||
// If there is an error, it returns 0,0.
|
||||
func decodeVarint(b []byte) (uint64, int) {
|
||||
var x, y uint64
|
||||
if len(b) <= 0 {
|
||||
if len(b) == 0 {
|
||||
goto bad
|
||||
}
|
||||
x = uint64(b[0])
|
||||
|
||||
601
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
601
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go
generated
vendored
File diff suppressed because it is too large
Load Diff
11
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
generated
vendored
11
vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.proto
generated
vendored
@@ -417,6 +417,17 @@ message FileOptions {
|
||||
// determining the namespace.
|
||||
optional string php_namespace = 41;
|
||||
|
||||
|
||||
// Use this option to change the namespace of php generated metadata classes.
|
||||
// Default is empty. When this option is empty, the proto file name will be used
|
||||
// for determining the namespace.
|
||||
optional string php_metadata_namespace = 44;
|
||||
|
||||
// Use this option to change the package of ruby generated classes. Default
|
||||
// is empty. When this option is not set, the package name will be used for
|
||||
// determining the ruby package.
|
||||
optional string ruby_package = 45;
|
||||
|
||||
// The parser stores options it doesn't recognize here.
|
||||
// See the documentation for the "Options" section above.
|
||||
repeated UninterpretedOption uninterpreted_option = 999;
|
||||
|
||||
389
vendor/github.com/matterbridge/go-whatsapp/conn.go
generated
vendored
389
vendor/github.com/matterbridge/go-whatsapp/conn.go
generated
vendored
@@ -1,389 +0,0 @@
|
||||
//Package whatsapp provides a developer API to interact with the WhatsAppWeb-Servers.
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/matterbridge/go-whatsapp/binary"
|
||||
"github.com/matterbridge/go-whatsapp/crypto/cbc"
|
||||
)
|
||||
|
||||
type metric byte
|
||||
|
||||
const (
|
||||
debugLog metric = iota + 1
|
||||
queryResume
|
||||
queryReceipt
|
||||
queryMedia
|
||||
queryChat
|
||||
queryContacts
|
||||
queryMessages
|
||||
presence
|
||||
presenceSubscribe
|
||||
group
|
||||
read
|
||||
chat
|
||||
received
|
||||
pic
|
||||
status
|
||||
message
|
||||
queryActions
|
||||
block
|
||||
queryGroup
|
||||
queryPreview
|
||||
queryEmoji
|
||||
queryMessageInfo
|
||||
spam
|
||||
querySearch
|
||||
queryIdentity
|
||||
queryUrl
|
||||
profile
|
||||
contact
|
||||
queryVcard
|
||||
queryStatus
|
||||
queryStatusUpdate
|
||||
privacyStatus
|
||||
queryLiveLocations
|
||||
liveLocation
|
||||
queryVname
|
||||
queryLabels
|
||||
call
|
||||
queryCall
|
||||
queryQuickReplies
|
||||
)
|
||||
|
||||
type flag byte
|
||||
|
||||
const (
|
||||
ignore flag = 1 << (7 - iota)
|
||||
ackRequest
|
||||
available
|
||||
notAvailable
|
||||
expires
|
||||
skipOffline
|
||||
)
|
||||
|
||||
/*
|
||||
Conn is created by NewConn. Interacting with the initialized Conn is the main way of interacting with our package.
|
||||
It holds all necessary information to make the package work internally.
|
||||
*/
|
||||
type Conn struct {
|
||||
wsConn *websocket.Conn
|
||||
wsConnOK bool
|
||||
wsConnMutex sync.RWMutex
|
||||
session *Session
|
||||
listener map[string]chan string
|
||||
listenerMutex sync.RWMutex
|
||||
writeChan chan wsMsg
|
||||
handler []Handler
|
||||
msgCount int
|
||||
msgTimeout time.Duration
|
||||
Info *Info
|
||||
Store *Store
|
||||
ServerLastSeen time.Time
|
||||
|
||||
longClientName string
|
||||
shortClientName string
|
||||
}
|
||||
|
||||
type wsMsg struct {
|
||||
messageType int
|
||||
data []byte
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a new connection with a given timeout. The websocket connection to the WhatsAppWeb servers get´s established.
|
||||
The goroutine for handling incoming messages is started
|
||||
*/
|
||||
func NewConn(timeout time.Duration) (*Conn, error) {
|
||||
wac := &Conn{
|
||||
wsConn: nil, // will be set in connect()
|
||||
wsConnMutex: sync.RWMutex{},
|
||||
listener: make(map[string]chan string),
|
||||
listenerMutex: sync.RWMutex{},
|
||||
writeChan: make(chan wsMsg),
|
||||
handler: make([]Handler, 0),
|
||||
msgCount: 0,
|
||||
msgTimeout: timeout,
|
||||
Store: newStore(),
|
||||
|
||||
longClientName: "github.com/rhymen/go-whatsapp",
|
||||
shortClientName: "go-whatsapp",
|
||||
}
|
||||
|
||||
if err := wac.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go wac.readPump()
|
||||
go wac.writePump()
|
||||
go wac.keepAlive(20000, 60000)
|
||||
|
||||
return wac, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) isConnected() bool {
|
||||
wac.wsConnMutex.RLock()
|
||||
defer wac.wsConnMutex.RUnlock()
|
||||
if wac.wsConn == nil {
|
||||
return false
|
||||
}
|
||||
if wac.wsConnOK {
|
||||
return true
|
||||
}
|
||||
|
||||
// just send a keepalive to test the connection
|
||||
wac.sendKeepAlive()
|
||||
|
||||
// this method is expected to be called by loops. So we can just return false
|
||||
return false
|
||||
}
|
||||
|
||||
// connect should be guarded with wsConnMutex
|
||||
func (wac *Conn) connect() error {
|
||||
dialer := &websocket.Dialer{
|
||||
ReadBufferSize: 25 * 1024 * 1024,
|
||||
WriteBufferSize: 10 * 1024 * 1024,
|
||||
HandshakeTimeout: wac.msgTimeout,
|
||||
}
|
||||
|
||||
headers := http.Header{"Origin": []string{"https://web.whatsapp.com"}}
|
||||
wsConn, _, err := dialer.Dial("wss://web.whatsapp.com/ws", headers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("couldn't dial whatsapp web websocket: %v", err)
|
||||
}
|
||||
|
||||
wsConn.SetCloseHandler(func(code int, text string) error {
|
||||
fmt.Fprintf(os.Stderr, "websocket connection closed(%d, %s)\n", code, text)
|
||||
|
||||
// from default CloseHandler
|
||||
message := websocket.FormatCloseMessage(code, "")
|
||||
wsConn.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
||||
|
||||
// our close handling
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
|
||||
fmt.Println("Trigger reconnect")
|
||||
go wac.reconnect()
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
wac.wsConn = wsConn
|
||||
wac.wsConnOK = true
|
||||
return nil
|
||||
}
|
||||
|
||||
// reconnect should be run as go routine
|
||||
func (wac *Conn) reconnect() {
|
||||
wac.wsConnMutex.Lock()
|
||||
wac.wsConn.Close()
|
||||
wac.wsConn = nil
|
||||
wac.wsConnOK = false
|
||||
wac.wsConnMutex.Unlock()
|
||||
|
||||
// wait up to 60 seconds and then reconnect. As writePump should send immediately, it might
|
||||
// reconnect as well. So we check its existance before reconnecting
|
||||
for !wac.isConnected() {
|
||||
time.Sleep(time.Duration(rand.Intn(60)) * time.Second)
|
||||
|
||||
wac.wsConnMutex.Lock()
|
||||
if wac.wsConn == nil {
|
||||
if err := wac.connect(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "could not reconnect to websocket: %v\n", err)
|
||||
}
|
||||
}
|
||||
wac.wsConnMutex.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) write(data []interface{}) (<-chan string, error) {
|
||||
d, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ts := time.Now().Unix()
|
||||
messageTag := fmt.Sprintf("%d.--%d", ts, wac.msgCount)
|
||||
msg := fmt.Sprintf("%s,%s", messageTag, d)
|
||||
|
||||
ch := make(chan string, 1)
|
||||
|
||||
wac.listenerMutex.Lock()
|
||||
wac.listener[messageTag] = ch
|
||||
wac.listenerMutex.Unlock()
|
||||
|
||||
wac.writeChan <- wsMsg{websocket.TextMessage, []byte(msg)}
|
||||
|
||||
wac.msgCount++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) writeBinary(node binary.Node, metric metric, flag flag, tag string) (<-chan string, error) {
|
||||
if len(tag) < 2 {
|
||||
return nil, fmt.Errorf("no tag specified or to short")
|
||||
}
|
||||
b, err := binary.Marshal(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cipher, err := cbc.Encrypt(wac.session.EncKey, nil, b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := hmac.New(sha256.New, wac.session.MacKey)
|
||||
h.Write(cipher)
|
||||
hash := h.Sum(nil)
|
||||
|
||||
data := []byte(tag + ",")
|
||||
data = append(data, byte(metric), byte(flag))
|
||||
data = append(data, hash[:32]...)
|
||||
data = append(data, cipher...)
|
||||
|
||||
ch := make(chan string, 1)
|
||||
|
||||
wac.listenerMutex.Lock()
|
||||
wac.listener[tag] = ch
|
||||
wac.listenerMutex.Unlock()
|
||||
|
||||
msg := wsMsg{websocket.BinaryMessage, data}
|
||||
wac.writeChan <- msg
|
||||
|
||||
wac.msgCount++
|
||||
return ch, nil
|
||||
}
|
||||
|
||||
func (wac *Conn) readPump() {
|
||||
defer wac.wsConn.Close()
|
||||
|
||||
for {
|
||||
msgType, msg, err := wac.wsConn.ReadMessage()
|
||||
if err != nil {
|
||||
wac.wsConnOK = false
|
||||
if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway) {
|
||||
wac.handle(fmt.Errorf("unexpected websocket close: %v", err))
|
||||
}
|
||||
// sleep for a second and retry reading the next message
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
wac.wsConnOK = true
|
||||
|
||||
data := strings.SplitN(string(msg), ",", 2)
|
||||
|
||||
//Kepp-Alive Timestmap
|
||||
if data[0][0] == '!' {
|
||||
msecs, err := strconv.ParseInt(data[0][1:], 10, 64)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error converting time string to uint: %v\n", err)
|
||||
continue
|
||||
}
|
||||
wac.ServerLastSeen = time.Unix(msecs/1000, (msecs%1000)*int64(time.Millisecond))
|
||||
continue
|
||||
}
|
||||
|
||||
wac.listenerMutex.RLock()
|
||||
listener, hasListener := wac.listener[data[0]]
|
||||
wac.listenerMutex.RUnlock()
|
||||
|
||||
if len(data[1]) == 0 {
|
||||
continue
|
||||
} else if hasListener {
|
||||
listener <- data[1]
|
||||
|
||||
wac.listenerMutex.Lock()
|
||||
delete(wac.listener, data[0])
|
||||
wac.listenerMutex.Unlock()
|
||||
} else if msgType == 2 && wac.session != nil && wac.session.EncKey != nil {
|
||||
message, err := wac.decryptBinaryMessage([]byte(data[1]))
|
||||
if err != nil {
|
||||
wac.handle(fmt.Errorf("error decoding binary: %v", err))
|
||||
continue
|
||||
}
|
||||
|
||||
wac.dispatch(message)
|
||||
} else {
|
||||
wac.handle(string(data[1]))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) writePump() {
|
||||
for msg := range wac.writeChan {
|
||||
for !wac.isConnected() {
|
||||
// reconnect to send the message ASAP
|
||||
wac.wsConnMutex.Lock()
|
||||
if wac.wsConn == nil {
|
||||
if err := wac.connect(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "could not reconnect to websocket: %v\n", err)
|
||||
}
|
||||
}
|
||||
wac.wsConnMutex.Unlock()
|
||||
if !wac.isConnected() {
|
||||
// reconnecting failed. Sleep for a while and try again afterwards
|
||||
time.Sleep(time.Duration(rand.Intn(5)) * time.Second)
|
||||
}
|
||||
}
|
||||
if err := wac.wsConn.WriteMessage(msg.messageType, msg.data); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error writing to socket: %v\n", err)
|
||||
wac.wsConnOK = false
|
||||
// add message to channel again to no loose it
|
||||
go func() {
|
||||
wac.writeChan <- msg
|
||||
}()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) sendKeepAlive() {
|
||||
// whatever issues might be there allow sending this message
|
||||
wac.wsConnOK = true
|
||||
wac.writeChan <- wsMsg{
|
||||
messageType: websocket.TextMessage,
|
||||
data: []byte("?,,"),
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) keepAlive(minIntervalMs int, maxIntervalMs int) {
|
||||
for {
|
||||
wac.sendKeepAlive()
|
||||
interval := rand.Intn(maxIntervalMs-minIntervalMs) + minIntervalMs
|
||||
<-time.After(time.Duration(interval) * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) decryptBinaryMessage(msg []byte) (*binary.Node, error) {
|
||||
//message validation
|
||||
h2 := hmac.New(sha256.New, wac.session.MacKey)
|
||||
h2.Write([]byte(msg[32:]))
|
||||
if !hmac.Equal(h2.Sum(nil), msg[:32]) {
|
||||
return nil, fmt.Errorf("message received with invalid hmac")
|
||||
}
|
||||
|
||||
// message decrypt
|
||||
d, err := cbc.Decrypt(wac.session.EncKey, nil, msg[32:])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decrypting message with AES: %v", err)
|
||||
}
|
||||
|
||||
// message unmarshal
|
||||
message, err := binary.Unmarshal(d)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding binary: %v", err)
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
8
vendor/github.com/matterbridge/go-whatsapp/go.mod
generated
vendored
8
vendor/github.com/matterbridge/go-whatsapp/go.mod
generated
vendored
@@ -1,8 +0,0 @@
|
||||
module github.com/matterbridge/go-whatsapp
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/gorilla/websocket v1.4.0
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||
)
|
||||
7
vendor/github.com/matterbridge/go-whatsapp/go.sum
generated
vendored
7
vendor/github.com/matterbridge/go-whatsapp/go.sum
generated
vendored
@@ -1,7 +0,0 @@
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613 h1:MQ/ZZiDsUapFFiMS+vzwXkCTeEKaum+Do5rINYJDmxc=
|
||||
golang.org/x/crypto v0.0.0-20190131182504-b8fe1690c613/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
45
vendor/github.com/matterbridge/go-whatsapp/store.go
generated
vendored
45
vendor/github.com/matterbridge/go-whatsapp/store.go
generated
vendored
@@ -1,45 +0,0 @@
|
||||
package whatsapp
|
||||
|
||||
import (
|
||||
"github.com/matterbridge/go-whatsapp/binary"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Store struct {
|
||||
Contacts map[string]Contact
|
||||
}
|
||||
|
||||
type Contact struct {
|
||||
Jid string
|
||||
Notify string
|
||||
Name string
|
||||
Short string
|
||||
}
|
||||
|
||||
func newStore() *Store {
|
||||
return &Store{
|
||||
make(map[string]Contact),
|
||||
}
|
||||
}
|
||||
|
||||
func (wac *Conn) updateContacts(contacts interface{}) {
|
||||
c, ok := contacts.([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, contact := range c {
|
||||
contactNode, ok := contact.(binary.Node)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
jid := strings.Replace(contactNode.Attributes["jid"], "@c.us", "@s.whatsapp.net", 1)
|
||||
wac.Store.Contacts[jid] = Contact{
|
||||
jid,
|
||||
contactNode.Attributes["notify"],
|
||||
contactNode.Attributes["name"],
|
||||
contactNode.Attributes["short"],
|
||||
}
|
||||
}
|
||||
}
|
||||
661
vendor/github.com/matterbridge/mautrix-whatsapp/LICENSE
generated
vendored
661
vendor/github.com/matterbridge/mautrix-whatsapp/LICENSE
generated
vendored
@@ -1,661 +0,0 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
147
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/chat.go
generated
vendored
147
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/chat.go
generated
vendored
@@ -1,147 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type ChatUpdateCommand string
|
||||
|
||||
const (
|
||||
ChatUpdateCommandAction ChatUpdateCommand = "action"
|
||||
)
|
||||
|
||||
type ChatUpdate struct {
|
||||
JID string `json:"id"`
|
||||
Command ChatUpdateCommand `json:"cmd"`
|
||||
Data ChatUpdateData `json:"data"`
|
||||
}
|
||||
|
||||
type ChatActionType string
|
||||
|
||||
const (
|
||||
ChatActionNameChange ChatActionType = "subject"
|
||||
ChatActionAddTopic ChatActionType = "desc_add"
|
||||
ChatActionRemoveTopic ChatActionType = "desc_remove"
|
||||
ChatActionRestrict ChatActionType = "restrict"
|
||||
ChatActionAnnounce ChatActionType = "announce"
|
||||
ChatActionPromote ChatActionType = "promote"
|
||||
ChatActionDemote ChatActionType = "demote"
|
||||
)
|
||||
|
||||
type ChatUpdateData struct {
|
||||
Action ChatActionType
|
||||
SenderJID string
|
||||
|
||||
NameChange struct {
|
||||
Name string `json:"subject"`
|
||||
SetAt int64 `json:"s_t"`
|
||||
SetBy string `json:"s_o"`
|
||||
}
|
||||
|
||||
AddTopic struct {
|
||||
Topic string `json:"desc"`
|
||||
ID string `json:"descId"`
|
||||
SetAt int64 `json:"descTime"`
|
||||
}
|
||||
|
||||
RemoveTopic struct {
|
||||
ID string `json:"descId"`
|
||||
}
|
||||
|
||||
Restrict bool
|
||||
|
||||
Announce bool
|
||||
|
||||
PermissionChange struct {
|
||||
JIDs []string `json:"participants"`
|
||||
}
|
||||
}
|
||||
|
||||
func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
|
||||
var arr []json.RawMessage
|
||||
err := json.Unmarshal(data, &arr)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if len(arr) < 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
err = json.Unmarshal(arr[0], &cud.Action)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(arr[1], &cud.SenderJID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cud.SenderJID = strings.Replace(cud.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
|
||||
var unmarshalTo interface{}
|
||||
switch cud.Action {
|
||||
case ChatActionNameChange:
|
||||
unmarshalTo = &cud.NameChange
|
||||
case ChatActionAddTopic:
|
||||
unmarshalTo = &cud.AddTopic
|
||||
case ChatActionRemoveTopic:
|
||||
unmarshalTo = &cud.RemoveTopic
|
||||
case ChatActionRestrict:
|
||||
unmarshalTo = &cud.Restrict
|
||||
case ChatActionAnnounce:
|
||||
unmarshalTo = &cud.Announce
|
||||
case ChatActionPromote, ChatActionDemote:
|
||||
unmarshalTo = &cud.PermissionChange
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
err = json.Unmarshal(arr[2], unmarshalTo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cud.NameChange.SetBy = strings.Replace(cud.NameChange.SetBy, OldUserSuffix, NewUserSuffix, 1)
|
||||
for index, jid := range cud.PermissionChange.JIDs {
|
||||
cud.PermissionChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ChatUpdateHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleChatUpdate(ChatUpdate)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageChatUpdate(message []byte) {
|
||||
var event ChatUpdate
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||
for _, handler := range ext.handlers {
|
||||
chatUpdateHandler, ok := handler.(ChatUpdateHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
go chatUpdateHandler.HandleChatUpdate(event)
|
||||
}
|
||||
}
|
||||
59
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/cmd.go
generated
vendored
59
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/cmd.go
generated
vendored
@@ -1,59 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type CommandType string
|
||||
|
||||
const (
|
||||
CommandPicture CommandType = "picture"
|
||||
)
|
||||
|
||||
type Command struct {
|
||||
Type CommandType `json:"type"`
|
||||
JID string `json:"jid"`
|
||||
|
||||
*ProfilePicInfo
|
||||
}
|
||||
|
||||
type CommandHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleCommand(Command)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageCommand(message []byte) {
|
||||
var event Command
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||
for _, handler := range ext.handlers {
|
||||
commandHandler, ok := handler.(CommandHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
go commandHandler.HandleCommand(event)
|
||||
}
|
||||
}
|
||||
60
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/conn.go
generated
vendored
60
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/conn.go
generated
vendored
@@ -1,60 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type ConnInfo struct {
|
||||
ProtocolVersion []int `json:"protoVersion"`
|
||||
BinaryVersion int `json:"binVersion"`
|
||||
Phone struct {
|
||||
WhatsAppVersion string `json:"wa_version"`
|
||||
MCC string `json:"mcc"`
|
||||
MNC string `json:"mnc"`
|
||||
OSVersion string `json:"os_version"`
|
||||
DeviceManufacturer string `json:"device_manufacturer"`
|
||||
DeviceModel string `json:"device_model"`
|
||||
OSBuildNumber string `json:"os_build_number"`
|
||||
} `json:"phone"`
|
||||
Features map[string]interface{} `json:"features"`
|
||||
PushName string `json:"pushname"`
|
||||
}
|
||||
|
||||
type ConnInfoHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleConnInfo(ConnInfo)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageConn(message []byte) {
|
||||
var event ConnInfo
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
for _, handler := range ext.handlers {
|
||||
connInfoHandler, ok := handler.(ConnInfoHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
connInfoHandler.HandleConnInfo(event)
|
||||
}
|
||||
}
|
||||
102
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/jsonmessage.go
generated
vendored
102
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/jsonmessage.go
generated
vendored
@@ -1,102 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type JSONMessage []json.RawMessage
|
||||
|
||||
type JSONMessageType string
|
||||
|
||||
const (
|
||||
MessageMsgInfo JSONMessageType = "MsgInfo"
|
||||
MessageMsg JSONMessageType = "Msg"
|
||||
MessagePresence JSONMessageType = "Presence"
|
||||
MessageStream JSONMessageType = "Stream"
|
||||
MessageConn JSONMessageType = "Conn"
|
||||
MessageProps JSONMessageType = "Props"
|
||||
MessageCmd JSONMessageType = "Cmd"
|
||||
MessageChat JSONMessageType = "Chat"
|
||||
)
|
||||
|
||||
func (ext *ExtendedConn) AddHandler(handler whatsapp.Handler) {
|
||||
ext.Conn.AddHandler(handler)
|
||||
ext.handlers = append(ext.handlers, handler)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) HandleError(error) {}
|
||||
|
||||
type UnhandledJSONMessageHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleUnhandledJSONMessage(string)
|
||||
}
|
||||
|
||||
type JSONParseErrorHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleJSONParseError(error)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) jsonParseError(err error) {
|
||||
for _, handler := range ext.handlers {
|
||||
errorHandler, ok := handler.(JSONParseErrorHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
errorHandler.HandleJSONParseError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) HandleJsonMessage(message string) {
|
||||
msg := JSONMessage{}
|
||||
err := json.Unmarshal([]byte(message), &msg)
|
||||
if err != nil || len(msg) < 2 {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
|
||||
var msgType JSONMessageType
|
||||
json.Unmarshal(msg[0], &msgType)
|
||||
|
||||
switch msgType {
|
||||
case MessagePresence:
|
||||
ext.handleMessagePresence(msg[1])
|
||||
case MessageStream:
|
||||
ext.handleMessageStream(msg[1:])
|
||||
case MessageConn:
|
||||
ext.handleMessageConn(msg[1])
|
||||
case MessageProps:
|
||||
ext.handleMessageProps(msg[1])
|
||||
case MessageMsgInfo, MessageMsg:
|
||||
ext.handleMessageMsgInfo(msgType, msg[1])
|
||||
case MessageCmd:
|
||||
ext.handleMessageCommand(msg[1])
|
||||
case MessageChat:
|
||||
ext.handleMessageChatUpdate(msg[1])
|
||||
default:
|
||||
for _, handler := range ext.handlers {
|
||||
ujmHandler, ok := handler.(UnhandledJSONMessageHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ujmHandler.HandleUnhandledJSONMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
90
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/msginfo.go
generated
vendored
90
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/msginfo.go
generated
vendored
@@ -1,90 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type MsgInfoCommand string
|
||||
|
||||
const (
|
||||
MsgInfoCommandAck MsgInfoCommand = "ack"
|
||||
MsgInfoCommandAcks MsgInfoCommand = "acks"
|
||||
)
|
||||
|
||||
type Acknowledgement int
|
||||
|
||||
const (
|
||||
AckMessageSent Acknowledgement = 1
|
||||
AckMessageDelivered Acknowledgement = 2
|
||||
AckMessageRead Acknowledgement = 3
|
||||
)
|
||||
|
||||
type JSONStringOrArray []string
|
||||
|
||||
func (jsoa *JSONStringOrArray) UnmarshalJSON(data []byte) error {
|
||||
var str string
|
||||
if json.Unmarshal(data, &str) == nil {
|
||||
*jsoa = []string{str}
|
||||
return nil
|
||||
}
|
||||
var strs []string
|
||||
json.Unmarshal(data, &strs)
|
||||
*jsoa = strs
|
||||
return nil
|
||||
}
|
||||
|
||||
type MsgInfo struct {
|
||||
Command MsgInfoCommand `json:"cmd"`
|
||||
IDs JSONStringOrArray `json:"id"`
|
||||
Acknowledgement Acknowledgement `json:"ack"`
|
||||
MessageFromJID string `json:"from"`
|
||||
SenderJID string `json:"participant"`
|
||||
ToJID string `json:"to"`
|
||||
Timestamp int64 `json:"t"`
|
||||
}
|
||||
|
||||
type MsgInfoHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleMsgInfo(MsgInfo)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageMsgInfo(msgType JSONMessageType, message []byte) {
|
||||
var event MsgInfo
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
event.MessageFromJID = strings.Replace(event.MessageFromJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
event.ToJID = strings.Replace(event.ToJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
if msgType == MessageMsg {
|
||||
event.SenderJID = event.ToJID
|
||||
}
|
||||
for _, handler := range ext.handlers {
|
||||
msgInfoHandler, ok := handler.(MsgInfoHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
go msgInfoHandler.HandleMsgInfo(event)
|
||||
}
|
||||
}
|
||||
67
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/presence.go
generated
vendored
67
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/presence.go
generated
vendored
@@ -1,67 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type PresenceType string
|
||||
|
||||
const (
|
||||
PresenceUnavailable PresenceType = "unavailable"
|
||||
PresenceAvailable PresenceType = "available"
|
||||
PresenceComposing PresenceType = "composing"
|
||||
)
|
||||
|
||||
type Presence struct {
|
||||
JID string `json:"id"`
|
||||
SenderJID string `json:"participant"`
|
||||
Status PresenceType `json:"type"`
|
||||
Timestamp int64 `json:"t"`
|
||||
Deny bool `json:"deny"`
|
||||
}
|
||||
|
||||
type PresenceHandler interface {
|
||||
whatsapp.Handler
|
||||
HandlePresence(Presence)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessagePresence(message []byte) {
|
||||
var event Presence
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
event.JID = strings.Replace(event.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||
if len(event.SenderJID) == 0 {
|
||||
event.SenderJID = event.JID
|
||||
} else {
|
||||
event.SenderJID = strings.Replace(event.SenderJID, OldUserSuffix, NewUserSuffix, 1)
|
||||
}
|
||||
for _, handler := range ext.handlers {
|
||||
presenceHandler, ok := handler.(PresenceHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
go presenceHandler.HandlePresence(event)
|
||||
}
|
||||
}
|
||||
68
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/props.go
generated
vendored
68
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/props.go
generated
vendored
@@ -1,68 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type ProtocolProps struct {
|
||||
WebPresence bool `json:"webPresence"`
|
||||
NotificationQuery bool `json:"notificationQuery"`
|
||||
FacebookCrashLog bool `json:"fbCrashlog"`
|
||||
Bucket string `json:"bucket"`
|
||||
GIFSearch string `json:"gifSearch"`
|
||||
Spam bool `json:"SPAM"`
|
||||
SetBlock bool `json:"SET_BLOCK"`
|
||||
MessageInfo bool `json:"MESSAGE_INFO"`
|
||||
MaxFileSize int `json:"maxFileSize"`
|
||||
Media int `json:"media"`
|
||||
GroupNameLength int `json:"maxSubject"`
|
||||
GroupDescriptionLength int `json:"groupDescLength"`
|
||||
MaxParticipants int `json:"maxParticipants"`
|
||||
VideoMaxEdge int `json:"videoMaxEdge"`
|
||||
ImageMaxEdge int `json:"imageMaxEdge"`
|
||||
ImageMaxKilobytes int `json:"imageMaxKBytes"`
|
||||
Edit int `json:"edit"`
|
||||
FwdUIStartTimestamp int `json:"fwdUiStartTs"`
|
||||
GroupsV3 int `json:"groupsV3"`
|
||||
RestrictGroups int `json:"restrictGroups"`
|
||||
AnnounceGroups int `json:"announceGroups"`
|
||||
}
|
||||
|
||||
type ProtocolPropsHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleProtocolProps(ProtocolProps)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageProps(message []byte) {
|
||||
var event ProtocolProps
|
||||
err := json.Unmarshal(message, &event)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
for _, handler := range ext.handlers {
|
||||
protocolPropsHandler, ok := handler.(ProtocolPropsHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
go protocolPropsHandler.HandleProtocolProps(event)
|
||||
}
|
||||
}
|
||||
63
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/stream.go
generated
vendored
63
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/stream.go
generated
vendored
@@ -1,63 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
type StreamType string
|
||||
|
||||
const (
|
||||
StreamUpdate = "update"
|
||||
StreamSleep = "asleep"
|
||||
)
|
||||
|
||||
type StreamEvent struct {
|
||||
Type StreamType
|
||||
Boolean bool
|
||||
Version string
|
||||
}
|
||||
|
||||
type StreamEventHandler interface {
|
||||
whatsapp.Handler
|
||||
HandleStreamEvent(StreamEvent)
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) handleMessageStream(message []json.RawMessage) {
|
||||
var event StreamEvent
|
||||
err := json.Unmarshal(message[0], &event.Type)
|
||||
if err != nil {
|
||||
ext.jsonParseError(err)
|
||||
return
|
||||
}
|
||||
|
||||
if event.Type == StreamUpdate && len(message) > 4 {
|
||||
json.Unmarshal(message[1], event.Boolean)
|
||||
json.Unmarshal(message[2], event.Version)
|
||||
}
|
||||
|
||||
for _, handler := range ext.handlers {
|
||||
streamHandler, ok := handler.(StreamEventHandler)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
go streamHandler.HandleStreamEvent(event)
|
||||
}
|
||||
}
|
||||
132
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/whatsapp.go
generated
vendored
132
vendor/github.com/matterbridge/mautrix-whatsapp/whatsapp-ext/whatsapp.go
generated
vendored
@@ -1,132 +0,0 @@
|
||||
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
|
||||
// Copyright (C) 2019 Tulir Asokan
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package whatsappExt
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/matterbridge/go-whatsapp"
|
||||
)
|
||||
|
||||
const (
|
||||
OldUserSuffix = "@c.us"
|
||||
NewUserSuffix = "@s.whatsapp.net"
|
||||
)
|
||||
|
||||
type ExtendedConn struct {
|
||||
*whatsapp.Conn
|
||||
|
||||
handlers []whatsapp.Handler
|
||||
}
|
||||
|
||||
func ExtendConn(conn *whatsapp.Conn) *ExtendedConn {
|
||||
ext := &ExtendedConn{
|
||||
Conn: conn,
|
||||
}
|
||||
ext.Conn.AddHandler(ext)
|
||||
return ext
|
||||
}
|
||||
|
||||
type GroupInfo struct {
|
||||
JID string `json:"jid"`
|
||||
OwnerJID string `json:"owner"`
|
||||
|
||||
Name string `json:"subject"`
|
||||
NameSetTime int64 `json:"subjectTime"`
|
||||
NameSetBy string `json:"subjectOwner"`
|
||||
|
||||
Topic string `json:"desc"`
|
||||
TopicID string `json:"descId"`
|
||||
TopicSetAt int64 `json:"descTime"`
|
||||
TopicSetBy string `json:"descOwner"`
|
||||
|
||||
GroupCreated int64 `json:"creation"`
|
||||
|
||||
Status int16 `json:"status"`
|
||||
|
||||
Participants []struct {
|
||||
JID string `json:"id"`
|
||||
IsAdmin bool `json:"isAdmin"`
|
||||
IsSuperAdmin bool `json:"isSuperAdmin"`
|
||||
} `json:"participants"`
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) GetGroupMetaData(jid string) (*GroupInfo, error) {
|
||||
data, err := ext.Conn.GetGroupMetaData(jid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get group metadata: %v", err)
|
||||
}
|
||||
content := <-data
|
||||
|
||||
info := &GroupInfo{}
|
||||
err = json.Unmarshal([]byte(content), info)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to unmarshal group metadata: %v", err)
|
||||
}
|
||||
|
||||
for index, participant := range info.Participants {
|
||||
info.Participants[index].JID = strings.Replace(participant.JID, OldUserSuffix, NewUserSuffix, 1)
|
||||
}
|
||||
info.NameSetBy = strings.Replace(info.NameSetBy, OldUserSuffix, NewUserSuffix, 1)
|
||||
info.TopicSetBy = strings.Replace(info.TopicSetBy, OldUserSuffix, NewUserSuffix, 1)
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
type ProfilePicInfo struct {
|
||||
URL string `json:"eurl"`
|
||||
Tag string `json:"tag"`
|
||||
|
||||
Status int16 `json:"status"`
|
||||
}
|
||||
|
||||
func (ppi *ProfilePicInfo) Download() (io.ReadCloser, error) {
|
||||
resp, err := http.Get(ppi.URL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
|
||||
func (ppi *ProfilePicInfo) DownloadBytes() ([]byte, error) {
|
||||
body, err := ppi.Download()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer body.Close()
|
||||
data, err := ioutil.ReadAll(body)
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (ext *ExtendedConn) GetProfilePicThumb(jid string) (*ProfilePicInfo, error) {
|
||||
data, err := ext.Conn.GetProfilePicThumb(jid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get avatar: %v", err)
|
||||
}
|
||||
content := <-data
|
||||
info := &ProfilePicInfo{}
|
||||
err = json.Unmarshal([]byte(content), info)
|
||||
if err != nil {
|
||||
return info, fmt.Errorf("failed to unmarshal avatar info: %v", err)
|
||||
}
|
||||
return info, nil
|
||||
}
|
||||
12
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
12
vendor/github.com/pkg/errors/.travis.yml
generated
vendored
@@ -1,10 +1,14 @@
|
||||
language: go
|
||||
go_import_path: github.com/pkg/errors
|
||||
go:
|
||||
- 1.4.3
|
||||
- 1.5.4
|
||||
- 1.6.2
|
||||
- 1.7.1
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- tip
|
||||
|
||||
script:
|
||||
|
||||
4
vendor/github.com/pkg/errors/README.md
generated
vendored
4
vendor/github.com/pkg/errors/README.md
generated
vendored
@@ -1,4 +1,4 @@
|
||||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors)
|
||||
# errors [](https://travis-ci.org/pkg/errors) [](https://ci.appveyor.com/project/davecheney/errors/branch/master) [](http://godoc.org/github.com/pkg/errors) [](https://goreportcard.com/report/github.com/pkg/errors) [](https://sourcegraph.com/github.com/pkg/errors?badge)
|
||||
|
||||
Package errors provides simple error handling primitives.
|
||||
|
||||
@@ -47,6 +47,6 @@ We welcome pull requests, bug fixes and issue reports. With that said, the bar f
|
||||
|
||||
Before proposing a change, please discuss your change by raising an issue.
|
||||
|
||||
## Licence
|
||||
## License
|
||||
|
||||
BSD-2-Clause
|
||||
|
||||
43
vendor/github.com/pkg/errors/errors.go
generated
vendored
43
vendor/github.com/pkg/errors/errors.go
generated
vendored
@@ -6,7 +6,7 @@
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which applied recursively up the call stack results in error reports
|
||||
// which when applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
@@ -15,16 +15,17 @@
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// and the supplied message. For example
|
||||
// together with the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required the errors.WithStack and errors.WithMessage
|
||||
// functions destructure errors.Wrap into its component operations of annotating
|
||||
// an error with a stack trace and an a message, respectively.
|
||||
// If additional control is required, the errors.WithStack and
|
||||
// errors.WithMessage functions destructure errors.Wrap into its component
|
||||
// operations: annotating an error with a stack trace and with a message,
|
||||
// respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
@@ -38,7 +39,7 @@
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error which does not implement causer, which is assumed to be
|
||||
// the topmost error that does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
@@ -48,16 +49,16 @@
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// causer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
// Although the causer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported
|
||||
// be formatted by the fmt package. The following verbs are supported:
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively
|
||||
// printed recursively.
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
@@ -65,13 +66,13 @@
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface.
|
||||
// invoked. This information can be retrieved with the following interface:
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// Where errors.StackTrace is defined as
|
||||
// The returned errors.StackTrace type is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
@@ -85,8 +86,8 @@
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// stackTracer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
// Although the stackTracer interface is not exported by this package, it is
|
||||
// considered a part of its stable public interface.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
@@ -192,7 +193,7 @@ func Wrap(err error, message string) error {
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is call, and the format specifier.
|
||||
// at the point Wrapf is called, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
@@ -220,6 +221,18 @@ func WithMessage(err error, message string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagef annotates err with the format specifier.
|
||||
// If err is nil, WithMessagef returns nil.
|
||||
func WithMessagef(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
|
||||
51
vendor/github.com/pkg/errors/stack.go
generated
vendored
51
vendor/github.com/pkg/errors/stack.go
generated
vendored
@@ -46,7 +46,8 @@ func (f Frame) line() int {
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s path of source file relative to the compile time GOPATH
|
||||
// %+s function name and path of source file relative to the compile time
|
||||
// GOPATH separated by \n\t (<funcname>\n\t<path>)
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
@@ -79,6 +80,14 @@ func (f Frame) Format(s fmt.State, verb rune) {
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
// Format formats the stack of Frames according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s lists source files for each Frame in the stack
|
||||
// %v lists the source file and line number for each Frame in the stack
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+v Prints filename, function, and line number for each Frame in the stack.
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
@@ -136,43 +145,3 @@ func funcname(name string) string {
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
|
||||
func trimGOPATH(name, file string) string {
|
||||
// Here we want to get the source file path relative to the compile time
|
||||
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
||||
// GOPATH at runtime, but we can infer the number of path segments in the
|
||||
// GOPATH. We note that fn.Name() returns the function name qualified by
|
||||
// the import path, which does not include the GOPATH. Thus we can trim
|
||||
// segments from the beginning of the file path until the number of path
|
||||
// separators remaining is one more than the number of path separators in
|
||||
// the function name. For example, given:
|
||||
//
|
||||
// GOPATH /home/user
|
||||
// file /home/user/src/pkg/sub/file.go
|
||||
// fn.Name() pkg/sub.Type.Method
|
||||
//
|
||||
// We want to produce:
|
||||
//
|
||||
// pkg/sub/file.go
|
||||
//
|
||||
// From this we can easily see that fn.Name() has one less path separator
|
||||
// than our desired output. We count separators from the end of the file
|
||||
// path until it finds two more than in the function name and then move
|
||||
// one character forward to preserve the initial path segment without a
|
||||
// leading separator.
|
||||
const sep = "/"
|
||||
goal := strings.Count(name, sep) + 2
|
||||
i := len(file)
|
||||
for n := 0; n < goal; n++ {
|
||||
i = strings.LastIndex(file[:i], sep)
|
||||
if i == -1 {
|
||||
// not enough separators found, set i so that the slice expression
|
||||
// below leaves file unmodified
|
||||
i = -len(sep)
|
||||
break
|
||||
}
|
||||
}
|
||||
// get back to 0 or trim the leading separator
|
||||
file = file[i+len(sep):]
|
||||
return file
|
||||
}
|
||||
|
||||
11
vendor/golang.org/x/crypto/internal/chacha20/chacha_s390x.go
generated
vendored
11
vendor/golang.org/x/crypto/internal/chacha20/chacha_s390x.go
generated
vendored
@@ -6,15 +6,14 @@
|
||||
|
||||
package chacha20
|
||||
|
||||
var haveAsm = hasVectorFacility()
|
||||
import (
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
var haveAsm = cpu.S390X.HasVX
|
||||
|
||||
const bufSize = 256
|
||||
|
||||
// hasVectorFacility reports whether the machine supports the vector
|
||||
// facility (vx).
|
||||
// Implementation in asm_s390x.s.
|
||||
func hasVectorFacility() bool
|
||||
|
||||
// xorKeyStreamVX is an assembly implementation of XORKeyStream. It must only
|
||||
// be called when the vector facility is available.
|
||||
// Implementation in asm_s390x.s.
|
||||
|
||||
23
vendor/golang.org/x/crypto/internal/chacha20/chacha_s390x.s
generated
vendored
23
vendor/golang.org/x/crypto/internal/chacha20/chacha_s390x.s
generated
vendored
@@ -258,26 +258,3 @@ tail:
|
||||
MOVD R8, R3
|
||||
MOVD $0, R4
|
||||
JMP continue
|
||||
|
||||
// func hasVectorFacility() bool
|
||||
TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
|
||||
MOVD $x-24(SP), R1
|
||||
XC $24, 0(R1), 0(R1) // clear the storage
|
||||
MOVD $2, R0 // R0 is the number of double words stored -1
|
||||
WORD $0xB2B01000 // STFLE 0(R1)
|
||||
XOR R0, R0 // reset the value of R0
|
||||
MOVBZ z-8(SP), R1
|
||||
AND $0x40, R1
|
||||
BEQ novector
|
||||
|
||||
vectorinstalled:
|
||||
// check if the vector instruction has been enabled
|
||||
VLEIB $0, $0xF, V16
|
||||
VLGVB $0, V16, R1
|
||||
CMPBNE R1, $0xF, novector
|
||||
MOVB $1, ret+0(FP) // have vx
|
||||
RET
|
||||
|
||||
novector:
|
||||
MOVB $0, ret+0(FP) // no vx
|
||||
RET
|
||||
|
||||
11
vendor/golang.org/x/crypto/poly1305/mac_noasm.go
generated
vendored
Normal file
11
vendor/golang.org/x/crypto/poly1305/mac_noasm.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 gccgo appengine
|
||||
|
||||
package poly1305
|
||||
|
||||
type mac struct{ macGeneric }
|
||||
|
||||
func newMAC(key *[32]byte) mac { return mac{newMACGeneric(key)} }
|
||||
80
vendor/golang.org/x/crypto/poly1305/poly1305.go
generated
vendored
80
vendor/golang.org/x/crypto/poly1305/poly1305.go
generated
vendored
@@ -2,21 +2,19 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package poly1305 implements Poly1305 one-time message authentication code as
|
||||
specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
|
||||
|
||||
Poly1305 is a fast, one-time authentication function. It is infeasible for an
|
||||
attacker to generate an authenticator for a message without the key. However, a
|
||||
key must only be used for a single message. Authenticating two different
|
||||
messages with the same key allows an attacker to forge authenticators for other
|
||||
messages with the same key.
|
||||
|
||||
Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
|
||||
used with a fixed key in order to generate one-time keys from an nonce.
|
||||
However, in this package AES isn't used and the one-time key is specified
|
||||
directly.
|
||||
*/
|
||||
// Package poly1305 implements Poly1305 one-time message authentication code as
|
||||
// specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
|
||||
//
|
||||
// Poly1305 is a fast, one-time authentication function. It is infeasible for an
|
||||
// attacker to generate an authenticator for a message without the key. However, a
|
||||
// key must only be used for a single message. Authenticating two different
|
||||
// messages with the same key allows an attacker to forge authenticators for other
|
||||
// messages with the same key.
|
||||
//
|
||||
// Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
|
||||
// used with a fixed key in order to generate one-time keys from an nonce.
|
||||
// However, in this package AES isn't used and the one-time key is specified
|
||||
// directly.
|
||||
package poly1305 // import "golang.org/x/crypto/poly1305"
|
||||
|
||||
import "crypto/subtle"
|
||||
@@ -31,3 +29,55 @@ func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
|
||||
Sum(&tmp, m, key)
|
||||
return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
|
||||
}
|
||||
|
||||
// New returns a new MAC computing an authentication
|
||||
// tag of all data written to it with the given key.
|
||||
// This allows writing the message progressively instead
|
||||
// of passing it as a single slice. Common users should use
|
||||
// the Sum function instead.
|
||||
//
|
||||
// The key must be unique for each message, as authenticating
|
||||
// two different messages with the same key allows an attacker
|
||||
// to forge messages at will.
|
||||
func New(key *[32]byte) *MAC {
|
||||
return &MAC{
|
||||
mac: newMAC(key),
|
||||
finalized: false,
|
||||
}
|
||||
}
|
||||
|
||||
// MAC is an io.Writer computing an authentication tag
|
||||
// of the data written to it.
|
||||
//
|
||||
// MAC cannot be used like common hash.Hash implementations,
|
||||
// because using a poly1305 key twice breaks its security.
|
||||
// Therefore writing data to a running MAC after calling
|
||||
// Sum causes it to panic.
|
||||
type MAC struct {
|
||||
mac // platform-dependent implementation
|
||||
|
||||
finalized bool
|
||||
}
|
||||
|
||||
// Size returns the number of bytes Sum will return.
|
||||
func (h *MAC) Size() int { return TagSize }
|
||||
|
||||
// Write adds more data to the running message authentication code.
|
||||
// It never returns an error.
|
||||
//
|
||||
// It must not be called after the first call of Sum.
|
||||
func (h *MAC) Write(p []byte) (n int, err error) {
|
||||
if h.finalized {
|
||||
panic("poly1305: write to MAC after Sum")
|
||||
}
|
||||
return h.mac.Write(p)
|
||||
}
|
||||
|
||||
// Sum computes the authenticator of all data written to the
|
||||
// message authentication code.
|
||||
func (h *MAC) Sum(b []byte) []byte {
|
||||
var mac [TagSize]byte
|
||||
h.mac.Sum(&mac)
|
||||
h.finalized = true
|
||||
return append(b, mac[:]...)
|
||||
}
|
||||
|
||||
60
vendor/golang.org/x/crypto/poly1305/sum_amd64.go
generated
vendored
60
vendor/golang.org/x/crypto/poly1305/sum_amd64.go
generated
vendored
@@ -6,17 +6,63 @@
|
||||
|
||||
package poly1305
|
||||
|
||||
// This function is implemented in sum_amd64.s
|
||||
//go:noescape
|
||||
func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
|
||||
func initialize(state *[7]uint64, key *[32]byte)
|
||||
|
||||
//go:noescape
|
||||
func update(state *[7]uint64, msg []byte)
|
||||
|
||||
//go:noescape
|
||||
func finalize(tag *[TagSize]byte, state *[7]uint64)
|
||||
|
||||
// Sum generates an authenticator for m using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) {
|
||||
var mPtr *byte
|
||||
if len(m) > 0 {
|
||||
mPtr = &m[0]
|
||||
}
|
||||
poly1305(out, mPtr, uint64(len(m)), key)
|
||||
h := newMAC(key)
|
||||
h.Write(m)
|
||||
h.Sum(out)
|
||||
}
|
||||
|
||||
func newMAC(key *[32]byte) (h mac) {
|
||||
initialize(&h.state, key)
|
||||
return
|
||||
}
|
||||
|
||||
type mac struct {
|
||||
state [7]uint64 // := uint64{ h0, h1, h2, r0, r1, pad0, pad1 }
|
||||
|
||||
buffer [TagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (h *mac) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
if h.offset > 0 {
|
||||
remaining := TagSize - h.offset
|
||||
if n < remaining {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
return n, nil
|
||||
}
|
||||
copy(h.buffer[h.offset:], p[:remaining])
|
||||
p = p[remaining:]
|
||||
h.offset = 0
|
||||
update(&h.state, h.buffer[:])
|
||||
}
|
||||
if nn := len(p) - (len(p) % TagSize); nn > 0 {
|
||||
update(&h.state, p[:nn])
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (h *mac) Sum(out *[16]byte) {
|
||||
state := h.state
|
||||
if h.offset > 0 {
|
||||
update(&state, h.buffer[:h.offset])
|
||||
}
|
||||
finalize(out, &state)
|
||||
}
|
||||
|
||||
61
vendor/golang.org/x/crypto/poly1305/sum_amd64.s
generated
vendored
61
vendor/golang.org/x/crypto/poly1305/sum_amd64.s
generated
vendored
@@ -58,20 +58,17 @@ DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
|
||||
TEXT ·poly1305(SB), $0-32
|
||||
MOVQ out+0(FP), DI
|
||||
MOVQ m+8(FP), SI
|
||||
MOVQ mlen+16(FP), R15
|
||||
MOVQ key+24(FP), AX
|
||||
// func update(state *[7]uint64, msg []byte)
|
||||
TEXT ·update(SB), $0-32
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ msg_base+8(FP), SI
|
||||
MOVQ msg_len+16(FP), R15
|
||||
|
||||
MOVQ 0(AX), R11
|
||||
MOVQ 8(AX), R12
|
||||
ANDQ ·poly1305Mask<>(SB), R11 // r0
|
||||
ANDQ ·poly1305Mask<>+8(SB), R12 // r1
|
||||
XORQ R8, R8 // h0
|
||||
XORQ R9, R9 // h1
|
||||
XORQ R10, R10 // h2
|
||||
MOVQ 0(DI), R8 // h0
|
||||
MOVQ 8(DI), R9 // h1
|
||||
MOVQ 16(DI), R10 // h2
|
||||
MOVQ 24(DI), R11 // r0
|
||||
MOVQ 32(DI), R12 // r1
|
||||
|
||||
CMPQ R15, $16
|
||||
JB bytes_between_0_and_15
|
||||
@@ -109,16 +106,42 @@ flush_buffer:
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVQ R8, AX
|
||||
MOVQ R9, BX
|
||||
MOVQ R8, 0(DI)
|
||||
MOVQ R9, 8(DI)
|
||||
MOVQ R10, 16(DI)
|
||||
RET
|
||||
|
||||
// func initialize(state *[7]uint64, key *[32]byte)
|
||||
TEXT ·initialize(SB), $0-16
|
||||
MOVQ state+0(FP), DI
|
||||
MOVQ key+8(FP), SI
|
||||
|
||||
// state[0...7] is initialized with zero
|
||||
MOVOU 0(SI), X0
|
||||
MOVOU 16(SI), X1
|
||||
MOVOU ·poly1305Mask<>(SB), X2
|
||||
PAND X2, X0
|
||||
MOVOU X0, 24(DI)
|
||||
MOVOU X1, 40(DI)
|
||||
RET
|
||||
|
||||
// func finalize(tag *[TagSize]byte, state *[7]uint64)
|
||||
TEXT ·finalize(SB), $0-16
|
||||
MOVQ tag+0(FP), DI
|
||||
MOVQ state+8(FP), SI
|
||||
|
||||
MOVQ 0(SI), AX
|
||||
MOVQ 8(SI), BX
|
||||
MOVQ 16(SI), CX
|
||||
MOVQ AX, R8
|
||||
MOVQ BX, R9
|
||||
SUBQ $0xFFFFFFFFFFFFFFFB, AX
|
||||
SBBQ $0xFFFFFFFFFFFFFFFF, BX
|
||||
SBBQ $3, R10
|
||||
SBBQ $3, CX
|
||||
CMOVQCS R8, AX
|
||||
CMOVQCS R9, BX
|
||||
MOVQ key+24(FP), R8
|
||||
ADDQ 16(R8), AX
|
||||
ADCQ 24(R8), BX
|
||||
ADDQ 40(SI), AX
|
||||
ADCQ 48(SI), BX
|
||||
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ BX, 8(DI)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -6,21 +6,79 @@ package poly1305
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
msgBlock = uint32(1 << 24)
|
||||
finalBlock = uint32(0)
|
||||
)
|
||||
|
||||
// sumGeneric generates an authenticator for msg using a one-time key and
|
||||
// puts the 16-byte result into out. This is the generic implementation of
|
||||
// Sum and should be called if no assembly implementation is available.
|
||||
func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
var (
|
||||
h0, h1, h2, h3, h4 uint32 // the hash accumulators
|
||||
r0, r1, r2, r3, r4 uint64 // the r part of the key
|
||||
)
|
||||
h := newMACGeneric(key)
|
||||
h.Write(msg)
|
||||
h.Sum(out)
|
||||
}
|
||||
|
||||
r0 = uint64(binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff)
|
||||
r1 = uint64((binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03)
|
||||
r2 = uint64((binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff)
|
||||
r3 = uint64((binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff)
|
||||
r4 = uint64((binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff)
|
||||
func newMACGeneric(key *[32]byte) (h macGeneric) {
|
||||
h.r[0] = binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff
|
||||
h.r[1] = (binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03
|
||||
h.r[2] = (binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff
|
||||
h.r[3] = (binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff
|
||||
h.r[4] = (binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff
|
||||
|
||||
h.s[0] = binary.LittleEndian.Uint32(key[16:])
|
||||
h.s[1] = binary.LittleEndian.Uint32(key[20:])
|
||||
h.s[2] = binary.LittleEndian.Uint32(key[24:])
|
||||
h.s[3] = binary.LittleEndian.Uint32(key[28:])
|
||||
return
|
||||
}
|
||||
|
||||
type macGeneric struct {
|
||||
h, r [5]uint32
|
||||
s [4]uint32
|
||||
|
||||
buffer [TagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (h *macGeneric) Write(p []byte) (n int, err error) {
|
||||
n = len(p)
|
||||
if h.offset > 0 {
|
||||
remaining := TagSize - h.offset
|
||||
if n < remaining {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
return n, nil
|
||||
}
|
||||
copy(h.buffer[h.offset:], p[:remaining])
|
||||
p = p[remaining:]
|
||||
h.offset = 0
|
||||
updateGeneric(h.buffer[:], msgBlock, &(h.h), &(h.r))
|
||||
}
|
||||
if nn := len(p) - (len(p) % TagSize); nn > 0 {
|
||||
updateGeneric(p, msgBlock, &(h.h), &(h.r))
|
||||
p = p[nn:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
h.offset += copy(h.buffer[h.offset:], p)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (h *macGeneric) Sum(out *[16]byte) {
|
||||
H, R := h.h, h.r
|
||||
if h.offset > 0 {
|
||||
var buffer [TagSize]byte
|
||||
copy(buffer[:], h.buffer[:h.offset])
|
||||
buffer[h.offset] = 1 // invariant: h.offset < TagSize
|
||||
updateGeneric(buffer[:], finalBlock, &H, &R)
|
||||
}
|
||||
finalizeGeneric(out, &H, &(h.s))
|
||||
}
|
||||
|
||||
func updateGeneric(msg []byte, flag uint32, h, r *[5]uint32) {
|
||||
h0, h1, h2, h3, h4 := h[0], h[1], h[2], h[3], h[4]
|
||||
r0, r1, r2, r3, r4 := uint64(r[0]), uint64(r[1]), uint64(r[2]), uint64(r[3]), uint64(r[4])
|
||||
R1, R2, R3, R4 := r1*5, r2*5, r3*5, r4*5
|
||||
|
||||
for len(msg) >= TagSize {
|
||||
@@ -29,7 +87,7 @@ func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
h1 += (binary.LittleEndian.Uint32(msg[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(msg[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(msg[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | (1 << 24)
|
||||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | flag
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
@@ -52,36 +110,11 @@ func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
msg = msg[TagSize:]
|
||||
}
|
||||
|
||||
if len(msg) > 0 {
|
||||
var block [TagSize]byte
|
||||
off := copy(block[:], msg)
|
||||
block[off] = 0x01
|
||||
h[0], h[1], h[2], h[3], h[4] = h0, h1, h2, h3, h4
|
||||
}
|
||||
|
||||
// h += msg
|
||||
h0 += binary.LittleEndian.Uint32(block[0:]) & 0x3ffffff
|
||||
h1 += (binary.LittleEndian.Uint32(block[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(block[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(block[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(block[12:]) >> 8)
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2)
|
||||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3)
|
||||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4)
|
||||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0)
|
||||
|
||||
// h %= p
|
||||
h0 = uint32(d0) & 0x3ffffff
|
||||
h1 = uint32(d1) & 0x3ffffff
|
||||
h2 = uint32(d2) & 0x3ffffff
|
||||
h3 = uint32(d3) & 0x3ffffff
|
||||
h4 = uint32(d4) & 0x3ffffff
|
||||
|
||||
h0 += uint32(d4>>26) * 5
|
||||
h1 += h0 >> 26
|
||||
h0 = h0 & 0x3ffffff
|
||||
}
|
||||
func finalizeGeneric(out *[TagSize]byte, h *[5]uint32, s *[4]uint32) {
|
||||
h0, h1, h2, h3, h4 := h[0], h[1], h[2], h[3], h[4]
|
||||
|
||||
// h %= p reduction
|
||||
h2 += h1 >> 26
|
||||
@@ -123,13 +156,13 @@ func sumGeneric(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
|
||||
// s: the s part of the key
|
||||
// tag = (h + s) % (2^128)
|
||||
t := uint64(h0) + uint64(binary.LittleEndian.Uint32(key[16:]))
|
||||
t := uint64(h0) + uint64(s[0])
|
||||
h0 = uint32(t)
|
||||
t = uint64(h1) + uint64(binary.LittleEndian.Uint32(key[20:])) + (t >> 32)
|
||||
t = uint64(h1) + uint64(s[1]) + (t >> 32)
|
||||
h1 = uint32(t)
|
||||
t = uint64(h2) + uint64(binary.LittleEndian.Uint32(key[24:])) + (t >> 32)
|
||||
t = uint64(h2) + uint64(s[2]) + (t >> 32)
|
||||
h2 = uint32(t)
|
||||
t = uint64(h3) + uint64(binary.LittleEndian.Uint32(key[28:])) + (t >> 32)
|
||||
t = uint64(h3) + uint64(s[3]) + (t >> 32)
|
||||
h3 = uint32(t)
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], h0)
|
||||
4
vendor/golang.org/x/crypto/poly1305/sum_noasm.go
generated
vendored
4
vendor/golang.org/x/crypto/poly1305/sum_noasm.go
generated
vendored
@@ -10,5 +10,7 @@ package poly1305
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
sumGeneric(out, msg, key)
|
||||
h := newMAC(key)
|
||||
h.Write(msg)
|
||||
h.Sum(out)
|
||||
}
|
||||
|
||||
17
vendor/golang.org/x/crypto/poly1305/sum_s390x.go
generated
vendored
17
vendor/golang.org/x/crypto/poly1305/sum_s390x.go
generated
vendored
@@ -6,16 +6,9 @@
|
||||
|
||||
package poly1305
|
||||
|
||||
// hasVectorFacility reports whether the machine supports
|
||||
// the vector facility (vx).
|
||||
func hasVectorFacility() bool
|
||||
|
||||
// hasVMSLFacility reports whether the machine supports
|
||||
// Vector Multiply Sum Logical (VMSL).
|
||||
func hasVMSLFacility() bool
|
||||
|
||||
var hasVX = hasVectorFacility()
|
||||
var hasVMSL = hasVMSLFacility()
|
||||
import (
|
||||
"golang.org/x/sys/cpu"
|
||||
)
|
||||
|
||||
// poly1305vx is an assembly implementation of Poly1305 that uses vector
|
||||
// instructions. It must only be called if the vector facility (vx) is
|
||||
@@ -33,12 +26,12 @@ func poly1305vmsl(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) {
|
||||
if hasVX {
|
||||
if cpu.S390X.HasVX {
|
||||
var mPtr *byte
|
||||
if len(m) > 0 {
|
||||
mPtr = &m[0]
|
||||
}
|
||||
if hasVMSL && len(m) > 256 {
|
||||
if cpu.S390X.HasVXE && len(m) > 256 {
|
||||
poly1305vmsl(out, mPtr, uint64(len(m)), key)
|
||||
} else {
|
||||
poly1305vx(out, mPtr, uint64(len(m)), key)
|
||||
|
||||
22
vendor/golang.org/x/crypto/poly1305/sum_s390x.s
generated
vendored
22
vendor/golang.org/x/crypto/poly1305/sum_s390x.s
generated
vendored
@@ -376,25 +376,3 @@ b1:
|
||||
|
||||
MOVD $0, R3
|
||||
BR multiply
|
||||
|
||||
TEXT ·hasVectorFacility(SB), NOSPLIT, $24-1
|
||||
MOVD $x-24(SP), R1
|
||||
XC $24, 0(R1), 0(R1) // clear the storage
|
||||
MOVD $2, R0 // R0 is the number of double words stored -1
|
||||
WORD $0xB2B01000 // STFLE 0(R1)
|
||||
XOR R0, R0 // reset the value of R0
|
||||
MOVBZ z-8(SP), R1
|
||||
AND $0x40, R1
|
||||
BEQ novector
|
||||
|
||||
vectorinstalled:
|
||||
// check if the vector instruction has been enabled
|
||||
VLEIB $0, $0xF, V16
|
||||
VLGVB $0, V16, R1
|
||||
CMPBNE R1, $0xF, novector
|
||||
MOVB $1, ret+0(FP) // have vx
|
||||
RET
|
||||
|
||||
novector:
|
||||
MOVB $0, ret+0(FP) // no vx
|
||||
RET
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user