matrix

package module
v0.0.0-...-e41826d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 27, 2021 License: MIT Imports: 13 Imported by: 9

README

Matrix Client Library

GoDoc

Matrix library built for frost

Development

Testing

Most of the testsuite needs an matrix server and user to test against. The environment variables MATRIX_TEST_MXID and MATRIX_TEST_PASSWORD control which user and server will be used (if they are not set all tests, which rely on an matrix server, will not be run). Note that trying to run the tests against an ordirary matrix server will most certainly get you rate limited.

To spin up a temporary instance of synapse using docker and execute tests using this server see scripts/test.sh

Documentation

Overview

Package matrix implements a matrix client. This includes a lot of abstractionns on top of the http api. This package tries to use as few as possible HTTP requests. We try to accomplish this by saving as much information as possible from the /sync API endpoint. This means that whenever we initialize a new backend, we do a request with full_state=true which might take some seconds to complete. To avoid this you should use a persistent backend. This package is fully thread-safe. All errors returned by methods in this package are (unless otherwise noted) either a NetworkError or a LogicError wrapping the original error

Index

Examples

Constants

View Source
const (
	MegolmName = "m.megolm.v1.aes-sha2"
	OlmName    = "m.olm.v1.curve25519-aes-sha2"
)
View Source
const (
	PrivateChat        CreateRoomPreset = "private_chat"
	PublicChat                          = "public_chat"
	TrustedPrivateChat                  = "trusted_private_chat"
)
View Source
const CryptoSupport = true

If set to true the library is able to handle encrypted events

Variables

View Source
var (
	ErrDeviceKeys           = errors.New("the device is lacking a ed25519 or curve25519 key")
	ErrCryptoDisabled       = errors.New("cryptography has been disabled in this build")
	ErrMalformedEvent       = errors.New("event does not have all neccessary keys")
	ErrUnknownSession       = errors.New("olm/megolm session not known")
	ErrUnsupportedAlgorithm = errors.New("the algorithm used to encrypt the event is not supported")
	ErrSessionNotSetUp      = errors.New("no cryptography session has been set up for this device")
	ErrAlreadySetUp         = errors.New("the session for this device has already been set up")
)

CryptoError

View Source
var ErrInvalidURL = errors.New("invalid mxc:// url")
View Source
var ErrUnsupportedVersion = errors.New("the homeserver does not support API version r0.5.0. The Client might or might not work correctly")

Functions

func ResolveHomeserverURL

func ResolveHomeserverURL(cli *http.Client, serverName string) (*url.URL, error)

ResolveHomeserverURL gets the actual URL of a homeserver based on the server name from a mxid. It returns a NetworkError if the http transport was not successful and a LogicError if the server provided a broken response (e.g malformatted JSON) TODO Identity server & extra information XXX Really use LogicError here?

func SplitMxID

func SplitMxID(id string) (localpart string, serverName string, err error)

SplitMxID splits a mxid and return localpart, server name and validity respectively.

Types

type AccountDataEvent

type AccountDataEvent struct {
	Type    string                 `json:"type"`
	Content map[string]interface{} `json:"content"`
	// contains filtered or unexported fields
}

AccountDataEvent is a matrix account data event. If this event is attached to a Room, this Room can be obtained using the Room method.

func (AccountDataEvent) Base

func (e AccountDataEvent) Base() BaseEvent

Base implements the Event interface

func (AccountDataEvent) Room

func (e AccountDataEvent) Room() *Room

Room returns the room on which the event was stored.

func (AccountDataEvent) WithRoom

func (e AccountDataEvent) WithRoom(room Room) AccountDataEvent

WithRoom sets the originating room of the RoomEvent. This has to be called after loading the struct from any kind of storage format (e.g JSON). Otherwise calling methods of e.Room will panic at some point

type ApplicationService

type ApplicationService struct {
	// contains filtered or unexported fields
}

ApplicationService allows to utilize the application service API.

func NewApplicationService

func NewApplicationService(reg Registration, opts ApplicationServiceOpts) *ApplicationService

func (*ApplicationService) Notify

func (as *ApplicationService) Notify(ch chan<- Event)

Notify implements the Notifier interface. The channel will receive all events the application service is interested in.

func (*ApplicationService) Stop

func (as *ApplicationService) Stop(ch chan<- Event)

func (*ApplicationService) Sync

func (as *ApplicationService) Sync(ctx context.Context) error

type ApplicationServiceOpts

type ApplicationServiceOpts struct {
	Addr string
}

type Backend

