aboutsummaryrefslogblamecommitdiff
path: root/appservice/matrix.go
blob: b2da2f25e58950da5399d618aeeedd50ff9e138e (plain) (tree)
1
2
3
4
5
6
7
8
9


                  

                       
             
                  
                 
              
 

                                        
                                                           
                                                         

 

                                                     



                                                                                       
                                                        
                                                                                                                              











                                                                            



                                                                             

       











                                                                
                                                        
 
                                                                       












                                                                                  
                                                                         
 
                                                                                         













                                                                                   
                                                                          
 
                                                                                          








                                                                  
                                                                       











                                                           
                                                                      







                                                         
                                               

























                                                                                               
                                                                                              







                                                                               
                                              
                                     



                                                        

                                            
                                                    
                                      

                                                         

                                                   
                  








                                                                        


                                                                                         

                             
                               
                                                        

                                            
                                                    




                                      
                                                                                                          





                              




                                                   
                                                                                                    








                                                                
                                                                                                  





                                                   
                                                                                                                                 





                                                    
                                                                                                                                  


                  
                                                                                                  
                                       








                                                                                  
                                          
                               
                                
         



                                                                                                                     

                                        


                                                                                  

                  
 
                                                                   
                                          




                                                                      
                                                                     
                                          



                                                                       
package appservice

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"time"

	log "github.com/sirupsen/logrus"

	"git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
	. "git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib"
)

const EASYBRIDGE_SYSTEM_PROTOCOL string = "✯◡✯"

func ezbrMxId() string {
	return fmt.Sprintf("@%s:%s", registration.SenderLocalpart, config.MatrixDomain)
}

func ezbrSystemRoom(user_mx_id string) (string, error) {
	return dbGetMxPmRoom(EASYBRIDGE_SYSTEM_PROTOCOL, connector.UserID("Easybridge"), ezbrMxId(), user_mx_id, "easybridge")
}

func ezbrSystemSend(user_mx_id string, msg string) {
	mx_room_id, err := ezbrSystemRoom(user_mx_id)
	if err == nil {
		err = mxSendMessageAs(mx_room_id, "m.text", msg, ezbrMxId())
	}
	if err != nil {
		log.Warnf("(%s) %s", user_mx_id, msg)
	}
}

func ezbrSystemSendf(user_mx_id string, format string, args ...interface{}) {
	ezbrSystemSend(user_mx_id, fmt.Sprintf(format, args...))
}

// ----

var httpClient *http.Client

func init() {
	tr := &http.Transport{
		MaxIdleConns:       10,
		IdleConnTimeout:    30 * time.Second,
		DisableCompression: true,
	}
	httpClient = &http.Client{Transport: tr}
}

func mxGetApiCall(endpoint string, response interface{}) error {
	log.Debugf("Matrix GET request: %s\n", endpoint)

	req, err := http.NewRequest("GET", config.Server+endpoint, nil)
	if err != nil {
		return err
	}

	return mxDoAndParse(req, response)
}

