2020-02-16 21:07:41 +00:00
|
|
|
package appservice
|
|
|
|
|
|
|
|
import (
|
2020-02-17 18:02:26 +00:00
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
2020-02-16 21:07:41 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
2020-02-17 18:02:26 +00:00
|
|
|
"net/url"
|
2020-02-16 21:07:41 +00:00
|
|
|
"time"
|
|
|
|
|
2020-02-17 08:41:08 +00:00
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
|
2020-02-17 18:52:50 +00:00
|
|
|
"git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
2020-02-17 20:25:22 +00:00
|
|
|
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/mxlib"
|
2020-02-16 21:07:41 +00:00
|
|
|
)
|
|
|
|
|
2020-02-17 20:04:21 +00:00
|
|
|
const EASYBRIDGE_SYSTEM_PROTOCOL string = "✯◡✯"
|
|
|
|
|
2020-02-17 15:28:32 +00:00
|
|
|
func ezbrMxId() string {
|
|
|
|
return fmt.Sprintf("@%s:%s", registration.SenderLocalpart, config.MatrixDomain)
|
|
|
|
}
|
|
|
|
|
2020-02-17 18:52:50 +00:00
|
|
|
func ezbrSystemRoom(user_mx_id string) (string, error) {
|
2020-02-17 20:04:21 +00:00
|
|
|
return dbGetMxPmRoom(EASYBRIDGE_SYSTEM_PROTOCOL, connector.UserID("Easybridge"), ezbrMxId(), user_mx_id, "easybridge")
|
2020-02-17 18:52:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-17 20:04:21 +00:00
|
|
|
func ezbrSystemSendf(user_mx_id string, format string, args ...interface{}) {
|
|
|
|
ezbrSystemSend(user_mx_id, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
|
2020-02-17 15:28:32 +00:00
|
|
|
// ----
|
|
|
|
|
2020-02-16 21:07:41 +00:00
|
|
|
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 {
|
2020-02-17 08:41:08 +00:00
|
|
|
log.Debugf("Matrix GET request: %s\n", endpoint)
|
2020-02-16 21:07:41 +00:00
|
|
|
|
2020-02-17 18:02:26 +00:00
|
|
|
req, err := http.NewRequest("GET", config.Server+endpoint, nil)
|
2020-02-16 21:07:41 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-02-17 08:41:08 +00:00
|
|
|
log.Debugf("Matrix PUT request: %s %s\n", endpoint, string(body))
|
2020-02-16 21:07:41 +00:00
|
|
|
|
2020-02-17 18:02:26 +00:00
|
|
|
req, err := http.NewRequest("PUT", config.Server+endpoint, bytes.NewBuffer(body))
|
2020-02-16 21:07:41 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-02-17 08:41:08 +00:00
|
|
|
log.Debugf("Matrix POST request: %s %s\n", endpoint, string(body))
|
2020-02-16 21:07:41 +00:00
|
|
|
|
2020-02-17 18:02:26 +00:00
|
|
|
req, err := http.NewRequest("POST", config.Server+endpoint, bytes.NewBuffer(body))
|
2020-02-16 21:07:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
|
|
|
|
return mxDoAndParse(req, response)
|
|
|
|
}
|
|
|
|
|
|
|
|
func mxDoAndParse(req *http.Request, response interface{}) error {
|
2020-02-17 18:02:26 +00:00
|
|
|
req.Header.Add("Authorization", "Bearer "+registration.AsToken)
|
2020-02-16 21:07:41 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
2020-02-17 08:41:08 +00:00
|
|
|
log.Debugf("Response (%d): %#v\n", resp.StatusCode, e)
|
2020-02-16 21:07:41 +00:00
|
|
|
return &e
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.NewDecoder(resp.Body).Decode(response)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-02-17 08:41:08 +00:00
|
|
|
log.Debugf("Response: %#v\n", response)
|
2020-02-16 21:07:41 +00:00
|
|
|
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
|
2020-02-17 18:02:26 +00:00
|
|
|
err := mxGetApiCall("/_matrix/client/r0/directory/room/"+url.QueryEscape(alias), &rep)
|
2020-02-16 21:07:41 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return rep.RoomId, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func mxCreateRoom(name string, alias string, invite []string) (string, error) {
|
|
|
|
rq := CreateRoomRequest{
|
2020-02-17 18:02:26 +00:00
|
|
|
Preset: "private_chat",
|
2020-02-16 21:07:41 +00:00
|
|
|
RoomAliasName: alias,
|
2020-02-17 18:02:26 +00:00
|
|
|
Name: name,
|
|
|
|
Topic: "",
|
|
|
|
Invite: invite,
|
|
|
|
CreationContent: map[string]interface{}{
|
2020-02-16 21:07:41 +00:00
|
|
|
"m.federate": false,
|
|
|
|
},
|
2020-02-17 18:02:26 +00:00
|
|
|
PowerLevels: map[string]interface{}{
|
2020-02-16 21:57:30 +00:00
|
|
|
"invite": 100,
|
2020-02-17 18:02:26 +00:00
|
|
|
"events": map[string]interface{}{
|
|
|
|
"m.room.topic": 0,
|
2020-02-17 14:30:01 +00:00
|
|
|
"m.room.avatar": 0,
|
|
|
|
},
|
2020-02-16 21:57:30 +00:00
|
|
|
},
|
2020-02-16 21:07:41 +00:00
|
|
|
}
|
|
|
|
var rep CreateRoomResponse
|
|
|
|
err := mxPostApiCall("/_matrix/client/r0/createRoom", &rq, &rep)
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return rep.RoomId, nil
|
|
|
|
}
|
|
|
|
|
2020-02-16 21:57:30 +00:00
|
|
|
func mxCreateDirectRoomAs(name string, invite []string, as_user string) (string, error) {
|
|
|
|
rq := CreateRoomNoAliasRequest{
|
|
|
|
Preset: "private_chat",
|
2020-02-17 18:02:26 +00:00
|
|
|
Name: name,
|
|
|
|
Topic: "",
|
2020-02-16 21:57:30 +00:00
|
|
|
Invite: invite,
|
2020-02-17 18:02:26 +00:00
|
|
|
CreationContent: map[string]interface{}{
|
2020-02-16 21:57:30 +00:00
|
|
|
"m.federate": false,
|
|
|
|
},
|
2020-02-17 18:02:26 +00:00
|
|
|
PowerLevels: map[string]interface{}{
|
2020-02-16 21:57:30 +00:00
|
|
|
"invite": 100,
|
|
|
|
},
|
|
|
|
IsDirect: true,
|
|
|
|
}
|
|
|
|
var rep CreateRoomResponse
|
2020-02-17 18:02:26 +00:00
|
|
|
err := mxPostApiCall("/_matrix/client/r0/createRoom?user_id="+url.QueryEscape(as_user), &rq, &rep)
|
2020-02-16 21:57:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
return rep.RoomId, nil
|
|
|
|
}
|
|
|
|
|
2020-02-16 21:07:41 +00:00
|
|
|
func mxRoomInvite(room string, user string) error {
|
|
|
|
rq := RoomInviteRequest{
|
|
|
|
UserId: user,
|
|
|
|
}
|
|
|
|
var rep struct{}
|
2020-02-17 18:02:26 +00:00
|
|
|
err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/invite", &rq, &rep)
|
2020-02-16 21:07:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func mxRoomKick(room string, user string, reason string) error {
|
|
|
|
rq := RoomKickRequest{
|
|
|
|
UserId: user,
|
|
|
|
Reason: reason,
|
|
|
|
}
|
|
|
|
var rep struct{}
|
2020-02-17 18:02:26 +00:00
|
|
|
err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/kick", &rq, &rep)
|
2020-02-16 21:07:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func mxRoomJoinAs(room string, user string) error {
|
|
|
|
rq := struct{}{}
|
|
|
|
var rep RoomJoinResponse
|
2020-02-17 18:02:26 +00:00
|
|
|
err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/join?user_id="+url.QueryEscape(user), &rq, &rep)
|
2020-02-16 21:07:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
func mxRoomLeaveAs(room string, user string) error {
|
|
|
|
rq := struct{}{}
|
|
|
|
var rep struct{}
|
2020-02-17 18:02:26 +00:00
|
|
|
err := mxPostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/leave?user_id="+url.QueryEscape(user), &rq, &rep)
|
2020-02-16 21:07:41 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-02-17 14:30:01 +00:00
|
|
|
func mxSendAs(room string, event_type string, content map[string]interface{}, user string) error {
|
2020-02-16 21:07:41 +00:00
|
|
|
txn_id := time.Now().UnixNano()
|
2020-02-17 14:30:01 +00:00
|
|
|
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 {
|
2020-02-17 18:02:26 +00:00
|
|
|
content := map[string]interface{}{
|
2020-02-17 14:30:01 +00:00
|
|
|
"msgtype": typ,
|
2020-02-17 18:02:26 +00:00
|
|
|
"body": body,
|
2020-02-16 21:07:41 +00:00
|
|
|
}
|
2020-02-17 14:30:01 +00:00
|
|
|
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 {
|
2020-02-16 21:07:41 +00:00
|
|
|
var rep RoomSendResponse
|
|
|
|
err := mxPutApiCall(fmt.Sprintf(
|
2020-02-17 14:30:01 +00:00
|
|
|
"/_matrix/client/r0/rooms/%s/state/%s/%s?user_id=%s",
|
|
|
|
url.QueryEscape(room), event_type, key, url.QueryEscape(as_user)),
|
|
|
|
&content, &rep)
|
2020-02-16 21:07:41 +00:00
|
|
|
return err
|
|
|
|
}
|
2020-02-17 14:30:01 +00:00
|
|
|
|
2020-02-17 15:28:32 +00:00
|
|
|
func mxRoomNameAs(room string, name string, as_user string) error {
|
2020-02-17 18:02:26 +00:00
|
|
|
content := map[string]interface{}{
|
2020-02-17 15:28:32 +00:00
|
|
|
"name": name,
|
|
|
|
}
|
|
|
|
return mxPutStateAs(room, "m.room.name", "", content, as_user)
|
|
|
|
}
|
|
|
|
|
2020-02-17 14:30:01 +00:00
|
|
|
func mxRoomTopicAs(room string, topic string, as_user string) error {
|
2020-02-17 18:02:26 +00:00
|
|
|
content := map[string]interface{}{
|
2020-02-17 14:30:01 +00:00
|
|
|
"topic": topic,
|
|
|
|
}
|
|
|
|
return mxPutStateAs(room, "m.room.topic", "", content, as_user)
|
|
|
|
}
|