type Backend interface {
	// Initialize configures the backend to store information
	// for the provided device. This should be called only
	// when a new device is created or mxid, deviceID and
	// access token have been persisted in another way.
	// Initialize panics if there is a persisted session
	// available (i.e if Open(mxid, deviceID) would return
	// nil.
	// Calling Initialize multiple times panics.
	Initialize(mxid, deviceID, accessToken string)

	// Open configures the backend to load already an already
	// persisted session. This should be called if the
	// Backend implementation is able to persist information
	// between restarts. If the provided mxid-deviceID
	// combination is not known this returns an error.
	// Calling Open multiple times panics.
	Open(mxid, deviceID string) error

	// MxID returns the MxID of the user owing the device.
	// This method must return an empty string when the backend
	// has not been configured yet.
	MxID() string

	// DeviceID returns the device ID of the device.
	// This method must return an empty string when the backend
	// has not been configured yet.
	DeviceID() string

	// AccessToken returns the access token used by the device.
	// This method must return an empty string when the backend
	// has not been configured yet.
	AccessToken() string

	// AccountData gets a account data event of the specified
	// type from the given room. If room is nil the event
	// is searched in the users global account data.
	// If no event is found nil is returned.
	AccountData(room *Room, type_ string) (AccountDataEvent, bool)

	// UpdateAccountData updates the chached account data.
	UpdateAccountData(AccountDataEvent)

	// RoomState gets a StateEvent from a room.
	RoomState(room Room, type_, key string) (StateEvent, bool)

	// RoomStateList gets all state events of the specified type
	// from the room.
	RoomStateList(room Room, type_ string) []StateEvent

	// UpdateRoomState updates the cached room state.
	UpdateRoomState(event StateEvent)

	// AddLatestEvents adds events freshly received from the /sync
	// endpoint and the prevBatch token used to iterate room history
	// into the cache.
	AddLatestEvents(room Room, events []Event, prevBatch string)

	// LatestEvents returns the latest Events received from the /sync
	// endpoint and the prevBatch token from the cache. The order of the
	// events must stay the same.
	LatestEvents(room Room) ([]Event, string)

	// DeviceInfo returns the device info for a specific device
	DeviceInfo(user User, id string) (DeviceInfo, bool)

	// DeviceList returns all devices of the user. Note that this only contains devices that have identity keys
	// set up (as there is no possibility to see devices of a user that do not support encryption).
	DeviceList(user User) []Device

	// DeviceListUntrack removes all stored devices for the given user. This is called when the user's ID appears
	// in the "left" secion of the device_lists property of the /sync response.
	DeviceListUntrack(user User)

	// DeviceListUpdate updates the device list of a user. "devices" contains all current devices of the user. The Backend
	// must completely replace the list it has currently stored.
	DeviceListUpdate(user User, devices []DeviceInfo)

	CryptoBackend
}

The Backend is responsible for storing session information (mxid, device ID, access token) and caching. A Backend is tied to exactly one session (or device in matrix terminology). A Backend implementation must be threadsafe! A Backend implementation should not try to set correct internal values on the returned structs (with WithClient or WithRoom) the client will handle this. If neither Initialize nor Open have been called on a backend, all methods except for MxID, DeviceID and AccessToken may panic or return otherwise unexpected results.

type BaseEvent

type BaseEvent struct {
	Type    string
	Content map[string]interface{}
}

BaseEvent is the lowest common denominator of all events.

type Client

type Client struct {
	// A event is sent to this channel when the first sync request has completed.
	// If the Sync routine is stopped and then restarted a event is again sent.
	InitialSyncDone chan struct{}
	// contains filtered or unexported fields
}

Client implements communication using the matrix protocol. All methods interacting with the content repository do not implement any kind of caching since there is no one-fits-all solution to this. To use caching you either have to build your own solution or use whats in git.sr.ht/~f4814n/matrix/util/cache

Example (Echo)
package main

import (
	"context"
	"fmt"
	"os"

	"git.sr.ht/~f4814n/matrix"
	"git.sr.ht/~f4814n/matrix/backend/memory"
)

func main() {
	mxid := os.Getenv("MATRIX_TEST_MXID")
	password := os.Getenv("MATRIX_TEST_PASSWORD")

	cli := matrix.NewClient(matrix.ClientOpts{Backend: memory.New()})

	if err := cli.Login(mxid, password); err != nil {
		fmt.Println(err.Error())
		return
	}

	go cli.Sync(context.Background(), &matrix.SyncOpts{
		OnError: func(err error) error {
			fmt.Printf("Sync Error: %s\n", err)
			return nil // Continue syncing
		},
	})

	<-cli.InitialSyncDone // Avoid echoing events sent before the client was started

	events := make(chan matrix.Event)
	cli.Notify(events)

	for event := range events {
		if event, ok := event.(matrix.RoomEvent); ok {
			if event.Type == "m.room.message" && !event.Own() {
				_, err := event.Room.SendRoomEvent("m.room.message", event.Content)
				if err != nil {
					fmt.Printf("Could not send event: %s", err)
				}
			}
		}
	}
}
Output:

func NewClient

func NewClient(opts ClientOpts) *Client

NewClient creates a new client. This only initializes everything needed but does not make any attempt to use the network.

func (*Client) AccessToken

func (cli *Client) AccessToken() string

AccessToken returns the access token used by the client. "" if there is no token

func (*Client) Device

func (cli *Client) Device() (Device, bool)

Device returns the device the client is authenticated as. False if the client is not authenticated

func (*Client) DeviceID

func (cli *Client) DeviceID() string

DeviceID returns the device ID of the Client.

func (*Client) DownloadFile

func (cli *Client) DownloadFile(url *url.URL, filename string) (File, error)

DownloadFile downloads a file from the content repository of the homeserver. url must be a valid mxc:// URL.

func (*Client) DownloadThumbnail

func (cli *Client) DownloadThumbnail(url *url.URL, width, height int, method ThumbnailMethod) (Thumbnail, error)

DownloadThumbnail downloads a thumbnail of the specified URL from the homeserver

func (*Client) LoadToken

func (cli *Client) LoadToken(mxid, deviceID string) error

LoadToken calls the Open method of the backend with the provided parameters, checks if the homeserver exists, configures the client to use the homeserver and finally calls the whoami API endpoint to check, whether the access token matches the mxID.

func (*Client) Login

func (cli *Client) Login(mxid string, password string) error

Login using a matrix user id and a password (m.login.password)

Example
package main

