Artifact Content
Not logged in

Artifact 1e3705615d8a2a8cca322cd12a9bc478897d67b0:

// Copyright 2017 Michael Telatynski <>
// 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
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

package mxclient

import (
	log ""

// This is a Truncated RespInitialSync as we only need SOME information from it.
type RespInitialSync struct {
	// AccountData []gomatrix.Event `json:"account_data"`

	Messages gomatrix.RespMessages `json:"messages"`
	// Membership string                 `json:"membership"`
	State []gomatrix.Event `json:"state"`
	// RoomID     string                 `json:"room_id"`
	// Receipts   []*gomatrix.Event      `json:"receipts"`
	// Presence   []*PresenceEvent       `json:"presence"`

// Our Client extension adds some methods
// and a field expanding an MXC URL
type Client struct {
	MediaBaseURL string

// Register makes an HTTP request according to
func (m *Client) RoomInitialSync(roomID string, limit int) (resp *RespInitialSync, err error) {
	urlPath := m.BuildURLWithQuery([]string{"rooms", roomID, "initialSync"}, map[string]string{
		"limit": strconv.Itoa(limit),
	err = m.MakeRequest("GET", urlPath, nil, &resp)

type RespRoomDirectoryAlias struct {
	RoomID  string   `json:"room_id"`
	Servers []string `json:"servers"`

func (m *Client) GetRoomDirectoryAlias(roomAlias string) (resp *RespRoomDirectoryAlias, err error) {
	urlPath := m.BuildURL("directory", "room", roomAlias)
	err = m.MakeRequest("GET", urlPath, nil, &resp)

const minimumPagination = 64

// TODO split into runs of max size recursively otherwise synapse may enforce its own limit (999?)
func (m *Client) backpaginateRoom(room *Room, amount int) (int, error) {
	loggerWithFields := log.WithField("roomID", room.ID).WithField("amount", amount)
	loggerWithFields.Info("Backpaginating Room")

	amount = utils.Max(amount, minimumPagination)
	resp, err := m.Messages(room.ID, room.backPaginationToken, "", 'b', amount)

	if err != nil {
		loggerWithFields.WithError(err).Error("Failed Backpaginating Room")
		return -1, err

	room.concatBackpagination(resp.Chunk, resp.End)
	loggerWithFields.Info("Finished Backpaginating Room")
	return len(resp.Chunk), nil

func (m *Client) forwardpaginateRoom(room *Room, amount int) (int, error) {
	amount = utils.Max(amount, minimumPagination)
	resp, err := m.Messages(room.ID, room.forwardPaginationToken, "", 'f', amount)

	if err != nil {
		return -1, err

	// I would have thought to use resp.Start here but NOPE
	room.concatForwardPagination(resp.Chunk, resp.End)
	return len(resp.Chunk), nil

// NewRawClient returns a wrapped client with http client timeouts applied.
func NewRawClient(homeserverURL, mediaBaseURL, userID, accessToken string) (*Client, error) {
	cli, err := gomatrix.NewClient(homeserverURL, userID, accessToken)
	cli.Client = &http.Client{
		Timeout: 30 * time.Second,
	return &Client{cli, mediaBaseURL}, err

// The struct representing the json config file format.
type Config 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"`
	MediaBaseUrl string `json:"media_base_url"`

// NewClient returns a Client configured by the config file found at configPath or an error if encountered.
func NewClient(configPath string) (*Client, error) {
	var config Config

	if _, err := os.Stat(configPath); err != nil {
		return nil, errors.New("config file not found")

	file, err := ioutil.ReadFile(configPath)
	if err != nil {
		return nil, err

	json.Unmarshal(file, &config)

	if config.HomeServer == "" {
		return nil, errors.New("no user configuration found")

	if config.MediaBaseUrl == "" {
		config.MediaBaseUrl = config.HomeServer

	return NewRawClient(config.HomeServer, config.MediaBaseUrl, config.UserID, config.AccessToken)