WIP: mautrix

This commit is contained in:
Simon THOBY
2021-07-08 23:35:37 +02:00
parent 23e785deb9
commit 9818f32203
74 changed files with 7740 additions and 2466 deletions

View File

@@ -1,14 +1,13 @@
package bmatrix
import (
"encoding/json"
"errors"
"fmt"
"html"
"strings"
"time"
matrix "github.com/matrix-org/gomatrix"
matrix "maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
)
func newMatrixUsername(username string) *matrixUsername {
@@ -28,7 +27,7 @@ func newMatrixUsername(username string) *matrixUsername {
}
// getRoomID retrieves a matching room ID from the channel name.
func (b *Bmatrix) getRoomID(channel string) string {
func (b *Bmatrix) getRoomID(channel string) id.RoomID {
b.RLock()
defer b.RUnlock()
for ID, name := range b.RoomMap {
@@ -40,21 +39,10 @@ func (b *Bmatrix) getRoomID(channel string) string {
return ""
}
// interface2Struct marshals and immediately unmarshals an interface.
// Useful for converting map[string]interface{} to a struct.
func interface2Struct(in interface{}, out interface{}) error {
jsonObj, err := json.Marshal(in)
if err != nil {
return err //nolint:wrapcheck
}
return json.Unmarshal(jsonObj, out)
}
// getDisplayName retrieves the displayName for mxid, querying the homeserver if the mxid is not in the cache.
func (b *Bmatrix) getDisplayName(channelID string, mxid string) string {
func (b *Bmatrix) getDisplayName(channelID id.RoomID, mxid id.UserID) string {
if b.GetBool("UseUserName") {
return mxid[1:]
return string(mxid)[1:]
}
b.RLock()
@@ -74,7 +62,7 @@ func (b *Bmatrix) getDisplayName(channelID string, mxid string) string {
}
if err != nil {
return b.cacheDisplayName(channelID, mxid, mxid[1:])
return b.cacheDisplayName(channelID, mxid, string(mxid)[1:])
}
return b.cacheDisplayName(channelID, mxid, displayName.DisplayName)
@@ -82,12 +70,12 @@ func (b *Bmatrix) getDisplayName(channelID string, mxid string) string {
// cacheDisplayName stores the mapping between a mxid and a display name, to be reused later without performing a query to the homeserver.
// Note that old entries are cleaned when this function is called.
func (b *Bmatrix) cacheDisplayName(channelID string, mxid string, displayName string) string {
func (b *Bmatrix) cacheDisplayName(channelID id.RoomID, mxid id.UserID, displayName string) string {
now := time.Now()
// scan to delete old entries, to stop memory usage from becoming high with obsolete entries.
// In addition, we detect if another user have the same username, and if so, we append their mxids to their usernames to differentiate them.
toDelete := map[string]string{}
toDelete := map[id.RoomID]id.UserID{}
conflict := false
b.Lock()
@@ -118,7 +106,7 @@ func (b *Bmatrix) cacheDisplayName(channelID string, mxid string, displayName st
}
if _, channelPresent := b.NicknameMap[channelID]; !channelPresent {
b.NicknameMap[channelID] = make(map[string]NicknameCacheEntry)
b.NicknameMap[channelID] = make(map[id.UserID]NicknameCacheEntry)
}
b.NicknameMap[channelID][mxid] = NicknameCacheEntry{
@@ -130,77 +118,43 @@ func (b *Bmatrix) cacheDisplayName(channelID string, mxid string, displayName st
return displayName
}
// handleError converts errors into httpError.
//nolint:exhaustivestruct
func handleError(err error) *httpError {
var mErr matrix.HTTPError
if !errors.As(err, &mErr) {
return &httpError{
Err: "not a HTTPError",
}
}
var httpErr httpError
if err := json.Unmarshal(mErr.Contents, &httpErr); err != nil {
return &httpError{
Err: "unmarshal failed",
}
}
return &httpErr
}
func (b *Bmatrix) containsAttachment(content map[string]interface{}) bool {
// Skip empty messages
if content["msgtype"] == nil {
return false
}
// Only allow image,video or file msgtypes
if !(content["msgtype"].(string) == "m.image" ||
content["msgtype"].(string) == "m.video" ||
content["msgtype"].(string) == "m.file") {
return false
}
return true
}
// getAvatarURL returns the avatar URL of the specified sender.
func (b *Bmatrix) getAvatarURL(sender string) string {
urlPath := b.mc.BuildURL("profile", sender, "avatar_url")
s := struct {
AvatarURL string `json:"avatar_url"`
}{}
err := b.mc.MakeRequest("GET", urlPath, nil, &s)
func (b *Bmatrix) getAvatarURL(sender id.UserID) string {
url, err := b.mc.GetAvatarURL(sender)
if err != nil {
b.Log.Errorf("getAvatarURL failed: %s", err)
b.Log.Errorf("Couldn't retrieve the URL of the avatar for MXID %s", sender)
return ""
}
url := strings.ReplaceAll(s.AvatarURL, "mxc://", b.GetString("Server")+"/_matrix/media/r0/thumbnail/")
if url != "" {
url += "?width=37&height=37&method=crop"
}
return url
return url.String()
}
// handleRatelimit handles the ratelimit errors and return if we're ratelimited and the amount of time to sleep
func (b *Bmatrix) handleRatelimit(err error) (time.Duration, bool) {
httpErr := handleError(err)
if httpErr.Errcode != "M_LIMIT_EXCEEDED" {
var mErr matrix.HTTPError
if !errors.As(err, &mErr) {
b.Log.Errorf("Received a non-HTTPError, don't know what to make of it:\n%#v", err)
return 0, false
}
b.Log.Debugf("ratelimited: %s", httpErr.Err)
b.Log.Infof("getting ratelimited by matrix, sleeping approx %d seconds before retrying", httpErr.RetryAfterMs/1000)
if mErr.RespError.ErrCode != "M_LIMIT_EXCEEDED" {
return 0, false
}
return time.Duration(httpErr.RetryAfterMs) * time.Millisecond, true
b.Log.Debugf("ratelimited: %s", mErr.RespError.Err)
// fallback to a one-second delay
retryDelayMs := 1000
if retryDelayString, present := mErr.RespError.ExtraData["retry_after_ms"]; present {
if retryDelayInt, correct := retryDelayString.(int); correct && retryDelayInt > retryDelayMs {
retryDelayMs = retryDelayInt
}
}
b.Log.Infof("getting ratelimited by matrix, sleeping approx %d seconds before retrying", retryDelayMs/1000)
return time.Duration(retryDelayMs) * time.Millisecond, true
}
// retry function will check if we're ratelimited and retries again when backoff time expired

View File

@@ -1,3 +1,4 @@
//nolint: exhaustivestruct
package bmatrix
import (
@@ -9,10 +10,13 @@ import (
"sync"
"time"
matrix "maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"github.com/42wim/matterbridge/bridge"
"github.com/42wim/matterbridge/bridge/config"
"github.com/42wim/matterbridge/bridge/helper"
matrix "github.com/matrix-org/gomatrix"
)
var (
@@ -26,20 +30,15 @@ type NicknameCacheEntry struct {
}
type Bmatrix struct {
mc *matrix.Client
UserID string
// channelId -> mxid -> NickNameCacheEntry
NicknameMap map[string]map[string]NicknameCacheEntry
RoomMap map[string]string
mc *matrix.Client
UserID id.UserID
NicknameMap map[id.RoomID]map[id.UserID]NicknameCacheEntry
RoomMap map[id.RoomID]string
rateMutex sync.RWMutex
sync.RWMutex
*bridge.Config
}
type httpError struct {
Errcode string `json:"errcode"`
Err string `json:"error"`
RetryAfterMs int `json:"retry_after_ms"`
stop chan int
stopAck chan int
}
type matrixUsername struct {
@@ -47,29 +46,12 @@ type matrixUsername struct {
formatted string
}
// SubTextMessage represents the new content of the message in edit messages.
type SubTextMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
}
// MessageRelation explains how the current message relates to a previous message.
// Notably used for message edits.
type MessageRelation struct {
EventID string `json:"event_id"`
Type string `json:"rel_type"`
}
type EditedMessage struct {
NewContent SubTextMessage `json:"m.new_content"`
RelatedTo MessageRelation `json:"m.relates_to"`
matrix.TextMessage
}
func New(cfg *bridge.Config) bridge.Bridger {
b := &Bmatrix{Config: cfg}
b.RoomMap = make(map[string]string)
b.NicknameMap = make(map[string]map[string]NicknameCacheEntry)
b.RoomMap = make(map[id.RoomID]string)
b.NicknameMap = make(map[id.RoomID]map[id.UserID]NicknameCacheEntry)
b.stop = make(chan int, 1)
b.stopAck = make(chan int, 1)
return b
}
@@ -77,13 +59,13 @@ func (b *Bmatrix) Connect() error {
var err error
b.Log.Infof("Connecting %s", b.GetString("Server"))
if b.GetString("MxID") != "" && b.GetString("Token") != "" {
b.UserID = id.UserID(b.GetString("MxID"))
b.mc, err = matrix.NewClient(
b.GetString("Server"), b.GetString("MxID"), b.GetString("Token"),
b.GetString("Server"), b.UserID, b.GetString("Token"),
)
if err != nil {
return err
}
b.UserID = b.GetString("MxID")
b.Log.Info("Using existing Matrix credentials")
} else {
b.mc, err = matrix.NewClient(b.GetString("Server"), "", "")
@@ -91,15 +73,14 @@ func (b *Bmatrix) Connect() error {
return err
}
resp, err := b.mc.Login(&matrix.ReqLogin{
Type: "m.login.password",
User: b.GetString("Login"),
Password: b.GetString("Password"),
Identifier: matrix.NewUserIdentifier(b.GetString("Login")),
Type: matrix.AuthTypePassword,
Password: b.GetString("Password"),
Identifier: matrix.UserIdentifier{Type: matrix.IdentifierTypeUser, User: b.GetString("Login")},
StoreCredentials: true,
})
if err != nil {
return err
}
b.mc.SetCredentials(resp.UserID, resp.AccessToken)
b.UserID = resp.UserID
b.Log.Info("Connection succeeded")
}
@@ -108,6 +89,13 @@ func (b *Bmatrix) Connect() error {
}
func (b *Bmatrix) Disconnect() error {
// tell the Sync() loop to exit
b.stop <- 0
b.mc.StopSync()
// wait for the syncer to terminate
<-b.stopAck
return nil
}
@@ -136,26 +124,13 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
// Make a action /me of the message
if msg.Event == config.EventUserAction {
m := matrix.TextMessage{
MsgType: "m.emote",
m := event.MessageEventContent{
MsgType: event.MsgEmote,
Body: username.plain + msg.Text,
FormattedBody: username.formatted + msg.Text,
}
msgID := ""
err := b.retry(func() error {
resp, err := b.mc.SendMessageEvent(channel, "m.room.message", m)
if err != nil {
return err
}
msgID = resp.EventID
return err
})
return msgID, err
return b.sendEventWithRetries(channel, event.EventMessage, m)
}
// Delete message
@@ -167,12 +142,12 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
msgID := ""
err := b.retry(func() error {
resp, err := b.mc.RedactEvent(channel, msg.ID, &matrix.ReqRedact{})
resp, err := b.mc.RedactEvent(channel, id.EventID(msg.ID), matrix.ReqRedact{})
if err != nil {
return err
}
msgID = resp.EventID
msgID = string(resp.EventID)
return err
})
@@ -183,13 +158,12 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
// Upload a file if it exists
if msg.Extra != nil {
for _, rmsg := range helper.HandleExtra(&msg, b.General) {
rmsg := rmsg
m := event.MessageEventContent{
MsgType: event.MsgText,
Body: rmsg.Username + rmsg.Text,
}
err := b.retry(func() error {
_, err := b.mc.SendText(channel, rmsg.Username+rmsg.Text)
return err
})
_, err := b.sendEventWithRetries(channel, event.EventMessage, m)
if err != nil {
b.Log.Errorf("sendText failed: %s", err)
}
@@ -202,89 +176,84 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
// Edit message if we have an ID
if msg.ID != "" {
rmsg := EditedMessage{TextMessage: matrix.TextMessage{
rmsg := event.MessageEventContent{
Body: username.plain + msg.Text,
MsgType: "m.text",
}}
MsgType: event.MsgText,
}
if b.GetBool("HTMLDisable") {
rmsg.TextMessage.FormattedBody = username.formatted + "* " + msg.Text
rmsg.FormattedBody = username.formatted + "* " + msg.Text
} else {
rmsg.Format = "org.matrix.custom.html"
rmsg.TextMessage.FormattedBody = username.formatted + "* " + helper.ParseMarkdown(msg.Text)
rmsg.Format = event.FormatHTML
rmsg.FormattedBody = username.formatted + "* " + helper.ParseMarkdown(msg.Text)
}
rmsg.NewContent = SubTextMessage{
Body: rmsg.TextMessage.Body,
MsgType: "m.text",
rmsg.NewContent = &event.MessageEventContent{
Body: rmsg.Body,
MsgType: event.MsgText,
}
rmsg.RelatedTo = MessageRelation{
EventID: msg.ID,
Type: "m.replace",
rmsg.RelatesTo = &event.RelatesTo{
EventID: id.EventID(msg.ID),
Type: event.RelReplace,
}
err := b.retry(func() error {
_, err := b.mc.SendMessageEvent(channel, "m.room.message", rmsg)
return b.sendEventWithRetries(channel, event.EventMessage, rmsg)
}
return err
})
if err != nil {
return "", err
}
return msg.ID, nil
m := event.MessageEventContent{
Body: username.plain + msg.Text,
FormattedBody: username.formatted + msg.Text,
}
// Use notices to send join/leave events
if msg.Event == config.EventJoinLeave {
m := matrix.TextMessage{
MsgType: "m.notice",
Body: username.plain + msg.Text,
FormattedBody: username.formatted + msg.Text,
m.MsgType = event.MsgNotice
} else {
m.MsgType = event.MsgText
if b.GetBool("HTMLDisable") {
m.FormattedBody = ""
} else {
m.FormattedBody = username.formatted + helper.ParseMarkdown(msg.Text)
}
var (
resp *matrix.RespSendEvent
err error
)
err = b.retry(func() error {
resp, err = b.mc.SendMessageEvent(channel, "m.room.message", m)
return err
})
if err != nil {
return "", err
}
return resp.EventID, err
}
if b.GetBool("HTMLDisable") {
var (
resp *matrix.RespSendEvent
err error
)
return b.sendEventWithRetries(channel, event.EventMessage, m)
}
err = b.retry(func() error {
resp, err = b.mc.SendText(channel, username.plain+msg.Text)
func (b *Bmatrix) handlematrix() {
syncer, ok := b.mc.Syncer.(*matrix.DefaultSyncer)
if !ok {
b.Log.Errorln("couldn't convert the Syncer object to a DefaultSyncer structure, the matrix bridge won't work")
return err
})
if err != nil {
return "", err
}
return resp.EventID, err
return
}
// Post normal message with HTML support (eg riot.im)
syncer.OnEventType(event.EventRedaction, b.handleEvent)
syncer.OnEventType(event.EventMessage, b.handleEvent)
syncer.OnEventType(event.StateMember, b.handleMemberChange)
go func() {
for {
select {
case <-b.stop:
b.stopAck <- 1
return
default:
if err := b.mc.Sync(); err != nil {
b.Log.Println("Sync() returned ", err)
}
}
}
}()
}
//nolint: wrapcheck
func (b *Bmatrix) sendEventWithRetries(channel id.RoomID, eventType event.Type, eventData interface{}) (string, error) {
var (
resp *matrix.RespSendEvent
err error
)
err = b.retry(func() error {
resp, err = b.mc.SendFormattedText(channel, username.plain+msg.Text,
username.formatted+helper.ParseMarkdown(msg.Text))
resp, err = b.mc.SendMessageEvent(channel, eventType, eventData)
return err
})
@@ -292,63 +261,57 @@ func (b *Bmatrix) Send(msg config.Message) (string, error) {
return "", err
}
return resp.EventID, err
return string(resp.EventID), err
}
func (b *Bmatrix) handlematrix() {
syncer := b.mc.Syncer.(*matrix.DefaultSyncer)
syncer.OnEventType("m.room.redaction", b.handleEvent)
syncer.OnEventType("m.room.message", b.handleEvent)
syncer.OnEventType("m.room.member", b.handleMemberChange)
go func() {
for {
if err := b.mc.Sync(); err != nil {
b.Log.Println("Sync() returned ", err)
}
func (b *Bmatrix) handleMemberChange(source matrix.EventSource, ev *event.Event) {
member := ev.Content.AsMember()
if member == nil {
b.Log.Errorf("Couldn't process a member event:\n%#v", ev)
return
}
// Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information
if member.Membership == event.MembershipJoin {
b.cacheDisplayName(ev.RoomID, ev.Sender, member.Displayname)
}
}
func (b *Bmatrix) handleMessage(rmsg config.Message, msg event.MessageEventContent, ev *event.Event) {
rmsg.Text = msg.Body
// Do we have a /me action
if msg.MsgType == event.MsgEmote {
rmsg.Event = config.EventUserAction
}
// Is it an edit?
if msg.RelatesTo != nil && msg.NewContent != nil && msg.RelatesTo.Type == event.RelReplace {
rmsg.ID = string(msg.RelatesTo.EventID)
rmsg.Text = msg.NewContent.Body
b.Remote <- rmsg
return
}
// Do we have attachments (we only allow image,video or file msgtypes)
if msg.MsgType == event.MsgImage || msg.MsgType == event.MsgVideo || msg.MsgType == event.MsgFile {
err := b.handleDownloadFile(&rmsg, msg)
if err != nil {
b.Log.Errorf("download failed: %#v", err)
}
}()
}
func (b *Bmatrix) handleEdit(ev *matrix.Event, rmsg config.Message) bool {
relationInterface, present := ev.Content["m.relates_to"]
newContentInterface, present2 := ev.Content["m.new_content"]
if !(present && present2) {
return false
}
var relation MessageRelation
if err := interface2Struct(relationInterface, &relation); err != nil {
b.Log.Warnf("Couldn't parse 'm.relates_to' object with value %#v", relationInterface)
return false
}
var newContent SubTextMessage
if err := interface2Struct(newContentInterface, &newContent); err != nil {
b.Log.Warnf("Couldn't parse 'm.new_content' object with value %#v", newContentInterface)
return false
}
if relation.Type != "m.replace" {
return false
}
rmsg.ID = relation.EventID
rmsg.Text = newContent.Body
b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
b.Remote <- rmsg
return true
}
func (b *Bmatrix) handleMemberChange(ev *matrix.Event) {
// Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information
if ev.Content["membership"] == "join" {
if dn, ok := ev.Content["displayname"].(string); ok {
b.cacheDisplayName(ev.RoomID, ev.Sender, dn)
}
// not crucial, so no ratelimit check here
if err := b.mc.MarkRead(ev.RoomID, ev.ID); err != nil {
b.Log.Errorf("couldn't mark message as read %s", err.Error())
}
}
func (b *Bmatrix) handleEvent(ev *matrix.Event) {
func (b *Bmatrix) handleEvent(source matrix.EventSource, ev *event.Event) {
b.Log.Debugf("== Receiving event: %#v", ev)
if ev.Sender != b.UserID {
b.RLock()
@@ -364,8 +327,8 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
Username: b.getDisplayName(ev.RoomID, ev.Sender),
Channel: channel,
Account: b.Account,
UserID: ev.Sender,
ID: ev.ID,
UserID: string(ev.Sender),
ID: string(ev.ID),
Avatar: b.getAvatarURL(ev.Sender),
}
@@ -376,95 +339,52 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
}
// Delete event
if ev.Type == "m.room.redaction" {
if ev.Type == event.EventRedaction {
rmsg.Event = config.EventMsgDelete
rmsg.ID = ev.Redacts
rmsg.ID = string(ev.Redacts)
rmsg.Text = config.EventMsgDelete
b.Remote <- rmsg
return
}
// Text must be a string
if rmsg.Text, ok = ev.Content["body"].(string); !ok {
b.Log.Errorf("Content[body] is not a string: %T\n%#v",
ev.Content["body"], ev.Content)
msg := ev.Content.AsMessage()
if msg == nil {
b.Log.Errorf("matterbridge don't support this event type: %s", ev.Type.Type)
b.Log.Debugf("Full event: %#v", ev)
return
}
// Do we have a /me action
if ev.Content["msgtype"].(string) == "m.emote" {
rmsg.Event = config.EventUserAction
}
// Is it an edit?
if b.handleEdit(ev, rmsg) {
return
}
// Do we have attachments
if b.containsAttachment(ev.Content) {
err := b.handleDownloadFile(&rmsg, ev.Content)
if err != nil {
b.Log.Errorf("download failed: %#v", err)
}
}
b.Log.Debugf("<= Sending message from %s on %s to gateway", ev.Sender, b.Account)
b.Remote <- rmsg
// not crucial, so no ratelimit check here
if err := b.mc.MarkRead(ev.RoomID, ev.ID); err != nil {
b.Log.Errorf("couldn't mark message as read %s", err.Error())
}
b.handleMessage(rmsg, *msg, ev)
}
}
// handleDownloadFile handles file download
func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, content map[string]interface{}) error {
var (
ok bool
url, name, msgtype, mtype string
info map[string]interface{}
size float64
)
func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, msg event.MessageEventContent) error {
rmsg.Extra = make(map[string][]interface{})
if url, ok = content["url"].(string); !ok {
return fmt.Errorf("url isn't a %T", url)
if msg.URL == "" || msg.Info == nil {
b.Log.Error("couldn't download a file with no URL or no file informations (invalid event ?)")
b.Log.Debugf("Full Message content:\n%#v", msg)
}
url = strings.Replace(url, "mxc://", b.GetString("Server")+"/_matrix/media/v1/download/", -1)
if info, ok = content["info"].(map[string]interface{}); !ok {
return fmt.Errorf("info isn't a %T", info)
}
if size, ok = info["size"].(float64); !ok {
return fmt.Errorf("size isn't a %T", size)
}
if name, ok = content["body"].(string); !ok {
return fmt.Errorf("name isn't a %T", name)
}
if msgtype, ok = content["msgtype"].(string); !ok {
return fmt.Errorf("msgtype isn't a %T", msgtype)
}
if mtype, ok = info["mimetype"].(string); !ok {
return fmt.Errorf("mtype isn't a %T", mtype)
}
url := strings.ReplaceAll(string(msg.URL), "mxc://", b.GetString("Server")+"/_matrix/media/v1/download/")
filename := msg.Body
// check if we have an image uploaded without extension
if !strings.Contains(name, ".") {
if msgtype == "m.image" {
mext, _ := mime.ExtensionsByType(mtype)
if len(mext) > 0 {
name += mext[0]
}
if !strings.Contains(filename, ".") {
mext, _ := mime.ExtensionsByType(msg.Info.MimeType)
if len(mext) > 0 {
filename += mext[0]
} else {
// just a default .png extension if we don't have mime info
name += ".png"
if msg.MsgType == event.MsgImage {
// just a default .png extension if we don't have mime info
filename += ".png"
}
}
}
// check if the size is ok
err := helper.HandleDownloadSize(b.Log, rmsg, name, int64(size), b.General)
err := helper.HandleDownloadSize(b.Log, rmsg, filename, int64(msg.Info.Size), b.General)
if err != nil {
return err
}
@@ -474,12 +394,12 @@ func (b *Bmatrix) handleDownloadFile(rmsg *config.Message, content map[string]in
return fmt.Errorf("download %s failed %#v", url, err)
}
// add the downloaded data to the message
helper.HandleDownloadData(b.Log, rmsg, name, "", url, data, b.General)
helper.HandleDownloadData(b.Log, rmsg, filename, "", url, data, b.General)
return nil
}
// handleUploadFiles handles native upload of files.
func (b *Bmatrix) handleUploadFiles(msg *config.Message, channel string) (string, error) {
func (b *Bmatrix) handleUploadFiles(msg *config.Message, channel id.RoomID) (string, error) {
for _, f := range msg.Extra["file"] {
if fi, ok := f.(config.FileInfo); ok {
b.handleUploadFile(msg, channel, &fi)
@@ -489,17 +409,21 @@ func (b *Bmatrix) handleUploadFiles(msg *config.Message, channel string) (string
}
// handleUploadFile handles native upload of a file.
func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *config.FileInfo) {
//nolint: funlen
func (b *Bmatrix) handleUploadFile(msg *config.Message, channel id.RoomID, fi *config.FileInfo) {
username := newMatrixUsername(msg.Username)
content := bytes.NewReader(*fi.Data)
sp := strings.Split(fi.Name, ".")
mtype := mime.TypeByExtension("." + sp[len(sp)-1])
// image and video uploads send no username, we have to do this ourself here #715
err := b.retry(func() error {
_, err := b.mc.SendFormattedText(channel, username.plain+fi.Comment, username.formatted+fi.Comment)
return err
})
// image and video uploads send no username, we have to do this ourself here #715
m := event.MessageEventContent{
MsgType: event.MsgText,
Body: username.plain + fi.Comment,
FormattedBody: username.formatted + fi.Comment,
}
_, err := b.sendEventWithRetries(channel, event.EventMessage, m)
if err != nil {
b.Log.Errorf("file comment failed: %#v", err)
}
@@ -507,9 +431,14 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
b.Log.Debugf("uploading file: %s %s", fi.Name, mtype)
var res *matrix.RespMediaUpload
req := matrix.ReqUploadMedia{
Content: content,
ContentType: mtype,
ContentLength: fi.Size,
}
err = b.retry(func() error {
res, err = b.mc.UploadToContentRepo(content, mtype, int64(len(*fi.Data)))
res, err = b.mc.UploadMedia(req)
return err
})
@@ -519,63 +448,42 @@ func (b *Bmatrix) handleUploadFile(msg *config.Message, channel string, fi *conf
return
}
b.Log.Debugf("result: %#v", res)
m = event.MessageEventContent{
Body: fi.Name,
URL: res.ContentURI.CUString(),
}
switch {
case strings.Contains(mtype, "video"):
b.Log.Debugf("sendVideo %s", res.ContentURI)
err = b.retry(func() error {
_, err = b.mc.SendVideo(channel, fi.Name, res.ContentURI)
return err
})
if err != nil {
b.Log.Errorf("sendVideo failed: %#v", err)
}
m.MsgType = event.MsgVideo
case strings.Contains(mtype, "image"):
b.Log.Debugf("sendImage %s", res.ContentURI)
err = b.retry(func() error {
_, err = b.mc.SendImage(channel, fi.Name, res.ContentURI)
return err
})
if err != nil {
b.Log.Errorf("sendImage failed: %#v", err)
}
m.MsgType = event.MsgImage
case strings.Contains(mtype, "audio"):
b.Log.Debugf("sendAudio %s", res.ContentURI)
err = b.retry(func() error {
_, err = b.mc.SendMessageEvent(channel, "m.room.message", matrix.AudioMessage{
MsgType: "m.audio",
Body: fi.Name,
URL: res.ContentURI,
Info: matrix.AudioInfo{
Mimetype: mtype,
Size: uint(len(*fi.Data)),
},
})
return err
})
if err != nil {
b.Log.Errorf("sendAudio failed: %#v", err)
m.MsgType = event.MsgAudio
m.Info = &event.FileInfo{
MimeType: mtype,
Size: len(*fi.Data),
}
default:
b.Log.Debugf("sendFile %s", res.ContentURI)
err = b.retry(func() error {
_, err = b.mc.SendMessageEvent(channel, "m.room.message", matrix.FileMessage{
MsgType: "m.file",
Body: fi.Name,
URL: res.ContentURI,
Info: matrix.FileInfo{
Mimetype: mtype,
Size: uint(len(*fi.Data)),
},
})
return err
})
if err != nil {
b.Log.Errorf("sendFile failed: %#v", err)
m.MsgType = event.MsgFile
m.Info = &event.FileInfo{
MimeType: mtype,
Size: len(*fi.Data),
}
}
b.Log.Debugf("result: %#v", res)
_, err = b.sendEventWithRetries(channel, event.EventMessage, m)
if err != nil {
b.Log.Errorf("sending the message referencing the uploaded file failed: %#v", err)
}
}

3
go.mod
View File

@@ -22,7 +22,7 @@ require (
github.com/kyokomi/emoji/v2 v2.2.8
github.com/labstack/echo/v4 v4.3.0
github.com/lrstanley/girc v0.0.0-20210611213246-771323f1624b
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 // indirect
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20210403163225-761e8622445d
github.com/matterbridge/discordgo v0.21.2-0.20210201201054-fb39a175b4f7
github.com/matterbridge/go-xmpp v0.0.0-20200418225040-c8a3a57b4050
@@ -55,6 +55,7 @@ require (
gomod.garykim.dev/nc-talk v0.3.0
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376
layeh.com/gumble v0.0.0-20200818122324-146f9205029b
maunium.net/go/mautrix v0.9.14
)
go 1.15

49
go.sum
View File

@@ -56,7 +56,6 @@ github.com/Azure/azure-sdk-for-go v26.5.0+incompatible/go.mod h1:9XXNKU+eRnpl9mo
github.com/Azure/go-autorest v11.5.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
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/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
@@ -94,6 +93,7 @@ github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrU
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/advancedlogic/GoOse v0.0.0-20191112112754-e742535969c1/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
github.com/advancedlogic/GoOse v0.0.0-20200830213114-1225d531e0ad/go.mod h1:f3HCSN1fBWjcpGtXyM119MJgeQl838v6so/PQOqvE1w=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -146,6 +146,16 @@ github.com/blevesearch/zap/v14 v14.0.3/go.mod h1:oObAhcDHw7p1ahiTCqhRkdxdl7UA8qp
github.com/blevesearch/zap/v15 v15.0.1/go.mod h1:ho0frqAex2ktT9cYFAxQpoQXsxb/KEfdjpx4s49rf/M=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
@@ -192,6 +202,7 @@ github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs
github.com/cznic/strutil v0.0.0-20181122101858-275e90344537/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
github.com/d5/tengo/v2 v2.7.0 h1:oAGQ+gcas0/T0bdqvNxfKzjOJhpQRwceWIF5gldB3aM=
github.com/d5/tengo/v2 v2.7.0/go.mod h1:XRGjEs5I9jYIKTxly6HCF8oiiilk5E/RYXOZ5b0DZC8=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -284,7 +295,6 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81 h1:FdZThbRF0R+2qgyBl3KCVNWWBmKm68E+stT3rnQ02Ww=
github.com/go-telegram-bot-api/telegram-bot-api v1.0.1-0.20200524105306-7434b0456e81/go.mod h1:lDm2E64X4OjFdBUA4hlN4mEvbSitvhJdKw7rsA8KHgI=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
@@ -348,7 +358,6 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
@@ -382,7 +391,6 @@ github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4 h1:4EZlYQIiyecYJlUbV
github.com/gopackage/ddp v0.0.0-20170117053602-652027933df4/go.mod h1:lEO7XoHJ/xNRBCxrn4h/CEB67h0kW1B0t4ooP2yrjUA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
@@ -468,6 +476,7 @@ github.com/jamiealquiza/envy v1.1.0/go.mod h1:MP36BriGCLwEHhi1OU8E9569JNZrjWfCvz
github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -481,6 +490,7 @@ github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUB
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -489,7 +499,6 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
@@ -507,6 +516,7 @@ github.com/keybase/go-ps v0.0.0-20190827175125-91aafc93ba19/go.mod h1:hY+WOq6m2F
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@@ -523,12 +533,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kyokomi/emoji/v2 v2.2.8 h1:jcofPxjHWEkJtkIbcLHvZhxKgCPl6C7MyjTrD4KDqUE=
github.com/kyokomi/emoji/v2 v2.2.8/go.mod h1:JUcn42DTdsXJo1SWanHh4HKDEyPaR5CqkmoirZZP9qE=
@@ -544,6 +552,7 @@ github.com/levigross/exp-html v0.0.0-20120902181939-8df60c69a8f5/go.mod h1:QMe2w
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lrstanley/girc v0.0.0-20210611213246-771323f1624b h1:jrLvME7VuLW6NRysbiZtenTB9QcNlR9RPKK4LFfZn60=
@@ -556,7 +565,6 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/marstr/guid v0.0.0-20170427235115-8bdf7d1a087c/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16 h1:ZtO5uywdd5dLDCud4r0r55eP4j9FuUNpl60Gmntcop4=
github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16/go.mod h1:/gBX06Kw0exX1HrwmoBibFA98yBk/jxKpGVeyQbff+s=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20210403163225-761e8622445d h1:Csy27nDj00vRGp2+QvwSanaW0RA60SZUHXCk4BBT0AU=
github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20210403163225-761e8622445d/go.mod h1:c6MxwqHD+0HvtAJjsHMIdPCiAwGiQwPRPTp69ACMg8A=
@@ -600,6 +608,7 @@ github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/godown v0.0.1 h1:39uk50ufLVQFs0eapIJVX5fCS74a1Fs2g5f1MVqIHdE=
github.com/mattn/godown v0.0.1/go.mod h1:/ivCKurgV/bx6yqtP/Jtc2Xmrv3beCYBvlfAUl4X5g4=
@@ -667,7 +676,6 @@ github.com/nelsonken/gomf v0.0.0-20180504123937-a9dd2f9deae9/go.mod h1:A5SRAcpTe
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/ngdinhtoan/glide-cleanup v0.2.0/go.mod h1:UQzsmiDOb8YV3nOsCxK/c9zPpCZVNoHScRE3EO9pVMM=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
@@ -683,13 +691,11 @@ github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4=
github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.2 h1:aY/nuoWlKJud2J6U0E3NWsjlg+0GtwXxgEqthRdzlcs=
github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/oov/psd v0.0.0-20201002182931-74231384897f/go.mod h1:GHI1bnmAcbp96z6LNfBJvtrjxhaXGkbsk967utPlvL8=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
@@ -791,6 +797,7 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww=
github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -843,10 +850,8 @@ github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDq
github.com/slack-go/slack v0.9.1 h1:pekQBs0RmrdAgoqzcMCzUCWSyIkhzUU3F83ExAdZrKo=
github.com/slack-go/slack v0.9.1/go.mod h1:wWL//kk0ho+FcQXcBTmEafUI5dz4qz5f4mMk8oIkioQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0 h1:UVQPSSmc3qtTi+zPPkCXvZX9VvW/xT/NsRvKfwY81a8=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
@@ -899,6 +904,10 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7
github.com/technoweenie/multipartstreamer v1.0.1 h1:XRztA5MXiR1TIRHxH2uNxXxaIkKQDeX7m2XsSOlQEnM=
github.com/technoweenie/multipartstreamer v1.0.1/go.mod h1:jNVxdtShOxzAsukZwTSw6MDx5eUJoiEBsSvzDU9uzog=
github.com/throttled/throttled v2.2.5+incompatible/go.mod h1:0BjlrEGQmvxps+HuXLsyRdqpSRvJpq0PNIsOtqP9Nos=
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tinylib/msgp v1.1.2 h1:gWmO7n0Ys2RBEb7GPYB9Ujq8Mk5p2U08lRnmMcGy6BQ=
github.com/tinylib/msgp v1.1.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
@@ -944,7 +953,6 @@ github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPy
github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
github.com/writeas/go-strip-markdown v2.0.1+incompatible h1:IIqxTM5Jr7RzhigcL6FkrCNfXkvbR+Nbu1ls48pXYcw=
github.com/writeas/go-strip-markdown v2.0.1+incompatible/go.mod h1:Rsyu10ZhbEK9pXdk8V6MVnZmTzRG0alMNLMwa0J01fE=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
@@ -1010,6 +1018,7 @@ go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
golang.org/x/build v0.0.0-20190314133821-5284462c4bec/go.mod h1:atTaCNAy0f16Ah5aV1gMSwgiKVHwu/JncqDpuRr7lS4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1022,10 +1031,12 @@ golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1124,6 +1135,7 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
@@ -1229,6 +1241,7 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1315,10 +1328,7 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gomod.garykim.dev/nc-talk v0.2.2 h1:+U+daJFPPuwM7yRXYazeMHZgIBSGP6SeQURO0O5a32I=
gomod.garykim.dev/nc-talk v0.2.2/go.mod h1:q/Adot/H7iqi+H4lANopV7/xcMf+sX3AZXUXqiITwok=
gomod.garykim.dev/nc-talk v0.3.0 h1:MZxLc/gX2/+bdOw4xt6pi+qQFUQld1woGfw1hEJ0fbM=
gomod.garykim.dev/nc-talk v0.3.0/go.mod h1:q/Adot/H7iqi+H4lANopV7/xcMf+sX3AZXUXqiITwok=
google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
@@ -1456,7 +1466,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
@@ -1477,7 +1486,6 @@ gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJc
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M=
gopkg.in/olivere/elastic.v6 v6.2.35/go.mod h1:2cTT8Z+/LcArSWpCgvZqBgt3VOqXiy7v00w12Lz8bd4=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -1507,6 +1515,9 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
layeh.com/gopus v0.0.0-20161224163843-0ebf989153aa/go.mod h1:AOef7vHz0+v4sWwJnr0jSyHiX/1NgsMoaxl+rEPz/I0=
layeh.com/gumble v0.0.0-20200818122324-146f9205029b h1:Kne6wkHqbqrygRsqs5XUNhSs84DFG5TYMeCkCbM56sY=
layeh.com/gumble v0.0.0-20200818122324-146f9205029b/go.mod h1:tWPVA9ZAfImNwabjcd9uDE+Mtz0Hfs7a7G3vxrnrwyc=
maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
maunium.net/go/mautrix v0.9.14 h1:2MMJ630VM+xfa4Q5AooMAhPG1+wQnQybSr/z8PlRZ8A=
maunium.net/go/mautrix v0.9.14/go.mod h1:7IzKfWvpQtN+W2Lzxc0rLvIxFM3ryKX6Ys3S/ZoWbg8=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

16
vendor/github.com/btcsuite/btcutil/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,16 @@
ISC License
Copyright (c) 2013-2017 The btcsuite developers
Copyright (c) 2016-2017 The Lightning Network Developers
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

34
vendor/github.com/btcsuite/btcutil/base58/README.md generated vendored Normal file
View File

@@ -0,0 +1,34 @@
base58
==========
[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil)
[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org)
[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg)](http://godoc.org/github.com/btcsuite/btcutil/base58)
Package base58 provides an API for encoding and decoding to and from the
modified base58 encoding. It also provides an API to do Base58Check encoding,
as described [here](https://en.bitcoin.it/wiki/Base58Check_encoding).
A comprehensive suite of tests is provided to ensure proper functionality.
## Installation and Updating
```bash
$ go get -u github.com/btcsuite/btcutil/base58
```
## Examples
* [Decode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Decode)
Demonstrates how to decode modified base58 encoded data.
* [Encode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-Encode)
Demonstrates how to encode data using the modified base58 encoding scheme.
* [CheckDecode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckDecode)
Demonstrates how to decode Base58Check encoded data.
* [CheckEncode Example](http://godoc.org/github.com/btcsuite/btcutil/base58#example-CheckEncode)
Demonstrates how to encode data using the Base58Check encoding scheme.
## License
Package base58 is licensed under the [copyfree](http://copyfree.org) ISC
License.

49
vendor/github.com/btcsuite/btcutil/base58/alphabet.go generated vendored Normal file
View File

@@ -0,0 +1,49 @@
// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// AUTOGENERATED by genalphabet.go; do not edit.
package base58
const (
// alphabet is the modified base58 alphabet used by Bitcoin.
alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
alphabetIdx0 = '1'
)
var b58 = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6,
7, 8, 255, 255, 255, 255, 255, 255,
255, 9, 10, 11, 12, 13, 14, 15,
16, 255, 17, 18, 19, 20, 21, 255,
22, 23, 24, 25, 26, 27, 28, 29,
30, 31, 32, 255, 255, 255, 255, 255,
255, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 255, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54,
55, 56, 57, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255,
}

75
vendor/github.com/btcsuite/btcutil/base58/base58.go generated vendored Normal file
View File

@@ -0,0 +1,75 @@
// Copyright (c) 2013-2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
import (
"math/big"
)
//go:generate go run genalphabet.go
var bigRadix = big.NewInt(58)
var bigZero = big.NewInt(0)
// Decode decodes a modified base58 string to a byte slice.
func Decode(b string) []byte {
answer := big.NewInt(0)
j := big.NewInt(1)
scratch := new(big.Int)
for i := len(b) - 1; i >= 0; i-- {
tmp := b58[b[i]]
if tmp == 255 {
return []byte("")
}
scratch.SetInt64(int64(tmp))
scratch.Mul(j, scratch)
answer.Add(answer, scratch)
j.Mul(j, bigRadix)
}
tmpval := answer.Bytes()
var numZeros int
for numZeros = 0; numZeros < len(b); numZeros++ {
if b[numZeros] != alphabetIdx0 {
break
}
}
flen := numZeros + len(tmpval)
val := make([]byte, flen)
copy(val[numZeros:], tmpval)
return val
}
// Encode encodes a byte slice to a modified base58 string.
func Encode(b []byte) string {
x := new(big.Int)
x.SetBytes(b)
answer := make([]byte, 0, len(b)*136/100)
for x.Cmp(bigZero) > 0 {
mod := new(big.Int)
x.DivMod(x, bigRadix, mod)
answer = append(answer, alphabet[mod.Int64()])
}
// leading zero bytes
for _, i := range b {
if i != 0 {
break
}
answer = append(answer, alphabetIdx0)
}
// reverse
alen := len(answer)
for i := 0; i < alen/2; i++ {
answer[i], answer[alen-1-i] = answer[alen-1-i], answer[i]
}
return string(answer)
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2013-2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package base58
import (
"crypto/sha256"
"errors"
)
// ErrChecksum indicates that the checksum of a check-encoded string does not verify against
// the checksum.
var ErrChecksum = errors.New("checksum error")
// ErrInvalidFormat indicates that the check-encoded string has an invalid format.
var ErrInvalidFormat = errors.New("invalid format: version and/or checksum bytes missing")
// checksum: first four bytes of sha256^2
func checksum(input []byte) (cksum [4]byte) {
h := sha256.Sum256(input)
h2 := sha256.Sum256(h[:])
copy(cksum[:], h2[:4])
return
}
// CheckEncode prepends a version byte and appends a four byte checksum.
func CheckEncode(input []byte, version byte) string {
b := make([]byte, 0, 1+len(input)+4)
b = append(b, version)
b = append(b, input[:]...)
cksum := checksum(b)
b = append(b, cksum[:]...)
return Encode(b)
}
// CheckDecode decodes a string that was encoded with CheckEncode and verifies the checksum.
func CheckDecode(input string) (result []byte, version byte, err error) {
decoded := Decode(input)
if len(decoded) < 5 {
return nil, 0, ErrInvalidFormat
}
version = decoded[0]
var cksum [4]byte
copy(cksum[:], decoded[len(decoded)-4:])
if checksum(decoded[:len(decoded)-4]) != cksum {
return nil, 0, ErrChecksum
}
payload := decoded[1 : len(decoded)-4]
result = append(result, payload...)
return
}

View File

@@ -0,0 +1,17 @@
#!/bin/sh
# This script uses gocov to generate a test coverage report.
# The gocov tool my be obtained with the following command:
# go get github.com/axw/gocov/gocov
#
# It will be installed to $GOPATH/bin, so ensure that location is in your $PATH.
# Check for gocov.
type gocov >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo >&2 "This script requires the gocov tool."
echo >&2 "You may obtain it with the following command:"
echo >&2 "go get github.com/axw/gocov/gocov"
exit 1
fi
gocov test | gocov report

29
vendor/github.com/btcsuite/btcutil/base58/doc.go generated vendored Normal file
View File

@@ -0,0 +1,29 @@
// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
/*
Package base58 provides an API for working with modified base58 and Base58Check
encodings.
Modified Base58 Encoding
Standard base58 encoding is similar to standard base64 encoding except, as the
name implies, it uses a 58 character alphabet which results in an alphanumeric
string and allows some characters which are problematic for humans to be
excluded. Due to this, there can be various base58 alphabets.
The modified base58 alphabet used by Bitcoin, and hence this package, omits the
0, O, I, and l characters that look the same in many fonts and are therefore
hard to humans to distinguish.
Base58Check Encoding Scheme
The Base58Check encoding scheme is primarily used for Bitcoin addresses at the
time of this writing, however it can be used to generically encode arbitrary
byte arrays into human-readable strings along with a version byte that can be
used to differentiate the same payload. For Bitcoin addresses, the extra
version is used to differentiate the network of otherwise identical public keys
which helps prevent using an address intended for one network on another.
*/
package base58

View File

@@ -1,28 +0,0 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
*.out
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
# test editor files
*.swp

View File

@@ -1,21 +0,0 @@
run:
timeout: 5m
linters:
enable:
- vet
- vetshadow
- typecheck
- deadcode
- gocyclo
- golint
- varcheck
- structcheck
- maligned
- ineffassign
- misspell
- unparam
- goimports
- goconst
- unconvert
- errcheck
- interfacer

View File

@@ -1,7 +0,0 @@
language: go
go:
- 1.13.10
install:
- go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.24.0
- go build
script: ./hooks/pre-commit

View File

@@ -1 +0,0 @@
## Release 0.1.0 (UNRELEASED)

View File

@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,71 +0,0 @@
# gomatrix
[![GoDoc](https://godoc.org/github.com/matrix-org/gomatrix?status.svg)](https://godoc.org/github.com/matrix-org/gomatrix)
A Golang Matrix client.
**THIS IS UNDER ACTIVE DEVELOPMENT: BREAKING CHANGES ARE FREQUENT.**
# Contributing
All contributions are greatly appreciated!
## How to report issues
Please check the current open issues for similar reports
in order to avoid duplicates.
Some general guidelines:
- Include a [minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) when possible.
- Describe the expected behaviour and what actually happened
including a full trace-back in case of exceptions.
- Make sure to list details about your environment
## Setting up your environment
If you intend to contribute to gomatrix you'll first need Go installed on your machine (version 1.12+ is required). Also, make sure to have golangci-lint properly set up since we use it for pre-commit hooks (for instructions on how to install it, check the [official docs](https://golangci-lint.run/usage/install/#local-installation)).
- Fork gomatrix to your GitHub account by clicking the [Fork](https://github.com/matrix-org/gomatrix/fork) button.
- [Clone](https://help.github.com/en/articles/fork-a-repo#step-2-create-a-local-clone-of-your-fork) the main repository (not your fork) to your local machine.
$ git clone https://github.com/matrix-org/gomatrix
$ cd gomatrix
- Add your fork as a remote to push your contributions.Replace
``{username}`` with your username.
git remote add fork https://github.com/{username}/gomatrix
- Create a new branch to identify what feature you are working on.
$ git fetch origin
$ git checkout -b your-branch-name origin/master
- Make your changes, including tests that cover any code changes you make, and run them as described below.
- Execute pre-commit hooks by running
<gomatrix dir>/hooks/pre-commit
- Push your changes to your fork and [create a pull request](https://help.github.com/en/articles/creating-a-pull-request) describing your changes.
$ git push --set-upstream fork your-branch-name
- Finally, create a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests)
## How to run tests
You can run the test suite and example code with `$ go test -v`
# Running Coverage
To run coverage, first generate the coverage report using `go test`
go test -v -cover -coverprofile=coverage.out
You can now show the generated report as a html page with `go tool`
go tool cover -html=coverage.out

View File

@@ -1,805 +0,0 @@
// Package gomatrix implements the Matrix Client-Server API.
//
// Specification can be found at http://matrix.org/docs/spec/client_server/r0.2.0.html
package gomatrix
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"path"
"strconv"
"strings"
"sync"
"time"
)
// Client represents a Matrix client.
type Client struct {
HomeserverURL *url.URL // The base homeserver URL
Prefix string // The API prefix eg '/_matrix/client/r0'
UserID string // The user ID of the client. Used for forming HTTP paths which use the client's user ID.
AccessToken string // The access_token for the client.
Client *http.Client // The underlying HTTP client which will be used to make HTTP requests.
Syncer Syncer // The thing which can process /sync responses
Store Storer // The thing which can store rooms/tokens/ids
// The ?user_id= query parameter for application services. This must be set *prior* to calling a method. If this is empty,
// no user_id parameter will be sent.
// See http://matrix.org/docs/spec/application_service/unstable.html#identity-assertion
AppServiceUserID string
syncingMutex sync.Mutex // protects syncingID
syncingID uint32 // Identifies the current Sync. Only one Sync can be active at any given time.
}
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
type HTTPError struct {
Contents []byte
WrappedError error
Message string
Code int
}
func (e HTTPError) Error() string {
var wrappedErrMsg string
if e.WrappedError != nil {
wrappedErrMsg = e.WrappedError.Error()
}
return fmt.Sprintf("contents=%v msg=%s code=%d wrapped=%s", e.Contents, e.Message, e.Code, wrappedErrMsg)
}
// BuildURL builds a URL with the Client's homeserver/prefix set already.
func (cli *Client) BuildURL(urlPath ...string) string {
ps := append([]string{cli.Prefix}, urlPath...)
return cli.BuildBaseURL(ps...)
}
// BuildBaseURL builds a URL with the Client's homeserver set already. You must
// supply the prefix in the path.
func (cli *Client) BuildBaseURL(urlPath ...string) string {
// copy the URL. Purposefully ignore error as the input is from a valid URL already
hsURL, _ := url.Parse(cli.HomeserverURL.String())
parts := []string{hsURL.Path}
parts = append(parts, urlPath...)
hsURL.Path = path.Join(parts...)
// Manually add the trailing slash back to the end of the path if it's explicitly needed
if strings.HasSuffix(urlPath[len(urlPath)-1], "/") {
hsURL.Path = hsURL.Path + "/"
}
query := hsURL.Query()
if cli.AppServiceUserID != "" {
query.Set("user_id", cli.AppServiceUserID)
}
hsURL.RawQuery = query.Encode()
return hsURL.String()
}
// BuildURLWithQuery builds a URL with query parameters in addition to the Client's homeserver/prefix set already.
func (cli *Client) BuildURLWithQuery(urlPath []string, urlQuery map[string]string) string {
u, _ := url.Parse(cli.BuildURL(urlPath...))
q := u.Query()
for k, v := range urlQuery {
q.Set(k, v)
}
u.RawQuery = q.Encode()
return u.String()
}
// SetCredentials sets the user ID and access token on this client instance.
func (cli *Client) SetCredentials(userID, accessToken string) {
cli.AccessToken = accessToken
cli.UserID = userID
}
// ClearCredentials removes the user ID and access token on this client instance.
func (cli *Client) ClearCredentials() {
cli.AccessToken = ""
cli.UserID = ""
}
// Sync starts syncing with the provided Homeserver. If Sync() is called twice then the first sync will be stopped and the
// error will be nil.
//
// This function will block until a fatal /sync error occurs, so it should almost always be started as a new goroutine.
// Fatal sync errors can be caused by:
// - The failure to create a filter.
// - Client.Syncer.OnFailedSync returning an error in response to a failed sync.
// - Client.Syncer.ProcessResponse returning an error.
// If you wish to continue retrying in spite of these fatal errors, call Sync() again.
func (cli *Client) Sync() error {
// Mark the client as syncing.
// We will keep syncing until the syncing state changes. Either because
// Sync is called or StopSync is called.
syncingID := cli.incrementSyncingID()
nextBatch := cli.Store.LoadNextBatch(cli.UserID)
filterID := cli.Store.LoadFilterID(cli.UserID)
if filterID == "" {
filterJSON := cli.Syncer.GetFilterJSON(cli.UserID)
resFilter, err := cli.CreateFilter(filterJSON)
if err != nil {
return err
}
filterID = resFilter.FilterID
cli.Store.SaveFilterID(cli.UserID, filterID)
}
for {
resSync, err := cli.SyncRequest(30000, nextBatch, filterID, false, "")
if err != nil {
duration, err2 := cli.Syncer.OnFailedSync(resSync, err)
if err2 != nil {
return err2
}
time.Sleep(duration)
continue
}
// Check that the syncing state hasn't changed
// Either because we've stopped syncing or another sync has been started.
// We discard the response from our sync.
if cli.getSyncingID() != syncingID {
return nil
}
// Save the token now *before* processing it. This means it's possible
// to not process some events, but it means that we won't get constantly stuck processing
// a malformed/buggy event which keeps making us panic.
cli.Store.SaveNextBatch(cli.UserID, resSync.NextBatch)
if err = cli.Syncer.ProcessResponse(resSync, nextBatch); err != nil {
return err
}
nextBatch = resSync.NextBatch
}
}
func (cli *Client) incrementSyncingID() uint32 {
cli.syncingMutex.Lock()
defer cli.syncingMutex.Unlock()
cli.syncingID++
return cli.syncingID
}
func (cli *Client) getSyncingID() uint32 {
cli.syncingMutex.Lock()
defer cli.syncingMutex.Unlock()
return cli.syncingID
}
// StopSync stops the ongoing sync started by Sync.
func (cli *Client) StopSync() {
// Advance the syncing state so that any running Syncs will terminate.
cli.incrementSyncingID()
}
// MakeRequest makes a JSON HTTP request to the given URL.
// The response body will be stream decoded into an interface. This will automatically stop if the response
// body is nil.
//
// Returns an error if the response is not 2xx along with the HTTP body bytes if it got that far. This error is
// an HTTPError which includes the returned HTTP status code, byte contents of the response body and possibly a
// RespError as the WrappedError, if the HTTP body could be decoded as a RespError.
func (cli *Client) MakeRequest(method string, httpURL string, reqBody interface{}, resBody interface{}) error {
var req *http.Request
var err error
if reqBody != nil {
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(reqBody); err != nil {
return err
}
req, err = http.NewRequest(method, httpURL, buf)
} else {
req, err = http.NewRequest(method, httpURL, nil)
}
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
if cli.AccessToken != "" {
req.Header.Set("Authorization", "Bearer "+cli.AccessToken)
}
res, err := cli.Client.Do(req)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return err
}
if res.StatusCode/100 != 2 { // not 2xx
contents, err := ioutil.ReadAll(res.Body)
if err != nil {
return err
}
var wrap error
var respErr RespError
if _ = json.Unmarshal(contents, &respErr); respErr.ErrCode != "" {
wrap = respErr
}
// If we failed to decode as RespError, don't just drop the HTTP body, include it in the
// HTTP error instead (e.g proxy errors which return HTML).
msg := "Failed to " + method + " JSON to " + req.URL.Path
if wrap == nil {
msg = msg + ": " + string(contents)
}
return HTTPError{
Contents: contents,
Code: res.StatusCode,
Message: msg,
WrappedError: wrap,
}
}
if resBody != nil && res.Body != nil {
return json.NewDecoder(res.Body).Decode(&resBody)
}
return nil
}
// CreateFilter makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
func (cli *Client) CreateFilter(filter json.RawMessage) (resp *RespCreateFilter, err error) {
urlPath := cli.BuildURL("user", cli.UserID, "filter")
err = cli.MakeRequest("POST", urlPath, &filter, &resp)
return
}
// SyncRequest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
func (cli *Client) SyncRequest(timeout int, since, filterID string, fullState bool, setPresence string) (resp *RespSync, err error) {
query := map[string]string{
"timeout": strconv.Itoa(timeout),
}
if since != "" {
query["since"] = since
}
if filterID != "" {
query["filter"] = filterID
}
if setPresence != "" {
query["set_presence"] = setPresence
}
if fullState {
query["full_state"] = "true"
}
urlPath := cli.BuildURLWithQuery([]string{"sync"}, query)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
func (cli *Client) register(u string, req *ReqRegister) (resp *RespRegister, uiaResp *RespUserInteractive, err error) {
err = cli.MakeRequest("POST", u, req, &resp)
if err != nil {
httpErr, ok := err.(HTTPError)
if !ok { // network error
return
}
if httpErr.Code == 401 {
// body should be RespUserInteractive, if it isn't, fail with the error
err = json.Unmarshal(httpErr.Contents, &uiaResp)
return
}
}
return
}
// Register makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
//
// Registers with kind=user. For kind=guest, see RegisterGuest.
func (cli *Client) Register(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
u := cli.BuildURL("register")
return cli.register(u, req)
}
// RegisterGuest makes an HTTP request according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
// with kind=guest.
//
// For kind=user, see Register.
func (cli *Client) RegisterGuest(req *ReqRegister) (*RespRegister, *RespUserInteractive, error) {
query := map[string]string{
"kind": "guest",
}
u := cli.BuildURLWithQuery([]string{"register"}, query)
return cli.register(u, req)
}
// RegisterDummy performs m.login.dummy registration according to https://matrix.org/docs/spec/client_server/r0.2.0.html#dummy-auth
//
// Only a username and password need to be provided on the ReqRegister struct. Most local/developer homeservers will allow registration
// this way. If the homeserver does not, an error is returned.
//
// This does not set credentials on the client instance. See SetCredentials() instead.
//
// res, err := cli.RegisterDummy(&gomatrix.ReqRegister{
// Username: "alice",
// Password: "wonderland",
// })
// if err != nil {
// panic(err)
// }
// token := res.AccessToken
func (cli *Client) RegisterDummy(req *ReqRegister) (*RespRegister, error) {
res, uia, err := cli.Register(req)
if err != nil && uia == nil {
return nil, err
}
if uia != nil && uia.HasSingleStageFlow("m.login.dummy") {
req.Auth = struct {
Type string `json:"type"`
Session string `json:"session,omitempty"`
}{"m.login.dummy", uia.Session}
res, _, err = cli.Register(req)
if err != nil {
return nil, err
}
}
if res == nil {
return nil, fmt.Errorf("registration failed: does this server support m.login.dummy?")
}
return res, nil
}
// Login a user to the homeserver according to http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
// This does not set credentials on this client instance. See SetCredentials() instead.
func (cli *Client) Login(req *ReqLogin) (resp *RespLogin, err error) {
urlPath := cli.BuildURL("login")
err = cli.MakeRequest("POST", urlPath, req, &resp)
return
}
// Logout the current user. See http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout
// This does not clear the credentials from the client instance. See ClearCredentials() instead.
func (cli *Client) Logout() (resp *RespLogout, err error) {
urlPath := cli.BuildURL("logout")
err = cli.MakeRequest("POST", urlPath, nil, &resp)
return
}
// LogoutAll logs the current user out on all devices. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all
// This does not clear the credentials from the client instance. See ClearCredentails() instead.
func (cli *Client) LogoutAll() (resp *RespLogoutAll, err error) {
urlPath := cli.BuildURL("logout/all")
err = cli.MakeRequest("POST", urlPath, nil, &resp)
return
}
// Versions returns the list of supported Matrix versions on this homeserver. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
func (cli *Client) Versions() (resp *RespVersions, err error) {
urlPath := cli.BuildBaseURL("_matrix", "client", "versions")
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// PublicRooms returns the list of public rooms on target server. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-unstable-publicrooms
func (cli *Client) PublicRooms(limit int, since string, server string) (resp *RespPublicRooms, err error) {
args := map[string]string{}
if limit != 0 {
args["limit"] = strconv.Itoa(limit)
}
if since != "" {
args["since"] = since
}
if server != "" {
args["server"] = server
}
urlPath := cli.BuildURLWithQuery([]string{"publicRooms"}, args)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// PublicRoomsFiltered returns a subset of PublicRooms filtered server side.
// See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-unstable-publicrooms
func (cli *Client) PublicRoomsFiltered(limit int, since string, server string, filter string) (resp *RespPublicRooms, err error) {
content := map[string]string{}
if limit != 0 {
content["limit"] = strconv.Itoa(limit)
}
if since != "" {
content["since"] = since
}
if filter != "" {
content["filter"] = filter
}
var urlPath string
if server == "" {
urlPath = cli.BuildURL("publicRooms")
} else {
urlPath = cli.BuildURLWithQuery([]string{"publicRooms"}, map[string]string{
"server": server,
})
}
err = cli.MakeRequest("POST", urlPath, content, &resp)
return
}
// JoinRoom joins the client to a room ID or alias. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-join-roomidoralias
//
// If serverName is specified, this will be added as a query param to instruct the homeserver to join via that server. If content is specified, it will
// be JSON encoded and used as the request body.
func (cli *Client) JoinRoom(roomIDorAlias, serverName string, content interface{}) (resp *RespJoinRoom, err error) {
var urlPath string
if serverName != "" {
urlPath = cli.BuildURLWithQuery([]string{"join", roomIDorAlias}, map[string]string{
"server_name": serverName,
})
} else {
urlPath = cli.BuildURL("join", roomIDorAlias)
}
err = cli.MakeRequest("POST", urlPath, content, &resp)
return
}
// GetDisplayName returns the display name of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
func (cli *Client) GetDisplayName(mxid string) (resp *RespUserDisplayName, err error) {
urlPath := cli.BuildURL("profile", mxid, "displayname")
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// GetOwnDisplayName returns the user's display name. See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
func (cli *Client) GetOwnDisplayName() (resp *RespUserDisplayName, err error) {
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// SetDisplayName sets the user's profile display name. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-displayname
func (cli *Client) SetDisplayName(displayName string) (err error) {
urlPath := cli.BuildURL("profile", cli.UserID, "displayname")
s := struct {
DisplayName string `json:"displayname"`
}{displayName}
err = cli.MakeRequest("PUT", urlPath, &s, nil)
return
}
// GetAvatarURL gets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-avatar-url
func (cli *Client) GetAvatarURL() (string, error) {
urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
s := struct {
AvatarURL string `json:"avatar_url"`
}{}
err := cli.MakeRequest("GET", urlPath, nil, &s)
if err != nil {
return "", err
}
return s.AvatarURL, nil
}
// SetAvatarURL sets the user's avatar URL. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-profile-userid-avatar-url
func (cli *Client) SetAvatarURL(url string) error {
urlPath := cli.BuildURL("profile", cli.UserID, "avatar_url")
s := struct {
AvatarURL string `json:"avatar_url"`
}{url}
err := cli.MakeRequest("PUT", urlPath, &s, nil)
if err != nil {
return err
}
return nil
}
// GetStatus returns the status of the user from the specified MXID. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status
func (cli *Client) GetStatus(mxid string) (resp *RespUserStatus, err error) {
urlPath := cli.BuildURL("presence", mxid, "status")
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// GetOwnStatus returns the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status
func (cli *Client) GetOwnStatus() (resp *RespUserStatus, err error) {
return cli.GetStatus(cli.UserID)
}
// SetStatus sets the user's status. See https://matrix.org/docs/spec/client_server/r0.6.0#put-matrix-client-r0-presence-userid-status
func (cli *Client) SetStatus(presence, status string) (err error) {
urlPath := cli.BuildURL("presence", cli.UserID, "status")
s := struct {
Presence string `json:"presence"`
StatusMsg string `json:"status_msg"`
}{presence, status}
err = cli.MakeRequest("PUT", urlPath, &s, nil)
return
}
// SendMessageEvent sends a message event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendMessageEvent(roomID string, eventType string, contentJSON interface{}) (resp *RespSendEvent, err error) {
txnID := txnID()
urlPath := cli.BuildURL("rooms", roomID, "send", eventType, txnID)
err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return
}
// SendStateEvent sends a state event into a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey
// contentJSON should be a pointer to something that can be encoded as JSON using json.Marshal.
func (cli *Client) SendStateEvent(roomID, eventType, stateKey string, contentJSON interface{}) (resp *RespSendEvent, err error) {
urlPath := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
err = cli.MakeRequest("PUT", urlPath, contentJSON, &resp)
return
}
// SendText sends an m.room.message event into the given room with a msgtype of m.text
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-text
func (cli *Client) SendText(roomID, text string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
TextMessage{MsgType: "m.text", Body: text})
}
// SendFormattedText sends an m.room.message event into the given room with a msgtype of m.text, supports a subset of HTML for formatting.
// See https://matrix.org/docs/spec/client_server/r0.6.0#m-text
func (cli *Client) SendFormattedText(roomID, text, formattedText string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
TextMessage{MsgType: "m.text", Body: text, FormattedBody: formattedText, Format: "org.matrix.custom.html"})
}
// SendImage sends an m.room.message event into the given room with a msgtype of m.image
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
func (cli *Client) SendImage(roomID, body, url string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
ImageMessage{
MsgType: "m.image",
Body: body,
URL: url,
})
}
// SendVideo sends an m.room.message event into the given room with a msgtype of m.video
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
func (cli *Client) SendVideo(roomID, body, url string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
VideoMessage{
MsgType: "m.video",
Body: body,
URL: url,
})
}
// SendNotice sends an m.room.message event into the given room with a msgtype of m.notice
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#m-notice
func (cli *Client) SendNotice(roomID, text string) (*RespSendEvent, error) {
return cli.SendMessageEvent(roomID, "m.room.message",
TextMessage{MsgType: "m.notice", Body: text})
}
// RedactEvent redacts the given event. See http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
func (cli *Client) RedactEvent(roomID, eventID string, req *ReqRedact) (resp *RespSendEvent, err error) {
txnID := txnID()
urlPath := cli.BuildURL("rooms", roomID, "redact", eventID, txnID)
err = cli.MakeRequest("PUT", urlPath, req, &resp)
return
}
// MarkRead marks eventID in roomID as read, signifying the event, and all before it have been read. See https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-rooms-roomid-receipt-receipttype-eventid
func (cli *Client) MarkRead(roomID, eventID string) error {
urlPath := cli.BuildURL("rooms", roomID, "receipt", "m.read", eventID)
return cli.MakeRequest("POST", urlPath, nil, nil)
}
// CreateRoom creates a new Matrix room. See https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
// resp, err := cli.CreateRoom(&gomatrix.ReqCreateRoom{
// Preset: "public_chat",
// })
// fmt.Println("Room:", resp.RoomID)
func (cli *Client) CreateRoom(req *ReqCreateRoom) (resp *RespCreateRoom, err error) {
urlPath := cli.BuildURL("createRoom")
err = cli.MakeRequest("POST", urlPath, req, &resp)
return
}
// LeaveRoom leaves the given room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
func (cli *Client) LeaveRoom(roomID string) (resp *RespLeaveRoom, err error) {
u := cli.BuildURL("rooms", roomID, "leave")
err = cli.MakeRequest("POST", u, struct{}{}, &resp)
return
}
// ForgetRoom forgets a room entirely. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
func (cli *Client) ForgetRoom(roomID string) (resp *RespForgetRoom, err error) {
u := cli.BuildURL("rooms", roomID, "forget")
err = cli.MakeRequest("POST", u, struct{}{}, &resp)
return
}
// InviteUser invites a user to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
func (cli *Client) InviteUser(roomID string, req *ReqInviteUser) (resp *RespInviteUser, err error) {
u := cli.BuildURL("rooms", roomID, "invite")
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// InviteUserByThirdParty invites a third-party identifier to a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#invite-by-third-party-id-endpoint
func (cli *Client) InviteUserByThirdParty(roomID string, req *ReqInvite3PID) (resp *RespInviteUser, err error) {
u := cli.BuildURL("rooms", roomID, "invite")
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// KickUser kicks a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
func (cli *Client) KickUser(roomID string, req *ReqKickUser) (resp *RespKickUser, err error) {
u := cli.BuildURL("rooms", roomID, "kick")
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// BanUser bans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
func (cli *Client) BanUser(roomID string, req *ReqBanUser) (resp *RespBanUser, err error) {
u := cli.BuildURL("rooms", roomID, "ban")
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// UnbanUser unbans a user from a room. See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
func (cli *Client) UnbanUser(roomID string, req *ReqUnbanUser) (resp *RespUnbanUser, err error) {
u := cli.BuildURL("rooms", roomID, "unban")
err = cli.MakeRequest("POST", u, req, &resp)
return
}
// UserTyping sets the typing status of the user. See https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
func (cli *Client) UserTyping(roomID string, typing bool, timeout int64) (resp *RespTyping, err error) {
req := ReqTyping{Typing: typing, Timeout: timeout}
u := cli.BuildURL("rooms", roomID, "typing", cli.UserID)
err = cli.MakeRequest("PUT", u, req, &resp)
return
}
// StateEvent gets a single state event in a room. It will attempt to JSON unmarshal into the given "outContent" struct with
// the HTTP response body, or return an error.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey
func (cli *Client) StateEvent(roomID, eventType, stateKey string, outContent interface{}) (err error) {
u := cli.BuildURL("rooms", roomID, "state", eventType, stateKey)
err = cli.MakeRequest("GET", u, nil, outContent)
return
}
// UploadLink uploads an HTTP URL and then returns an MXC URI.
func (cli *Client) UploadLink(link string) (*RespMediaUpload, error) {
res, err := cli.Client.Get(link)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return nil, err
}
return cli.UploadToContentRepo(res.Body, res.Header.Get("Content-Type"), res.ContentLength)
}
// UploadToContentRepo uploads the given bytes to the content repository and returns an MXC URI.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
func (cli *Client) UploadToContentRepo(content io.Reader, contentType string, contentLength int64) (*RespMediaUpload, error) {
req, err := http.NewRequest("POST", cli.BuildBaseURL("_matrix/media/r0/upload"), content)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", contentType)
req.Header.Set("Authorization", "Bearer "+cli.AccessToken)
req.ContentLength = contentLength
res, err := cli.Client.Do(req)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
contents, err := ioutil.ReadAll(res.Body)
if err != nil {
return nil, HTTPError{
Message: "Upload request failed - Failed to read response body: " + err.Error(),
Code: res.StatusCode,
}
}
return nil, HTTPError{
Contents: contents,
Message: "Upload request failed: " + string(contents),
Code: res.StatusCode,
}
}
var m RespMediaUpload
if err := json.NewDecoder(res.Body).Decode(&m); err != nil {
return nil, err
}
return &m, nil
}
// JoinedMembers returns a map of joined room members. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
//
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
// This API is primarily designed for application services which may want to efficiently look up joined members in a room.
func (cli *Client) JoinedMembers(roomID string) (resp *RespJoinedMembers, err error) {
u := cli.BuildURL("rooms", roomID, "joined_members")
err = cli.MakeRequest("GET", u, nil, &resp)
return
}
// JoinedRooms returns a list of rooms which the client is joined to. See TODO-SPEC. https://github.com/matrix-org/synapse/pull/1680
//
// In general, usage of this API is discouraged in favour of /sync, as calling this API can race with incoming membership changes.
// This API is primarily designed for application services which may want to efficiently look up joined rooms.
func (cli *Client) JoinedRooms() (resp *RespJoinedRooms, err error) {
u := cli.BuildURL("joined_rooms")
err = cli.MakeRequest("GET", u, nil, &resp)
return
}
// Messages returns a list of message and state events for a room. It uses
// pagination query parameters to paginate history in the room.
// See https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
func (cli *Client) Messages(roomID, from, to string, dir rune, limit int) (resp *RespMessages, err error) {
query := map[string]string{
"from": from,
"dir": string(dir),
}
if to != "" {
query["to"] = to
}
if limit != 0 {
query["limit"] = strconv.Itoa(limit)
}
urlPath := cli.BuildURLWithQuery([]string{"rooms", roomID, "messages"}, query)
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
// TurnServer returns turn server details and credentials for the client to use when initiating calls.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-voip-turnserver
func (cli *Client) TurnServer() (resp *RespTurnServer, err error) {
urlPath := cli.BuildURL("voip", "turnServer")
err = cli.MakeRequest("GET", urlPath, nil, &resp)
return
}
func txnID() string {
return "go" + strconv.FormatInt(time.Now().UnixNano(), 10)
}
// NewClient creates a new Matrix Client ready for syncing
func NewClient(homeserverURL, userID, accessToken string) (*Client, error) {
hsURL, err := url.Parse(homeserverURL)
if err != nil {
return nil, err
}
// By default, use an in-memory store which will never save filter ids / next batch tokens to disk.
// The client will work with this storer: it just won't remember across restarts.
// In practice, a database backend should be used.
store := NewInMemoryStore()
cli := Client{
AccessToken: accessToken,
HomeserverURL: hsURL,
UserID: userID,
Prefix: "/_matrix/client/r0",
Syncer: NewDefaultSyncer(userID, store),
Store: store,
}
// By default, use the default HTTP client.
cli.Client = http.DefaultClient
return &cli, nil
}

View File

@@ -1,157 +0,0 @@
package gomatrix
import (
"html"
"regexp"
)
// Event represents a single Matrix event.
type Event struct {
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
Sender string `json:"sender"` // The user ID of the sender of the event
Type string `json:"type"` // The event type
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
ID string `json:"event_id"` // The unique ID of this event
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
Unsigned map[string]interface{} `json:"unsigned"` // The unsigned portions of the event, such as age and prev_content
Content map[string]interface{} `json:"content"` // The JSON content of the event.
PrevContent map[string]interface{} `json:"prev_content,omitempty"` // The JSON prev_content of the event.
}
// Body returns the value of the "body" key in the event content if it is
// present and is a string.
func (event *Event) Body() (body string, ok bool) {
value, exists := event.Content["body"]
if !exists {
return
}
body, ok = value.(string)
return
}
// MessageType returns the value of the "msgtype" key in the event content if
// it is present and is a string.
func (event *Event) MessageType() (msgtype string, ok bool) {
value, exists := event.Content["msgtype"]
if !exists {
return
}
msgtype, ok = value.(string)
return
}
// TextMessage is the contents of a Matrix formated message event.
type TextMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
FormattedBody string `json:"formatted_body"`
Format string `json:"format"`
}
// ThumbnailInfo contains info about an thumbnail image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
type ThumbnailInfo struct {
Height uint `json:"h,omitempty"`
Width uint `json:"w,omitempty"`
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"`
}
// ImageInfo contains info about an image - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-image
type ImageInfo struct {
Height uint `json:"h,omitempty"`
Width uint `json:"w,omitempty"`
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"`
ThumbnailInfo ThumbnailInfo `json:"thumbnail_info,omitempty"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
}
// VideoInfo contains info about a video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
type VideoInfo struct {
Mimetype string `json:"mimetype,omitempty"`
ThumbnailInfo ThumbnailInfo `json:"thumbnail_info"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
Height uint `json:"h,omitempty"`
Width uint `json:"w,omitempty"`
Duration uint `json:"duration,omitempty"`
Size uint `json:"size,omitempty"`
}
// VideoMessage is an m.video - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-video
type VideoMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info VideoInfo `json:"info"`
}
// ImageMessage is an m.image event
type ImageMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info ImageInfo `json:"info"`
}
// An HTMLMessage is the contents of a Matrix HTML formated message event.
type HTMLMessage struct {
Body string `json:"body"`
MsgType string `json:"msgtype"`
Format string `json:"format"`
FormattedBody string `json:"formatted_body"`
}
// FileInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file
type FileInfo struct {
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"` //filesize in bytes
}
// FileMessage is an m.file event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-file
type FileMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Filename string `json:"filename"`
Info FileInfo `json:"info,omitempty"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"`
}
// LocationMessage is an m.location event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-location
type LocationMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
GeoURI string `json:"geo_uri"`
ThumbnailURL string `json:"thumbnail_url,omitempty"`
ThumbnailInfo ImageInfo `json:"thumbnail_info,omitempty"`
}
// AudioInfo contains info about an file - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
type AudioInfo struct {
Mimetype string `json:"mimetype,omitempty"`
Size uint `json:"size,omitempty"` //filesize in bytes
Duration uint `json:"duration,omitempty"` //audio duration in ms
}
// AudioMessage is an m.audio event - http://matrix.org/docs/spec/client_server/r0.2.0.html#m-audio
type AudioMessage struct {
MsgType string `json:"msgtype"`
Body string `json:"body"`
URL string `json:"url"`
Info AudioInfo `json:"info,omitempty"`
}
var htmlRegex = regexp.MustCompile("<[^<]+?>")
// GetHTMLMessage returns an HTMLMessage with the body set to a stripped version of the provided HTML, in addition
// to the provided HTML.
func GetHTMLMessage(msgtype, htmlText string) HTMLMessage {
return HTMLMessage{
Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
MsgType: msgtype,
Format: "org.matrix.custom.html",
FormattedBody: htmlText,
}
}

View File

@@ -1,90 +0,0 @@
// Copyright 2017 Jan Christian Grünhage
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomatrix
import "errors"
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
//Specified by: https://matrix.org/docs/spec/client_server/r0.2.0.html#filtering
type Filter struct {
AccountData FilterPart `json:"account_data,omitempty"`
EventFields []string `json:"event_fields,omitempty"`
EventFormat string `json:"event_format,omitempty"`
Presence FilterPart `json:"presence,omitempty"`
Room RoomFilter `json:"room,omitempty"`
}
// RoomFilter is used to define filtering rules for room events
type RoomFilter struct {
AccountData FilterPart `json:"account_data,omitempty"`
Ephemeral FilterPart `json:"ephemeral,omitempty"`
IncludeLeave bool `json:"include_leave,omitempty"`
NotRooms []string `json:"not_rooms,omitempty"`
Rooms []string `json:"rooms,omitempty"`
State FilterPart `json:"state,omitempty"`
Timeline FilterPart `json:"timeline,omitempty"`
}
// FilterPart is used to define filtering rules for specific categories of events
type FilterPart struct {
NotRooms []string `json:"not_rooms,omitempty"`
Rooms []string `json:"rooms,omitempty"`
Limit int `json:"limit,omitempty"`
NotSenders []string `json:"not_senders,omitempty"`
NotTypes []string `json:"not_types,omitempty"`
Senders []string `json:"senders,omitempty"`
Types []string `json:"types,omitempty"`
ContainsURL *bool `json:"contains_url,omitempty"`
}
// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
if filter.EventFormat != "client" && filter.EventFormat != "federation" {
return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
}
return nil
}
// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request
func DefaultFilter() Filter {
return Filter{
AccountData: DefaultFilterPart(),
EventFields: nil,
EventFormat: "client",
Presence: DefaultFilterPart(),
Room: RoomFilter{
AccountData: DefaultFilterPart(),
Ephemeral: DefaultFilterPart(),
IncludeLeave: false,
NotRooms: nil,
Rooms: nil,
State: DefaultFilterPart(),
Timeline: DefaultFilterPart(),
},
}
}
// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request
func DefaultFilterPart() FilterPart {
return FilterPart{
NotRooms: nil,
Rooms: nil,
Limit: 20,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
}
}

View File

@@ -1,3 +0,0 @@
module github.com/matrix-org/gomatrix
go 1.12

View File

@@ -1,69 +0,0 @@
package gomatrix
// Identifier is the interface for https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types
type Identifier interface {
// Returns the identifier type
// https://matrix.org/docs/spec/client_server/r0.6.0#identifier-types
Type() string
}
// UserIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#matrix-user-id
type UserIdentifier struct {
IDType string `json:"type"` // Set by NewUserIdentifer
User string `json:"user"`
}
// Type implements the Identifier interface
func (i UserIdentifier) Type() string {
return "m.id.user"
}
// NewUserIdentifier creates a new UserIdentifier with IDType set to "m.id.user"
func NewUserIdentifier(user string) UserIdentifier {
return UserIdentifier{
IDType: "m.id.user",
User: user,
}
}
// ThirdpartyIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#third-party-id
type ThirdpartyIdentifier struct {
IDType string `json:"type"` // Set by NewThirdpartyIdentifier
Medium string `json:"medium"`
Address string `json:"address"`
}
// Type implements the Identifier interface
func (i ThirdpartyIdentifier) Type() string {
return "m.id.thirdparty"
}
// NewThirdpartyIdentifier creates a new UserIdentifier with IDType set to "m.id.user"
func NewThirdpartyIdentifier(medium, address string) ThirdpartyIdentifier {
return ThirdpartyIdentifier{
IDType: "m.id.thirdparty",
Medium: medium,
Address: address,
}
}
// PhoneIdentifier is the Identifier for https://matrix.org/docs/spec/client_server/r0.6.0#phone-number
type PhoneIdentifier struct {
IDType string `json:"type"` // Set by NewPhoneIdentifier
Country string `json:"country"`
Phone string `json:"phone"`
}
// Type implements the Identifier interface
func (i PhoneIdentifier) Type() string {
return "m.id.phone"
}
// NewPhoneIdentifier creates a new UserIdentifier with IDType set to "m.id.user"
func NewPhoneIdentifier(country, phone string) PhoneIdentifier {
return PhoneIdentifier{
IDType: "m.id.phone",
Country: country,
Phone: phone,
}
}

View File

@@ -1,79 +0,0 @@
package gomatrix
// ReqRegister is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
type ReqRegister struct {
Username string `json:"username,omitempty"`
BindEmail bool `json:"bind_email,omitempty"`
Password string `json:"password,omitempty"`
DeviceID string `json:"device_id,omitempty"`
InitialDeviceDisplayName string `json:"initial_device_display_name"`
Auth interface{} `json:"auth,omitempty"`
}
// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login
type ReqLogin struct {
Type string `json:"type"`
Identifier Identifier `json:"identifier,omitempty"`
Password string `json:"password,omitempty"`
Medium string `json:"medium,omitempty"`
User string `json:"user,omitempty"`
Address string `json:"address,omitempty"`
Token string `json:"token,omitempty"`
DeviceID string `json:"device_id,omitempty"`
InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
}
// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
type ReqCreateRoom struct {
Visibility string `json:"visibility,omitempty"`
RoomAliasName string `json:"room_alias_name,omitempty"`
Name string `json:"name,omitempty"`
Topic string `json:"topic,omitempty"`
Invite []string `json:"invite,omitempty"`
Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"`
CreationContent map[string]interface{} `json:"creation_content,omitempty"`
InitialState []Event `json:"initial_state,omitempty"`
Preset string `json:"preset,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
}
// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
type ReqRedact struct {
Reason string `json:"reason,omitempty"`
}
// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57
// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
type ReqInvite3PID struct {
IDServer string `json:"id_server"`
Medium string `json:"medium"`
Address string `json:"address"`
}
// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
type ReqInviteUser struct {
UserID string `json:"user_id"`
}
// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
type ReqKickUser struct {
Reason string `json:"reason,omitempty"`
UserID string `json:"user_id"`
}
// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
type ReqBanUser struct {
Reason string `json:"reason,omitempty"`
UserID string `json:"user_id"`
}
// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
type ReqUnbanUser struct {
UserID string `json:"user_id"`
}
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
type ReqTyping struct {
Typing bool `json:"typing"`
Timeout int64 `json:"timeout"`
}

View File

@@ -1,210 +0,0 @@
package gomatrix
// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
// See http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
type RespError struct {
ErrCode string `json:"errcode"`
Err string `json:"error"`
}
// Error returns the errcode and error message.
func (e RespError) Error() string {
return e.ErrCode + ": " + e.Err
}
// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
type RespCreateFilter struct {
FilterID string `json:"filter_id"`
}
// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-versions
type RespVersions struct {
Versions []string `json:"versions"`
}
// RespPublicRooms is the JSON response for http://matrix.org/speculator/spec/HEAD/client_server/unstable.html#get-matrix-client-unstable-publicrooms
type RespPublicRooms struct {
TotalRoomCountEstimate int `json:"total_room_count_estimate"`
PrevBatch string `json:"prev_batch"`
NextBatch string `json:"next_batch"`
Chunk []PublicRoom `json:"chunk"`
}
// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join
type RespJoinRoom struct {
RoomID string `json:"room_id"`
}
// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
type RespLeaveRoom struct{}
// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
type RespForgetRoom struct{}
// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
type RespInviteUser struct{}
// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
type RespKickUser struct{}
// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
type RespBanUser struct{}
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
type RespUnbanUser struct{}
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
type RespTyping struct{}
// RespJoinedRooms is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
type RespJoinedRooms struct {
JoinedRooms []string `json:"joined_rooms"`
}
// RespJoinedMembers is the JSON response for TODO-SPEC https://github.com/matrix-org/synapse/pull/1680
type RespJoinedMembers struct {
Joined map[string]struct {
DisplayName *string `json:"display_name"`
AvatarURL *string `json:"avatar_url"`
} `json:"joined"`
}
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
type RespMessages struct {
Start string `json:"start"`
Chunk []Event `json:"chunk"`
End string `json:"end"`
}
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
type RespSendEvent struct {
EventID string `json:"event_id"`
}
// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
type RespMediaUpload struct {
ContentURI string `json:"content_uri"`
}
// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
type RespUserInteractive struct {
Flows []struct {
Stages []string `json:"stages"`
} `json:"flows"`
Params map[string]interface{} `json:"params"`
Session string `json:"session"`
Completed []string `json:"completed"`
ErrCode string `json:"errcode"`
Error string `json:"error"`
}
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
func (r RespUserInteractive) HasSingleStageFlow(stageName string) bool {
for _, f := range r.Flows {
if len(f.Stages) == 1 && f.Stages[0] == stageName {
return true
}
}
return false
}
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
type RespUserDisplayName struct {
DisplayName string `json:"displayname"`
}
// RespUserStatus is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-presence-userid-status
type RespUserStatus struct {
Presence string `json:"presence"`
StatusMsg string `json:"status_msg"`
LastActiveAgo int `json:"last_active_ago"`
CurrentlyActive bool `json:"currently_active"`
}
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
type RespRegister struct {
AccessToken string `json:"access_token"`
DeviceID string `json:"device_id"`
HomeServer string `json:"home_server"`
RefreshToken string `json:"refresh_token"`
UserID string `json:"user_id"`
}
// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-login
type RespLogin struct {
AccessToken string `json:"access_token"`
DeviceID string `json:"device_id"`
HomeServer string `json:"home_server"`
UserID string `json:"user_id"`
WellKnown DiscoveryInformation `json:"well_known"`
}
// DiscoveryInformation is the JSON Response for https://matrix.org/docs/spec/client_server/r0.6.0#get-well-known-matrix-client and a part of the JSON Response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-login
type DiscoveryInformation struct {
Homeserver struct {
BaseURL string `json:"base_url"`
} `json:"m.homeserver"`
IdentityServer struct {
BaseURL string `json:"base_url"`
} `json:"m.identitiy_server"`
}
// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-logout
type RespLogout struct{}
// RespLogoutAll is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0#post-matrix-client-r0-logout-all
type RespLogoutAll struct{}
// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
type RespCreateRoom struct {
RoomID string `json:"room_id"`
}
// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-sync
type RespSync struct {
NextBatch string `json:"next_batch"`
AccountData struct {
Events []Event `json:"events"`
} `json:"account_data"`
Presence struct {
Events []Event `json:"events"`
} `json:"presence"`
Rooms struct {
Leave map[string]struct {
State struct {
Events []Event `json:"events"`
} `json:"state"`
Timeline struct {
Events []Event `json:"events"`
Limited bool `json:"limited"`
PrevBatch string `json:"prev_batch"`
} `json:"timeline"`
} `json:"leave"`
Join map[string]struct {
State struct {
Events []Event `json:"events"`
} `json:"state"`
Timeline struct {
Events []Event `json:"events"`
Limited bool `json:"limited"`
PrevBatch string `json:"prev_batch"`
} `json:"timeline"`
Ephemeral struct {
Events []Event `json:"events"`
} `json:"ephemeral"`
} `json:"join"`
Invite map[string]struct {
State struct {
Events []Event
} `json:"invite_state"`
} `json:"invite"`
} `json:"rooms"`
}
// RespTurnServer is the JSON response from a Turn Server
type RespTurnServer struct {
Username string `json:"username"`
Password string `json:"password"`
TTL int `json:"ttl"`
URIs []string `json:"uris"`
}

View File

@@ -1,63 +0,0 @@
package gomatrix
// Room represents a single Matrix room.
type Room struct {
ID string
State map[string]map[string]*Event
}
// PublicRoom represents the information about a public room obtainable from the room directory
type PublicRoom struct {
CanonicalAlias string `json:"canonical_alias"`
Name string `json:"name"`
WorldReadable bool `json:"world_readable"`
Topic string `json:"topic"`
NumJoinedMembers int `json:"num_joined_members"`
AvatarURL string `json:"avatar_url"`
RoomID string `json:"room_id"`
GuestCanJoin bool `json:"guest_can_join"`
Aliases []string `json:"aliases"`
}
// UpdateState updates the room's current state with the given Event. This will clobber events based
// on the type/state_key combination.
func (room Room) UpdateState(event *Event) {
_, exists := room.State[event.Type]
if !exists {
room.State[event.Type] = make(map[string]*Event)
}
room.State[event.Type][*event.StateKey] = event
}
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
func (room Room) GetStateEvent(eventType string, stateKey string) *Event {
stateEventMap := room.State[eventType]
event := stateEventMap[stateKey]
return event
}
// GetMembershipState returns the membership state of the given user ID in this room. If there is
// no entry for this member, 'leave' is returned for consistency with left users.
func (room Room) GetMembershipState(userID string) string {
state := "leave"
event := room.GetStateEvent("m.room.member", userID)
if event != nil {
membershipState, found := event.Content["membership"]
if found {
mState, isString := membershipState.(string)
if isString {
state = mState
}
}
}
return state
}
// NewRoom creates a new Room with the given ID
func NewRoom(roomID string) *Room {
// Init the State map and return a pointer to the Room
return &Room{
ID: roomID,
State: make(map[string]map[string]*Event),
}
}

View File

@@ -1,65 +0,0 @@
package gomatrix
// Storer is an interface which must be satisfied to store client data.
//
// You can either write a struct which persists this data to disk, or you can use the
// provided "InMemoryStore" which just keeps data around in-memory which is lost on
// restarts.
type Storer interface {
SaveFilterID(userID, filterID string)
LoadFilterID(userID string) string
SaveNextBatch(userID, nextBatchToken string)
LoadNextBatch(userID string) string
SaveRoom(room *Room)
LoadRoom(roomID string) *Room
}
// InMemoryStore implements the Storer interface.
//
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
// or next batch tokens on any goroutine other than the syncing goroutine: the one
// which called Client.Sync().
type InMemoryStore struct {
Filters map[string]string
NextBatch map[string]string
Rooms map[string]*Room
}
// SaveFilterID to memory.
func (s *InMemoryStore) SaveFilterID(userID, filterID string) {
s.Filters[userID] = filterID
}
// LoadFilterID from memory.
func (s *InMemoryStore) LoadFilterID(userID string) string {
return s.Filters[userID]
}
// SaveNextBatch to memory.
func (s *InMemoryStore) SaveNextBatch(userID, nextBatchToken string) {
s.NextBatch[userID] = nextBatchToken
}
// LoadNextBatch from memory.
func (s *InMemoryStore) LoadNextBatch(userID string) string {
return s.NextBatch[userID]
}
// SaveRoom to memory.
func (s *InMemoryStore) SaveRoom(room *Room) {
s.Rooms[room.ID] = room
}
// LoadRoom from memory.
func (s *InMemoryStore) LoadRoom(roomID string) *Room {
return s.Rooms[roomID]
}
// NewInMemoryStore constructs a new InMemoryStore.
func NewInMemoryStore() *InMemoryStore {
return &InMemoryStore{
Filters: make(map[string]string),
NextBatch: make(map[string]string),
Rooms: make(map[string]*Room),
}
}

View File

@@ -1,168 +0,0 @@
package gomatrix
import (
"encoding/json"
"fmt"
"runtime/debug"
"time"
)
// Syncer represents an interface that must be satisfied in order to do /sync requests on a client.
type Syncer interface {
// Process the /sync response. The since parameter is the since= value that was used to produce the response.
// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped
// permanently.
ProcessResponse(resp *RespSync, since string) error
// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
OnFailedSync(res *RespSync, err error) (time.Duration, error)
// GetFilterJSON for the given user ID. NOT the filter ID.
GetFilterJSON(userID string) json.RawMessage
}
// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
type DefaultSyncer struct {
UserID string
Store Storer
listeners map[string][]OnEventListener // event type to listeners array
}
// OnEventListener can be used with DefaultSyncer.OnEventType to be informed of incoming events.
type OnEventListener func(*Event)
// NewDefaultSyncer returns an instantiated DefaultSyncer
func NewDefaultSyncer(userID string, store Storer) *DefaultSyncer {
return &DefaultSyncer{
UserID: userID,
Store: store,
listeners: make(map[string][]OnEventListener),
}
}
// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
// unrepeating events. Returns a fatal error if a listener panics.
func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
if !s.shouldProcessResponse(res, since) {
return
}
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.UserID, since, r, debug.Stack())
}
}()
for roomID, roomData := range res.Rooms.Join {
room := s.getOrCreateRoom(roomID)
for _, event := range roomData.State.Events {
event.RoomID = roomID
room.UpdateState(&event)
s.notifyListeners(&event)
}
for _, event := range roomData.Timeline.Events {
event.RoomID = roomID
s.notifyListeners(&event)
}
for _, event := range roomData.Ephemeral.Events {
event.RoomID = roomID
s.notifyListeners(&event)
}
}
for roomID, roomData := range res.Rooms.Invite {
room := s.getOrCreateRoom(roomID)
for _, event := range roomData.State.Events {
event.RoomID = roomID
room.UpdateState(&event)
s.notifyListeners(&event)
}
}
for roomID, roomData := range res.Rooms.Leave {
room := s.getOrCreateRoom(roomID)
for _, event := range roomData.Timeline.Events {
if event.StateKey != nil {
event.RoomID = roomID
room.UpdateState(&event)
s.notifyListeners(&event)
}
}
}
return
}
// OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks.
func (s *DefaultSyncer) OnEventType(eventType string, callback OnEventListener) {
_, exists := s.listeners[eventType]
if !exists {
s.listeners[eventType] = []OnEventListener{}
}
s.listeners[eventType] = append(s.listeners[eventType], callback)
}
// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
// stuff that shouldn't be processed.
func (s *DefaultSyncer) shouldProcessResponse(resp *RespSync, since string) bool {
if since == "" {
return false
}
// This is a horrible hack because /sync will return the most recent messages for a room
// as soon as you /join it. We do NOT want to process those events in that particular room
// because they may have already been processed (if you toggle the bot in/out of the room).
//
// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
// exists and is "join" and then discard processing that room entirely if so.
// TODO: We probably want to process messages from after the last join event in the timeline.
for roomID, roomData := range resp.Rooms.Join {
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
e := roomData.Timeline.Events[i]
if e.Type == "m.room.member" && e.StateKey != nil && *e.StateKey == s.UserID {
m := e.Content["membership"]
mship, ok := m.(string)
if !ok {
continue
}
if mship == "join" {
_, ok := resp.Rooms.Join[roomID]
if !ok {
continue
}
delete(resp.Rooms.Join, roomID) // don't re-process messages
delete(resp.Rooms.Invite, roomID) // don't re-process invites
break
}
}
}
}
return true
}
// getOrCreateRoom must only be called by the Sync() goroutine which calls ProcessResponse()
func (s *DefaultSyncer) getOrCreateRoom(roomID string) *Room {
room := s.Store.LoadRoom(roomID)
if room == nil { // create a new Room
room = NewRoom(roomID)
s.Store.SaveRoom(room)
}
return room
}
func (s *DefaultSyncer) notifyListeners(event *Event) {
listeners, exists := s.listeners[event.Type]
if !exists {
return
}
for _, fn := range listeners {
fn(event)
}
}
// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
return 10 * time.Second, nil
}
// GetFilterJSON returns a filter with a timeline limit of 50.
func (s *DefaultSyncer) GetFilterJSON(userID string) json.RawMessage {
return json.RawMessage(`{"room":{"timeline":{"limit":50}}}`)
}

View File

@@ -1,26 +0,0 @@
// Copyright 2019 Sumukha PK
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package gomatrix
// TagContent contains the data for an m.tag message type
// https://matrix.org/docs/spec/client_server/r0.4.0.html#m-tag
type TagContent struct {
Tags map[string]TagProperties `json:"tags"`
}
// TagProperties contains the properties of a Tag
type TagProperties struct {
Order float32 `json:"order,omitempty"` // Empty values must be neglected
}

77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go generated vendored Normal file
View File

@@ -0,0 +1,77 @@
// Copyright 2012 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.
/*
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
2898 / PKCS #5 v2.0.
A key derivation function is useful when encrypting data based on a password
or any other not-fully-random data. It uses a pseudorandom function to derive
a secure encryption key based on the password.
While v2.0 of the standard defines only one pseudorandom function to use,
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
choose, you can pass the `New` functions from the different SHA packages to
pbkdf2.Key.
*/
package pbkdf2 // import "golang.org/x/crypto/pbkdf2"
import (
"crypto/hmac"
"hash"
)
// Key derives a key from the password, salt and iteration count, returning a
// []byte of length keylen that can be used as cryptographic key. The key is
// derived based on the method described as PBKDF2 with the HMAC variant using
// the supplied hash function.
//
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
// doing:
//
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
//
// Remember to get a good random salt. At least 8 bytes is recommended by the
// RFC.
//
// Using a higher iteration count will increase the cost of an exhaustive
// search but will also make derivation proportionally slower.
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
prf := hmac.New(h, password)
hashLen := prf.Size()
numBlocks := (keyLen + hashLen - 1) / hashLen
var buf [4]byte
dk := make([]byte, 0, numBlocks*hashLen)
U := make([]byte, hashLen)
for block := 1; block <= numBlocks; block++ {
// N.B.: || means concatenation, ^ means XOR
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
// U_1 = PRF(password, salt || uint(i))
prf.Reset()
prf.Write(salt)
buf[0] = byte(block >> 24)
buf[1] = byte(block >> 16)
buf[2] = byte(block >> 8)
buf[3] = byte(block)
prf.Write(buf[:4])
dk = prf.Sum(dk)
T := dk[len(dk)-hashLen:]
copy(U, T)
// U_n = PRF(password, U_(n-1))
for n := 2; n <= iter; n++ {
prf.Reset()
prf.Write(U)
U = U[:0]
U = prf.Sum(U)
for x := range U {
T[x] ^= U[x]
}
}
}
return dk[:keyLen]
}

2
vendor/maunium.net/go/mautrix/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
.idea/
.vscode/

374
vendor/maunium.net/go/mautrix/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,374 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

24
vendor/maunium.net/go/mautrix/README.md generated vendored Normal file
View File

@@ -0,0 +1,24 @@
# mautrix-go
[![GoDoc](https://godoc.org/maunium.net/go/mautrix?status.svg)](https://godoc.org/maunium.net/go/mautrix)
A Golang Matrix framework. Used by [gomuks](https://matrix.org/docs/projects/client/gomuks),
[go-neb](https://github.com/matrix-org/go-neb), [mautrix-whatsapp](https://github.com/tulir/mautrix-whatsapp)
and others.
Matrix room: [`#maunium:maunium.net`](https://matrix.to/#/#maunium:maunium.net)
This project is based on [matrix-org/gomatrix](https://github.com/matrix-org/gomatrix).
The original project is licensed under [Apache 2.0](https://github.com/matrix-org/gomatrix/blob/master/LICENSE).
In addition to the basic client API features the original project has, this framework also has:
* Appservice support (Intent API like mautrix-python, room state storage, etc)
* End-to-end encryption support (incl. interactive SAS verification)
* Structs for parsing event content
* Helpers for parsing and generating Matrix HTML
* Helpers for handling push rules
This project contains modules that are licensed under Apache 2.0:
* [maunium.net/go/mautrix/crypto/canonicaljson](crypto/canonicaljson)
* [maunium.net/go/mautrix/crypto/olm](crypto/olm)

1344
vendor/maunium.net/go/mautrix/client.go generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,171 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package attachment
import (
"crypto/aes"
"crypto/cipher"
"crypto/sha256"
"encoding/base64"
"errors"
"hash"
"io"
"maunium.net/go/mautrix/crypto/utils"
)
var (
HashMismatch = errors.New("mismatching SHA-256 digest")
UnsupportedVersion = errors.New("unsupported Matrix file encryption version")
UnsupportedAlgorithm = errors.New("unsupported JWK encryption algorithm")
InvalidKey = errors.New("failed to decode key")
InvalidInitVector = errors.New("failed to decode initialization vector")
ReaderClosed = errors.New("encrypting reader was already closed")
)
var (
keyBase64Length = base64.RawURLEncoding.EncodedLen(utils.AESCTRKeyLength)
ivBase64Length = base64.RawStdEncoding.EncodedLen(utils.AESCTRIVLength)
hashBase64Length = base64.RawStdEncoding.EncodedLen(utils.SHAHashLength)
)
type JSONWebKey struct {
Key string `json:"k"`
Algorithm string `json:"alg"`
Extractable bool `json:"ext"`
KeyType string `json:"kty"`
KeyOps []string `json:"key_ops"`
}
type EncryptedFileHashes struct {
SHA256 string `json:"sha256"`
}
type decodedKeys struct {
key [utils.AESCTRKeyLength]byte
iv [utils.AESCTRIVLength]byte
}
type EncryptedFile struct {
Key JSONWebKey `json:"key"`
InitVector string `json:"iv"`
Hashes EncryptedFileHashes `json:"hashes"`
Version string `json:"v"`
decoded *decodedKeys `json:"-"`
}
func NewEncryptedFile() *EncryptedFile {
key, iv := utils.GenAttachmentA256CTR()
return &EncryptedFile{
Key: JSONWebKey{
Key: base64.RawURLEncoding.EncodeToString(key[:]),
Algorithm: "A256CTR",
Extractable: true,
KeyType: "oct",
KeyOps: []string{"encrypt", "decrypt"},
},
InitVector: base64.RawStdEncoding.EncodeToString(iv[:]),
Version: "v2",
decoded: &decodedKeys{key, iv},
}
}
func (ef *EncryptedFile) decodeKeys() error {
if ef.decoded != nil {
return nil
} else if len(ef.Key.Key) != keyBase64Length {
return InvalidKey
} else if len(ef.InitVector) != ivBase64Length {
return InvalidInitVector
}
ef.decoded = &decodedKeys{}
_, err := base64.RawURLEncoding.Decode(ef.decoded.key[:], []byte(ef.Key.Key))
if err != nil {
return InvalidKey
}
_, err = base64.RawStdEncoding.Decode(ef.decoded.iv[:], []byte(ef.InitVector))
if err != nil {
return InvalidInitVector
}
return nil
}
func (ef *EncryptedFile) Encrypt(plaintext []byte) []byte {
ef.decodeKeys()
ciphertext := utils.XorA256CTR(plaintext, ef.decoded.key, ef.decoded.iv)
checksum := sha256.Sum256(ciphertext)
ef.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(checksum[:])
return ciphertext
}
// encryptingReader is a variation of cipher.StreamReader that also hashes the content.
type encryptingReader struct {
stream cipher.Stream
hash hash.Hash
source io.Reader
file *EncryptedFile
closed bool
}
func (r *encryptingReader) Read(dst []byte) (n int, err error) {
if r.closed {
return 0, ReaderClosed
}
n, err = r.source.Read(dst)
r.stream.XORKeyStream(dst[:n], dst[:n])
r.hash.Write(dst[:n])
return
}
func (r *encryptingReader) Close() (err error) {
closer, ok := r.source.(io.ReadCloser)
if ok {
err = closer.Close()
}
r.file.Hashes.SHA256 = base64.RawStdEncoding.EncodeToString(r.hash.Sum(nil))
r.closed = true
return
}
func (ef *EncryptedFile) EncryptStream(reader io.Reader) io.ReadCloser {
ef.decodeKeys()
block, _ := aes.NewCipher(ef.decoded.key[:])
return &encryptingReader{
stream: cipher.NewCTR(block, ef.decoded.iv[:]),
hash: sha256.New(),
source: reader,
file: ef,
}
}
func (ef *EncryptedFile) checkHash(ciphertext []byte) bool {
if len(ef.Hashes.SHA256) != hashBase64Length {
return false
}
var checksum [utils.SHAHashLength]byte
_, err := base64.RawStdEncoding.Decode(checksum[:], []byte(ef.Hashes.SHA256))
if err != nil {
return false
}
return checksum == sha256.Sum256(ciphertext)
}
func (ef *EncryptedFile) Decrypt(ciphertext []byte) ([]byte, error) {
if ef.Version != "v2" {
return nil, UnsupportedVersion
} else if ef.Key.Algorithm != "A256CTR" {
return nil, UnsupportedAlgorithm
} else if !ef.checkHash(ciphertext) {
return nil, HashMismatch
} else if err := ef.decodeKeys(); err != nil {
return nil, err
} else {
return utils.XorA256CTR(ciphertext, ef.decoded.key, ef.decoded.iv), nil
}
}

133
vendor/maunium.net/go/mautrix/crypto/utils/utils.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright (c) 2020 Nikos Filippakis
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package utils
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"math/rand"
"strings"
"github.com/btcsuite/btcutil/base58"
"golang.org/x/crypto/hkdf"
"golang.org/x/crypto/pbkdf2"
)
const (
// AESCTRKeyLength is the length of the AES256-CTR key used.
AESCTRKeyLength = 32
// AESCTRIVLength is the length of the AES256-CTR IV used.
AESCTRIVLength = 16
// HMACKeyLength is the length of the HMAC key used.
HMACKeyLength = 32
// SHAHashLength is the length of the SHA hash used.
SHAHashLength = 32
)
// XorA256CTR encrypts the input with the keystream generated by the AES256-CTR algorithm with the given arguments.
func XorA256CTR(source []byte, key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) []byte {
block, _ := aes.NewCipher(key[:])
result := make([]byte, len(source))
cipher.NewCTR(block, iv[:]).XORKeyStream(result, source)
return result
}
// GenAttachmentA256CTR generates a new random AES256-CTR key and IV suitable for encrypting attachments.
func GenAttachmentA256CTR() (key [AESCTRKeyLength]byte, iv [AESCTRIVLength]byte) {
_, err := rand.Read(key[:])
if err != nil {
panic(err)
}
// The last 8 bytes of the IV act as the counter in AES-CTR, which means they're left empty here
_, err = rand.Read(iv[:8])
if err != nil {
panic(err)
}
return
}
// GenA256CTRIV generates a random IV for AES256-CTR with the last bit set to zero.
func GenA256CTRIV() (iv [AESCTRIVLength]byte) {
_, err := rand.Read(iv[:])
if err != nil {
panic(err)
}
iv[8] &= 0x7F
return
}
// DeriveKeysSHA256 derives an AES and a HMAC key from the given recovery key.
func DeriveKeysSHA256(key []byte, name string) ([AESCTRKeyLength]byte, [HMACKeyLength]byte) {
var zeroBytes [32]byte
derivedHkdf := hkdf.New(sha256.New, key[:], zeroBytes[:], []byte(name))
var aesKey [AESCTRKeyLength]byte
var hmacKey [HMACKeyLength]byte
derivedHkdf.Read(aesKey[:])
derivedHkdf.Read(hmacKey[:])
return aesKey, hmacKey
}
// PBKDF2SHA512 generates a key of the given bit-length using the given passphrase, salt and iteration count.
func PBKDF2SHA512(password []byte, salt []byte, iters int, keyLenBits int) []byte {
return pbkdf2.Key(password, salt, iters, keyLenBits/8, sha512.New)
}
// DecodeBase58RecoveryKey recovers the secret storage from a recovery key.
func DecodeBase58RecoveryKey(recoveryKey string) []byte {
noSpaces := strings.ReplaceAll(recoveryKey, " ", "")
decoded := base58.Decode(noSpaces)
if len(decoded) != AESCTRKeyLength+3 { // AESCTRKeyLength bytes key and 3 bytes prefix / parity
return nil
}
var parity byte
for _, b := range decoded[:34] {
parity ^= b
}
if parity != decoded[34] || decoded[0] != 0x8B || decoded[1] != 1 {
return nil
}
return decoded[2:34]
}
// EncodeBase58RecoveryKey recovers the secret storage from a recovery key.
func EncodeBase58RecoveryKey(key []byte) string {
var inputBytes [35]byte
copy(inputBytes[2:34], key[:])
inputBytes[0] = 0x8B
inputBytes[1] = 1
var parity byte
for _, b := range inputBytes[:34] {
parity ^= b
}
inputBytes[34] = parity
recoveryKey := base58.Encode(inputBytes[:])
var spacedKey string
for i, c := range recoveryKey {
if i > 0 && i%4 == 0 {
spacedKey += " "
}
spacedKey += string(c)
}
return spacedKey
}
// HMACSHA256B64 calculates the base64 of the SHA256 hmac of the input with the given key.
func HMACSHA256B64(input []byte, hmacKey [HMACKeyLength]byte) string {
h := hmac.New(sha256.New, hmacKey[:])
h.Write(input)
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

144
vendor/maunium.net/go/mautrix/error.go generated vendored Normal file
View File

@@ -0,0 +1,144 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package mautrix
import (
"encoding/json"
"errors"
"fmt"
"net/http"
)
// Common error codes from https://matrix.org/docs/spec/client_server/latest#api-standards
//
// Can be used with errors.Is() to check the response code without casting the error:
// err := client.Sync()
// if errors.Is(err, MUnknownToken) {
// // logout
// }
var (
// Forbidden access, e.g. joining a room without permission, failed login.
MForbidden = RespError{ErrCode: "M_FORBIDDEN"}
// The access token specified was not recognised.
MUnknownToken = RespError{ErrCode: "M_UNKNOWN_TOKEN"}
// No access token was specified for the request.
MMissingToken = RespError{ErrCode: "M_MISSING_TOKEN"}
// Request contained valid JSON, but it was malformed in some way, e.g. missing required keys, invalid values for keys.
MBadJSON = RespError{ErrCode: "M_BAD_JSON"}
// Request did not contain valid JSON.
MNotJSON = RespError{ErrCode: "M_NOT_JSON"}
// No resource was found for this request.
MNotFound = RespError{ErrCode: "M_NOT_FOUND"}
// Too many requests have been sent in a short period of time. Wait a while then try again.
MLimitExceeded = RespError{ErrCode: "M_LIMIT_EXCEEDED"}
// The user ID associated with the request has been deactivated.
// Typically for endpoints that prove authentication, such as /login.
MUserDeactivated = RespError{ErrCode: "M_USER_DEACTIVATED"}
// Encountered when trying to register a user ID which has been taken.
MUserInUse = RespError{ErrCode: "M_USER_IN_USE"}
// Encountered when trying to register a user ID which is not valid.
MInvalidUsername = RespError{ErrCode: "M_INVALID_USERNAME"}
// Sent when the room alias given to the createRoom API is already in use.
MRoomInUse = RespError{ErrCode: "M_ROOM_IN_USE"}
// The state change requested cannot be performed, such as attempting to unban a user who is not banned.
MBadState = RespError{ErrCode: "M_BAD_STATE"}
// The request or entity was too large.
MTooLarge = RespError{ErrCode: "M_TOO_LARGE"}
// The resource being requested is reserved by an application service, or the application service making the request has not created the resource.
MExclusive = RespError{ErrCode: "M_EXCLUSIVE"}
// The client's request to create a room used a room version that the server does not support.
MUnsupportedRoomVersion = RespError{ErrCode: "M_UNSUPPORTED_ROOM_VERSION"}
// The client attempted to join a room that has a version the server does not support.
// Inspect the room_version property of the error response for the room's version.
MIncompatibleRoomVersion = RespError{ErrCode: "M_INCOMPATIBLE_ROOM_VERSION"}
)
// HTTPError An HTTP Error response, which may wrap an underlying native Go Error.
type HTTPError struct {
Request *http.Request
Response *http.Response
ResponseBody string
WrappedError error
RespError *RespError
Message string
}
func (e HTTPError) Is(err error) bool {
return (e.RespError != nil && errors.Is(e.RespError, err)) || (e.WrappedError != nil && errors.Is(e.WrappedError, err))
}
func (e HTTPError) IsStatus(code int) bool {
return e.Response != nil && e.Response.StatusCode == code
}
func (e HTTPError) Error() string {
if e.WrappedError != nil {
return fmt.Sprintf("%s: %v", e.Message, e.WrappedError)
} else if e.RespError != nil {
return fmt.Sprintf("failed to %s %s: %s (HTTP %d): %s", e.Request.Method, e.Request.URL.Path,
e.RespError.ErrCode, e.Response.StatusCode, e.RespError.Err)
} else {
msg := fmt.Sprintf("failed to %s %s: %s", e.Request.Method, e.Request.URL.Path, e.Response.Status)
if len(e.ResponseBody) > 0 {
msg = fmt.Sprintf("%s\n%s", msg, e.ResponseBody)
}
return msg
}
}
func (e HTTPError) Unwrap() error {
if e.WrappedError != nil {
return e.WrappedError
} else if e.RespError != nil {
return *e.RespError
}
return nil
}
// RespError is the standard JSON error response from Homeservers. It also implements the Golang "error" interface.
// See http://matrix.org/docs/spec/client_server/r0.6.1.html#api-standards
type RespError struct {
ErrCode string
Err string
ExtraData map[string]interface{}
}
func (e *RespError) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &e.ExtraData)
if err != nil {
return err
}
e.ErrCode, _ = e.ExtraData["errcode"].(string)
e.Err, _ = e.ExtraData["error"].(string)
return nil
}
func (e *RespError) MarshalJSON() ([]byte, error) {
if e.ExtraData == nil {
e.ExtraData = make(map[string]interface{})
}
e.ExtraData["errcode"] = e.ErrCode
e.ExtraData["error"] = e.Err
return json.Marshal(&e.ExtraData)
}
// Error returns the errcode and error message.
func (e RespError) Error() string {
return e.ErrCode + ": " + e.Err
}
func (e RespError) Is(err error) bool {
e2, ok := err.(RespError)
if !ok {
return false
}
if e.ErrCode == "M_UNKNOWN" && e2.ErrCode == "M_UNKNOWN" {
return e.Err == e2.Err
}
return e2.ErrCode == e.ErrCode
}

45
vendor/maunium.net/go/mautrix/event/accountdata.go generated vendored Normal file
View File

@@ -0,0 +1,45 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"maunium.net/go/mautrix/id"
)
// TagEventContent represents the content of a m.tag room account data event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-tag
type TagEventContent struct {
Tags Tags `json:"tags"`
}
type Tags map[string]Tag
type Tag struct {
Order json.Number `json:"order,omitempty"`
}
// DirectChatsEventContent represents the content of a m.direct account data event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-direct
type DirectChatsEventContent map[id.UserID][]id.RoomID
// FullyReadEventContent represents the content of a m.fully_read account data event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-fully-read
type FullyReadEventContent struct {
EventID id.EventID `json:"event_id"`
}
// IgnoredUserListEventContent represents the content of a m.ignored_user_list account data event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-ignored-user-list
type IgnoredUserListEventContent struct {
IgnoredUsers map[id.UserID]IgnoredUser `json:"ignored_users"`
}
type IgnoredUser struct {
// This is an empty object
}

457
vendor/maunium.net/go/mautrix/event/content.go generated vendored Normal file
View File

@@ -0,0 +1,457 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/gob"
"encoding/json"
"errors"
"fmt"
"reflect"
)
// TypeMap is a mapping from event type to the content struct type.
// This is used by Content.ParseRaw() for creating the correct type of struct.
var TypeMap = map[Type]reflect.Type{
StateMember: reflect.TypeOf(MemberEventContent{}),
StatePowerLevels: reflect.TypeOf(PowerLevelsEventContent{}),
StateCanonicalAlias: reflect.TypeOf(CanonicalAliasEventContent{}),
StateRoomName: reflect.TypeOf(RoomNameEventContent{}),
StateRoomAvatar: reflect.TypeOf(RoomAvatarEventContent{}),
StateTopic: reflect.TypeOf(TopicEventContent{}),
StateTombstone: reflect.TypeOf(TombstoneEventContent{}),
StateCreate: reflect.TypeOf(CreateEventContent{}),
StateJoinRules: reflect.TypeOf(JoinRulesEventContent{}),
StateHistoryVisibility: reflect.TypeOf(HistoryVisibilityEventContent{}),
StateGuestAccess: reflect.TypeOf(GuestAccessEventContent{}),
StatePinnedEvents: reflect.TypeOf(PinnedEventsEventContent{}),
StateEncryption: reflect.TypeOf(EncryptionEventContent{}),
EventMessage: reflect.TypeOf(MessageEventContent{}),
EventSticker: reflect.TypeOf(MessageEventContent{}),
EventEncrypted: reflect.TypeOf(EncryptedEventContent{}),
EventRedaction: reflect.TypeOf(RedactionEventContent{}),
EventReaction: reflect.TypeOf(ReactionEventContent{}),
AccountDataRoomTags: reflect.TypeOf(TagEventContent{}),
AccountDataDirectChats: reflect.TypeOf(DirectChatsEventContent{}),
AccountDataFullyRead: reflect.TypeOf(FullyReadEventContent{}),
AccountDataIgnoredUserList: reflect.TypeOf(IgnoredUserListEventContent{}),
EphemeralEventTyping: reflect.TypeOf(TypingEventContent{}),
EphemeralEventReceipt: reflect.TypeOf(ReceiptEventContent{}),
EphemeralEventPresence: reflect.TypeOf(PresenceEventContent{}),
InRoomVerificationStart: reflect.TypeOf(VerificationStartEventContent{}),
InRoomVerificationReady: reflect.TypeOf(VerificationReadyEventContent{}),
InRoomVerificationAccept: reflect.TypeOf(VerificationAcceptEventContent{}),
InRoomVerificationKey: reflect.TypeOf(VerificationKeyEventContent{}),
InRoomVerificationMAC: reflect.TypeOf(VerificationMacEventContent{}),
InRoomVerificationCancel: reflect.TypeOf(VerificationCancelEventContent{}),
ToDeviceRoomKey: reflect.TypeOf(RoomKeyEventContent{}),
ToDeviceForwardedRoomKey: reflect.TypeOf(ForwardedRoomKeyEventContent{}),
ToDeviceRoomKeyRequest: reflect.TypeOf(RoomKeyRequestEventContent{}),
ToDeviceEncrypted: reflect.TypeOf(EncryptedEventContent{}),
ToDeviceRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}),
ToDeviceVerificationStart: reflect.TypeOf(VerificationStartEventContent{}),
ToDeviceVerificationAccept: reflect.TypeOf(VerificationAcceptEventContent{}),
ToDeviceVerificationKey: reflect.TypeOf(VerificationKeyEventContent{}),
ToDeviceVerificationMAC: reflect.TypeOf(VerificationMacEventContent{}),
ToDeviceVerificationCancel: reflect.TypeOf(VerificationCancelEventContent{}),
ToDeviceVerificationRequest: reflect.TypeOf(VerificationRequestEventContent{}),
ToDeviceOrgMatrixRoomKeyWithheld: reflect.TypeOf(RoomKeyWithheldEventContent{}),
CallInvite: reflect.TypeOf(CallInviteEventContent{}),
CallCandidates: reflect.TypeOf(CallCandidatesEventContent{}),
CallAnswer: reflect.TypeOf(CallAnswerEventContent{}),
CallReject: reflect.TypeOf(CallRejectEventContent{}),
CallSelectAnswer: reflect.TypeOf(CallSelectAnswerEventContent{}),
CallNegotiate: reflect.TypeOf(CallNegotiateEventContent{}),
CallHangup: reflect.TypeOf(CallHangupEventContent{}),
}
// Content stores the content of a Matrix event.
//
// By default, the content is only parsed into a map[string]interface{}. However, you can call ParseRaw with the
// correct event type to parse the content into a nicer struct, which you can then access from Parsed or via the
// helper functions.
type Content struct {
VeryRaw json.RawMessage
Raw map[string]interface{}
Parsed interface{}
}
type Relatable interface {
GetRelatesTo() *RelatesTo
OptionalGetRelatesTo() *RelatesTo
SetRelatesTo(rel *RelatesTo)
}
func (content *Content) UnmarshalJSON(data []byte) error {
content.VeryRaw = data
err := json.Unmarshal(data, &content.Raw)
return err
}
func (content *Content) MarshalJSON() ([]byte, error) {
if content.Raw == nil {
if content.Parsed == nil {
if content.VeryRaw == nil {
return []byte("{}"), nil
}
return content.VeryRaw, nil
}
return json.Marshal(content.Parsed)
} else if content.Parsed != nil {
// TODO this whole thing is incredibly hacky
// It needs to produce JSON, where:
// * content.Parsed is applied after content.Raw
// * MarshalJSON() is respected inside content.Parsed
// * Custom field inside nested objects of content.Raw are preserved,
// even if content.Parsed contains the higher-level objects.
// * content.Raw is not modified
unparsed, err := json.Marshal(content.Parsed)
if err != nil {
return nil, err
}
var rawParsed map[string]interface{}
err = json.Unmarshal(unparsed, &rawParsed)
if err != nil {
return nil, err
}
output := make(map[string]interface{})
for key, value := range content.Raw {
output[key] = value
}
mergeMaps(output, rawParsed)
return json.Marshal(output)
}
return json.Marshal(content.Raw)
}
func IsUnsupportedContentType(err error) bool {
return errors.Is(err, UnsupportedContentType)
}
var ContentAlreadyParsed = errors.New("content is already parsed")
var UnsupportedContentType = errors.New("unsupported event type")
func (content *Content) ParseRaw(evtType Type) error {
if content.Parsed != nil {
return ContentAlreadyParsed
}
structType, ok := TypeMap[evtType]
if !ok {
return fmt.Errorf("%w %s", UnsupportedContentType, evtType.Repr())
}
content.Parsed = reflect.New(structType).Interface()
return json.Unmarshal(content.VeryRaw, &content.Parsed)
}
func mergeMaps(into, from map[string]interface{}) {
for key, newValue := range from {
existingValue, ok := into[key]
if !ok {
into[key] = newValue
continue
}
existingValueMap, okEx := existingValue.(map[string]interface{})
newValueMap, okNew := newValue.(map[string]interface{})
if okEx && okNew {
mergeMaps(existingValueMap, newValueMap)
} else {
into[key] = newValue
}
}
}
func init() {
gob.Register(&MemberEventContent{})
gob.Register(&PowerLevelsEventContent{})
gob.Register(&CanonicalAliasEventContent{})
gob.Register(&EncryptionEventContent{})
gob.Register(&RoomNameEventContent{})
gob.Register(&RoomAvatarEventContent{})
gob.Register(&TopicEventContent{})
gob.Register(&TombstoneEventContent{})
gob.Register(&CreateEventContent{})
gob.Register(&JoinRulesEventContent{})
gob.Register(&HistoryVisibilityEventContent{})
gob.Register(&GuestAccessEventContent{})
gob.Register(&PinnedEventsEventContent{})
gob.Register(&MessageEventContent{})
gob.Register(&MessageEventContent{})
gob.Register(&EncryptedEventContent{})
gob.Register(&RedactionEventContent{})
gob.Register(&ReactionEventContent{})
gob.Register(&TagEventContent{})
gob.Register(&DirectChatsEventContent{})
gob.Register(&FullyReadEventContent{})
gob.Register(&IgnoredUserListEventContent{})
gob.Register(&TypingEventContent{})
gob.Register(&ReceiptEventContent{})
gob.Register(&PresenceEventContent{})
gob.Register(&RoomKeyEventContent{})
gob.Register(&ForwardedRoomKeyEventContent{})
gob.Register(&RoomKeyRequestEventContent{})
gob.Register(&RoomKeyWithheldEventContent{})
}
// Helper cast functions below
func (content *Content) AsMember() *MemberEventContent {
casted, ok := content.Parsed.(*MemberEventContent)
if !ok {
return &MemberEventContent{}
}
return casted
}
func (content *Content) AsPowerLevels() *PowerLevelsEventContent {
casted, ok := content.Parsed.(*PowerLevelsEventContent)
if !ok {
return &PowerLevelsEventContent{}
}
return casted
}
func (content *Content) AsCanonicalAlias() *CanonicalAliasEventContent {
casted, ok := content.Parsed.(*CanonicalAliasEventContent)
if !ok {
return &CanonicalAliasEventContent{}
}
return casted
}
func (content *Content) AsRoomName() *RoomNameEventContent {
casted, ok := content.Parsed.(*RoomNameEventContent)
if !ok {
return &RoomNameEventContent{}
}
return casted
}
func (content *Content) AsRoomAvatar() *RoomAvatarEventContent {
casted, ok := content.Parsed.(*RoomAvatarEventContent)
if !ok {
return &RoomAvatarEventContent{}
}
return casted
}
func (content *Content) AsTopic() *TopicEventContent {
casted, ok := content.Parsed.(*TopicEventContent)
if !ok {
return &TopicEventContent{}
}
return casted
}
func (content *Content) AsTombstone() *TombstoneEventContent {
casted, ok := content.Parsed.(*TombstoneEventContent)
if !ok {
return &TombstoneEventContent{}
}
return casted
}
func (content *Content) AsCreate() *CreateEventContent {
casted, ok := content.Parsed.(*CreateEventContent)
if !ok {
return &CreateEventContent{}
}
return casted
}
func (content *Content) AsJoinRules() *JoinRulesEventContent {
casted, ok := content.Parsed.(*JoinRulesEventContent)
if !ok {
return &JoinRulesEventContent{}
}
return casted
}
func (content *Content) AsHistoryVisibility() *HistoryVisibilityEventContent {
casted, ok := content.Parsed.(*HistoryVisibilityEventContent)
if !ok {
return &HistoryVisibilityEventContent{}
}
return casted
}
func (content *Content) AsGuestAccess() *GuestAccessEventContent {
casted, ok := content.Parsed.(*GuestAccessEventContent)
if !ok {
return &GuestAccessEventContent{}
}
return casted
}
func (content *Content) AsPinnedEvents() *PinnedEventsEventContent {
casted, ok := content.Parsed.(*PinnedEventsEventContent)
if !ok {
return &PinnedEventsEventContent{}
}
return casted
}
func (content *Content) AsEncryption() *EncryptionEventContent {
casted, ok := content.Parsed.(*EncryptionEventContent)
if !ok {
return &EncryptionEventContent{}
}
return casted
}
func (content *Content) AsMessage() *MessageEventContent {
casted, ok := content.Parsed.(*MessageEventContent)
if !ok {
return &MessageEventContent{}
}
return casted
}
func (content *Content) AsEncrypted() *EncryptedEventContent {
casted, ok := content.Parsed.(*EncryptedEventContent)
if !ok {
return &EncryptedEventContent{}
}
return casted
}
func (content *Content) AsRedaction() *RedactionEventContent {
casted, ok := content.Parsed.(*RedactionEventContent)
if !ok {
return &RedactionEventContent{}
}
return casted
}
func (content *Content) AsReaction() *ReactionEventContent {
casted, ok := content.Parsed.(*ReactionEventContent)
if !ok {
return &ReactionEventContent{}
}
return casted
}
func (content *Content) AsTag() *TagEventContent {
casted, ok := content.Parsed.(*TagEventContent)
if !ok {
return &TagEventContent{}
}
return casted
}
func (content *Content) AsDirectChats() *DirectChatsEventContent {
casted, ok := content.Parsed.(*DirectChatsEventContent)
if !ok {
return &DirectChatsEventContent{}
}
return casted
}
func (content *Content) AsFullyRead() *FullyReadEventContent {
casted, ok := content.Parsed.(*FullyReadEventContent)
if !ok {
return &FullyReadEventContent{}
}
return casted
}
func (content *Content) AsIgnoredUserList() *IgnoredUserListEventContent {
casted, ok := content.Parsed.(*IgnoredUserListEventContent)
if !ok {
return &IgnoredUserListEventContent{}
}
return casted
}
func (content *Content) AsTyping() *TypingEventContent {
casted, ok := content.Parsed.(*TypingEventContent)
if !ok {
return &TypingEventContent{}
}
return casted
}
func (content *Content) AsReceipt() *ReceiptEventContent {
casted, ok := content.Parsed.(*ReceiptEventContent)
if !ok {
return &ReceiptEventContent{}
}
return casted
}
func (content *Content) AsPresence() *PresenceEventContent {
casted, ok := content.Parsed.(*PresenceEventContent)
if !ok {
return &PresenceEventContent{}
}
return casted
}
func (content *Content) AsRoomKey() *RoomKeyEventContent {
casted, ok := content.Parsed.(*RoomKeyEventContent)
if !ok {
return &RoomKeyEventContent{}
}
return casted
}
func (content *Content) AsForwardedRoomKey() *ForwardedRoomKeyEventContent {
casted, ok := content.Parsed.(*ForwardedRoomKeyEventContent)
if !ok {
return &ForwardedRoomKeyEventContent{}
}
return casted
}
func (content *Content) AsRoomKeyRequest() *RoomKeyRequestEventContent {
casted, ok := content.Parsed.(*RoomKeyRequestEventContent)
if !ok {
return &RoomKeyRequestEventContent{}
}
return casted
}
func (content *Content) AsRoomKeyWithheld() *RoomKeyWithheldEventContent {
casted, ok := content.Parsed.(*RoomKeyWithheldEventContent)
if !ok {
return &RoomKeyWithheldEventContent{}
}
return casted
}
func (content *Content) AsCallInvite() *CallInviteEventContent {
casted, ok := content.Parsed.(*CallInviteEventContent)
if !ok {
return &CallInviteEventContent{}
}
return casted
}
func (content *Content) AsCallCandidates() *CallCandidatesEventContent {
casted, ok := content.Parsed.(*CallCandidatesEventContent)
if !ok {
return &CallCandidatesEventContent{}
}
return casted
}
func (content *Content) AsCallAnswer() *CallAnswerEventContent {
casted, ok := content.Parsed.(*CallAnswerEventContent)
if !ok {
return &CallAnswerEventContent{}
}
return casted
}
func (content *Content) AsCallReject() *CallRejectEventContent {
casted, ok := content.Parsed.(*CallRejectEventContent)
if !ok {
return &CallRejectEventContent{}
}
return casted
}
func (content *Content) AsCallSelectAnswer() *CallSelectAnswerEventContent {
casted, ok := content.Parsed.(*CallSelectAnswerEventContent)
if !ok {
return &CallSelectAnswerEventContent{}
}
return casted
}
func (content *Content) AsCallNegotiate() *CallNegotiateEventContent {
casted, ok := content.Parsed.(*CallNegotiateEventContent)
if !ok {
return &CallNegotiateEventContent{}
}
return casted
}
func (content *Content) AsCallHangup() *CallHangupEventContent {
casted, ok := content.Parsed.(*CallHangupEventContent)
if !ok {
return &CallHangupEventContent{}
}
return casted
}

141
vendor/maunium.net/go/mautrix/event/encryption.go generated vendored Normal file
View File

@@ -0,0 +1,141 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"maunium.net/go/mautrix/id"
)
// EncryptionEventContent represents the content of a m.room.encryption state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-encryption
type EncryptionEventContent struct {
// The encryption algorithm to be used to encrypt messages sent in this room. Must be 'm.megolm.v1.aes-sha2'.
Algorithm id.Algorithm `json:"algorithm"`
// How long the session should be used before changing it. 604800000 (a week) is the recommended default.
RotationPeriodMillis int64 `json:"rotation_period_ms,omitempty"`
// How many messages should be sent before changing the session. 100 is the recommended default.
RotationPeriodMessages int `json:"rotation_period_msgs,omitempty"`
}
// EncryptedEventContent represents the content of a m.room.encrypted message event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-encrypted
type EncryptedEventContent struct {
Algorithm id.Algorithm `json:"algorithm"`
SenderKey id.SenderKey `json:"sender_key"`
DeviceID id.DeviceID `json:"device_id,omitempty"`
SessionID id.SessionID `json:"session_id,omitempty"`
Ciphertext json.RawMessage `json:"ciphertext"`
MegolmCiphertext []byte `json:"-"`
OlmCiphertext OlmCiphertexts `json:"-"`
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
type OlmCiphertexts map[id.Curve25519]struct {
Body string `json:"body"`
Type id.OlmMsgType `json:"type"`
}
type serializableEncryptedEventContent EncryptedEventContent
func (content *EncryptedEventContent) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*serializableEncryptedEventContent)(content))
if err != nil {
return err
}
switch content.Algorithm {
case id.AlgorithmOlmV1:
content.OlmCiphertext = make(OlmCiphertexts)
return json.Unmarshal(content.Ciphertext, &content.OlmCiphertext)
case id.AlgorithmMegolmV1:
if len(content.Ciphertext) == 0 || content.Ciphertext[0] != '"' || content.Ciphertext[len(content.Ciphertext)-1] != '"' {
return id.InputNotJSONString
}
content.MegolmCiphertext = content.Ciphertext[1 : len(content.Ciphertext)-1]
}
return nil
}
func (content *EncryptedEventContent) MarshalJSON() ([]byte, error) {
var err error
switch content.Algorithm {
case id.AlgorithmOlmV1:
content.Ciphertext, err = json.Marshal(content.OlmCiphertext)
case id.AlgorithmMegolmV1:
content.Ciphertext = make([]byte, len(content.MegolmCiphertext)+2)
content.Ciphertext[0] = '"'
content.Ciphertext[len(content.Ciphertext)-1] = '"'
copy(content.Ciphertext[1:len(content.Ciphertext)-1], content.MegolmCiphertext)
}
if err != nil {
return nil, err
}
return json.Marshal((*serializableEncryptedEventContent)(content))
}
// RoomKeyEventContent represents the content of a m.room_key to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-key
type RoomKeyEventContent struct {
Algorithm id.Algorithm `json:"algorithm"`
RoomID id.RoomID `json:"room_id"`
SessionID id.SessionID `json:"session_id"`
SessionKey string `json:"session_key"`
}
// ForwardedRoomKeyEventContent represents the content of a m.forwarded_room_key to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-forwarded-room-key
type ForwardedRoomKeyEventContent struct {
RoomKeyEventContent
SenderKey id.SenderKey `json:"sender_key"`
SenderClaimedKey id.Ed25519 `json:"sender_claimed_ed25519_key"`
ForwardingKeyChain []string `json:"forwarding_curve25519_key_chain"`
}
type KeyRequestAction string
const (
KeyRequestActionRequest = "request"
KeyRequestActionCancel = "request_cancellation"
)
// RoomKeyRequestEventContent represents the content of a m.room_key_request to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-key-request
type RoomKeyRequestEventContent struct {
Body RequestedKeyInfo `json:"body"`
Action KeyRequestAction `json:"action"`
RequestingDeviceID id.DeviceID `json:"requesting_device_id"`
RequestID string `json:"request_id"`
}
type RequestedKeyInfo struct {
Algorithm id.Algorithm `json:"algorithm"`
RoomID id.RoomID `json:"room_id"`
SenderKey id.SenderKey `json:"sender_key"`
SessionID id.SessionID `json:"session_id"`
}
type RoomKeyWithheldCode string
const (
RoomKeyWithheldBlacklisted RoomKeyWithheldCode = "m.blacklisted"
RoomKeyWithheldUnverified RoomKeyWithheldCode = "m.unverified"
RoomKeyWithheldUnauthorized RoomKeyWithheldCode = "m.unauthorized"
RoomKeyWithheldUnavailable RoomKeyWithheldCode = "m.unavailable"
RoomKeyWithheldNoOlmSession RoomKeyWithheldCode = "m.no_olm"
)
type RoomKeyWithheldEventContent struct {
RoomID id.RoomID `json:"room_id,omitempty"`
Algorithm id.Algorithm `json:"algorithm"`
SessionID id.SessionID `json:"session_id,omitempty"`
SenderKey id.SenderKey `json:"sender_key"`
Code RoomKeyWithheldCode `json:"code"`
Reason string `json:"reason,omitempty"`
}

80
vendor/maunium.net/go/mautrix/event/ephemeral.go generated vendored Normal file
View File

@@ -0,0 +1,80 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"maunium.net/go/mautrix/id"
)
// TagEventContent represents the content of a m.typing ephemeral event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-typing
type TypingEventContent struct {
UserIDs []id.UserID `json:"user_ids"`
}
// ReceiptEventContent represents the content of a m.receipt ephemeral event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-receipt
type ReceiptEventContent map[id.EventID]Receipts
type Receipts struct {
Read map[id.UserID]ReadReceipt `json:"m.read"`
}
type ReadReceipt struct {
Timestamp int64 `json:"ts"`
// Extra contains any unknown fields in the read receipt event.
// Most servers don't allow clients to set them, so this will be empty in most cases.
Extra map[string]interface{} `json:"-"`
}
func (rr *ReadReceipt) UnmarshalJSON(data []byte) error {
// Hacky compatibility hack against crappy clients that send double-encoded read receipts.
// TODO is this actually needed? clients can't currently set custom content in receipts 🤔
if data[0] == '"' && data[len(data)-1] == '"' {
var strData string
err := json.Unmarshal(data, &strData)
if err != nil {
return err
}
data = []byte(strData)
}
var parsed map[string]interface{}
err := json.Unmarshal(data, &parsed)
if err != nil {
return err
}
ts, _ := parsed["ts"].(float64)
delete(parsed, "ts")
*rr = ReadReceipt{
Timestamp: int64(ts),
Extra: parsed,
}
return nil
}
type Presence string
const (
PresenceOnline Presence = "online"
PresenceOffline Presence = "offline"
PresenceUnavailable Presence = "unavailable"
)
// PresenceEventContent represents the content of a m.presence ephemeral event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-presence
type PresenceEventContent struct {
Presence Presence `json:"presence"`
Displayname string `json:"displayname,omitempty"`
AvatarURL id.ContentURIString `json:"avatar_url,omitempty"`
LastActiveAgo int64 `json:"last_active_ago,omitempty"`
CurrentlyActive bool `json:"currently_active,omitempty"`
StatusMessage string `json:"status_msg,omitempty"`
}

54
vendor/maunium.net/go/mautrix/event/events.go generated vendored Normal file
View File

@@ -0,0 +1,54 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"maunium.net/go/mautrix/id"
)
// Event represents a single Matrix event.
type Event struct {
StateKey *string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
Sender id.UserID `json:"sender"` // The user ID of the sender of the event
Type Type `json:"type"` // The event type
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
ID id.EventID `json:"event_id"` // The unique ID of this event
RoomID id.RoomID `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
Content Content `json:"content"` // The JSON content of the event.
Redacts id.EventID `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
Mautrix MautrixInfo `json:"-"`
}
type MautrixInfo struct {
Verified bool
}
func (evt *Event) GetStateKey() string {
if evt.StateKey != nil {
return *evt.StateKey
}
return ""
}
type StrippedState struct {
Content Content `json:"content"`
Type Type `json:"type"`
StateKey string `json:"state_key"`
}
type Unsigned struct {
PrevContent *Content `json:"prev_content,omitempty"`
PrevSender id.UserID `json:"prev_sender,omitempty"`
ReplacesState id.EventID `json:"replaces_state,omitempty"`
Age int64 `json:"age,omitempty"`
TransactionID string `json:"transaction_id,omitempty"`
Relations Relations `json:"m.relations,omitempty"`
RedactedBecause *Event `json:"redacted_because,omitempty"`
InviteRoomState []StrippedState `json:"invite_room_state"`
}

53
vendor/maunium.net/go/mautrix/event/member.go generated vendored Normal file
View File

@@ -0,0 +1,53 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"maunium.net/go/mautrix/id"
)
// Membership is an enum specifying the membership state of a room member.
type Membership string
func (ms Membership) IsInviteOrJoin() bool {
return ms == MembershipJoin || ms == MembershipInvite
}
func (ms Membership) IsLeaveOrBan() bool {
return ms == MembershipLeave || ms == MembershipBan
}
// The allowed membership states as specified in spec section 10.5.5.
const (
MembershipJoin Membership = "join"
MembershipLeave Membership = "leave"
MembershipInvite Membership = "invite"
MembershipBan Membership = "ban"
MembershipKnock Membership = "knock"
)
// MemberEventContent represents the content of a m.room.member state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-member
type MemberEventContent struct {
Membership Membership `json:"membership"`
AvatarURL id.ContentURIString `json:"avatar_url,omitempty"`
Displayname string `json:"displayname,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
ThirdPartyInvite *ThirdPartyInvite `json:"third_party_invite,omitempty"`
Reason string `json:"reason,omitempty"`
}
type ThirdPartyInvite struct {
DisplayName string `json:"display_name"`
Signed struct {
Token string `json:"token"`
Signatures json.RawMessage `json:"signatures"`
MXID string `json:"mxid"`
}
}

231
vendor/maunium.net/go/mautrix/event/message.go generated vendored Normal file
View File

@@ -0,0 +1,231 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"strconv"
"maunium.net/go/mautrix/crypto/attachment"
"maunium.net/go/mautrix/id"
)
// MessageType is the sub-type of a m.room.message event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-message-msgtypes
type MessageType string
// Msgtypes
const (
MsgText MessageType = "m.text"
MsgEmote MessageType = "m.emote"
MsgNotice MessageType = "m.notice"
MsgImage MessageType = "m.image"
MsgLocation MessageType = "m.location"
MsgVideo MessageType = "m.video"
MsgAudio MessageType = "m.audio"
MsgFile MessageType = "m.file"
MsgVerificationRequest MessageType = "m.key.verification.request"
)
// Format specifies the format of the formatted_body in m.room.message events.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-message-msgtypes
type Format string
// Message formats
const (
FormatHTML Format = "org.matrix.custom.html"
)
// RedactionEventContent represents the content of a m.room.redaction message event.
//
// The redacted event ID is still at the top level, but will move in a future room version.
// See https://github.com/matrix-org/matrix-doc/pull/2244 and https://github.com/matrix-org/matrix-doc/pull/2174
//
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-redaction
type RedactionEventContent struct {
Reason string `json:"reason,omitempty"`
}
// ReactionEventContent represents the content of a m.reaction message event.
// This is not yet in a spec release, see https://github.com/matrix-org/matrix-doc/pull/1849
type ReactionEventContent struct {
RelatesTo RelatesTo `json:"m.relates_to"`
}
func (content *ReactionEventContent) GetRelatesTo() *RelatesTo {
return &content.RelatesTo
}
func (content *ReactionEventContent) OptionalGetRelatesTo() *RelatesTo {
return &content.RelatesTo
}
func (content *ReactionEventContent) SetRelatesTo(rel *RelatesTo) {
content.RelatesTo = *rel
}
// MssageEventContent represents the content of a m.room.message event.
//
// It is also used to represent m.sticker events, as they are equivalent to m.room.message
// with the exception of the msgtype field.
//
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-message
type MessageEventContent struct {
// Base m.room.message fields
MsgType MessageType `json:"msgtype"`
Body string `json:"body"`
// Extra fields for text types
Format Format `json:"format,omitempty"`
FormattedBody string `json:"formatted_body,omitempty"`
// Extra field for m.location
GeoURI string `json:"geo_uri,omitempty"`
// Extra fields for media types
URL id.ContentURIString `json:"url,omitempty"`
Info *FileInfo `json:"info,omitempty"`
File *EncryptedFileInfo `json:"file,omitempty"`
// Edits and relations
NewContent *MessageEventContent `json:"m.new_content,omitempty"`
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
// In-room verification
To id.UserID `json:"to,omitempty"`
FromDevice id.DeviceID `json:"from_device,omitempty"`
Methods []VerificationMethod `json:"methods,omitempty"`
replyFallbackRemoved bool
}
func (content *MessageEventContent) GetRelatesTo() *RelatesTo {
if content.RelatesTo == nil {
content.RelatesTo = &RelatesTo{}
}
return content.RelatesTo
}
func (content *MessageEventContent) OptionalGetRelatesTo() *RelatesTo {
return content.RelatesTo
}
func (content *MessageEventContent) SetRelatesTo(rel *RelatesTo) {
content.RelatesTo = rel
}
func (content *MessageEventContent) GetFile() *EncryptedFileInfo {
if content.File == nil {
content.File = &EncryptedFileInfo{}
}
return content.File
}
func (content *MessageEventContent) GetInfo() *FileInfo {
if content.Info == nil {
content.Info = &FileInfo{}
}
return content.Info
}
type EncryptedFileInfo struct {
attachment.EncryptedFile
URL id.ContentURIString `json:"url"`
}
type FileInfo struct {
MimeType string `json:"mimetype,omitempty"`
ThumbnailInfo *FileInfo `json:"thumbnail_info,omitempty"`
ThumbnailURL id.ContentURIString `json:"thumbnail_url,omitempty"`
ThumbnailFile *EncryptedFileInfo `json:"thumbnail_file,omitempty"`
Width int `json:"-"`
Height int `json:"-"`
Duration int `json:"-"`
Size int `json:"-"`
}
type serializableFileInfo struct {
MimeType string `json:"mimetype,omitempty"`
ThumbnailInfo *serializableFileInfo `json:"thumbnail_info,omitempty"`
ThumbnailURL id.ContentURIString `json:"thumbnail_url,omitempty"`
ThumbnailFile *EncryptedFileInfo `json:"thumbnail_file,omitempty"`
Width json.Number `json:"w,omitempty"`
Height json.Number `json:"h,omitempty"`
Duration json.Number `json:"duration,omitempty"`
Size json.Number `json:"size,omitempty"`
}
func (sfi *serializableFileInfo) CopyFrom(fileInfo *FileInfo) *serializableFileInfo {
if fileInfo == nil {
return nil
}
*sfi = serializableFileInfo{
MimeType: fileInfo.MimeType,
ThumbnailURL: fileInfo.ThumbnailURL,
ThumbnailInfo: (&serializableFileInfo{}).CopyFrom(fileInfo.ThumbnailInfo),
ThumbnailFile: fileInfo.ThumbnailFile,
}
if fileInfo.Width > 0 {
sfi.Width = json.Number(strconv.Itoa(fileInfo.Width))
}
if fileInfo.Height > 0 {
sfi.Height = json.Number(strconv.Itoa(fileInfo.Height))
}
if fileInfo.Size > 0 {
sfi.Size = json.Number(strconv.Itoa(fileInfo.Size))
}
if fileInfo.Duration > 0 {
sfi.Duration = json.Number(strconv.Itoa(int(fileInfo.Duration)))
}
return sfi
}
func (sfi *serializableFileInfo) CopyTo(fileInfo *FileInfo) {
*fileInfo = FileInfo{
Width: numberToInt(sfi.Width),
Height: numberToInt(sfi.Height),
Size: numberToInt(sfi.Size),
Duration: numberToInt(sfi.Duration),
MimeType: sfi.MimeType,
ThumbnailURL: sfi.ThumbnailURL,
}
if sfi.ThumbnailInfo != nil {
fileInfo.ThumbnailInfo = &FileInfo{}
sfi.ThumbnailInfo.CopyTo(fileInfo.ThumbnailInfo)
}
}
func (fileInfo *FileInfo) UnmarshalJSON(data []byte) error {
sfi := &serializableFileInfo{}
if err := json.Unmarshal(data, sfi); err != nil {
return err
}
sfi.CopyTo(fileInfo)
return nil
}
func (fileInfo *FileInfo) MarshalJSON() ([]byte, error) {
return json.Marshal((&serializableFileInfo{}).CopyFrom(fileInfo))
}
func numberToInt(val json.Number) int {
f64, _ := val.Float64()
if f64 > 0 {
return int(f64)
}
return 0
}
func (fileInfo *FileInfo) GetThumbnailInfo() *FileInfo {
if fileInfo.ThumbnailInfo == nil {
fileInfo.ThumbnailInfo = &FileInfo{}
}
return fileInfo.ThumbnailInfo
}

128
vendor/maunium.net/go/mautrix/event/powerlevels.go generated vendored Normal file
View File

@@ -0,0 +1,128 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"sync"
"maunium.net/go/mautrix/id"
)
// PowerLevelsEventContent represents the content of a m.room.power_levels state event content.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-power-levels
type PowerLevelsEventContent struct {
usersLock sync.RWMutex `json:"-"`
Users map[id.UserID]int `json:"users"`
UsersDefault int `json:"users_default"`
eventsLock sync.RWMutex `json:"-"`
Events map[string]int `json:"events"`
EventsDefault int `json:"events_default"`
StateDefaultPtr *int `json:"state_default,omitempty"`
InvitePtr *int `json:"invite,omitempty"`
KickPtr *int `json:"kick,omitempty"`
BanPtr *int `json:"ban,omitempty"`
RedactPtr *int `json:"redact,omitempty"`
}
func (pl *PowerLevelsEventContent) Invite() int {
if pl.InvitePtr != nil {
return *pl.InvitePtr
}
return 50
}
func (pl *PowerLevelsEventContent) Kick() int {
if pl.KickPtr != nil {
return *pl.KickPtr
}
return 50
}
func (pl *PowerLevelsEventContent) Ban() int {
if pl.BanPtr != nil {
return *pl.BanPtr
}
return 50
}
func (pl *PowerLevelsEventContent) Redact() int {
if pl.RedactPtr != nil {
return *pl.RedactPtr
}
return 50
}
func (pl *PowerLevelsEventContent) StateDefault() int {
if pl.StateDefaultPtr != nil {
return *pl.StateDefaultPtr
}
return 50
}
func (pl *PowerLevelsEventContent) GetUserLevel(userID id.UserID) int {
pl.usersLock.RLock()
defer pl.usersLock.RUnlock()
level, ok := pl.Users[userID]
if !ok {
return pl.UsersDefault
}
return level
}
func (pl *PowerLevelsEventContent) SetUserLevel(userID id.UserID, level int) {
pl.usersLock.Lock()
defer pl.usersLock.Unlock()
if level == pl.UsersDefault {
delete(pl.Users, userID)
} else {
pl.Users[userID] = level
}
}
func (pl *PowerLevelsEventContent) EnsureUserLevel(userID id.UserID, level int) bool {
existingLevel := pl.GetUserLevel(userID)
if existingLevel != level {
pl.SetUserLevel(userID, level)
return true
}
return false
}
func (pl *PowerLevelsEventContent) GetEventLevel(eventType Type) int {
pl.eventsLock.RLock()
defer pl.eventsLock.RUnlock()
level, ok := pl.Events[eventType.String()]
if !ok {
if eventType.IsState() {
return pl.StateDefault()
}
return pl.EventsDefault
}
return level
}
func (pl *PowerLevelsEventContent) SetEventLevel(eventType Type, level int) {
pl.eventsLock.Lock()
defer pl.eventsLock.Unlock()
if (eventType.IsState() && level == pl.StateDefault()) || (!eventType.IsState() && level == pl.EventsDefault) {
delete(pl.Events, eventType.String())
} else {
pl.Events[eventType.String()] = level
}
}
func (pl *PowerLevelsEventContent) EnsureEventLevel(eventType Type, level int) bool {
existingLevel := pl.GetEventLevel(eventType)
if existingLevel != level {
pl.SetEventLevel(eventType, level)
return true
}
return false
}

200
vendor/maunium.net/go/mautrix/event/relations.go generated vendored Normal file
View File

@@ -0,0 +1,200 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"maunium.net/go/mautrix/id"
)
type RelationType string
const (
RelReplace RelationType = "m.replace"
RelReference RelationType = "m.reference"
RelAnnotation RelationType = "m.annotation"
RelReply RelationType = "net.maunium.reply"
)
type RelatesTo struct {
Type RelationType
EventID id.EventID
Key string
}
type serializableInReplyTo struct {
EventID id.EventID `json:"event_id,omitempty"`
}
type serializableRelatesTo struct {
InReplyTo *serializableInReplyTo `json:"m.in_reply_to,omitempty"`
Type RelationType `json:"rel_type,omitempty"`
EventID id.EventID `json:"event_id,omitempty"`
Key string `json:"key,omitempty"`
}
func (rel *RelatesTo) GetReplaceID() id.EventID {
if rel.Type == RelReplace {
return rel.EventID
}
return ""
}
func (rel *RelatesTo) GetReferenceID() id.EventID {
if rel.Type == RelReference {
return rel.EventID
}
return ""
}
func (rel *RelatesTo) GetReplyID() id.EventID {
if rel.Type == RelReply {
return rel.EventID
}
return ""
}
func (rel *RelatesTo) GetAnnotationID() id.EventID {
if rel.Type == RelAnnotation {
return rel.EventID
}
return ""
}
func (rel *RelatesTo) GetAnnotationKey() string {
if rel.Type == RelAnnotation {
return rel.Key
}
return ""
}
func (rel *RelatesTo) UnmarshalJSON(data []byte) error {
var srel serializableRelatesTo
if err := json.Unmarshal(data, &srel); err != nil {
return err
}
if len(srel.Type) > 0 {
rel.Type = srel.Type
rel.EventID = srel.EventID
rel.Key = srel.Key
} else if srel.InReplyTo != nil && len(srel.InReplyTo.EventID) > 0 {
rel.Type = RelReply
rel.EventID = srel.InReplyTo.EventID
rel.Key = ""
}
return nil
}
func (rel *RelatesTo) MarshalJSON() ([]byte, error) {
srel := serializableRelatesTo{Type: rel.Type, EventID: rel.EventID, Key: rel.Key}
if rel.Type == RelReply {
srel.InReplyTo = &serializableInReplyTo{rel.EventID}
}
return json.Marshal(&srel)
}
type RelationChunkItem struct {
Type RelationType `json:"type"`
EventID string `json:"event_id,omitempty"`
Key string `json:"key,omitempty"`
Count int `json:"count,omitempty"`
}
type RelationChunk struct {
Chunk []RelationChunkItem `json:"chunk"`
Limited bool `json:"limited"`
Count int `json:"count"`
}
type AnnotationChunk struct {
RelationChunk
Map map[string]int `json:"-"`
}
type serializableAnnotationChunk AnnotationChunk
func (ac *AnnotationChunk) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, (*serializableAnnotationChunk)(ac)); err != nil {
return err
}
ac.Map = make(map[string]int)
for _, item := range ac.Chunk {
ac.Map[item.Key] += item.Count
}
return nil
}
func (ac *AnnotationChunk) Serialize() RelationChunk {
ac.Chunk = make([]RelationChunkItem, len(ac.Map))
i := 0
for key, count := range ac.Map {
ac.Chunk[i] = RelationChunkItem{
Type: RelAnnotation,
Key: key,
Count: count,
}
}
return ac.RelationChunk
}
type EventIDChunk struct {
RelationChunk
List []string `json:"-"`
}
type serializableEventIDChunk EventIDChunk
func (ec *EventIDChunk) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, (*serializableEventIDChunk)(ec)); err != nil {
return err
}
for _, item := range ec.Chunk {
ec.List = append(ec.List, item.EventID)
}
return nil
}
func (ec *EventIDChunk) Serialize(typ RelationType) RelationChunk {
ec.Chunk = make([]RelationChunkItem, len(ec.List))
for i, eventID := range ec.List {
ec.Chunk[i] = RelationChunkItem{
Type: typ,
EventID: eventID,
}
}
return ec.RelationChunk
}
type Relations struct {
Raw map[RelationType]RelationChunk `json:"-"`
Annotations AnnotationChunk `json:"m.annotation,omitempty"`
References EventIDChunk `json:"m.reference,omitempty"`
Replaces EventIDChunk `json:"m.replace,omitempty"`
}
type serializableRelations Relations
func (relations *Relations) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &relations.Raw); err != nil {
return err
}
return json.Unmarshal(data, (*serializableRelations)(relations))
}
func (relations *Relations) MarshalJSON() ([]byte, error) {
if relations.Raw == nil {
relations.Raw = make(map[RelationType]RelationChunk)
}
relations.Raw[RelAnnotation] = relations.Annotations.Serialize()
relations.Raw[RelReference] = relations.References.Serialize(RelReference)
relations.Raw[RelReplace] = relations.Replaces.Serialize(RelReplace)
return json.Marshal(relations.Raw)
}

108
vendor/maunium.net/go/mautrix/event/reply.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"fmt"
"regexp"
"strings"
"golang.org/x/net/html"
"maunium.net/go/mautrix/id"
)
var HTMLReplyFallbackRegex = regexp.MustCompile(`^<mx-reply>[\s\S]+?</mx-reply>`)
func TrimReplyFallbackHTML(html string) string {
return HTMLReplyFallbackRegex.ReplaceAllString(html, "")
}
func TrimReplyFallbackText(text string) string {
if !strings.HasPrefix(text, "> ") || !strings.Contains(text, "\n") {
return text
}
lines := strings.Split(text, "\n")
for len(lines) > 0 && strings.HasPrefix(lines[0], "> ") {
lines = lines[1:]
}
return strings.TrimSpace(strings.Join(lines, "\n"))
}
func (content *MessageEventContent) RemoveReplyFallback() {
if len(content.GetReplyTo()) > 0 && !content.replyFallbackRemoved {
if content.Format == FormatHTML {
content.FormattedBody = TrimReplyFallbackHTML(content.FormattedBody)
}
content.Body = TrimReplyFallbackText(content.Body)
content.replyFallbackRemoved = true
}
}
func (content *MessageEventContent) GetReplyTo() id.EventID {
if content.RelatesTo != nil && content.RelatesTo.Type == RelReply {
return content.RelatesTo.EventID
}
return ""
}
const ReplyFormat = `<mx-reply><blockquote><a href="https://matrix.to/#/%s/%s">In reply to</a> <a href="https://matrix.to/#/%s">%s</a><br>%s</blockquote></mx-reply>`
func (evt *Event) GenerateReplyFallbackHTML() string {
parsedContent, ok := evt.Content.Parsed.(*MessageEventContent)
if !ok {
return ""
}
parsedContent.RemoveReplyFallback()
body := parsedContent.FormattedBody
if len(body) == 0 {
body = html.EscapeString(parsedContent.Body)
}
senderDisplayName := evt.Sender
return fmt.Sprintf(ReplyFormat, evt.RoomID, evt.ID, evt.Sender, senderDisplayName, body)
}
func (evt *Event) GenerateReplyFallbackText() string {
parsedContent, ok := evt.Content.Parsed.(*MessageEventContent)
if !ok {
return ""
}
parsedContent.RemoveReplyFallback()
body := parsedContent.Body
lines := strings.Split(strings.TrimSpace(body), "\n")
firstLine, lines := lines[0], lines[1:]
senderDisplayName := evt.Sender
var fallbackText strings.Builder
_, _ = fmt.Fprintf(&fallbackText, "> <%s> %s", senderDisplayName, firstLine)
for _, line := range lines {
_, _ = fmt.Fprintf(&fallbackText, "\n> %s", line)
}
fallbackText.WriteString("\n\n")
return fallbackText.String()
}
func (content *MessageEventContent) SetReply(inReplyTo *Event) {
content.RelatesTo = &RelatesTo{
EventID: inReplyTo.ID,
Type: RelReply,
}
if content.MsgType == MsgText || content.MsgType == MsgNotice {
if len(content.FormattedBody) == 0 || content.Format != FormatHTML {
content.FormattedBody = html.EscapeString(content.Body)
content.Format = FormatHTML
}
content.FormattedBody = inReplyTo.GenerateReplyFallbackHTML() + content.FormattedBody
content.Body = inReplyTo.GenerateReplyFallbackText() + content.Body
content.replyFallbackRemoved = false
}
}

110
vendor/maunium.net/go/mautrix/event/state.go generated vendored Normal file
View File

@@ -0,0 +1,110 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"maunium.net/go/mautrix/id"
)
// CanonicalAliasEventContent represents the content of a m.room.canonical_alias state event.
// https://matrix.org/docs/spec/client_server/r0.6.1#m-room-canonical-alias
type CanonicalAliasEventContent struct {
Alias id.RoomAlias `json:"alias"`
AltAliases []id.RoomAlias `json:"alt_aliases,omitempty"`
}
// RoomNameEventContent represents the content of a m.room.name state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-name
type RoomNameEventContent struct {
Name string `json:"name"`
}
// RoomAvatarEventContent represents the content of a m.room.avatar state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-avatar
type RoomAvatarEventContent struct {
URL id.ContentURI `json:"url"`
}
// TopicEventContent represents the content of a m.room.topic state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-topic
type TopicEventContent struct {
Topic string `json:"topic"`
}
// TombstoneEventContent represents the content of a m.room.tombstone state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-tombstone
type TombstoneEventContent struct {
Body string `json:"body"`
ReplacementRoom id.RoomID `json:"replacement_room"`
}
// CreateEventContent represents the content of a m.room.create state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-create
type CreateEventContent struct {
Creator id.UserID `json:"creator"`
Federate bool `json:"m.federate,omitempty"`
RoomVersion string `json:"version,omitempty"`
Predecessor struct {
RoomID id.RoomID `json:"room_id"`
EventID id.EventID `json:"event_id"`
} `json:"predecessor"`
}
// JoinRule specifies how open a room is to new members.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-join-rules
type JoinRule string
const (
JoinRulePublic JoinRule = "public"
JoinRuleKnock JoinRule = "knock"
JoinRuleInvite JoinRule = "invite"
JoinRulePrivate JoinRule = "private"
)
// JoinRulesEventContent represents the content of a m.room.join_rules state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-join-rules
type JoinRulesEventContent struct {
JoinRule JoinRule `json:"join_rule"`
}
// PinnedEventsEventContent represents the content of a m.room.pinned_events state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-pinned-events
type PinnedEventsEventContent struct {
Pinned []id.EventID `json:"pinned"`
}
// HistoryVisibility specifies who can see new messages.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-history-visibility
type HistoryVisibility string
const (
HistoryVisibilityInvited HistoryVisibility = "invited"
HistoryVisibilityJoined HistoryVisibility = "joined"
HistoryVisibilityShared HistoryVisibility = "shared"
HistoryVisibilityWorldReadable HistoryVisibility = "world_readable"
)
// HistoryVisibilityEventContent represents the content of a m.room.history_visibility state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-history-visibility
type HistoryVisibilityEventContent struct {
HistoryVisibility HistoryVisibility `json:"history_visibility"`
}
// GuestAccess specifies whether or not guest accounts can join.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-guest-access
type GuestAccess string
const (
GuestAccessCanJoin GuestAccess = "can_join"
GuestAccessForbidden GuestAccess = "forbidden"
)
// GuestAccessEventContent represents the content of a m.room.guest_access state event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-room-guest-access
type GuestAccessEventContent struct {
GuestAccess GuestAccess `json:"guest_access"`
}

235
vendor/maunium.net/go/mautrix/event/type.go generated vendored Normal file
View File

@@ -0,0 +1,235 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"fmt"
"strings"
)
type TypeClass int
func (tc TypeClass) Name() string {
switch tc {
case MessageEventType:
return "message"
case StateEventType:
return "state"
case EphemeralEventType:
return "ephemeral"
case AccountDataEventType:
return "account data"
case ToDeviceEventType:
return "to-device"
default:
return "unknown"
}
}
const (
// Unknown events
UnknownEventType TypeClass = iota
// Normal message events
MessageEventType
// State events
StateEventType
// Ephemeral events
EphemeralEventType
// Account data events
AccountDataEventType
// Device-to-device events
ToDeviceEventType
)
type Type struct {
Type string
Class TypeClass
}
func NewEventType(name string) Type {
evtType := Type{Type: name}
evtType.Class = evtType.GuessClass()
return evtType
}
func (et *Type) IsState() bool {
return et.Class == StateEventType
}
func (et *Type) IsEphemeral() bool {
return et.Class == EphemeralEventType
}
func (et *Type) IsAccountData() bool {
return et.Class == AccountDataEventType
}
func (et *Type) IsToDevice() bool {
return et.Class == ToDeviceEventType
}
func (et *Type) IsInRoomVerification() bool {
switch et.Type {
case InRoomVerificationStart.Type, InRoomVerificationReady.Type, InRoomVerificationAccept.Type,
InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type:
return true
default:
return false
}
}
func (et *Type) IsCall() bool {
switch et.Type {
case CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type,
CallNegotiate.Type, CallHangup.Type:
return true
default:
return false
}
}
func (et *Type) IsCustom() bool {
return !strings.HasPrefix(et.Type, "m.")
}
func (et *Type) GuessClass() TypeClass {
switch et.Type {
case StateAliases.Type, StateCanonicalAlias.Type, StateCreate.Type, StateJoinRules.Type, StateMember.Type,
StatePowerLevels.Type, StateRoomName.Type, StateRoomAvatar.Type, StateTopic.Type, StatePinnedEvents.Type,
StateTombstone.Type, StateEncryption.Type:
return StateEventType
case EphemeralEventReceipt.Type, EphemeralEventTyping.Type, EphemeralEventPresence.Type:
return EphemeralEventType
case AccountDataDirectChats.Type, AccountDataPushRules.Type, AccountDataRoomTags.Type,
AccountDataSecretStorageKey.Type, AccountDataSecretStorageDefaultKey.Type,
AccountDataCrossSigningMaster.Type, AccountDataCrossSigningSelf.Type, AccountDataCrossSigningUser.Type:
return AccountDataEventType
case EventRedaction.Type, EventMessage.Type, EventEncrypted.Type, EventReaction.Type, EventSticker.Type,
InRoomVerificationStart.Type, InRoomVerificationReady.Type, InRoomVerificationAccept.Type,
InRoomVerificationKey.Type, InRoomVerificationMAC.Type, InRoomVerificationCancel.Type,
CallInvite.Type, CallCandidates.Type, CallAnswer.Type, CallReject.Type, CallSelectAnswer.Type,
CallNegotiate.Type, CallHangup.Type:
return MessageEventType
case ToDeviceRoomKey.Type, ToDeviceRoomKeyRequest.Type, ToDeviceForwardedRoomKey.Type, ToDeviceRoomKeyWithheld.Type:
return ToDeviceEventType
default:
return UnknownEventType
}
}
func (et *Type) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, &et.Type)
if err != nil {
return err
}
et.Class = et.GuessClass()
return nil
}
func (et *Type) MarshalJSON() ([]byte, error) {
return json.Marshal(&et.Type)
}
func (et Type) UnmarshalText(data []byte) error {
et.Type = string(data)
et.Class = et.GuessClass()
return nil
}
func (et Type) MarshalText() ([]byte, error) {
return []byte(et.Type), nil
}
func (et *Type) String() string {
return et.Type
}
func (et *Type) Repr() string {
return fmt.Sprintf("%s (%s)", et.Type, et.Class.Name())
}
// State events
var (
StateAliases = Type{"m.room.aliases", StateEventType}
StateCanonicalAlias = Type{"m.room.canonical_alias", StateEventType}
StateCreate = Type{"m.room.create", StateEventType}
StateJoinRules = Type{"m.room.join_rules", StateEventType}
StateHistoryVisibility = Type{"m.room.history_visibility", StateEventType}
StateGuestAccess = Type{"m.room.guest_access", StateEventType}
StateMember = Type{"m.room.member", StateEventType}
StatePowerLevels = Type{"m.room.power_levels", StateEventType}
StateRoomName = Type{"m.room.name", StateEventType}
StateTopic = Type{"m.room.topic", StateEventType}
StateRoomAvatar = Type{"m.room.avatar", StateEventType}
StatePinnedEvents = Type{"m.room.pinned_events", StateEventType}
StateTombstone = Type{"m.room.tombstone", StateEventType}
StateEncryption = Type{"m.room.encryption", StateEventType}
)
// Message events
var (
EventRedaction = Type{"m.room.redaction", MessageEventType}
EventMessage = Type{"m.room.message", MessageEventType}
EventEncrypted = Type{"m.room.encrypted", MessageEventType}
EventReaction = Type{"m.reaction", MessageEventType}
EventSticker = Type{"m.sticker", MessageEventType}
InRoomVerificationStart = Type{"m.key.verification.start", MessageEventType}
InRoomVerificationReady = Type{"m.key.verification.ready", MessageEventType}
InRoomVerificationAccept = Type{"m.key.verification.accept", MessageEventType}
InRoomVerificationKey = Type{"m.key.verification.key", MessageEventType}
InRoomVerificationMAC = Type{"m.key.verification.mac", MessageEventType}
InRoomVerificationCancel = Type{"m.key.verification.cancel", MessageEventType}
CallInvite = Type{"m.call.invite", MessageEventType}
CallCandidates = Type{"m.call.candidates", MessageEventType}
CallAnswer = Type{"m.call.answer", MessageEventType}
CallReject = Type{"m.call.reject", MessageEventType}
CallSelectAnswer = Type{"m.call.select_answer", MessageEventType}
CallNegotiate = Type{"m.call.negotiate", MessageEventType}
CallHangup = Type{"m.call.hangup", MessageEventType}
)
// Ephemeral events
var (
EphemeralEventReceipt = Type{"m.receipt", EphemeralEventType}
EphemeralEventTyping = Type{"m.typing", EphemeralEventType}
EphemeralEventPresence = Type{"m.presence", EphemeralEventType}
)
// Account data events
var (
AccountDataDirectChats = Type{"m.direct", AccountDataEventType}
AccountDataPushRules = Type{"m.push_rules", AccountDataEventType}
AccountDataRoomTags = Type{"m.tag", AccountDataEventType}
AccountDataFullyRead = Type{"m.fully_read", AccountDataEventType}
AccountDataIgnoredUserList = Type{"m.ignored_user_list", AccountDataEventType}
AccountDataSecretStorageDefaultKey = Type{"m.secret_storage.default_key", AccountDataEventType}
AccountDataSecretStorageKey = Type{"m.secret_storage.key", AccountDataEventType}
AccountDataCrossSigningMaster = Type{"m.cross_signing.master", AccountDataEventType}
AccountDataCrossSigningUser = Type{"m.cross_signing.user_signing", AccountDataEventType}
AccountDataCrossSigningSelf = Type{"m.cross_signing.self_signing", AccountDataEventType}
)
// Device-to-device events
var (
ToDeviceRoomKey = Type{"m.room_key", ToDeviceEventType}
ToDeviceRoomKeyRequest = Type{"m.room_key_request", ToDeviceEventType}
ToDeviceForwardedRoomKey = Type{"m.forwarded_room_key", ToDeviceEventType}
ToDeviceEncrypted = Type{"m.room.encrypted", ToDeviceEventType}
ToDeviceRoomKeyWithheld = Type{"m.room_key.withheld", ToDeviceEventType}
ToDeviceVerificationRequest = Type{"m.key.verification.request", ToDeviceEventType}
ToDeviceVerificationStart = Type{"m.key.verification.start", ToDeviceEventType}
ToDeviceVerificationAccept = Type{"m.key.verification.accept", ToDeviceEventType}
ToDeviceVerificationKey = Type{"m.key.verification.key", ToDeviceEventType}
ToDeviceVerificationMAC = Type{"m.key.verification.mac", ToDeviceEventType}
ToDeviceVerificationCancel = Type{"m.key.verification.cancel", ToDeviceEventType}
ToDeviceOrgMatrixRoomKeyWithheld = Type{"org.matrix.room_key.withheld", ToDeviceEventType}
)

306
vendor/maunium.net/go/mautrix/event/verification.go generated vendored Normal file
View File

@@ -0,0 +1,306 @@
// Copyright (c) 2020 Nikos Filippakis
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"maunium.net/go/mautrix/id"
)
type VerificationMethod string
const VerificationMethodSAS VerificationMethod = "m.sas.v1"
// VerificationRequestEventContent represents the content of a m.key.verification.request to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-key-verification-request
type VerificationRequestEventContent struct {
// The device ID which is initiating the request.
FromDevice id.DeviceID `json:"from_device"`
// An opaque identifier for the verification request. Must be unique with respect to the devices involved.
TransactionID string `json:"transaction_id,omitempty"`
// The verification methods supported by the sender.
Methods []VerificationMethod `json:"methods"`
// The POSIX timestamp in milliseconds for when the request was made.
Timestamp int64 `json:"timestamp,omitempty"`
// The user that the event is sent to for in-room verification.
To id.UserID `json:"to,omitempty"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
func (vrec *VerificationRequestEventContent) SupportsVerificationMethod(meth VerificationMethod) bool {
for _, supportedMeth := range vrec.Methods {
if supportedMeth == meth {
return true
}
}
return false
}
type KeyAgreementProtocol string
const (
KeyAgreementCurve25519 KeyAgreementProtocol = "curve25519"
KeyAgreementCurve25519HKDFSHA256 KeyAgreementProtocol = "curve25519-hkdf-sha256"
)
type VerificationHashMethod string
const VerificationHashSHA256 VerificationHashMethod = "sha256"
type MACMethod string
const HKDFHMACSHA256 MACMethod = "hkdf-hmac-sha256"
type SASMethod string
const (
SASDecimal SASMethod = "decimal"
SASEmoji SASMethod = "emoji"
)
// VerificationStartEventContent represents the content of a m.key.verification.start to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-key-verification-start
type VerificationStartEventContent struct {
// The device ID which is initiating the process.
FromDevice id.DeviceID `json:"from_device"`
// An opaque identifier for the verification process. Must be unique with respect to the devices involved.
TransactionID string `json:"transaction_id,omitempty"`
// The verification method to use.
Method VerificationMethod `json:"method"`
// The key agreement protocols the sending device understands.
KeyAgreementProtocols []KeyAgreementProtocol `json:"key_agreement_protocols"`
// The hash methods the sending device understands.
Hashes []VerificationHashMethod `json:"hashes"`
// The message authentication codes that the sending device understands.
MessageAuthenticationCodes []MACMethod `json:"message_authentication_codes"`
// The SAS methods the sending device (and the sending device's user) understands.
ShortAuthenticationString []SASMethod `json:"short_authentication_string"`
// The user that the event is sent to for in-room verification.
To id.UserID `json:"to,omitempty"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
func (vsec *VerificationStartEventContent) SupportsKeyAgreementProtocol(proto KeyAgreementProtocol) bool {
for _, supportedProto := range vsec.KeyAgreementProtocols {
if supportedProto == proto {
return true
}
}
return false
}
func (vsec *VerificationStartEventContent) SupportsHashMethod(alg VerificationHashMethod) bool {
for _, supportedAlg := range vsec.Hashes {
if supportedAlg == alg {
return true
}
}
return false
}
func (vsec *VerificationStartEventContent) SupportsMACMethod(meth MACMethod) bool {
for _, supportedMeth := range vsec.MessageAuthenticationCodes {
if supportedMeth == meth {
return true
}
}
return false
}
func (vsec *VerificationStartEventContent) SupportsSASMethod(meth SASMethod) bool {
for _, supportedMeth := range vsec.ShortAuthenticationString {
if supportedMeth == meth {
return true
}
}
return false
}
func (vsec *VerificationStartEventContent) GetRelatesTo() *RelatesTo {
if vsec.RelatesTo == nil {
vsec.RelatesTo = &RelatesTo{}
}
return vsec.RelatesTo
}
func (vsec *VerificationStartEventContent) OptionalGetRelatesTo() *RelatesTo {
return vsec.RelatesTo
}
func (vsec *VerificationStartEventContent) SetRelatesTo(rel *RelatesTo) {
vsec.RelatesTo = rel
}
// VerificationReadyEventContent represents the content of a m.key.verification.ready event.
type VerificationReadyEventContent struct {
// The device ID which accepted the process.
FromDevice id.DeviceID `json:"from_device"`
// The verification methods supported by the sender.
Methods []VerificationMethod `json:"methods"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
var _ Relatable = (*VerificationReadyEventContent)(nil)
func (vrec *VerificationReadyEventContent) GetRelatesTo() *RelatesTo {
if vrec.RelatesTo == nil {
vrec.RelatesTo = &RelatesTo{}
}
return vrec.RelatesTo
}
func (vrec *VerificationReadyEventContent) OptionalGetRelatesTo() *RelatesTo {
return vrec.RelatesTo
}
func (vrec *VerificationReadyEventContent) SetRelatesTo(rel *RelatesTo) {
vrec.RelatesTo = rel
}
// VerificationAcceptEventContent represents the content of a m.key.verification.accept to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-key-verification-accept
type VerificationAcceptEventContent struct {
// An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.
TransactionID string `json:"transaction_id,omitempty"`
// The verification method to use.
Method VerificationMethod `json:"method"`
// The key agreement protocol the device is choosing to use, out of the options in the m.key.verification.start message.
KeyAgreementProtocol KeyAgreementProtocol `json:"key_agreement_protocol"`
// The hash method the device is choosing to use, out of the options in the m.key.verification.start message.
Hash VerificationHashMethod `json:"hash"`
// The message authentication code the device is choosing to use, out of the options in the m.key.verification.start message.
MessageAuthenticationCode MACMethod `json:"message_authentication_code"`
// The SAS methods both devices involved in the verification process understand. Must be a subset of the options in the m.key.verification.start message.
ShortAuthenticationString []SASMethod `json:"short_authentication_string"`
// The hash (encoded as unpadded base64) of the concatenation of the device's ephemeral public key (encoded as unpadded base64) and the canonical JSON representation of the m.key.verification.start message.
Commitment string `json:"commitment"`
// The user that the event is sent to for in-room verification.
To id.UserID `json:"to,omitempty"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
func (vaec *VerificationAcceptEventContent) GetRelatesTo() *RelatesTo {
if vaec.RelatesTo == nil {
vaec.RelatesTo = &RelatesTo{}
}
return vaec.RelatesTo
}
func (vaec *VerificationAcceptEventContent) OptionalGetRelatesTo() *RelatesTo {
return vaec.RelatesTo
}
func (vaec *VerificationAcceptEventContent) SetRelatesTo(rel *RelatesTo) {
vaec.RelatesTo = rel
}
// VerificationKeyEventContent represents the content of a m.key.verification.key to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-key-verification-key
type VerificationKeyEventContent struct {
// An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.
TransactionID string `json:"transaction_id,omitempty"`
// The device's ephemeral public key, encoded as unpadded base64.
Key string `json:"key"`
// The user that the event is sent to for in-room verification.
To id.UserID `json:"to,omitempty"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
func (vkec *VerificationKeyEventContent) GetRelatesTo() *RelatesTo {
if vkec.RelatesTo == nil {
vkec.RelatesTo = &RelatesTo{}
}
return vkec.RelatesTo
}
func (vkec *VerificationKeyEventContent) OptionalGetRelatesTo() *RelatesTo {
return vkec.RelatesTo
}
func (vkec *VerificationKeyEventContent) SetRelatesTo(rel *RelatesTo) {
vkec.RelatesTo = rel
}
// VerificationMacEventContent represents the content of a m.key.verification.mac to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-key-verification-mac
type VerificationMacEventContent struct {
// An opaque identifier for the verification process. Must be the same as the one used for the m.key.verification.start message.
TransactionID string `json:"transaction_id,omitempty"`
// A map of the key ID to the MAC of the key, using the algorithm in the verification process. The MAC is encoded as unpadded base64.
Mac map[id.KeyID]string `json:"mac"`
// The MAC of the comma-separated, sorted, list of key IDs given in the mac property, encoded as unpadded base64.
Keys string `json:"keys"`
// The user that the event is sent to for in-room verification.
To id.UserID `json:"to,omitempty"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
func (vmec *VerificationMacEventContent) GetRelatesTo() *RelatesTo {
if vmec.RelatesTo == nil {
vmec.RelatesTo = &RelatesTo{}
}
return vmec.RelatesTo
}
func (vmec *VerificationMacEventContent) OptionalGetRelatesTo() *RelatesTo {
return vmec.RelatesTo
}
func (vmec *VerificationMacEventContent) SetRelatesTo(rel *RelatesTo) {
vmec.RelatesTo = rel
}
type VerificationCancelCode string
const (
VerificationCancelByUser VerificationCancelCode = "m.user"
VerificationCancelByTimeout VerificationCancelCode = "m.timeout"
VerificationCancelUnknownTransaction VerificationCancelCode = "m.unknown_transaction"
VerificationCancelUnknownMethod VerificationCancelCode = "m.unknown_method"
VerificationCancelUnexpectedMessage VerificationCancelCode = "m.unexpected_message"
VerificationCancelKeyMismatch VerificationCancelCode = "m.key_mismatch"
VerificationCancelUserMismatch VerificationCancelCode = "m.user_mismatch"
VerificationCancelInvalidMessage VerificationCancelCode = "m.invalid_message"
VerificationCancelAccepted VerificationCancelCode = "m.accepted"
VerificationCancelSASMismatch VerificationCancelCode = "m.mismatched_sas"
VerificationCancelCommitmentMismatch VerificationCancelCode = "m.mismatched_commitment"
)
// VerificationCancelEventContent represents the content of a m.key.verification.cancel to_device event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-key-verification-cancel
type VerificationCancelEventContent struct {
// The opaque identifier for the verification process/request.
TransactionID string `json:"transaction_id,omitempty"`
// A human readable description of the code. The client should only rely on this string if it does not understand the code.
Reason string `json:"reason"`
// The error code for why the process/request was cancelled by the user.
Code VerificationCancelCode `json:"code"`
// The user that the event is sent to for in-room verification.
To id.UserID `json:"to,omitempty"`
// Original event ID for in-room verification.
RelatesTo *RelatesTo `json:"m.relates_to,omitempty"`
}
func (vcec *VerificationCancelEventContent) GetRelatesTo() *RelatesTo {
if vcec.RelatesTo == nil {
vcec.RelatesTo = &RelatesTo{}
}
return vcec.RelatesTo
}
func (vcec *VerificationCancelEventContent) OptionalGetRelatesTo() *RelatesTo {
return vcec.RelatesTo
}
func (vcec *VerificationCancelEventContent) SetRelatesTo(rel *RelatesTo) {
vcec.RelatesTo = rel
}

116
vendor/maunium.net/go/mautrix/event/voip.go generated vendored Normal file
View File

@@ -0,0 +1,116 @@
// Copyright (c) 2021 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package event
import (
"encoding/json"
"fmt"
"strconv"
)
type CallHangupReason string
const (
CallHangupICEFailed CallHangupReason = "ice_failed"
CallHangupInviteTimeout CallHangupReason = "invite_timeout"
CallHangupUserHangup CallHangupReason = "user_hangup"
CallHangupUserMediaFailed CallHangupReason = "user_media_failed"
CallHangupUnknownError CallHangupReason = "unknown_error"
)
type CallDataType string
const (
CallDataTypeOffer CallDataType = "offer"
CallDataTypeAnswer CallDataType = "answer"
)
type CallData struct {
SDP string `json:"sdp"`
Type CallDataType `json:"type"`
}
type CallCandidate struct {
Candidate string `json:"candidate"`
SDPMLineIndex int `json:"sdpMLineIndex"`
SDPMID string `json:"sdpMid"`
}
type CallVersion string
func (cv *CallVersion) UnmarshalJSON(raw []byte) error {
var numberVersion int
err := json.Unmarshal(raw, &numberVersion)
if err != nil {
var stringVersion string
err = json.Unmarshal(raw, &stringVersion)
if err != nil {
return fmt.Errorf("failed to parse CallVersion: %w", err)
}
*cv = CallVersion(stringVersion)
} else {
*cv = CallVersion(strconv.Itoa(numberVersion))
}
return nil
}
func (cv *CallVersion) MarshalJSON() ([]byte, error) {
for _, char := range *cv {
if char < '0' || char > '9' {
// The version contains weird characters, return as string.
return json.Marshal(string(*cv))
}
}
// The version consists of only ASCII digits, return as an integer.
return []byte(*cv), nil
}
func (cv *CallVersion) Int() (int, error) {
return strconv.Atoi(string(*cv))
}
type BaseCallEventContent struct {
CallID string `json:"call_id"`
PartyID string `json:"party_id"`
Version CallVersion `json:"version"`
}
type CallInviteEventContent struct {
BaseCallEventContent
Lifetime int `json:"lifetime"`
Offer CallData `json:"offer"`
}
type CallCandidatesEventContent struct {
BaseCallEventContent
Candidates []CallCandidate `json:"candidates"`
}
type CallRejectEventContent struct {
BaseCallEventContent
}
type CallAnswerEventContent struct {
BaseCallEventContent
Answer CallData `json:"answer"`
}
type CallSelectAnswerEventContent struct {
BaseCallEventContent
SelectedPartyID string `json:"selected_party_id"`
}
type CallNegotiateEventContent struct {
BaseCallEventContent
Lifetime int `json:"lifetime"`
Description CallData `json:"description"`
}
type CallHangupEventContent struct {
BaseCallEventContent
Reason CallHangupReason `json:"reason"`
}

93
vendor/maunium.net/go/mautrix/filter.go generated vendored Normal file
View File

@@ -0,0 +1,93 @@
// Copyright 2017 Jan Christian Grünhage
package mautrix
import (
"errors"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
type EventFormat string
const (
EventFormatClient EventFormat = "client"
EventFormatFederation EventFormat = "federation"
)
//Filter is used by clients to specify how the server should filter responses to e.g. sync requests
//Specified by: https://matrix.org/docs/spec/client_server/r0.6.0.html#filtering
type Filter struct {
AccountData FilterPart `json:"account_data,omitempty"`
EventFields []string `json:"event_fields,omitempty"`
EventFormat EventFormat `json:"event_format,omitempty"`
Presence FilterPart `json:"presence,omitempty"`
Room RoomFilter `json:"room,omitempty"`
}
// RoomFilter is used to define filtering rules for room events
type RoomFilter struct {
AccountData FilterPart `json:"account_data,omitempty"`
Ephemeral FilterPart `json:"ephemeral,omitempty"`
IncludeLeave bool `json:"include_leave,omitempty"`
NotRooms []id.RoomID `json:"not_rooms,omitempty"`
Rooms []id.RoomID `json:"rooms,omitempty"`
State FilterPart `json:"state,omitempty"`
Timeline FilterPart `json:"timeline,omitempty"`
}
// FilterPart is used to define filtering rules for specific categories of events
type FilterPart struct {
NotRooms []id.RoomID `json:"not_rooms,omitempty"`
Rooms []id.RoomID `json:"rooms,omitempty"`
Limit int `json:"limit,omitempty"`
NotSenders []id.UserID `json:"not_senders,omitempty"`
NotTypes []event.Type `json:"not_types,omitempty"`
Senders []id.UserID `json:"senders,omitempty"`
Types []event.Type `json:"types,omitempty"`
ContainsURL *bool `json:"contains_url,omitempty"`
LazyLoadMembers bool `json:"lazy_load_members,omitempty"`
IncludeRedundantMembers bool `json:"include_redundant_members,omitempty"`
}
// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
if filter.EventFormat != EventFormatClient && filter.EventFormat != EventFormatFederation {
return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
}
return nil
}
// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in the request
func DefaultFilter() Filter {
return Filter{
AccountData: DefaultFilterPart(),
EventFields: nil,
EventFormat: "client",
Presence: DefaultFilterPart(),
Room: RoomFilter{
AccountData: DefaultFilterPart(),
Ephemeral: DefaultFilterPart(),
IncludeLeave: false,
NotRooms: nil,
Rooms: nil,
State: DefaultFilterPart(),
Timeline: DefaultFilterPart(),
},
}
}
// DefaultFilterPart returns the default filter part used by the Matrix server if no filter is provided in the request
func DefaultFilterPart() FilterPart {
return FilterPart{
NotRooms: nil,
Rooms: nil,
Limit: 20,
NotSenders: nil,
NotTypes: nil,
Senders: nil,
Types: nil,
}
}

19
vendor/maunium.net/go/mautrix/go.mod generated vendored Normal file
View File

@@ -0,0 +1,19 @@
module maunium.net/go/mautrix
go 1.14
require (
github.com/btcsuite/btcutil v1.0.2
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2
github.com/lib/pq v1.9.0
github.com/mattn/go-sqlite3 v1.14.6
github.com/russross/blackfriday/v2 v2.1.0
github.com/stretchr/testify v1.6.1
github.com/tidwall/gjson v1.6.8
github.com/tidwall/sjson v1.1.5
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d
gopkg.in/yaml.v2 v2.3.0
maunium.net/go/maulogger/v2 v2.2.4
)

77
vendor/maunium.net/go/mautrix/go.sum generated vendored Normal file
View File

@@ -0,0 +1,77 @@
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
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/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w=
github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.1.5 h1:wsUceI/XDyZk3J1FUvuuYlK62zJv2HO2Pzb8A5EWdUE=
github.com/tidwall/sjson v1.1.5/go.mod h1:VuJzsZnTowhSxWdOgsAnb886i4AjEyTkk7tNtsL7EYE=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d h1:1aflnvSoWWLI2k/dMUAl5lvU1YO4Mb4hz0gh+1rjcxU=
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/maulogger/v2 v2.2.4 h1:oV2GDeM4fx1uRysdpDC0FcrPg+thFicSd9XzPcYMbVY=
maunium.net/go/maulogger/v2 v2.2.4/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=

133
vendor/maunium.net/go/mautrix/id/contenturi.go generated vendored Normal file
View File

@@ -0,0 +1,133 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package id
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"strings"
)
var (
InvalidContentURI = errors.New("invalid Matrix content URI")
InputNotJSONString = errors.New("input doesn't look like a JSON string")
)
// ContentURIString is a string that's expected to be a Matrix content URI.
// It's useful for delaying the parsing of the content URI to move errors from the event content
// JSON parsing step to a later step where more appropriate errors can be produced.
type ContentURIString string
func (uriString ContentURIString) Parse() (ContentURI, error) {
return ParseContentURI(string(uriString))
}
func (uriString ContentURIString) ParseOrIgnore() ContentURI {
parsed, _ := ParseContentURI(string(uriString))
return parsed
}
// ContentURI represents a Matrix content URI.
// https://matrix.org/docs/spec/client_server/r0.6.0#matrix-content-mxc-uris
type ContentURI struct {
Homeserver string
FileID string
}
func MustParseContentURI(uri string) ContentURI {
parsed, err := ParseContentURI(uri)
if err != nil {
panic(err)
}
return parsed
}
// ParseContentURI parses a Matrix content URI.
func ParseContentURI(uri string) (parsed ContentURI, err error) {
if len(uri) == 0 {
return
} else if !strings.HasPrefix(uri, "mxc://") {
err = InvalidContentURI
} else if index := strings.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 {
err = InvalidContentURI
} else {
parsed.Homeserver = uri[6 : 6+index]
parsed.FileID = uri[6+index+1:]
}
return
}
var mxcBytes = []byte("mxc://")
func ParseContentURIBytes(uri []byte) (parsed ContentURI, err error) {
if len(uri) == 0 {
return
} else if !bytes.HasPrefix(uri, mxcBytes) {
err = InvalidContentURI
} else if index := bytes.IndexRune(uri[6:], '/'); index == -1 || index == len(uri)-7 {
err = InvalidContentURI
} else {
parsed.Homeserver = string(uri[6 : 6+index])
parsed.FileID = string(uri[6+index+1:])
}
return
}
func (uri *ContentURI) UnmarshalJSON(raw []byte) (err error) {
if string(raw) == "null" {
*uri = ContentURI{}
return nil
} else if len(raw) < 2 || raw[0] != '"' || raw[len(raw)-1] != '"' {
return InputNotJSONString
}
parsed, err := ParseContentURIBytes(raw[1:len(raw)-1])
if err != nil {
return err
}
*uri = parsed
return nil
}
func (uri *ContentURI) MarshalJSON() ([]byte, error) {
if uri.IsEmpty() {
return []byte("null"), nil
}
return json.Marshal(uri.String())
}
func (uri *ContentURI) UnmarshalText(raw []byte) (err error) {
parsed, err := ParseContentURIBytes(raw)
if err != nil {
return err
}
*uri = parsed
return nil
}
func (uri ContentURI) MarshalText() ([]byte, error) {
if uri.IsEmpty() {
return []byte(""), nil
}
return []byte(uri.String()), nil
}
func (uri *ContentURI) String() string {
if uri.IsEmpty() {
return ""
}
return fmt.Sprintf("mxc://%s/%s", uri.Homeserver, uri.FileID)
}
func (uri *ContentURI) CUString() ContentURIString {
return ContentURIString(uri.String())
}
func (uri *ContentURI) IsEmpty() bool {
return len(uri.Homeserver) == 0 || len(uri.FileID) == 0
}

114
vendor/maunium.net/go/mautrix/id/crypto.go generated vendored Normal file
View File

@@ -0,0 +1,114 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package id
import (
"fmt"
"strings"
)
// OlmMsgType is an Olm message type
type OlmMsgType int
const (
OlmMsgTypePreKey OlmMsgType = 0
OlmMsgTypeMsg OlmMsgType = 1
)
// Algorithm is a Matrix message encryption algorithm.
// https://matrix.org/docs/spec/client_server/r0.6.0#messaging-algorithm-names
type Algorithm string
const (
AlgorithmOlmV1 Algorithm = "m.olm.v1.curve25519-aes-sha2"
AlgorithmMegolmV1 Algorithm = "m.megolm.v1.aes-sha2"
)
type KeyAlgorithm string
const (
KeyAlgorithmCurve25519 KeyAlgorithm = "curve25519"
KeyAlgorithmEd25519 KeyAlgorithm = "ed25519"
KeyAlgorithmSignedCurve25519 KeyAlgorithm = "signed_curve25519"
)
type CrossSigningUsage string
const (
XSUsageMaster CrossSigningUsage = "master"
XSUsageSelfSigning CrossSigningUsage = "self_signing"
XSUsageUserSigning CrossSigningUsage = "user_signing"
)
// A SessionID is an arbitrary string that identifies an Olm or Megolm session.
type SessionID string
func (sessionID SessionID) String() string {
return string(sessionID)
}
// Ed25519 is the base64 representation of an Ed25519 public key
type Ed25519 string
type SigningKey = Ed25519
func (ed25519 Ed25519) String() string {
return string(ed25519)
}
// Curve25519 is the base64 representation of an Curve25519 public key
type Curve25519 string
type SenderKey = Curve25519
type IdentityKey = Curve25519
func (curve25519 Curve25519) String() string {
return string(curve25519)
}
// A DeviceID is an arbitrary string that references a specific device.
type DeviceID string
func (deviceID DeviceID) String() string {
return string(deviceID)
}
// A DeviceKeyID is a string formatted as <algorithm>:<device_id> that is used as the key in deviceid-key mappings.
type DeviceKeyID string
func NewDeviceKeyID(algorithm KeyAlgorithm, deviceID DeviceID) DeviceKeyID {
return DeviceKeyID(fmt.Sprintf("%s:%s", algorithm, deviceID))
}
func (deviceKeyID DeviceKeyID) String() string {
return string(deviceKeyID)
}
func (deviceKeyID DeviceKeyID) Parse() (Algorithm, DeviceID) {
index := strings.IndexRune(string(deviceKeyID), ':')
if index < 0 || len(deviceKeyID) <= index+1 {
return "", ""
}
return Algorithm(deviceKeyID[:index]), DeviceID(deviceKeyID[index+1:])
}
// A KeyID a string formatted as <keyalgorithm>:<key_id> that is used as the key in one-time-key mappings.
type KeyID string
func NewKeyID(algorithm KeyAlgorithm, keyID string) KeyID {
return KeyID(fmt.Sprintf("%s:%s", algorithm, keyID))
}
func (keyID KeyID) String() string {
return string(keyID)
}
func (keyID KeyID) Parse() (KeyAlgorithm, string) {
index := strings.IndexRune(string(keyID), ':')
if index < 0 || len(keyID) <= index+1 {
return "", ""
}
return KeyAlgorithm(keyID[:index]), string(keyID[index+1:])
}

293
vendor/maunium.net/go/mautrix/id/matrixuri.go generated vendored Normal file
View File

@@ -0,0 +1,293 @@
// Copyright (c) 2021 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package id
import (
"errors"
"fmt"
"net/url"
"strings"
)
// Errors that can happen when parsing matrix: URIs
var (
ErrInvalidScheme = errors.New("matrix URI scheme must be exactly 'matrix'")
ErrInvalidPartCount = errors.New("matrix URIs must have exactly 2 or 4 segments")
ErrInvalidFirstSegment = errors.New("invalid identifier in first segment of matrix URI")
ErrEmptySecondSegment = errors.New("the second segment of the matrix URI must not be empty")
ErrInvalidThirdSegment = errors.New("invalid identifier in third segment of matrix URI")
ErrEmptyFourthSegment = errors.New("the fourth segment of the matrix URI must not be empty when the third segment is present")
)
// Errors that can happen when parsing matrix.to URLs
var (
ErrNotMatrixTo = errors.New("that URL is not a matrix.to URL")
ErrInvalidMatrixToPartCount = errors.New("matrix.to URLs must have exactly 1 or 2 segments")
ErrEmptyMatrixToPrimaryIdentifier = errors.New("the primary identifier in the matrix.to URL is empty")
ErrInvalidMatrixToPrimaryIdentifier = errors.New("the primary identifier in the matrix.to URL has an invalid sigil")
ErrInvalidMatrixToSecondaryIdentifier = errors.New("the secondary identifier in the matrix.to URL has an invalid sigil")
)
var ErrNotMatrixToOrMatrixURI = errors.New("that URL is not a matrix.to URL nor matrix: URI")
// MatrixURI contains the result of parsing a matrix: URI using ParseMatrixURI
type MatrixURI struct {
Sigil1 rune
Sigil2 rune
MXID1 string
MXID2 string
Via []string
Action string
}
// SigilToPathSegment contains a mapping from Matrix identifier sigils to matrix: URI path segments.
var SigilToPathSegment = map[rune]string{
'$': "e",
'#': "r",
'!': "roomid",
'@': "u",
}
func (uri *MatrixURI) getQuery() url.Values {
q := make(url.Values)
if uri.Via != nil && len(uri.Via) > 0 {
q["via"] = uri.Via
}
if len(uri.Action) > 0 {
q.Set("action", uri.Action)
}
return q
}
// String converts the parsed matrix: URI back into the string representation.
func (uri *MatrixURI) String() string {
parts := []string{
SigilToPathSegment[uri.Sigil1],
uri.MXID1,
}
if uri.Sigil2 != 0 {
parts = append(parts, SigilToPathSegment[uri.Sigil2], uri.MXID2)
}
return (&url.URL{
Scheme: "matrix",
Opaque: strings.Join(parts, "/"),
RawQuery: uri.getQuery().Encode(),
}).String()
}
// MatrixToURL converts to parsed matrix: URI into a matrix.to URL
func (uri *MatrixURI) MatrixToURL() string {
fragment := fmt.Sprintf("#/%s", url.QueryEscape(uri.PrimaryIdentifier()))
if uri.Sigil2 != 0 {
fragment = fmt.Sprintf("%s/%s", fragment, url.QueryEscape(uri.SecondaryIdentifier()))
}
query := uri.getQuery().Encode()
if len(query) > 0 {
fragment = fmt.Sprintf("%s?%s", fragment, query)
}
// It would be nice to use URL{...}.String() here, but figuring out the Fragment vs RawFragment stuff is a pain
return fmt.Sprintf("https://matrix.to/%s", fragment)
}
// PrimaryIdentifier returns the first Matrix identifier in the URI.
// Currently room IDs, room aliases and user IDs can be in the primary identifier slot.
func (uri *MatrixURI) PrimaryIdentifier() string {
return fmt.Sprintf("%c%s", uri.Sigil1, uri.MXID1)
}
// SecondaryIdentifier returns the second Matrix identifier in the URI.
// Currently only event IDs can be in the secondary identifier slot.
func (uri *MatrixURI) SecondaryIdentifier() string {
if uri.Sigil2 == 0 {
return ""
}
return fmt.Sprintf("%c%s", uri.Sigil2, uri.MXID2)
}
// UserID returns the user ID from the URI if the primary identifier is a user ID.
func (uri *MatrixURI) UserID() UserID {
if uri.Sigil1 == '@' {
return UserID(uri.PrimaryIdentifier())
}
return ""
}
// RoomID returns the room ID from the URI if the primary identifier is a room ID.
func (uri *MatrixURI) RoomID() RoomID {
if uri.Sigil1 == '!' {
return RoomID(uri.PrimaryIdentifier())
}
return ""
}
// RoomAlias returns the room alias from the URI if the primary identifier is a room alias.
func (uri *MatrixURI) RoomAlias() RoomAlias {
if uri.Sigil1 == '#' {
return RoomAlias(uri.PrimaryIdentifier())
}
return ""
}
// EventID returns the event ID from the URI if the primary identifier is a room ID or alias and the secondary identifier is an event ID.
func (uri *MatrixURI) EventID() EventID {
if (uri.Sigil1 == '!' || uri.Sigil1 == '#') && uri.Sigil2 == '$' {
return EventID(uri.SecondaryIdentifier())
}
return ""
}
// ParseMatrixURIOrMatrixToURL parses the given matrix.to URL or matrix: URI into a unified representation.
func ParseMatrixURIOrMatrixToURL(uri string) (*MatrixURI, error) {
parsed, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("failed to parse URI: %w", err)
}
if parsed.Scheme == "matrix" {
return ProcessMatrixURI(parsed)
} else if strings.HasSuffix(parsed.Hostname(), "matrix.to") {
return ProcessMatrixToURL(parsed)
} else {
return nil, ErrNotMatrixToOrMatrixURI
}
}
// ParseMatrixURI implements the matrix: URI parsing algorithm.
//
// Currently specified in https://github.com/matrix-org/matrix-doc/blob/master/proposals/2312-matrix-uri.md#uri-parsing-algorithm
func ParseMatrixURI(uri string) (*MatrixURI, error) {
// Step 1: parse the URI according to RFC 3986
parsed, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("failed to parse URI: %w", err)
}
return ProcessMatrixURI(parsed)
}
// ProcessMatrixURI implements steps 2-7 of the matrix: URI parsing algorithm
// (i.e. everything except parsing the URI itself, which is done with url.Parse or ParseMatrixURI)
func ProcessMatrixURI(uri *url.URL) (*MatrixURI, error) {
// Step 2: check that scheme is exactly `matrix`
if uri.Scheme != "matrix" {
return nil, ErrInvalidScheme
}
// Step 3: split the path into segments separated by /
parts := strings.Split(uri.Opaque, "/")
// Step 4: Check that the URI contains either 2 or 4 segments
if len(parts) != 2 && len(parts) != 4 {
return nil, ErrInvalidPartCount
}
var parsed MatrixURI
// Step 5: Construct the top-level Matrix identifier
// a: find the sigil from the first segment
switch parts[0] {
case "u", "user":
parsed.Sigil1 = '@'
case "r", "room":
parsed.Sigil1 = '#'
case "roomid":
parsed.Sigil1 = '!'
default:
return nil, fmt.Errorf("%w: '%s'", ErrInvalidFirstSegment, parts[0])
}
// b: find the identifier from the second segment
if len(parts[1]) == 0 {
return nil, ErrEmptySecondSegment
}
parsed.MXID1 = parts[1]
// Step 6: if the first part is a room and the URI has 4 segments, construct a second level identifier
if (parsed.Sigil1 == '!' || parsed.Sigil1 == '#') && len(parts) == 4 {
// a: find the sigil from the third segment
switch parts[2] {
case "e", "event":
parsed.Sigil2 = '$'
default:
return nil, fmt.Errorf("%w: '%s'", ErrInvalidThirdSegment, parts[0])
}
// b: find the identifier from the fourth segment
if len(parts[3]) == 0 {
return nil, ErrEmptyFourthSegment
}
parsed.MXID2 = parts[3]
}
// Step 7: parse the query and extract via and action items
via, ok := uri.Query()["via"]
if ok && len(via) > 0 {
parsed.Via = via
}
action, ok := uri.Query()["action"]
if ok && len(action) > 0 {
parsed.Action = action[len(action)-1]
}
return &parsed, nil
}
// ParseMatrixToURL parses a matrix.to URL into the same container as ParseMatrixURI parses matrix: URIs.
func ParseMatrixToURL(uri string) (*MatrixURI, error) {
parsed, err := url.Parse(uri)
if err != nil {
return nil, fmt.Errorf("failed to parse URL: %w", err)
}
return ProcessMatrixToURL(parsed)
}
// ProcessMatrixToURL is the equivalent of ProcessMatrixURI for matrix.to URLs.
func ProcessMatrixToURL(uri *url.URL) (*MatrixURI, error) {
if !strings.HasSuffix(uri.Hostname(), "matrix.to") {
return nil, ErrNotMatrixTo
}
initialSplit := strings.SplitN(uri.Fragment, "?", 2)
parts := strings.Split(initialSplit[0], "/")
if len(initialSplit) > 1 {
uri.RawQuery = initialSplit[1]
}
if len(parts) < 2 || len(parts) > 3 {
return nil, ErrInvalidMatrixToPartCount
}
if len(parts[1]) == 0 {
return nil, ErrEmptyMatrixToPrimaryIdentifier
}
var parsed MatrixURI
parsed.Sigil1 = rune(parts[1][0])
parsed.MXID1 = parts[1][1:]
_, isKnown := SigilToPathSegment[parsed.Sigil1]
if !isKnown {
return nil, ErrInvalidMatrixToPrimaryIdentifier
}
if len(parts) == 3 && len(parts[2]) > 0 {
parsed.Sigil2 = rune(parts[2][0])
parsed.MXID2 = parts[2][1:]
_, isKnown = SigilToPathSegment[parsed.Sigil2]
if !isKnown {
return nil, ErrInvalidMatrixToSecondaryIdentifier
}
}
via, ok := uri.Query()["via"]
if ok && len(via) > 0 {
parsed.Via = via
}
action, ok := uri.Query()["action"]
if ok && len(action) > 0 {
parsed.Action = action[len(action)-1]
}
return &parsed, nil
}

41
vendor/maunium.net/go/mautrix/id/opaque.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package id
import (
"fmt"
)
// A RoomID is a string starting with ! that references a specific room.
// https://matrix.org/docs/spec/appendices#room-ids-and-event-ids
type RoomID string
// A RoomAlias is a string starting with # that can be resolved into.
// https://matrix.org/docs/spec/appendices#room-aliases
type RoomAlias string
func NewRoomAlias(localpart, server string) RoomAlias {
return RoomAlias(fmt.Sprintf("#%s:%s", localpart, server))
}
// An EventID is a string starting with $ that references a specific event.
//
// https://matrix.org/docs/spec/appendices#room-ids-and-event-ids
// https://matrix.org/docs/spec/rooms/v4#event-ids
type EventID string
func (roomID RoomID) String() string {
return string(roomID)
}
func (roomAlias RoomAlias) String() string {
return string(roomAlias)
}
func (eventID EventID) String() string {
return string(eventID)
}

View File

@@ -1,12 +1,95 @@
package gomatrix
// Copyright (c) 2021 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package id
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"regexp"
"strings"
)
// UserID represents a Matrix user ID.
// https://matrix.org/docs/spec/appendices#user-identifiers
type UserID string
const UserIDMaxLength = 255
func NewUserID(localpart, homeserver string) UserID {
return UserID(fmt.Sprintf("@%s:%s", localpart, homeserver))
}
func NewEncodedUserID(localpart, homeserver string) UserID {
return NewUserID(EncodeUserLocalpart(localpart), homeserver)
}
var (
ErrInvalidUserID = errors.New("is not a valid user ID")
ErrNoncompliantLocalpart = errors.New("contains characters that are not allowed")
ErrUserIDTooLong = errors.New("the given user ID is longer than 255 characters")
ErrEmptyLocalpart = errors.New("empty localparts are not allowed")
)
// Parse parses the user ID into the localpart and server name.
//
// Note that this only enforces very basic user ID formatting requirements: user IDs start with
// a @, and contain a : after the @. If you want to enforce localpart validity, see the
// ParseAndValidate and ValidateUserLocalpart functions.
func (userID UserID) Parse() (localpart, homeserver string, err error) {
if len(userID) == 0 || userID[0] != '@' || !strings.ContainsRune(string(userID), ':') {
// This error wrapping lets you use errors.Is() nicely even though the message contains the user ID
err = fmt.Errorf("'%s' %w", userID, ErrInvalidUserID)
return
}
parts := strings.SplitN(string(userID), ":", 2)
localpart, homeserver = strings.TrimPrefix(parts[0], "@"), parts[1]
return
}
var ValidLocalpartRegex = regexp.MustCompile("^[0-9a-z-.=_/]+$")
// ValidateUserLocalpart validates a Matrix user ID localpart using the grammar
// in https://matrix.org/docs/spec/appendices#user-identifier
func ValidateUserLocalpart(localpart string) error {
if len(localpart) == 0 {
return ErrEmptyLocalpart
} else if !ValidLocalpartRegex.MatchString(localpart) {
return fmt.Errorf("'%s' %w", localpart, ErrNoncompliantLocalpart)
}
return nil
}
// ParseAndValidate parses the user ID into the localpart and server name like Parse,
// and also validates that the localpart is allowed according to the user identifiers spec.
func (userID UserID) ParseAndValidate() (localpart, homeserver string, err error) {
localpart, homeserver, err = userID.Parse()
if err == nil {
err = ValidateUserLocalpart(localpart)
}
if err == nil && len(userID) > UserIDMaxLength {
err = ErrUserIDTooLong
}
return
}
func (userID UserID) ParseAndDecode() (localpart, homeserver string, err error) {
localpart, homeserver, err = userID.ParseAndValidate()
if err == nil {
localpart, err = DecodeUserLocalpart(localpart)
}
return
}
func (userID UserID) String() string {
return string(userID)
}
const lowerhex = "0123456789abcdef"
// encode the given byte using quoted-printable encoding (e.g "=2f")
@@ -116,15 +199,3 @@ func DecodeUserLocalpart(str string) (string, error) {
}
return outputBuffer.String(), nil
}
// ExtractUserLocalpart extracts the localpart portion of a user ID.
// See http://matrix.org/docs/spec/intro.html#user-identifiers
func ExtractUserLocalpart(userID string) (string, error) {
if len(userID) == 0 || userID[0] != '@' {
return "", fmt.Errorf("%s is not a valid user id", userID)
}
return strings.TrimPrefix(
strings.SplitN(userID, ":", 2)[0], // @foo:bar:8448 => [ "@foo", "bar:8448" ]
"@", // remove "@" prefix
), nil
}

124
vendor/maunium.net/go/mautrix/pushrules/action.go generated vendored Normal file
View File

@@ -0,0 +1,124 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package pushrules
import "encoding/json"
// PushActionType is the type of a PushAction
type PushActionType string
// The allowed push action types as specified in spec section 11.12.1.4.1.
const (
ActionNotify PushActionType = "notify"
ActionDontNotify PushActionType = "dont_notify"
ActionCoalesce PushActionType = "coalesce"
ActionSetTweak PushActionType = "set_tweak"
)
// PushActionTweak is the type of the tweak in SetTweak push actions.
type PushActionTweak string
// The allowed tweak types as specified in spec section 11.12.1.4.1.1.
const (
TweakSound PushActionTweak = "sound"
TweakHighlight PushActionTweak = "highlight"
)
// PushActionArray is an array of PushActions.
type PushActionArray []*PushAction
// PushActionArrayShould contains the important information parsed from a PushActionArray.
type PushActionArrayShould struct {
// Whether or not the array contained a Notify, DontNotify or Coalesce action type.
NotifySpecified bool
// Whether or not the event in question should trigger a notification.
Notify bool
// Whether or not the event in question should be highlighted.
Highlight bool
// Whether or not the event in question should trigger a sound alert.
PlaySound bool
// The name of the sound to play if PlaySound is true.
SoundName string
}
// Should parses this push action array and returns the relevant details wrapped in a PushActionArrayShould struct.
func (actions PushActionArray) Should() (should PushActionArrayShould) {
for _, action := range actions {
switch action.Action {
case ActionNotify, ActionCoalesce:
should.Notify = true
should.NotifySpecified = true
case ActionDontNotify:
should.Notify = false
should.NotifySpecified = true
case ActionSetTweak:
switch action.Tweak {
case TweakHighlight:
var ok bool
should.Highlight, ok = action.Value.(bool)
if !ok {
// Highlight value not specified, so assume true since the tweak is set.
should.Highlight = true
}
case TweakSound:
should.SoundName = action.Value.(string)
should.PlaySound = len(should.SoundName) > 0
}
}
}
return
}
// PushAction is a single action that should be triggered when receiving a message.
type PushAction struct {
Action PushActionType
Tweak PushActionTweak
Value interface{}
}
// UnmarshalJSON parses JSON into this PushAction.
//
// * If the JSON is a single string, the value is stored in the Action field.
// * If the JSON is an object with the set_tweak field, Action will be set to
// "set_tweak", Tweak will be set to the value of the set_tweak field and
// and Value will be set to the value of the value field.
// * In any other case, the function does nothing.
func (action *PushAction) UnmarshalJSON(raw []byte) error {
var data interface{}
err := json.Unmarshal(raw, &data)
if err != nil {
return err
}
switch val := data.(type) {
case string:
action.Action = PushActionType(val)
case map[string]interface{}:
tweak, ok := val["set_tweak"].(string)
if ok {
action.Action = ActionSetTweak
action.Tweak = PushActionTweak(tweak)
action.Value, _ = val["value"]
}
}
return nil
}
// MarshalJSON is the reverse of UnmarshalJSON()
func (action *PushAction) MarshalJSON() (raw []byte, err error) {
if action.Action == ActionSetTweak {
data := map[string]interface{}{
"set_tweak": action.Tweak,
"value": action.Value,
}
return json.Marshal(&data)
}
data := string(action.Action)
return json.Marshal(&data)
}

149
vendor/maunium.net/go/mautrix/pushrules/condition.go generated vendored Normal file
View File

@@ -0,0 +1,149 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package pushrules
import (
"regexp"
"strconv"
"strings"
"unicode"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/pushrules/glob"
)
// Room is an interface with the functions that are needed for processing room-specific push conditions
type Room interface {
GetOwnDisplayname() string
GetMemberCount() int
}
// PushCondKind is the type of a push condition.
type PushCondKind string
// The allowed push condition kinds as specified in section 11.12.1.4.3 of r0.3.0 of the Client-Server API.
const (
KindEventMatch PushCondKind = "event_match"
KindContainsDisplayName PushCondKind = "contains_display_name"
KindRoomMemberCount PushCondKind = "room_member_count"
)
// PushCondition wraps a condition that is required for a specific PushRule to be used.
type PushCondition struct {
// The type of the condition.
Kind PushCondKind `json:"kind"`
// The dot-separated field of the event to match. Only applicable if kind is EventMatch.
Key string `json:"key,omitempty"`
// The glob-style pattern to match the field against. Only applicable if kind is EventMatch.
Pattern string `json:"pattern,omitempty"`
// The condition that needs to be fulfilled for RoomMemberCount-type conditions.
// A decimal integer optionally prefixed by ==, <, >, >= or <=. Prefix "==" is assumed if no prefix found.
MemberCountCondition string `json:"is,omitempty"`
}
// MemberCountFilterRegex is the regular expression to parse the MemberCountCondition of PushConditions.
var MemberCountFilterRegex = regexp.MustCompile("^(==|[<>]=?)?([0-9]+)$")
// Match checks if this condition is fulfilled for the given event in the given room.
func (cond *PushCondition) Match(room Room, evt *event.Event) bool {
switch cond.Kind {
case KindEventMatch:
return cond.matchValue(room, evt)
case KindContainsDisplayName:
return cond.matchDisplayName(room, evt)
case KindRoomMemberCount:
return cond.matchMemberCount(room)
default:
return false
}
}
func (cond *PushCondition) matchValue(room Room, evt *event.Event) bool {
index := strings.IndexRune(cond.Key, '.')
key := cond.Key
subkey := ""
if index > 0 {
subkey = key[index+1:]
key = key[0:index]
}
pattern, err := glob.Compile(cond.Pattern)
if err != nil {
return false
}
switch key {
case "type":
return pattern.MatchString(evt.Type.String())
case "sender":
return pattern.MatchString(string(evt.Sender))
case "room_id":
return pattern.MatchString(string(evt.RoomID))
case "state_key":
if evt.StateKey == nil {
return cond.Pattern == ""
}
return pattern.MatchString(*evt.StateKey)
case "content":
val, _ := evt.Content.Raw[subkey].(string)
return pattern.MatchString(val)
default:
return false
}
}
func (cond *PushCondition) matchDisplayName(room Room, evt *event.Event) bool {
displayname := room.GetOwnDisplayname()
if len(displayname) == 0 {
return false
}
msg, ok := evt.Content.Raw["body"].(string)
if !ok {
return false
}
isAcceptable := func(r uint8) bool {
return unicode.IsSpace(rune(r)) || unicode.IsPunct(rune(r))
}
length := len(displayname)
for index := strings.Index(msg, displayname); index != -1; index = strings.Index(msg, displayname) {
if (index <= 0 || isAcceptable(msg[index-1])) && (index+length >= len(msg) || isAcceptable(msg[index+length])) {
return true
}
msg = msg[index+len(displayname):]
}
return false
}
func (cond *PushCondition) matchMemberCount(room Room) bool {
group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition)
if len(group) != 3 {
return false
}
operator := group[1]
wantedMemberCount, _ := strconv.Atoi(group[2])
memberCount := room.GetMemberCount()
switch operator {
case "==", "":
return memberCount == wantedMemberCount
case ">":
return memberCount > wantedMemberCount
case ">=":
return memberCount >= wantedMemberCount
case "<":
return memberCount < wantedMemberCount
case "<=":
return memberCount <= wantedMemberCount
default:
// Should be impossible due to regex.
return false
}
}

2
vendor/maunium.net/go/mautrix/pushrules/doc.go generated vendored Normal file
View File

@@ -0,0 +1,2 @@
// Package pushrules contains utilities to parse push notification rules.
package pushrules

22
vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,22 @@
Glob is licensed under the MIT "Expat" License:
Copyright (c) 2016: Zachary Yedidia.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

28
vendor/maunium.net/go/mautrix/pushrules/glob/README.md generated vendored Normal file
View File

@@ -0,0 +1,28 @@
# String globbing in Go
[![GoDoc](https://godoc.org/github.com/zyedidia/glob?status.svg)](http://godoc.org/github.com/zyedidia/glob)
This package adds support for globs in Go.
It simply converts glob expressions to regexps. I try to follow the standard defined [here](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13).
# Example
```go
package main
import "github.com/zyedidia/glob"
func main() {
glob, err := glob.Compile("{*.go,*.c}")
if err != nil {
// Error
}
glob.Match([]byte("test.c")) // true
glob.Match([]byte("hello.go")) // true
glob.Match([]byte("test.d")) // false
}
```
You can call all the same functions on a glob that you can call on a regexp.

108
vendor/maunium.net/go/mautrix/pushrules/glob/glob.go generated vendored Normal file
View File

@@ -0,0 +1,108 @@
// Package glob provides objects for matching strings with globs
package glob
import "regexp"
// Glob is a wrapper of *regexp.Regexp.
// It should contain a glob expression compiled into a regular expression.
type Glob struct {
*regexp.Regexp
}
// Compile a takes a glob expression as a string and transforms it
// into a *Glob object (which is really just a regular expression)
// Compile also returns a possible error.
func Compile(pattern string) (*Glob, error) {
r, err := globToRegex(pattern)
return &Glob{r}, err
}
func globToRegex(glob string) (*regexp.Regexp, error) {
regex := ""
inGroup := 0
inClass := 0
firstIndexInClass := -1
arr := []byte(glob)
hasGlobCharacters := false
for i := 0; i < len(arr); i++ {
ch := arr[i]
switch ch {
case '\\':
i++
if i >= len(arr) {
regex += "\\"
} else {
next := arr[i]
switch next {
case ',':
// Nothing
case 'Q', 'E':
regex += "\\\\"
default:
regex += "\\"
}
regex += string(next)
}
case '*':
if inClass == 0 {
regex += ".*"
} else {
regex += "*"
}
hasGlobCharacters = true
case '?':
if inClass == 0 {
regex += "."
} else {
regex += "?"
}
hasGlobCharacters = true
case '[':
inClass++
firstIndexInClass = i + 1
regex += "["
hasGlobCharacters = true
case ']':
inClass--
regex += "]"
case '.', '(', ')', '+', '|', '^', '$', '@', '%':
if inClass == 0 || (firstIndexInClass == i && ch == '^') {
regex += "\\"
}
regex += string(ch)
hasGlobCharacters = true
case '!':
if firstIndexInClass == i {
regex += "^"
} else {
regex += "!"
}
hasGlobCharacters = true
case '{':
inGroup++
regex += "("
hasGlobCharacters = true
case '}':
inGroup--
regex += ")"
case ',':
if inGroup > 0 {
regex += "|"
hasGlobCharacters = true
} else {
regex += ","
}
default:
regex += string(ch)
}
}
if hasGlobCharacters {
return regexp.Compile("^" + regex + "$")
} else {
return regexp.Compile(regex)
}
}

37
vendor/maunium.net/go/mautrix/pushrules/pushrules.go generated vendored Normal file
View File

@@ -0,0 +1,37 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package pushrules
import (
"encoding/gob"
"encoding/json"
"reflect"
"maunium.net/go/mautrix/event"
)
// EventContent represents the content of a m.push_rules account data event.
// https://matrix.org/docs/spec/client_server/r0.6.0#m-push-rules
type EventContent struct {
Ruleset *PushRuleset `json:"global"`
}
func init() {
event.TypeMap[event.AccountDataPushRules] = reflect.TypeOf(EventContent{})
gob.Register(&EventContent{})
}
// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON.
func EventToPushRules(evt *event.Event) (*PushRuleset, error) {
content := &EventContent{}
err := json.Unmarshal(evt.Content.VeryRaw, content)
if err != nil {
return nil, err
}
return content.Ruleset, nil
}

154
vendor/maunium.net/go/mautrix/pushrules/rule.go generated vendored Normal file
View File

@@ -0,0 +1,154 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package pushrules
import (
"encoding/gob"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/pushrules/glob"
)
func init() {
gob.Register(PushRuleArray{})
gob.Register(PushRuleMap{})
}
type PushRuleCollection interface {
GetActions(room Room, evt *event.Event) PushActionArray
}
type PushRuleArray []*PushRule
func (rules PushRuleArray) SetType(typ PushRuleType) PushRuleArray {
for _, rule := range rules {
rule.Type = typ
}
return rules
}
func (rules PushRuleArray) GetActions(room Room, evt *event.Event) PushActionArray {
for _, rule := range rules {
if !rule.Match(room, evt) {
continue
}
return rule.Actions
}
return nil
}
type PushRuleMap struct {
Map map[string]*PushRule
Type PushRuleType
}
func (rules PushRuleArray) SetTypeAndMap(typ PushRuleType) PushRuleMap {
data := PushRuleMap{
Map: make(map[string]*PushRule),
Type: typ,
}
for _, rule := range rules {
rule.Type = typ
data.Map[rule.RuleID] = rule
}
return data
}
func (ruleMap PushRuleMap) GetActions(room Room, evt *event.Event) PushActionArray {
var rule *PushRule
var found bool
switch ruleMap.Type {
case RoomRule:
rule, found = ruleMap.Map[string(evt.RoomID)]
case SenderRule:
rule, found = ruleMap.Map[string(evt.Sender)]
}
if found && rule.Match(room, evt) {
return rule.Actions
}
return nil
}
func (ruleMap PushRuleMap) Unmap() PushRuleArray {
array := make(PushRuleArray, len(ruleMap.Map))
index := 0
for _, rule := range ruleMap.Map {
array[index] = rule
index++
}
return array
}
type PushRuleType string
const (
OverrideRule PushRuleType = "override"
ContentRule PushRuleType = "content"
RoomRule PushRuleType = "room"
SenderRule PushRuleType = "sender"
UnderrideRule PushRuleType = "underride"
)
type PushRule struct {
// The type of this rule.
Type PushRuleType `json:"-"`
// The ID of this rule.
// For room-specific rules and user-specific rules, this is the room or user ID (respectively)
// For other types of rules, this doesn't affect anything.
RuleID string `json:"rule_id"`
// The actions this rule should trigger when matched.
Actions PushActionArray `json:"actions"`
// Whether this is a default rule, or has been set explicitly.
Default bool `json:"default"`
// Whether or not this push rule is enabled.
Enabled bool `json:"enabled"`
// The conditions to match in order to trigger this rule.
// Only applicable to generic underride/override rules.
Conditions []*PushCondition `json:"conditions,omitempty"`
// Pattern for content-specific push rules
Pattern string `json:"pattern,omitempty"`
}
func (rule *PushRule) Match(room Room, evt *event.Event) bool {
if !rule.Enabled {
return false
}
switch rule.Type {
case OverrideRule, UnderrideRule:
return rule.matchConditions(room, evt)
case ContentRule:
return rule.matchPattern(room, evt)
case RoomRule:
return id.RoomID(rule.RuleID) == evt.RoomID
case SenderRule:
return id.UserID(rule.RuleID) == evt.Sender
default:
return false
}
}
func (rule *PushRule) matchConditions(room Room, evt *event.Event) bool {
for _, cond := range rule.Conditions {
if !cond.Match(room, evt) {
return false
}
}
return true
}
func (rule *PushRule) matchPattern(room Room, evt *event.Event) bool {
pattern, err := glob.Compile(rule.Pattern)
if err != nil {
return false
}
msg, ok := evt.Content.Raw["body"].(string)
if !ok {
return false
}
return pattern.MatchString(msg)
}

88
vendor/maunium.net/go/mautrix/pushrules/ruleset.go generated vendored Normal file
View File

@@ -0,0 +1,88 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package pushrules
import (
"encoding/json"
"maunium.net/go/mautrix/event"
)
type PushRuleset struct {
Override PushRuleArray
Content PushRuleArray
Room PushRuleMap
Sender PushRuleMap
Underride PushRuleArray
}
type rawPushRuleset struct {
Override PushRuleArray `json:"override"`
Content PushRuleArray `json:"content"`
Room PushRuleArray `json:"room"`
Sender PushRuleArray `json:"sender"`
Underride PushRuleArray `json:"underride"`
}
// UnmarshalJSON parses JSON into this PushRuleset.
//
// For override, sender and underride push rule arrays, the type is added
// to each PushRule and the array is used as-is.
//
// For room and sender push rule arrays, the type is added to each PushRule
// and the array is converted to a map with the rule ID as the key and the
// PushRule as the value.
func (rs *PushRuleset) UnmarshalJSON(raw []byte) (err error) {
data := rawPushRuleset{}
err = json.Unmarshal(raw, &data)
if err != nil {
return
}
rs.Override = data.Override.SetType(OverrideRule)
rs.Content = data.Content.SetType(ContentRule)
rs.Room = data.Room.SetTypeAndMap(RoomRule)
rs.Sender = data.Sender.SetTypeAndMap(SenderRule)
rs.Underride = data.Underride.SetType(UnderrideRule)
return
}
// MarshalJSON is the reverse of UnmarshalJSON()
func (rs *PushRuleset) MarshalJSON() ([]byte, error) {
data := rawPushRuleset{
Override: rs.Override,
Content: rs.Content,
Room: rs.Room.Unmap(),
Sender: rs.Sender.Unmap(),
Underride: rs.Underride,
}
return json.Marshal(&data)
}
// DefaultPushActions is the value returned if none of the rule
// collections in a Ruleset match the event given to GetActions()
var DefaultPushActions = PushActionArray{&PushAction{Action: ActionDontNotify}}
// GetActions matches the given event against all of the push rule
// collections in this push ruleset in the order of priority as
// specified in spec section 11.12.1.4.
func (rs *PushRuleset) GetActions(room Room, evt *event.Event) (match PushActionArray) {
// Add push rule collections to array in priority order
arrays := []PushRuleCollection{rs.Override, rs.Content, rs.Room, rs.Sender, rs.Underride}
// Loop until one of the push rule collections matches the room/event combo.
for _, pra := range arrays {
if pra == nil {
continue
}
if match = pra.GetActions(room, evt); match != nil {
// Match found, return it.
return
}
}
// No match found, return default actions.
return DefaultPushActions
}

302
vendor/maunium.net/go/mautrix/requests.go generated vendored Normal file
View File

@@ -0,0 +1,302 @@
package mautrix
import (
"encoding/json"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
"maunium.net/go/mautrix/pushrules"
)
type AuthType string
const (
AuthTypePassword = "m.login.password"
AuthTypeReCAPTCHA = "m.login.recaptcha"
AuthTypeOAuth2 = "m.login.oauth2"
AuthTypeSSO = "m.login.sso"
AuthTypeEmail = "m.login.email.identity"
AuthTypeMSISDN = "m.login.msisdn"
AuthTypeToken = "m.login.token"
AuthTypeDummy = "m.login.dummy"
AuthTypeAppservice = "m.login.application_service"
AuthTypeHalfyAppservice = "uk.half-shot.msc2778.login.application_service"
)
type IdentifierType string
const (
IdentifierTypeUser = "m.id.user"
IdentifierTypeThirdParty = "m.id.thirdparty"
IdentifierTypePhone = "m.id.phone"
)
// ReqRegister is the JSON request for https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-register
type ReqRegister struct {
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
DeviceID id.DeviceID `json:"device_id,omitempty"`
InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
InhibitLogin bool `json:"inhibit_login,omitempty"`
Auth interface{} `json:"auth,omitempty"`
// Type for registration, only used for appservice user registrations
// https://matrix.org/docs/spec/application_service/r0.1.2#server-admin-style-permissions
Type AuthType `json:"type,omitempty"`
}
type BaseAuthData struct {
Type AuthType `json:"type"`
Session string `json:"session,omitempty"`
}
type UserIdentifier struct {
Type IdentifierType `json:"type"`
User string `json:"user,omitempty"`
Medium string `json:"medium,omitempty"`
Address string `json:"address,omitempty"`
Country string `json:"country,omitempty"`
Phone string `json:"phone,omitempty"`
}
// ReqLogin is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
type ReqLogin struct {
Type AuthType `json:"type"`
Identifier UserIdentifier `json:"identifier"`
Password string `json:"password,omitempty"`
Token string `json:"token,omitempty"`
DeviceID id.DeviceID `json:"device_id,omitempty"`
InitialDeviceDisplayName string `json:"initial_device_display_name,omitempty"`
// Whether or not the returned credentials should be stored in the Client
StoreCredentials bool `json:"-"`
}
type ReqUIAuthFallback struct {
Session string `json:"session"`
User string `json:"user"`
}
type ReqUIAuthLogin struct {
BaseAuthData
User string `json:"user"`
Password string `json:"password"`
}
// ReqCreateRoom is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
type ReqCreateRoom struct {
Visibility string `json:"visibility,omitempty"`
RoomAliasName string `json:"room_alias_name,omitempty"`
Name string `json:"name,omitempty"`
Topic string `json:"topic,omitempty"`
Invite []id.UserID `json:"invite,omitempty"`
Invite3PID []ReqInvite3PID `json:"invite_3pid,omitempty"`
CreationContent map[string]interface{} `json:"creation_content,omitempty"`
InitialState []*event.Event `json:"initial_state,omitempty"`
Preset string `json:"preset,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
}
// ReqRedact is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid
type ReqRedact struct {
Reason string `json:"reason,omitempty"`
TxnID string `json:"-"`
}
type ReqMembers struct {
At string `json:"at"`
Membership event.Membership `json:"membership,omitempty"`
NotMembership event.Membership `json:"not_membership,omitempty"`
}
// ReqInvite3PID is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#id57
// It is also a JSON object used in https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-createroom
type ReqInvite3PID struct {
IDServer string `json:"id_server"`
Medium string `json:"medium"`
Address string `json:"address"`
}
// ReqInviteUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
type ReqInviteUser struct {
UserID id.UserID `json:"user_id"`
}
// ReqKickUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
type ReqKickUser struct {
Reason string `json:"reason,omitempty"`
UserID id.UserID `json:"user_id"`
}
// ReqBanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
type ReqBanUser struct {
Reason string `json:"reason,omitempty"`
UserID id.UserID `json:"user_id"`
}
// ReqUnbanUser is the JSON request for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
type ReqUnbanUser struct {
UserID id.UserID `json:"user_id"`
}
// ReqTyping is the JSON request for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
type ReqTyping struct {
Typing bool `json:"typing"`
Timeout int64 `json:"timeout,omitempty"`
}
type ReqPresence struct {
Presence event.Presence `json:"presence"`
}
type ReqAliasCreate struct {
RoomID id.RoomID `json:"room_id"`
}
type OneTimeKey struct {
Key id.Curve25519 `json:"key"`
IsSigned bool `json:"-"`
Signatures Signatures `json:"signatures,omitempty"`
Unsigned map[string]interface{} `json:"unsigned,omitempty"`
}
type serializableOTK OneTimeKey
func (otk *OneTimeKey) UnmarshalJSON(data []byte) (err error) {
if len(data) > 0 && data[0] == '"' && data[len(data)-1] == '"' {
err = json.Unmarshal(data, &otk.Key)
otk.Signatures = nil
otk.Unsigned = nil
otk.IsSigned = false
} else {
err = json.Unmarshal(data, (*serializableOTK)(otk))
otk.IsSigned = true
}
return err
}
func (otk *OneTimeKey) MarshalJSON() ([]byte, error) {
if !otk.IsSigned {
return json.Marshal(otk.Key)
} else {
return json.Marshal((*serializableOTK)(otk))
}
}
type ReqUploadKeys struct {
DeviceKeys *DeviceKeys `json:"device_keys,omitempty"`
OneTimeKeys map[id.KeyID]OneTimeKey `json:"one_time_keys"`
}
type ReqKeysSignatures struct {
UserID id.UserID `json:"user_id"`
DeviceID id.DeviceID `json:"device_id,omitempty"`
Algorithms []id.Algorithm `json:"algorithms,omitempty"`
Usage []id.CrossSigningUsage `json:"usage,omitempty"`
Keys map[id.KeyID]string `json:"keys"`
Signatures Signatures `json:"signatures"`
}
type ReqUploadSignatures map[id.UserID]map[string]ReqKeysSignatures
type DeviceKeys struct {
UserID id.UserID `json:"user_id"`
DeviceID id.DeviceID `json:"device_id"`
Algorithms []id.Algorithm `json:"algorithms"`
Keys KeyMap `json:"keys"`
Signatures Signatures `json:"signatures"`
Unsigned map[string]interface{} `json:"unsigned,omitempty"`
}
type CrossSigningKeys struct {
UserID id.UserID `json:"user_id"`
Usage []id.CrossSigningUsage `json:"usage"`
Keys map[id.KeyID]id.Ed25519 `json:"keys"`
Signatures map[id.UserID]map[id.KeyID]string `json:"signatures,omitempty"`
}
func (csk *CrossSigningKeys) FirstKey() id.Ed25519 {
for _, key := range csk.Keys {
return key
}
return ""
}
type UploadCrossSigningKeysReq struct {
Master CrossSigningKeys `json:"master_key"`
SelfSigning CrossSigningKeys `json:"self_signing_key"`
UserSigning CrossSigningKeys `json:"user_signing_key"`
Auth interface{} `json:"auth,omitempty"`
}
type KeyMap map[id.DeviceKeyID]string
func (km KeyMap) GetEd25519(deviceID id.DeviceID) id.Ed25519 {
val, ok := km[id.NewDeviceKeyID(id.KeyAlgorithmEd25519, deviceID)]
if !ok {
return ""
}
return id.Ed25519(val)
}
func (km KeyMap) GetCurve25519(deviceID id.DeviceID) id.Curve25519 {
val, ok := km[id.NewDeviceKeyID(id.KeyAlgorithmCurve25519, deviceID)]
if !ok {
return ""
}
return id.Curve25519(val)
}
type Signatures map[id.UserID]map[id.KeyID]string
type ReqQueryKeys struct {
DeviceKeys DeviceKeysRequest `json:"device_keys"`
Timeout int64 `json:"timeout,omitempty"`
Token string `json:"token,omitempty"`
}
type DeviceKeysRequest map[id.UserID]DeviceIDList
type DeviceIDList []id.DeviceID
type ReqClaimKeys struct {
OneTimeKeys OneTimeKeysRequest `json:"one_time_keys"`
Timeout int64 `json:"timeout,omitempty"`
}
type OneTimeKeysRequest map[id.UserID]map[id.DeviceID]id.KeyAlgorithm
type ReqSendToDevice struct {
Messages map[id.UserID]map[id.DeviceID]*event.Content `json:"messages"`
}
// ReqDeviceInfo is the JSON request for https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-devices-deviceid
type ReqDeviceInfo struct {
DisplayName string `json:"display_name,omitempty"`
}
// ReqDeleteDevice is the JSON request for https://matrix.org/docs/spec/client_server/r0.6.1#delete-matrix-client-r0-devices-deviceid
type ReqDeleteDevice struct {
Auth interface{} `json:"auth,omitempty"`
}
// ReqDeleteDevices is the JSON request for https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-delete-devices
type ReqDeleteDevices struct {
Devices []id.DeviceID `json:"devices"`
Auth interface{} `json:"auth,omitempty"`
}
type ReqPutPushRule struct {
Before string `json:"-"`
After string `json:"-"`
Actions []pushrules.PushActionType `json:"actions"`
Conditions []pushrules.PushCondition `json:"conditions"`
Pattern string `json:"pattern"`
}

291
vendor/maunium.net/go/mautrix/responses.go generated vendored Normal file
View File

@@ -0,0 +1,291 @@
package mautrix
import (
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// RespWhoami is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-account-whoami
type RespWhoami struct {
UserID id.UserID `json:"user_id"`
// N.B. This field is not in the spec yet, it's expected to land in r0.6.2 or r0.7.0
DeviceID id.DeviceID `json:"device_id"`
}
// RespCreateFilter is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-user-userid-filter
type RespCreateFilter struct {
FilterID string `json:"filter_id"`
}
// RespVersions is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.1.html#get-matrix-client-versions
type RespVersions struct {
Versions []string `json:"versions"`
UnstableFeatures map[string]bool `json:"unstable_features"`
}
// RespJoinRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-join
type RespJoinRoom struct {
RoomID id.RoomID `json:"room_id"`
}
// RespLeaveRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-leave
type RespLeaveRoom struct{}
// RespForgetRoom is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-forget
type RespForgetRoom struct{}
// RespInviteUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-invite
type RespInviteUser struct{}
// RespKickUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-kick
type RespKickUser struct{}
// RespBanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-ban
type RespBanUser struct{}
// RespUnbanUser is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-rooms-roomid-unban
type RespUnbanUser struct{}
// RespTyping is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-typing-userid
type RespTyping struct{}
// RespPresence is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-presence-userid-status
type RespPresence struct {
Presence event.Presence `json:"presence"`
LastActiveAgo int `json:"last_active_ago"`
StatusMsg string `json:"status_msg"`
CurrentlyActive bool `json:"currently_active"`
}
// RespJoinedRooms is the JSON response for https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-joined-rooms
type RespJoinedRooms struct {
JoinedRooms []id.RoomID `json:"joined_rooms"`
}
// RespJoinedMembers is the JSON response for https://matrix.org/docs/spec/client_server/r0.4.0.html#get-matrix-client-r0-joined-rooms
type RespJoinedMembers struct {
Joined map[id.UserID]struct {
DisplayName *string `json:"display_name"`
AvatarURL *string `json:"avatar_url"`
} `json:"joined"`
}
// RespMessages is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-rooms-roomid-messages
type RespMessages struct {
Start string `json:"start"`
Chunk []*event.Event `json:"chunk"`
State []*event.Event `json:"state"`
End string `json:"end"`
}
// RespSendEvent is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid
type RespSendEvent struct {
EventID id.EventID `json:"event_id"`
}
// RespMediaUpload is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-media-r0-upload
type RespMediaUpload struct {
ContentURI id.ContentURI `json:"content_uri"`
}
// RespUserInteractive is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#user-interactive-authentication-api
type RespUserInteractive struct {
Flows []struct {
Stages []AuthType `json:"stages"`
} `json:"flows"`
Params map[AuthType]interface{} `json:"params"`
Session string `json:"session"`
Completed []string `json:"completed"`
ErrCode string `json:"errcode"`
Error string `json:"error"`
}
// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
func (r RespUserInteractive) HasSingleStageFlow(stageName AuthType) bool {
for _, f := range r.Flows {
if len(f.Stages) == 1 && f.Stages[0] == stageName {
return true
}
}
return false
}
// RespUserDisplayName is the JSON response for https://matrix.org/docs/spec/client_server/r0.2.0.html#get-matrix-client-r0-profile-userid-displayname
type RespUserDisplayName struct {
DisplayName string `json:"displayname"`
}
// RespRegister is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register
type RespRegister struct {
AccessToken string `json:"access_token"`
DeviceID id.DeviceID `json:"device_id"`
HomeServer string `json:"home_server"`
RefreshToken string `json:"refresh_token"`
UserID id.UserID `json:"user_id"`
}
type RespLoginFlows struct {
Flows []struct {
Type AuthType `json:"type"`
} `json:"flows"`
}
func (rlf *RespLoginFlows) HasFlow(flowType AuthType) bool {
for _, flow := range rlf.Flows {
if flow.Type == flowType {
return true
}
}
return false
}
// RespLogin is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-login
type RespLogin struct {
AccessToken string `json:"access_token"`
DeviceID id.DeviceID `json:"device_id"`
UserID id.UserID `json:"user_id"`
// TODO add .well-known field here
}
// RespLogout is the JSON response for http://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-logout
type RespLogout struct{}
// RespCreateRoom is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.0.html#post-matrix-client-r0-createroom
type RespCreateRoom struct {
RoomID id.RoomID `json:"room_id"`
}
type RespMembers struct {
Chunk []*event.Event `json:"chunk"`
}
type LazyLoadSummary struct {
Heroes []id.UserID `json:"m.heroes,omitempty"`
JoinedMemberCount *int `json:"m.joined_member_count,omitempty"`
InvitedMemberCount *int `json:"m.invited_member_count,omitempty"`
}
// RespSync is the JSON response for http://matrix.org/docs/spec/client_server/r0.6.0.html#get-matrix-client-r0-sync
type RespSync struct {
NextBatch string `json:"next_batch"`
AccountData struct {
Events []*event.Event `json:"events"`
} `json:"account_data"`
Presence struct {
Events []*event.Event `json:"events"`
} `json:"presence"`
ToDevice struct {
Events []*event.Event `json:"events"`
} `json:"to_device"`
DeviceLists struct {
Changed []id.UserID `json:"changed"`
Left []id.UserID `json:"left"`
} `json:"device_lists"`
DeviceOneTimeKeysCount OneTimeKeysCount `json:"device_one_time_keys_count"`
Rooms struct {
Leave map[id.RoomID]SyncLeftRoom `json:"leave"`
Join map[id.RoomID]SyncJoinedRoom `json:"join"`
Invite map[id.RoomID]SyncInvitedRoom `json:"invite"`
} `json:"rooms"`
}
type OneTimeKeysCount struct {
Curve25519 int `json:"curve25519"`
SignedCurve25519 int `json:"signed_curve25519"`
}
type SyncLeftRoom struct {
Summary LazyLoadSummary `json:"summary"`
State struct {
Events []*event.Event `json:"events"`
} `json:"state"`
Timeline struct {
Events []*event.Event `json:"events"`
Limited bool `json:"limited"`
PrevBatch string `json:"prev_batch"`
} `json:"timeline"`
}
type SyncJoinedRoom struct {
Summary LazyLoadSummary `json:"summary"`
State struct {
Events []*event.Event `json:"events"`
} `json:"state"`
Timeline struct {
Events []*event.Event `json:"events"`
Limited bool `json:"limited"`
PrevBatch string `json:"prev_batch"`
} `json:"timeline"`
Ephemeral struct {
Events []*event.Event `json:"events"`
} `json:"ephemeral"`
AccountData struct {
Events []*event.Event `json:"events"`
} `json:"account_data"`
}
type SyncInvitedRoom struct {
Summary LazyLoadSummary `json:"summary"`
State struct {
Events []*event.Event `json:"events"`
} `json:"invite_state"`
}
type RespTurnServer struct {
Username string `json:"username"`
Password string `json:"password"`
TTL int `json:"ttl"`
URIs []string `json:"uris"`
}
type RespAliasCreate struct{}
type RespAliasDelete struct{}
type RespAliasResolve struct {
RoomID id.RoomID `json:"room_id"`
Servers []string `json:"servers"`
}
type RespUploadKeys struct {
OneTimeKeyCounts OneTimeKeysCount `json:"one_time_key_counts"`
}
type RespQueryKeys struct {
Failures map[string]interface{} `json:"failures"`
DeviceKeys map[id.UserID]map[id.DeviceID]DeviceKeys `json:"device_keys"`
MasterKeys map[id.UserID]CrossSigningKeys `json:"master_keys"`
SelfSigningKeys map[id.UserID]CrossSigningKeys `json:"self_signing_keys"`
UserSigningKeys map[id.UserID]CrossSigningKeys `json:"user_signing_keys"`
}
type RespClaimKeys struct {
Failures map[string]interface{} `json:"failures"`
OneTimeKeys map[id.UserID]map[id.DeviceID]map[id.KeyID]OneTimeKey `json:"one_time_keys"`
}
type RespUploadSignatures struct {
Failures map[string]interface{} `json:"failures"`
}
type RespKeyChanges struct {
Changed []id.UserID `json:"changed"`
Left []id.UserID `json:"left"`
}
type RespSendToDevice struct{}
// RespDevicesInfo is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-devices
type RespDevicesInfo struct {
Devices []RespDeviceInfo `json:"devices"`
}
// RespDeviceInfo is the JSON response for https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-devices-deviceid
type RespDeviceInfo struct {
DeviceID id.DeviceID `json:"device_id"`
DisplayName string `json:"display_name"`
LastSeenIP string `json:"last_seen_ip"`
LastSeenTS int64 `json:"last_seen_ts"`
}

52
vendor/maunium.net/go/mautrix/room.go generated vendored Normal file
View File

@@ -0,0 +1,52 @@
package mautrix
import (
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// Room represents a single Matrix room.
type Room struct {
ID id.RoomID
State map[event.Type]map[string]*event.Event
}
// UpdateState updates the room's current state with the given Event. This will clobber events based
// on the type/state_key combination.
func (room Room) UpdateState(evt *event.Event) {
_, exists := room.State[evt.Type]
if !exists {
room.State[evt.Type] = make(map[string]*event.Event)
}
room.State[evt.Type][*evt.StateKey] = evt
}
// GetStateEvent returns the state event for the given type/state_key combo, or nil.
func (room Room) GetStateEvent(eventType event.Type, stateKey string) *event.Event {
stateEventMap, _ := room.State[eventType]
evt, _ := stateEventMap[stateKey]
return evt
}
// GetMembershipState returns the membership state of the given user ID in this room. If there is
// no entry for this member, 'leave' is returned for consistency with left users.
func (room Room) GetMembershipState(userID id.UserID) event.Membership {
state := event.MembershipLeave
evt := room.GetStateEvent(event.StateMember, string(userID))
if evt != nil {
membership, ok := evt.Content.Raw["membership"].(string)
if ok {
state = event.Membership(membership)
}
}
return state
}
// NewRoom creates a new Room with the given ID
func NewRoom(roomID id.RoomID) *Room {
// Init the State map and return a pointer to the Room
return &Room{
ID: roomID,
State: make(map[event.Type]map[string]*event.Event),
}
}

162
vendor/maunium.net/go/mautrix/store.go generated vendored Normal file
View File

@@ -0,0 +1,162 @@
package mautrix
import (
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// Storer is an interface which must be satisfied to store client data.
//
// You can either write a struct which persists this data to disk, or you can use the
// provided "InMemoryStore" which just keeps data around in-memory which is lost on
// restarts.
type Storer interface {
SaveFilterID(userID id.UserID, filterID string)
LoadFilterID(userID id.UserID) string
SaveNextBatch(userID id.UserID, nextBatchToken string)
LoadNextBatch(userID id.UserID) string
SaveRoom(room *Room)
LoadRoom(roomID id.RoomID) *Room
}
// InMemoryStore implements the Storer interface.
//
// Everything is persisted in-memory as maps. It is not safe to load/save filter IDs
// or next batch tokens on any goroutine other than the syncing goroutine: the one
// which called Client.Sync().
type InMemoryStore struct {
Filters map[id.UserID]string
NextBatch map[id.UserID]string
Rooms map[id.RoomID]*Room
}
// SaveFilterID to memory.
func (s *InMemoryStore) SaveFilterID(userID id.UserID, filterID string) {
s.Filters[userID] = filterID
}
// LoadFilterID from memory.
func (s *InMemoryStore) LoadFilterID(userID id.UserID) string {
return s.Filters[userID]
}
// SaveNextBatch to memory.
func (s *InMemoryStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
s.NextBatch[userID] = nextBatchToken
}
// LoadNextBatch from memory.
func (s *InMemoryStore) LoadNextBatch(userID id.UserID) string {
return s.NextBatch[userID]
}
// SaveRoom to memory.
func (s *InMemoryStore) SaveRoom(room *Room) {
s.Rooms[room.ID] = room
}
// LoadRoom from memory.
func (s *InMemoryStore) LoadRoom(roomID id.RoomID) *Room {
return s.Rooms[roomID]
}
// UpdateState stores a state event. This can be passed to DefaultSyncer.OnEvent to keep all room state cached.
func (s *InMemoryStore) UpdateState(_ EventSource, evt *event.Event) {
if !evt.Type.IsState() {
return
}
room := s.LoadRoom(evt.RoomID)
if room == nil {
room = NewRoom(evt.RoomID)
s.SaveRoom(room)
}
room.UpdateState(evt)
}
// NewInMemoryStore constructs a new InMemoryStore.
func NewInMemoryStore() *InMemoryStore {
return &InMemoryStore{
Filters: make(map[id.UserID]string),
NextBatch: make(map[id.UserID]string),
Rooms: make(map[id.RoomID]*Room),
}
}
// AccountDataStore uses account data to store the next batch token, and
// reuses the InMemoryStore for all other operations.
type AccountDataStore struct {
*InMemoryStore
eventType string
client *Client
}
type accountData struct {
NextBatch string `json:"next_batch"`
}
// SaveNextBatch to account data.
func (s *AccountDataStore) SaveNextBatch(userID id.UserID, nextBatchToken string) {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with bots")
}
data := accountData{
NextBatch: nextBatchToken,
}
err := s.client.SetAccountData(s.eventType, data)
if err != nil {
if s.client.Logger != nil {
s.client.Logger.Debugfln("failed to save next batch token to account data: %s", err.Error())
}
}
}
// LoadNextBatch from account data.
func (s *AccountDataStore) LoadNextBatch(userID id.UserID) string {
if userID.String() != s.client.UserID.String() {
panic("AccountDataStore must only be used with bots")
}
data := &accountData{}
err := s.client.GetAccountData(s.eventType, data)
if err != nil {
if s.client.Logger != nil {
s.client.Logger.Debugfln("failed to load next batch token to account data: %s", err.Error())
}
return ""
}
return data.NextBatch
}
// NewAccountDataStore returns a new AccountDataStore, which stores
// the next_batch token as a custom event in account data in the
// homeserver.
//
// AccountDataStore is only appropriate for bots, not appservices.
//
// eventType should be a reversed DNS name like tld.domain.sub.internal and
// must be unique for a client. The data stored in it is considered internal
// and must not be modified through outside means. You should also add a filter
// for account data changes of this event type, to avoid ending up in a sync
// loop:
//
// mautrix.Filter{
// AccountData: mautrix.FilterPart{
// Limit: 20,
// NotTypes: []event.Type{
// event.NewEventType(eventType),
// },
// },
// }
// mautrix.Client.CreateFilter(...)
//
func NewAccountDataStore(eventType string, client *Client) *AccountDataStore {
return &AccountDataStore{
InMemoryStore: NewInMemoryStore(),
eventType: eventType,
client: client,
}
}

283
vendor/maunium.net/go/mautrix/sync.go generated vendored Normal file
View File

@@ -0,0 +1,283 @@
// Copyright (c) 2020 Tulir Asokan
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package mautrix
import (
"fmt"
"runtime/debug"
"time"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// EventSource represents the part of the sync response that an event came from.
type EventSource int
const (
EventSourcePresence EventSource = 1 << iota
EventSourceJoin
EventSourceInvite
EventSourceLeave
EventSourceAccountData
EventSourceTimeline
EventSourceState
EventSourceEphemeral
EventSourceToDevice
)
func (es EventSource) String() string {
switch {
case es == EventSourcePresence:
return "presence"
case es == EventSourceAccountData:
return "user account data"
case es == EventSourceToDevice:
return "to-device"
case es&EventSourceJoin != 0:
es -= EventSourceJoin
switch es {
case EventSourceState:
return "joined state"
case EventSourceTimeline:
return "joined timeline"
case EventSourceEphemeral:
return "room ephemeral (joined)"
case EventSourceAccountData:
return "room account data (joined)"
}
case es&EventSourceInvite != 0:
es -= EventSourceInvite
switch es {
case EventSourceState:
return "invited state"
}
case es&EventSourceLeave != 0:
es -= EventSourceLeave
switch es {
case EventSourceState:
return "left state"
case EventSourceTimeline:
return "left timeline"
}
}
return fmt.Sprintf("unknown (%d)", es)
}
// EventHandler handles a single event from a sync response.
type EventHandler func(source EventSource, evt *event.Event)
// SyncHandler handles a whole sync response. If the return value is false, handling will be stopped completely.
type SyncHandler func(resp *RespSync, since string) bool
// Syncer is an interface that must be satisfied in order to do /sync requests on a client.
type Syncer interface {
// Process the /sync response. The since parameter is the since= value that was used to produce the response.
// This is useful for detecting the very first sync (since=""). If an error is return, Syncing will be stopped
// permanently.
ProcessResponse(resp *RespSync, since string) error
// OnFailedSync returns either the time to wait before retrying or an error to stop syncing permanently.
OnFailedSync(res *RespSync, err error) (time.Duration, error)
// GetFilterJSON for the given user ID. NOT the filter ID.
GetFilterJSON(userID id.UserID) *Filter
}
type ExtensibleSyncer interface {
OnSync(callback SyncHandler)
OnEvent(callback EventHandler)
OnEventType(eventType event.Type, callback EventHandler)
}
// DefaultSyncer is the default syncing implementation. You can either write your own syncer, or selectively
// replace parts of this default syncer (e.g. the ProcessResponse method). The default syncer uses the observer
// pattern to notify callers about incoming events. See DefaultSyncer.OnEventType for more information.
type DefaultSyncer struct {
// syncListeners want the whole sync response, e.g. the crypto machine
syncListeners []SyncHandler
// globalListeners want all events
globalListeners []EventHandler
// listeners want a specific event type
listeners map[event.Type][]EventHandler
// ParseEventContent determines whether or not event content should be parsed before passing to handlers.
ParseEventContent bool
// ParseErrorHandler is called when event.Content.ParseRaw returns an error.
// If it returns false, the event will not be forwarded to listeners.
ParseErrorHandler func(evt *event.Event, err error) bool
}
var _ Syncer = (*DefaultSyncer)(nil)
var _ ExtensibleSyncer = (*DefaultSyncer)(nil)
// NewDefaultSyncer returns an instantiated DefaultSyncer
func NewDefaultSyncer() *DefaultSyncer {
return &DefaultSyncer{
listeners: make(map[event.Type][]EventHandler),
syncListeners: []SyncHandler{},
globalListeners: []EventHandler{},
ParseEventContent: true,
ParseErrorHandler: func(evt *event.Event, err error) bool {
return false
},
}
}
// ProcessResponse processes the /sync response in a way suitable for bots. "Suitable for bots" means a stream of
// unrepeating events. Returns a fatal error if a listener panics.
func (s *DefaultSyncer) ProcessResponse(res *RespSync, since string) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ProcessResponse panicked! since=%s panic=%s\n%s", since, r, debug.Stack())
}
}()
for _, listener := range s.syncListeners {
if !listener(res, since) {
return
}
}
s.processSyncEvents("", res.Presence.Events, EventSourcePresence)
s.processSyncEvents("", res.AccountData.Events, EventSourceAccountData)
for roomID, roomData := range res.Rooms.Join {
s.processSyncEvents(roomID, roomData.State.Events, EventSourceJoin|EventSourceState)
s.processSyncEvents(roomID, roomData.Timeline.Events, EventSourceJoin|EventSourceTimeline)
s.processSyncEvents(roomID, roomData.Ephemeral.Events, EventSourceJoin|EventSourceEphemeral)
s.processSyncEvents(roomID, roomData.AccountData.Events, EventSourceJoin|EventSourceAccountData)
}
for roomID, roomData := range res.Rooms.Invite {
s.processSyncEvents(roomID, roomData.State.Events, EventSourceInvite|EventSourceState)
}
for roomID, roomData := range res.Rooms.Leave {
s.processSyncEvents(roomID, roomData.State.Events, EventSourceLeave|EventSourceState)
s.processSyncEvents(roomID, roomData.Timeline.Events, EventSourceLeave|EventSourceTimeline)
}
return
}
func (s *DefaultSyncer) processSyncEvents(roomID id.RoomID, events []*event.Event, source EventSource) {
for _, evt := range events {
s.processSyncEvent(roomID, evt, source)
}
}
func (s *DefaultSyncer) processSyncEvent(roomID id.RoomID, evt *event.Event, source EventSource) {
evt.RoomID = roomID
// Ensure the type class is correct. It's safe to mutate the class since the event type is not a pointer.
// Listeners are keyed by type structs, which means only the correct class will pass.
switch {
case evt.StateKey != nil:
evt.Type.Class = event.StateEventType
case source == EventSourcePresence, source&EventSourceEphemeral != 0:
evt.Type.Class = event.EphemeralEventType
case source&EventSourceAccountData != 0:
evt.Type.Class = event.AccountDataEventType
case source == EventSourceToDevice:
evt.Type.Class = event.ToDeviceEventType
default:
evt.Type.Class = event.MessageEventType
}
if s.ParseEventContent {
err := evt.Content.ParseRaw(evt.Type)
if err != nil && !s.ParseErrorHandler(evt, err) {
return
}
}
s.notifyListeners(source, evt)
}
func (s *DefaultSyncer) notifyListeners(source EventSource, evt *event.Event) {
for _, fn := range s.globalListeners {
fn(source, evt)
}
listeners, exists := s.listeners[evt.Type]
if exists {
for _, fn := range listeners {
fn(source, evt)
}
}
}
// OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks.
func (s *DefaultSyncer) OnEventType(eventType event.Type, callback EventHandler) {
_, exists := s.listeners[eventType]
if !exists {
s.listeners[eventType] = []EventHandler{}
}
s.listeners[eventType] = append(s.listeners[eventType], callback)
}
func (s *DefaultSyncer) OnSync(callback SyncHandler) {
s.syncListeners = append(s.syncListeners, callback)
}
func (s *DefaultSyncer) OnEvent(callback EventHandler) {
s.globalListeners = append(s.globalListeners, callback)
}
// OnFailedSync always returns a 10 second wait period between failed /syncs, never a fatal error.
func (s *DefaultSyncer) OnFailedSync(res *RespSync, err error) (time.Duration, error) {
return 10 * time.Second, nil
}
// GetFilterJSON returns a filter with a timeline limit of 50.
func (s *DefaultSyncer) GetFilterJSON(userID id.UserID) *Filter {
return &Filter{
Room: RoomFilter{
Timeline: FilterPart{
Limit: 50,
},
},
}
}
// OldEventIgnorer is an utility struct for bots to ignore events from before the bot joined the room.
// Create a struct and call Register with your DefaultSyncer to register the sync handler.
type OldEventIgnorer struct {
UserID id.UserID
}
func (oei *OldEventIgnorer) Register(syncer ExtensibleSyncer) {
syncer.OnSync(oei.DontProcessOldEvents)
}
// DontProcessOldEvents returns true if a sync response should be processed. May modify the response to remove
// stuff that shouldn't be processed.
func (oei *OldEventIgnorer) DontProcessOldEvents(resp *RespSync, since string) bool {
if since == "" {
return false
}
// This is a horrible hack because /sync will return the most recent messages for a room
// as soon as you /join it. We do NOT want to process those events in that particular room
// because they may have already been processed (if you toggle the bot in/out of the room).
//
// Work around this by inspecting each room's timeline and seeing if an m.room.member event for us
// exists and is "join" and then discard processing that room entirely if so.
// TODO: We probably want to process messages from after the last join event in the timeline.
for roomID, roomData := range resp.Rooms.Join {
for i := len(roomData.Timeline.Events) - 1; i >= 0; i-- {
evt := roomData.Timeline.Events[i]
if evt.Type == event.StateMember && evt.GetStateKey() == string(oei.UserID) {
membership, _ := evt.Content.Raw["membership"].(string)
if membership == "join" {
_, ok := resp.Rooms.Join[roomID]
if !ok {
continue
}
delete(resp.Rooms.Join, roomID) // don't re-process messages
delete(resp.Rooms.Invite, roomID) // don't re-process invites
break
}
}
}
}
return true
}

5
vendor/maunium.net/go/mautrix/version.go generated vendored Normal file
View File

@@ -0,0 +1,5 @@
package mautrix
const Version = "v0.9.14"
var DefaultUserAgent = "mautrix-go/" + Version

13
vendor/modules.txt vendored
View File

@@ -38,6 +38,8 @@ github.com/SevereCloud/vksdk/v2/longpoll-bot
github.com/SevereCloud/vksdk/v2/object
# github.com/blang/semver v3.5.1+incompatible
github.com/blang/semver
# github.com/btcsuite/btcutil v1.0.2
github.com/btcsuite/btcutil/base58
# github.com/d5/tengo/v2 v2.7.0
## explicit
github.com/d5/tengo/v2
@@ -137,7 +139,6 @@ github.com/lrstanley/girc
github.com/magiconair/properties
# github.com/matrix-org/gomatrix v0.0.0-20210324163249-be2af5ef2e16
## explicit
github.com/matrix-org/gomatrix
# github.com/matterbridge/Rocket.Chat.Go.SDK v0.0.0-20210403163225-761e8622445d
## explicit
github.com/matterbridge/Rocket.Chat.Go.SDK/models
@@ -325,6 +326,7 @@ golang.org/x/crypto/ed25519/internal/edwards25519
golang.org/x/crypto/hkdf
golang.org/x/crypto/internal/subtle
golang.org/x/crypto/nacl/secretbox
golang.org/x/crypto/pbkdf2
golang.org/x/crypto/poly1305
golang.org/x/crypto/salsa20/salsa
golang.org/x/crypto/ssh
@@ -445,3 +447,12 @@ layeh.com/gumble/gumble
layeh.com/gumble/gumble/MumbleProto
layeh.com/gumble/gumble/varint
layeh.com/gumble/gumbleutil
# maunium.net/go/mautrix v0.9.14
## explicit
maunium.net/go/mautrix
maunium.net/go/mautrix/crypto/attachment
maunium.net/go/mautrix/crypto/utils
maunium.net/go/mautrix/event
maunium.net/go/mautrix/id
maunium.net/go/mautrix/pushrules
maunium.net/go/mautrix/pushrules/glob