import (
	"fmt"
	"os"

	"git.sr.ht/~f4814n/matrix"
	"git.sr.ht/~f4814n/matrix/backend/memory"
)

func main() {
	mxid := os.Getenv("MATRIX_TEST_MXID")
	password := os.Getenv("MATRIX_TEST_PASSWORD")

	cli := matrix.NewClient(matrix.ClientOpts{Backend: memory.New()})

	if err := cli.Login(mxid, password); err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println("Login successful")
}
Output:

func (*Client) Logout

func (cli *Client) Logout() error

Logout invalidates the used access token

func (*Client) LogoutAll

func (cli *Client) LogoutAll() error

LogoutAll invalidates all access tokens of the user

func (*Client) MxID

func (cli *Client) MxID() string

MxID returns the matrix ID of the Client.

func (*Client) Notify

func (cli *Client) Notify(c chan<- Event)

Notify implements the Notify interface. This will contain all events received.

Example
package main

import (
	"context"
	"fmt"
	"os"

	"git.sr.ht/~f4814n/matrix"
	"git.sr.ht/~f4814n/matrix/backend/memory"
)

func main() {
	mxid := os.Getenv("MATRIX_TEST_MXID")
	password := os.Getenv("MATRIX_TEST_PASSWORD")

	cli := matrix.NewClient(matrix.ClientOpts{Backend: memory.New()})

	if err := cli.Login(mxid, password); err != nil {
		fmt.Println(err.Error())
		return
	}

	events := make(chan matrix.Event, 100)
	cli.Notify(events)

	go func() {
		for event := range events {
			fmt.Printf("%#v\n", event)
		}
	}()

	if err := cli.Sync(context.Background(), nil); err != nil {
		fmt.Println(err.Error())
	}
}
Output:

func (*Client) SendToDevice

func (cli *Client) SendToDevice(encrypted bool, eventType string, messages map[string]map[string]map[string]interface{}) error

SendToDevice sends a to-device event. If encrypted is true, the event will be encrypted using OLM and sent as an m.room.encrypted event. messages is a map from user ID to device ID to event content.

func (*Client) SetupCryptoSession

func (cli *Client) SetupCryptoSession() error

SetupCryptoSession will set up a crypto session for this device. This only has to be called once for a session (preferrably directly after calling cli.Login). Will return CryptoError{Err: ErrCryptoDisabled} if the build does not support cryptography. Will return CryptoError{Err: ErrAlreadySetUp} All methods encrypting/decrypting events will return CryptoError{Err: ErrSessionNotSetUp} if this has not been called before attempting to encrypt/decrypt.

func (*Client) Stop

func (cli *Client) Stop(c chan<- Event)

Stop implements the Notifier interface.

func (*Client) Sync

func (cli *Client) Sync(ctx context.Context, opts *SyncOpts) error

Sync starts receiving change events. This will start long-polling /sync and supply all Changers with new events. This can be stopped and then restarted without having to discard Changers. Stop this by terminating ctx. The returned error is either a NetworkError, a LogicError or Context.Canceled / Context.DeadlineExceeded.

func (*Client) UploadFile

func (cli *Client) UploadFile(file File) (*url.URL, error)

UploadFile uploads a file to the content repository of the homeserver and returns the created mxc:// URL.

func (*Client) User

func (cli *Client) User() (User, bool)

User returns the user the client is authenticated as. Nil if the client is not authenticated.

type ClientOpts

type ClientOpts struct {
	// HTTP Client to use. If nil http.DefaultClient will be used
	HTTPClient *http.Client

	// Backend
	Backend Backend

	// Logger to use. When no logger is supplied a no-op logger is used.
	Logger Logger
}

ClientOpts are optional options passed to Client. NewClient is able to handle ClientOpts values which have nil fields or even are nil.

type CreateRoomOpts

type CreateRoomOpts struct {
	// If Visibility is Public the room will be added to the public room directory
	// of the server. Defaults to Private
	Visibility RoomVisibility

	// The desired room alias *local part*. If set, a room alias will be created
	// and mapped to the newly created room. The alias will belong to the same
	// homeserver which created the room.
	Alias string

	// If set, an m.room.name event will be sent into the room.
	Name string

	// If set, an m.room.topic event will be sent into the room.
	Topic string

	// A list of users to invite to the room.
	Invite []User

	// The room version to set for the room. If not set, the homeserver will use
	// its configured default.
	RoomVersion string

	// A list of state events to set in the new room. Each element of the slice
	// is marshaled to JSON. The resulting JSON must be an object containing the
	// "type", "state_key" and "content" keys.
	InitialState []interface{}

	// Convenience parameter for setting various default state events based on
	// a preset.
	Preset CreateRoomPreset

	// Will set the is_direct flag on the events sent to the invited users.
	// Defaults to false.
	IsDirect bool

	// The power level content to override in the default power level event.
	// This object is applied on top of the generated m.room.power_levels event
	// content prior to it being sent to the room. Defaults to overriding nothing.
	PowerLevelContentOverride map[string]interface{}
}

type CreateRoomPreset

type CreateRoomPreset string

CreateRoomPreset is a convenience enum used in room creation. For more information see https://matrix.org/docs/spec/client_server/r0.5.0#post-matrix-client-r0-createroom

type CryptoBackend

type CryptoBackend interface {
	Account() string

	UpdateAccount(string)

	Sessions(deviceKey string) []string

	UpdateSessions(deviceKey string, sessions []string)

	InboundGroupSession(roomID, deviceKey, sessionID string) string

	AddInboundGroupSession(roomID, deviceKey, sessionID, session string)

	OutboundGroupSessions(roomID string) []string

	UpdateOutboundGroupSessions(roomID string, sessions []string)
}