func mxPutApiCall(endpoint string, data interface{}, response interface{}) error {
	body, err := json.Marshal(data)
	if err != nil {
		return err
	}

	log.Debugf("Matrix PUT request: %s %s\n", endpoint, string(body))

	req, err := http.NewRequest("PUT", config.Server+endpoint, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/json")

	return mxDoAndParse(req, response)
}

func mxPostApiCall(endpoint string, data interface{}, response interface{}) error {
	body, err := json.Marshal(data)
	if err != nil {
		return err
	}

	log.Debugf("Matrix POST request: %s %s\n", endpoint, string(body))

	req, err := http.NewRequest("POST", config.Server+endpoint, bytes.NewBuffer(body))
	if err != nil {
		return err
	}
	req.Header.Add("Content-Type", "application/json")

	return mxDoAndParse(req, response)
}

func mxDoAndParse(req *http.Request, response interface{}) error {
	req.Header.Add("Authorization", "Bearer "+registration.AsToken)

	resp, err := httpClient.Do(req)
	if err != nil {
		return err
	}

	if resp.StatusCode != http.StatusOK {
		var e MxError
		err = json.NewDecoder(resp.Body).Decode(&e)
		if err != nil {
			return err
		}
		log.Debugf("Response (%d): %#v\n", resp.StatusCode, e)
		return &e
	}

	err = json.NewDecoder(resp.Body).Decode(response)
	if err != nil {
		return err
	}

	log.Debugf("Response: %#v\n", response)
	return nil
}

// ----

func mxRegisterUser(username string) error {
	req := RegisterRequest{
		Username: username,
	}
	var rep RegisterResponse
	return mxPostApiCall("/_matrix/client/r0/register?kind=user", &req, &rep)
}

func mxProfileDisplayname(userid string, displayname string) error {
	req := ProfileDisplaynameRequest{
		Displayname: displayname,
	}
	var rep struct{}
	err := mxPutApiCall(fmt.Sprintf("/_matrix/client/r0/profile/%s/displayname?user_id=%s",
		url.QueryEscape(userid), url.QueryEscape(userid)),
		&req, &rep)
	return err
}

func mxDirectoryRoom(alias string) (string, error) {
	var rep DirectoryRoomResponse
	err := mxGetApiCall("/_matrix/client/r0/directory/room/"+url.QueryEscape(alias), &rep)
	if err != nil {
		return "", err
	}
	return rep.RoomId, nil
}

func mxCreateRoom(name string, alias string, invite []string) (string, error) {
	rq := CreateRoomRequest{
		Preset:        "private_chat",
		RoomAliasName: alias,
		Name:          name,
		Topic:         "",
		Invite:        invite,
		CreationContent: map[string]interface{}{
			"m.federate": false,
		},
		PowerLevels: map[string]interface{}{
			"invite": 100,
			"events": map[string]interface{}{
				"m.room.topic":  0,
				"m.room.avatar": 0,
			},
		},
	}
	var rep CreateRoomResponse
	err := mxPostApiCall("/_matrix/client/r0/createRoom", &rq, &rep)
	if err != nil {
		return "", err
	}
	return rep.RoomId, nil
}

func mxCreateDirectRoomAs(name string, invite []string, as_user string) (string, error) {
	rq := CreateRoomNoAliasRequest{
		Preset: "private_chat",
		Name:   name,
		Topic:  "",
		Invite: invite,
		CreationContent: map[string]interface{}{
			"m.federate": false,
		},
		PowerLevels: map[string]interface{}{
			"invite": 100,
		},
		IsDirect: true,
	}
	var rep CreateRoomResponse
	err := mxPostApiCall("/_matrix/client/r0/createRoom?user_id="+url.QueryEscape(as_user), &rq, &rep)
	if err != nil {
		return "", err
	}
	return rep.RoomId, nil
}

func mxRoomInvite(room string, user string) error {
	rq := RoomInviteRequest{
		UserId: user,
	}
	var rep struct{}
	err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/invite", &rq, &rep)
	return err
}

func mxRoomKick(room string, user string, reason string) error {
	rq := RoomKickRequest{
		UserId: user,
		Reason: reason,
	}
	var rep struct{}
	err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/kick", &rq, &rep)
	return err
}

func mxRoomJoinAs(room string, user string) error {
	rq := struct{}{}
	var rep RoomJoinResponse
	err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/join?user_id="+url.QueryEscape(user), &rq, &rep)
	return err
}

func mxRoomLeaveAs(room string, user string) error {
	rq := struct{}{}
	var rep struct{}
	err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/leave?user_id="+url.QueryEscape(user), &rq, &rep)
	return err
}

func mxSendAs(room string, event_type string, content map[string]interface{}, user string) error {
	txn_id := time.Now().UnixNano()
	var rep RoomSendResponse
	err := mxPutApiCall(fmt.Sprintf(
		"/_matrix/client/r0/rooms/%s/send/%s/%d?user_id=%s",
		url.QueryEscape(room), event_type, txn_id, url.QueryEscape(user)),
		&content, &rep)
	return err
}

func mxSendMessageAs(room string, typ string, body string, user string) error {
	content := map[string]interface{}{
		"msgtype": typ,
		"body":    body,
	}
	return mxSendAs(room, "m.room.message", content, user)
}

func mxPutStateAs(room string, event_type string, key string, content map[string]interface{}, as_user string) error {
	var rep RoomSendResponse
	err := mxPutApiCall(fmt.Sprintf(
		"/_matrix/client/r0/rooms/%s/state/%s/%s?user_id=%s",
		url.QueryEscape(room), event_type, key, url.QueryEscape(as_user)),
		&content, &rep)
	return err
}

func mxRoomNameAs(room string, name string, as_user string) error {
	content := map[string]interface{}{
		"name": name,
	}
	return mxPutStateAs(room, "m.room.name", "", content, as_user)
}

func mxRoomTopicAs(room string, topic string, as_user string) error {
	content := map[string]interface{}{
		"topic": topic,
	}
	return mxPutStateAs(room, "m.room.topic", "", content, as_user)
}