type CryptoError

type CryptoError struct {
	Err error
}

A CryptoError is returned, if something went wrong during encrypting or decrypting an event.

func (CryptoError) Error

func (e CryptoError) Error() string

func (CryptoError) Unwrap

func (e CryptoError) Unwrap() error

type Device

type Device struct {
	ID string
	// contains filtered or unexported fields
}

func (Device) Info

func (d Device) Info() DeviceInfo

func (Device) Send

func (d Device) Send(encrypted bool, eventType string, content map[string]interface{}) error

func (Device) WithUser

func (d Device) WithUser(user User) Device

type DeviceInfo

type DeviceInfo struct {
	ID string

	Algorithms []string
	Keys       map[string]string
	Signatures map[string]map[string]string
}

func (DeviceInfo) Curve25519

func (d DeviceInfo) Curve25519() (string, bool)

Curve25519 returns the public part of the devices Curve25519 identity key

func (DeviceInfo) Ed25519

func (d DeviceInfo) Ed25519() (string, bool)

Ed25519 returns the public part of the devices Ed25519 fingerprint key

func (DeviceInfo) Supports

func (d DeviceInfo) Supports(algorithm string) bool

Supports returns bool if the device supports the supplied algorithm. The library must support the algorithm too

type EphemeralEvent

type EphemeralEvent struct {
	Type    string
	Content map[string]interface{}
	// contains filtered or unexported fields
}

EphemeralEvent is a ephemeral matrix event.

func (EphemeralEvent) Base

func (e EphemeralEvent) Base() BaseEvent

Base implements the Event interface

func (EphemeralEvent) Room

func (e EphemeralEvent) Room() Room

func (EphemeralEvent) WithRoom

func (e EphemeralEvent) WithRoom(room Room) EphemeralEvent

type Event

type Event interface {
	Base() BaseEvent
}

Event is the interface implemented by the types to represent all kinds of matrix events.

type File

type File struct {
	Name    string
	Type    string
	Size    int64
	Content io.ReadCloser
}

File represents content of the matrix content repository. Don't forget to Close() File.Content after using it.

type History

type History struct {
	// Contains the current events. Paginated using History.Next()
	Events []Event
	Err    error
	// contains filtered or unexported fields
}

History makes the room history accessible.

Example
package main

import (
	"context"
	"fmt"
	"os"

	"git.sr.ht/~f4814n/matrix"
	"git.sr.ht/~f4814n/matrix/backend/memory"
)

func main() {
	mxid := os.Getenv("MATRIX_TEST_MXID")
	password := os.Getenv("MATRIX_TEST_PASSWORD")
	roomID := os.Getenv("MATRIX_TEST_ROOM")

	cli := matrix.NewClient(matrix.ClientOpts{Backend: memory.New()})

	if err := cli.Login(mxid, password); err != nil {
		fmt.Println(err.Error())
		return
	}

	go cli.Sync(context.Background(), &matrix.SyncOpts{
		OnError: func(err error) error {
			fmt.Printf("Sync Error: %s\n", err)
			return nil // Continue syncing
		},
	})

	// The initial sync must be completed so we know the prev_token of the sync request
	<-cli.InitialSyncDone

	room, err := matrix.LoadRoom(cli, roomID)
	if err != nil {
		panic(err)
	}

	history := room.History()

	for history.Next() {
		if history.Err != nil {
			panic(err)
		}

		for _, event := range history.Events {
			fmt.Printf("%#v\n", event)
		}
	}
}
Output:

func (*History) Next

func (h *History) Next() bool

Next uses the room messages API to traverse the history in a backwards way. If there is a communication error, next sets History.Err to the error and returns true. Even though the messages API does not return the most recent event (if it was sent by the last sync response), History includes those events too.

type JSONEvent

type JSONEvent struct {
	Event Event
}

JSONEvent uses a custom UnmarshalJSON mehtod to detect wheter the decoded event is a StateEvent or a RoomEvent

Example (Room)
raw := []byte(`
            {
              "content": {
                "body": "This is an example text message",
                "msgtype": "m.text",
                "format": "org.matrix.custom.html",
                "formatted_body": "<b>This is an example text message</b>"
              },
              "type": "m.room.message",
              "event_id": "$143273582443PhrSn:example.org",
              "room_id": "!726s6s6q:example.com",
              "sender": "@example:example.org",
              "origin_server_ts": 1432735824653,
              "unsigned": {
                "age": 1234
              }
            }
	`)

var helper JSONEvent

err := json.Unmarshal(raw, &helper)
if err != nil {
	panic(err)
}

fmt.Printf("%T\n", helper.Event)
fmt.Printf("%+v\n", helper.Event)
Output:

matrix.RoomEvent
{Content:map[body:This is an example text message format:org.matrix.custom.html formatted_body:<b>This is an example text message</b> msgtype:m.text] Type:m.room.message ID:$143273582443PhrSn:example.org OriginServerTS:2015-05-27 16:10:24.653 +0200 CEST Unsigned:{Age:1234 RedactedBecause:<nil> TransactionID:} Sender:{User:{ID:@example:example.org cli:<nil>} Room:{ID: cli:<nil>}} Room:{ID: cli:<nil>}}
Example (State)
raw := []byte(`
		{
			"content": {
				"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
				"displayname": "Alice Margatroid",
				"membership": "join"
			},
			"type": "m.room.member",
			"event_id": "$143273582443PhrSn:example.org",
			"unsigned": {
				"age": 1234,
				"transaction_id": ""
			},
			"state_key": "@alice:example.org",
			"origin_server_ts": 1603152000000,
			"sender": "@example:example.org"
		}
	`)

var helper JSONEvent

err := json.Unmarshal(raw, &helper)
if err != nil {
	panic(err)
}

fmt.Printf("%T\n", helper.Event)
fmt.Printf("%+v\n", helper.Event)
Output:

matrix.StateEvent
{Content:map[avatar_url:mxc://example.org/SEsfnsuifSDFSSEF displayname:Alice Margatroid membership:join] Type:m.room.member ID:$143273582443PhrSn:example.org OriginServerTS:2020-10-20 02:00:00 +0200 CEST Unsigned:{Age:1234 RedactedBecause:<nil> TransactionID:} StateKey:@alice:example.org PrevContent:map[] Sender:{User:{ID:@example:example.org cli:<nil>} Room:{ID: cli:<nil>}} Room:{ID: cli:<nil>}}

func (*JSONEvent) UnmarshalJSON

func (e *JSONEvent) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface

type Logger

type Logger interface {
	Debug(msg string, keysAndValues ...interface{})
	Info(msg string, keysAndValues ...interface{})
	Warn(msg string, keysAndValues ...interface{})
	Error(msg string, keysAndValues ...interface{})
	Fatal(msg string, keysAndValues ...interface{})
	Named(string) Logger
}

type LogicError

type LogicError struct {
	Err error
}

A LogicError is returned, if the HTTP connection was successful but the operation failed anyway. (For example due to insufficient authorization)

func (LogicError) Error

func (e LogicError) Error() string

func (LogicError) Unwrap

func (e LogicError) Unwrap() error

type Member

type Member struct {
	User
	Room Room
}

Member is a User with the additional information from a single room.

func (Member) Displayname

func (m Member) Displayname() string

Displayname gets the displayname of the user by searching m.room.member state events. If no name is set it returns the users MxID.

func (Member) Notify

func (m Member) Notify(c chan<- Event)

Notify implements the Notifier interface. This can not be used to be notified about ephemeral events relevant for the user (e.g. m.typing events), since it is not possible to attribute all ephemeral events to a certain set of users.

func (Member) State

func (m Member) State() MembershipState

func (Member) Stop

func (m Member) Stop(c chan<- Event)

Stop implements the Notifier interface

type MembershipState

type MembershipState uint8
const (
	Invite MembershipState = iota
	Join
	Leave
	Ban
	Knock
	Unspecified
)

func (MembershipState) String

func (i MembershipState) String() string

type MissingContentKeyError

type MissingContentKeyError struct {
	Key string
}

func (MissingContentKeyError) Error

func (e MissingContentKeyError) Error() string

type NetworkError

type NetworkError struct {
	Err error
}

A NetworkError is returned, if a correct HTTP transmission was not possible for whatever reason

func (NetworkError) Error

func (e NetworkError) Error() string

func (NetworkError) Unwrap

func (e NetworkError) Unwrap() error

type Notifier

type Notifier interface {
	// A copy of each event relecant to the Updater is sent here. It is up to the
	// use to ensure that the channel is empty. The event will be discarded, if
	// the channel is not empty. Calling this method with the same argument
	// twice will panic.
	Notify(chan<- Event)

	// Stop sending to the specified channel. This panics if the channel was
	// not registered before using Updater.Update() or if called multiple
	// times.
	Stop(chan<- Event)
}

A Notifier (Client, User, Member, Room) can receive updated events from a running Client.

type Registration

type Registration struct {
	ID              string                 `yaml:"id"`
	URL             string                 `yaml:"url"`
	ASToken         string                 `yaml:"as_token"`
	HSToken         string                 `yaml:"hs_token"`
	SenderLocalpart string                 `yaml:"sender_localpart"`
	Namespaces      RegistrationNamespaces `yaml:"namespaces"`
	RateLimited     bool                   `yaml:"rate_limited"`
	Protocols       []string               `yaml:"protocols"`
}

Registration is used to authenticate a ApplicationService to the homeserver. https://matrix.org/docs/spec/application_service/r0.1.2#registration

type RegistrationNamespace

type RegistrationNamespace struct {
	Exclusive bool   `yaml:"exclusive"`
	Regex     string `yaml:"regex"`
}

type RegistrationNamespaces

type RegistrationNamespaces struct {
	Users   []RegistrationNamespace `yaml:"users"`
	Aliases []RegistrationNamespace `yaml:"aliases"`
	Rooms   []RegistrationNamespace `yaml:"rooms"`
}

type Room

type Room struct {
	// The matrix ID of the room
	ID string
	// contains filtered or unexported fields
}

Room represents a matrix room. A room value is always tied to a client, that is used to do the actual communication. Multiple room values describing the same room may be used simultaneously. Room is thread-safe.

Example
package main

import (
	"context"
	"fmt"
	"os"

	"git.sr.ht/~f4814n/matrix"
	"git.sr.ht/~f4814n/matrix/backend/memory"
)

func main() {
	mxid := os.Getenv("MATRIX_TEST_MXID")
	password := os.Getenv("MATRIX_TEST_PASSWORD")
	roomID := os.Getenv("MATRIX_TEST_ROOM")

	cli := matrix.NewClient(matrix.ClientOpts{Backend: memory.New()})

	if err := cli.Login(mxid, password); err != nil {
		fmt.Println(err.Error())
		return
	}

	go cli.Sync(context.Background(), &matrix.SyncOpts{
		OnError: func(err error) error {
			fmt.Printf("Sync Error: %s\n", err)
			return nil // Continue syncing
		},
	})

	<-cli.InitialSyncDone

	room, err := matrix.LoadRoom(cli, roomID)
	if err != nil {
		panic(err)
	}

	if state := room.State(); state != matrix.Join {
		fmt.Printf("Your state is %s. This might not work as expected\n", state)
	}

	events := make(chan matrix.Event, 100)
	room.Notify(events)

	for event := range events {
		fmt.Println(event)
	}
}
Output:

func CreateRoom

func CreateRoom(cli *Client, opts CreateRoomOpts) (Room, error)

CreateRoom creates a new room.

func LoadRoom

func LoadRoom(cli *Client, id string) (Room, error)

LoadRoom takes a id or alias as argument and calls LoadRoomID or LoadRoomAlias depending on the first sign of the id.

func LoadRoomAlias

func LoadRoomAlias(cli *Client, alias string) (Room, error)

LoadRoomAlias loads a existent room identified by the room alias.

func LoadRoomID

func LoadRoomID(cli *Client, id string) (Room, error)

LoadRoomID loads a existent room identified by the room ID. This call is very fast (no HTTP Request) for rooms the user is invited to, has joined or has already left (for all rooms that are somehow part of the /sync endpoint). For other rooms this tries to look up the ID.

func (Room) AccountData

func (r Room) AccountData(type_ string) (AccountDataEvent, bool)

AccountData returns an AccountDataEvent of the given type if it exists

func (Room) Displayname

func (r Room) Displayname() string

Displayname returns the rooms display name. The name is calculated according to https://matrix.org/docs/spec/client_server/r0.5.0#calculating-the-display-name-for-a-room

func (Room) Forget

func (r Room) Forget() error

Forget forgets the room. The history of the room will no longer be accessible. If the user is currently joined to the room, they must leave the room before calling this.

func (Room) GetState

func (r Room) GetState(eventType, stateKey string) (StateEvent, bool)

GetState gets a specific event from the room state. If no such state event is known the return value is nil.

func (Room) History

func (r Room) History() History

History makes the history beginning from the most recently received prev_batch (/sync endpoint) accessible.

func (Room) Invite

func (r Room) Invite(u User) error

Invite a User to a Room.

func (Room) Leave

func (r Room) Leave() error

Leave leaves the room.

func (Room) Member

func (r Room) Member(mxid string) *Member

Member returns the Member with the given mxid in this room. If the specified user has no m.room.member event in this room it will return nil

func (Room) Members

func (r Room) Members() []Member

Members returns all Members of this room. At the moment lazy loading of room members is not used. So this method can not return an error.

func (Room) Notify

func (r Room) Notify(c chan<- Event)

Notify implements the Notifier interface

func (Room) SendRoomEvent

func (r Room) SendRoomEvent(eventType string, content map[string]interface{}) (string, error)

SendRoomEvent sends a room event to the room and returns the event ID and possibly an error.

func (Room) SendStateEvent

func (r Room) SendStateEvent(eventType string, stateKey string, content map[string]interface{}) (string, error)

SendStateEvent sends a state event to the room and returns the event ID and possibly an error.

func (Room) State

func (r Room) State() MembershipState

State returns the MembershipState of the Client in this room.

func (Room) Stop

func (r Room) Stop(c chan<- Event)

Stop implements the Notifier interface

type RoomEvent

type RoomEvent struct {
	Content        map[string]interface{} `json:"content"`
	Type           string                 `json:"type"`
	ID             string                 `json:"event_id"`
	OriginServerTS time.Time              `json:"origin_server_ts"`
	Unsigned       RoomEventUnsigned      `json:"unsigned"`
	Sender         Member                 `json:"sender"`
	Room           Room                   `json:"-"`
}

RoomEvent is a matrix room event that is not a state event at the same time. RoomEvent implements the interfaces of encoding/json in a way which allows to marshal and unmarshal JSON in the format the matrix spec dictates. Certain information about the used client and associated room will be lost in this process and has to be restored using the WithClient and WithRoom methods after Decoding from JSON

func (RoomEvent) Base

func (e RoomEvent) Base() BaseEvent

Base implements the Event interface

func (RoomEvent) Decrypt

func (e RoomEvent) Decrypt() (RoomEvent, error)

func (RoomEvent) MarshalJSON

func (e RoomEvent) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface

Example
t, _ := time.Parse("2006-Jan-02", "2020-Oct-20")
e := RoomEvent{
	Content: map[string]interface{}{
		"msgtype": "m.text",
		"body":    "Hello there",
	},
	Type:           "m.room.message",
	ID:             "$143273582443PhrSn:example.org",
	OriginServerTS: t,
	Unsigned: RoomEventUnsigned{
		Age: 1234,
	},
	// Information about the used client and the originating room is lost during marshal
	// RoomEvent.WithClient and RoomEvent.WithRoom can be used to change this information
	Sender: Member{User: User{ID: "@example:example.org"}, Room: Room{ID: "!726s6s6q:example.com"}},
}

out, err := json.Marshal(e)
if err != nil {
	panic(err)
}

var indent bytes.Buffer
json.Indent(&indent, out, "", "\t")
indent.WriteTo(os.Stdout)
Output:

{
	"content": {
		"body": "Hello there",
		"msgtype": "m.text"
	},
	"type": "m.room.message",
	"event_id": "$143273582443PhrSn:example.org",
	"unsigned": {
		"age": 1234,
		"transaction_id": ""
	},
	"origin_server_ts": 1603152000000,
	"sender": "@example:example.org"
}

func (RoomEvent) Own

func (e RoomEvent) Own() bool

Own is true if the event was sent by the same user the client is currently authenticated as.

func (*RoomEvent) UnmarshalJSON

func (e *RoomEvent) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaler interface

Example
raw := []byte(`
            {
              "content": {
                "body": "This is an example text message",
                "msgtype": "m.text",
                "format": "org.matrix.custom.html",
                "formatted_body": "<b>This is an example text message</b>"
              },
              "type": "m.room.message",
              "event_id": "$143273582443PhrSn:example.org",
              "room_id": "!726s6s6q:example.com",
              "sender": "@example:example.org",
              "origin_server_ts": 1432735824653,
              "unsigned": {
                "age": 1234
              }
            }
	`)

var e RoomEvent

err := json.Unmarshal(raw, &e)
if err != nil {
	panic(err)
}

fmt.Printf("%+v\n", e)
Output:

{Content:map[body:This is an example text message format:org.matrix.custom.html formatted_body:<b>This is an example text message</b> msgtype:m.text] Type:m.room.message ID:$143273582443PhrSn:example.org OriginServerTS:2015-05-27 16:10:24.653 +0200 CEST Unsigned:{Age:1234 RedactedBecause:<nil> TransactionID:} Sender:{User:{ID:@example:example.org cli:<nil>} Room:{ID: cli:<nil>}} Room:{ID: cli:<nil>}}

func (RoomEvent) WithClient

func (e RoomEvent) WithClient(cli *Client) RoomEvent

WithClient sets the underlying client of the RoomEvent. This has to be called after loading the struct from any kind of storage format (e.g JSON). Otherwise calling methods of e.Sender and e.Room will panic at some point

func (RoomEvent) WithRoom

func (e RoomEvent) WithRoom(room Room) RoomEvent

WithRoom sets the originating room of the RoomEvent. This has to be called after loading the struct from any kind of storage format (e.g JSON). Otherwise calling methods of e.Room will panic at some point

type RoomEventUnsigned

type RoomEventUnsigned struct {
	Age             int64  `json:"age,omitempty"`
	RedactedBecause Event  `json:"redacted_because,omitempty"`
	TransactionID   string `json:"transaction_id,omitemtpy"`
}

RoomEventUnsigned is used within RoomEvent.

type RoomVisibility

type RoomVisibility string

RoomVisibility shows the visibility state of the room. NB: This should not be confused with join_rules which also uses the word public.

const (
	// Public indicates that the room is part of the public room list.
	Public RoomVisibility = "public"

	// Private indicates that the room is not part of the public room list.
	Private = "private"
)

type StateEvent

type StateEvent struct {
	Content        map[string]interface{} `json:"content"`
	Type           string                 `json:"type"`
	ID             string                 `json:"event_id"`
	OriginServerTS time.Time              `json:"origin_server_ts"`
	Unsigned       RoomEventUnsigned      `json:"unsigned,omitempty"`
	StateKey       string                 `json:"state_key"`
	PrevContent    map[string]interface{} `json:"prev_content,omitempty"`
	Sender         Member                 `json:"sender"`
	Room           Room                   `json:"-"`
}

StateEvent is a matrix state event.

func (StateEvent) Base

func (e StateEvent) Base() BaseEvent

Base implements the Event interface

func (StateEvent) MarshalJSON

func (e StateEvent) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface

Example
t, _ := time.Parse("2006-Jan-02", "2020-Oct-20")
e := StateEvent{
	Content: map[string]interface{}{
		"membership":  "join",
		"avatar_url":  "mxc://example.org/SEsfnsuifSDFSSEF",
		"displayname": "Alice Margatroid",
	},
	Type:           "m.room.member",
	ID:             "$143273582443PhrSn:example.org",
	OriginServerTS: t,
	StateKey:       "@alice:example.org",
	Unsigned: RoomEventUnsigned{
		Age: 1234,
	},
	// Information about the used client and the originating room is lost during marshal
	// RoomEvent.WithClient and RoomEvent.WithRoom can be used to change this information
	Sender: Member{User: User{ID: "@example:example.org"}, Room: Room{ID: "!726s6s6q:example.com"}},
}

out, err := json.Marshal(e)
if err != nil {
	panic(err)
}

var indent bytes.Buffer
json.Indent(&indent, out, "", "\t")
indent.WriteTo(os.Stdout)
Output:

{
	"content": {
		"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
		"displayname": "Alice Margatroid",
		"membership": "join"
	},
	"type": "m.room.member",
	"event_id": "$143273582443PhrSn:example.org",
	"unsigned": {
		"age": 1234,
		"transaction_id": ""
	},
	"state_key": "@alice:example.org",
	"origin_server_ts": 1603152000000,
	"sender": "@example:example.org"
}

func (StateEvent) Own

func (e StateEvent) Own() bool

Own is true if the event was sent by the same user the client is currently authenticated as.

func (*StateEvent) UnmarshalJSON

func (e *StateEvent) UnmarshalJSON(data []byte) error

UnmarshalJSON impelments the json.Unmarshaler interface

Example
raw := []byte(`
            {
              "content": {
		"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
                "displayname": "Alice Margatroid"
              },
              "type": "m.room.member",
              "event_id": "$143273582443PhrSn:example.org",
              "sender": "@example:example.org",
              "origin_server_ts": 1432735824653,
              "unsigned": {
                "age": 1234
              }
            }
	`)

var e RoomEvent

err := json.Unmarshal(raw, &e)
if err != nil {
	panic(err)
}

fmt.Printf("%+v\n", e)
Output:

{Content:map[avatar_url:mxc://example.org/SEsfnsuifSDFSSEF displayname:Alice Margatroid] Type:m.room.member ID:$143273582443PhrSn:example.org OriginServerTS:2015-05-27 16:10:24.653 +0200 CEST Unsigned:{Age:1234 RedactedBecause:<nil> TransactionID:} Sender:{User:{ID:@example:example.org cli:<nil>} Room:{ID: cli:<nil>}} Room:{ID: cli:<nil>}}

func (StateEvent) WithClient

func (e StateEvent) WithClient(cli *Client) StateEvent

WithClient sets the underlying client of the RoomEvent. This has to be called after loading the struct from any kind of storage format (e.g JSON). Otherwise calling methods of e.Sender and e.Room will panic at some point

func (StateEvent) WithRoom

func (e StateEvent) WithRoom(room Room) StateEvent

WithRoom sets the originating room of the RoomEvent. This has to be called after loading the struct from any kind of storage format (e.g JSON). Otherwise calling methods of e.Room will panic at some point

type SyncOpts

type SyncOpts struct {
	// Execute this function if there if an error. If this returns nil, Sync
	// will sleep for SyncOpts.Timeout and then continue otherwise Sync()
	// will exit, and return the produced error.
	OnError func(error) error

	// Timeout in ms used for long-polling /sync. 3000 is used if this is negative.
	Timeout int64
}

SyncOpts are optional options to supply to the Sync function

type Thumbnail

type Thumbnail struct {
	Type    string
	Size    int64
	Content io.ReadCloser
}

Thumbnail represents a thumbnail of content in the content repository. Don't forget to Close() Thumbnail.Content after using it.

type ThumbnailMethod

type ThumbnailMethod string
const (
	Crop  ThumbnailMethod = "crop"
	Scale ThumbnailMethod = "scale"
)

type ToDeviceEvent

type ToDeviceEvent struct {
	Type    string                 `json:"type"`
	Sender  User                   `json:"sender"`
	Content map[string]interface{} `json:"content"`
}

func (ToDeviceEvent) Base

func (e ToDeviceEvent) Base() BaseEvent

func (ToDeviceEvent) Decrypt

func (e ToDeviceEvent) Decrypt() (ToDeviceEvent, error)

Decrypt decrypts the event. The libolm does not support decrypting the same olm message twice. For convenience we cache a copy of the decrypted plaintext internally, so that Decrypt() can be called multiple times. However this cache will be lost, when the underlying client is destroyed. This means, that it is not possible to serialize a encrypted ToDeviceEvent and decrypt it with another instance of Client.

func (ToDeviceEvent) MarshalJSON

func (e ToDeviceEvent) MarshalJSON() ([]byte, error)
Example
e := ToDeviceEvent{
	Type: "m.room.key",
	Content: map[string]interface{}{
		"session": 1234,
	},
	Sender: User{ID: "@alice:example.com"},
}

out, err := json.Marshal(e)
if err != nil {
	panic(err)
}

var indent bytes.Buffer
json.Indent(&indent, out, "", "\t")
indent.WriteTo(os.Stdout)
Output:

{
	"type": "m.room.key",
	"content": {
		"session": 1234
	},
	"sender": "@alice:example.com"
}

func (*ToDeviceEvent) UnmarshalJSON

func (e *ToDeviceEvent) UnmarshalJSON(data []byte) error
Example
raw := []byte(`
		{
			"content": {
				"session": "ABCED"
			},
			"type": "m.room.key",
			"sender": "@alice:example.com"
		}
	`)

var e ToDeviceEvent

err := json.Unmarshal(raw, &e)
if err != nil {
	panic(err)
}

fmt.Printf("%+v\n", e)
Output:

{Type:m.room.key Sender:{ID:@alice:example.com cli:<nil>} Content:map[session:ABCED]}

type User

type User struct {
	// The matrix ID of the user
	ID string
	// contains filtered or unexported fields
}

User is matrix User.

func LoadUser

func LoadUser(cli *Client, id string) (User, error)

LoadUser tries to find a matrix user. It returns a LogicError if the user does not exist.

func (User) Devices

func (u User) Devices() []Device

func (User) Displayname

func (u User) Displayname() (string, error)

Displayname gets the display name of the user by calling the HTTP API.

func (User) Notify

func (u User) Notify(c chan<- Event)

Notify implements the Notifier interface. This can not be used to be notified about ephemeral events relevant for the user (e.g. m.typing events), since it is not possible to attribute all ephemeral events to a certain set of users.

func (User) SendToDevice

func (u User) SendToDevice(encrypted bool, eventType string, messages map[string]map[string]interface{}) error

XXX wildcard allowed

func (User) Stop

func (u User) Stop(c chan<- Event)

Stop implements the Notifier interface

Directories

Path Synopsis
memory
Package memory contains an in-memory implementation of the Backend interface.
Package memory contains an in-memory implementation of the Backend interface.
examples
internal
canonicaljson
Parts of this package have been taken from https://github.com/tulir/mautrix-go/tree/master/crypto/canonicaljson
Parts of this package have been taken from https://github.com/tulir/mautrix-go/tree/master/crypto/canonicaljson
olm
Package olm contains bindings to libolm It was forked from https://github.com/Dhole/go-olm
Package olm contains bindings to libolm It was forked from https://github.com/Dhole/go-olm
util

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL