Refactor: move all mx* functions to mxlib/client.go
This commit is contained in:
parent
65ddb6be2b
commit
a4cf41536f
8 changed files with 328 additions and 317 deletions
|
@ -81,7 +81,7 @@ func (a *Account) joinedInternal(roomId RoomID) error {
|
||||||
|
|
||||||
log.Debugf("Joined %s (%s)\n", roomId, a.MatrixUser)
|
log.Debugf("Joined %s (%s)\n", roomId, a.MatrixUser)
|
||||||
|
|
||||||
err = mxRoomInvite(mx_room_id, a.MatrixUser)
|
err = mx.RoomInvite(mx_room_id, a.MatrixUser)
|
||||||
if err != nil && strings.Contains(err.Error(), "already in the room") {
|
if err != nil && strings.Contains(err.Error(), "already in the room") {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
@ -107,7 +107,7 @@ func (a *Account) leftInternal(roomId RoomID) error {
|
||||||
|
|
||||||
log.Printf("Left %s (%s)\n", roomId, a.MatrixUser)
|
log.Printf("Left %s (%s)\n", roomId, a.MatrixUser)
|
||||||
|
|
||||||
err = mxRoomKick(mx_room_id, a.MatrixUser, fmt.Sprintf("got leave room event on %s", a.Protocol))
|
err = mx.RoomKick(mx_room_id, a.MatrixUser, fmt.Sprintf("got leave room event on %s", a.Protocol))
|
||||||
if err != nil && strings.Contains(err.Error(), "not in the room") {
|
if err != nil && strings.Contains(err.Error(), "not in the room") {
|
||||||
err = nil
|
err = nil
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ func (a *Account) userInfoUpdatedInternal(user UserID, info *UserInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.DisplayName != "" {
|
if info.DisplayName != "" {
|
||||||
err2 := mxProfileDisplayname(mx_user_id, fmt.Sprintf("%s (%s)", info.DisplayName, a.Protocol))
|
err2 := mx.ProfileDisplayname(mx_user_id, fmt.Sprintf("%s (%s)", info.DisplayName, a.Protocol))
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
err = err2
|
err = err2
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.Topic != "" {
|
if info.Topic != "" {
|
||||||
err2 := mxRoomTopicAs(mx_room_id, info.Topic, as_mxid)
|
err2 := mx.RoomTopicAs(mx_room_id, info.Topic, as_mxid)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
err = err2
|
err = err2
|
||||||
}
|
}
|
||||||
|
@ -175,7 +175,7 @@ func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *Ro
|
||||||
|
|
||||||
if info.Name != "" {
|
if info.Name != "" {
|
||||||
name := fmt.Sprintf("%s (%s)", info.Name, a.Protocol)
|
name := fmt.Sprintf("%s (%s)", info.Name, a.Protocol)
|
||||||
err2 := mxRoomNameAs(mx_room_id, name, as_mxid)
|
err2 := mx.RoomNameAs(mx_room_id, name, as_mxid)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
err = err2
|
err = err2
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ func (a *Account) eventInternal(event *Event) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mxRoomInvite(mx_room_id, mx_user_id)
|
err = mx.RoomInvite(mx_room_id, mx_user_id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "already in the room") {
|
if strings.Contains(err.Error(), "already in the room") {
|
||||||
err = nil
|
err = nil
|
||||||
|
@ -219,7 +219,7 @@ func (a *Account) eventInternal(event *Event) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mxRoomJoinAs(mx_room_id, mx_user_id)
|
return mx.RoomJoinAs(mx_room_id, mx_user_id)
|
||||||
} else if event.Type == EVENT_LEAVE {
|
} else if event.Type == EVENT_LEAVE {
|
||||||
log.Printf("%s join %s %s", a.Protocol, event.Author, event.Room)
|
log.Printf("%s join %s %s", a.Protocol, event.Author, event.Room)
|
||||||
mx_room_id, err := dbGetMxRoom(a.Protocol, event.Room)
|
mx_room_id, err := dbGetMxRoom(a.Protocol, event.Room)
|
||||||
|
@ -227,7 +227,7 @@ func (a *Account) eventInternal(event *Event) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return mxRoomLeaveAs(mx_room_id, mx_user_id)
|
return mx.RoomLeaveAs(mx_room_id, mx_user_id)
|
||||||
} else {
|
} else {
|
||||||
log.Printf("%s msg %s %s", a.Protocol, event.Author, event.Room)
|
log.Printf("%s msg %s %s", a.Protocol, event.Author, event.Room)
|
||||||
mx_room_id := ""
|
mx_room_id := ""
|
||||||
|
@ -249,6 +249,6 @@ func (a *Account) eventInternal(event *Event) error {
|
||||||
typ = "m.emote"
|
typ = "m.emote"
|
||||||
}
|
}
|
||||||
|
|
||||||
return mxSendMessageAs(mx_room_id, typ, event.Text, mx_user_id)
|
return mx.SendMessageAs(mx_room_id, typ, event.Text, mx_user_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,13 +88,13 @@ func dbGetMxRoom(protocol string, roomId connector.RoomID) (string, error) {
|
||||||
if must_create {
|
if must_create {
|
||||||
alias := roomAlias(protocol, roomId)
|
alias := roomAlias(protocol, roomId)
|
||||||
// Lookup alias
|
// Lookup alias
|
||||||
mx_room_id, err := mxDirectoryRoom(fmt.Sprintf("#%s:%s", alias, config.MatrixDomain))
|
mx_room_id, err := mx.DirectoryRoom(fmt.Sprintf("#%s:%s", alias, config.MatrixDomain))
|
||||||
|
|
||||||
// If no alias found, create room
|
// If no alias found, create room
|
||||||
if err != nil {
|
if err != nil {
|
||||||
name := fmt.Sprintf("%s (%s)", roomId, protocol)
|
name := fmt.Sprintf("%s (%s)", roomId, protocol)
|
||||||
|
|
||||||
mx_room_id, err = mxCreateRoom(name, alias, []string{})
|
mx_room_id, err = mx.CreateRoom(name, alias, []string{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Could not create room for %s: %s", name, err)
|
log.Printf("Could not create room for %s: %s", name, err)
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -125,7 +125,7 @@ func dbGetMxPmRoom(protocol string, them connector.UserID, themMxId string, usMx
|
||||||
if must_create {
|
if must_create {
|
||||||
name := fmt.Sprintf("%s (%s)", them, protocol)
|
name := fmt.Sprintf("%s (%s)", them, protocol)
|
||||||
|
|
||||||
mx_room_id, err := mxCreateDirectRoomAs([]string{usMxId}, themMxId)
|
mx_room_id, err := mx.CreateDirectRoomAs([]string{usMxId}, themMxId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Could not create room for %s: %s", name, err)
|
log.Printf("Could not create room for %s: %s", name, err)
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -161,7 +161,7 @@ func dbGetMxUser(protocol string, userId connector.UserID) (string, error) {
|
||||||
if must_create {
|
if must_create {
|
||||||
username := userMxId(protocol, userId)
|
username := userMxId(protocol, userId)
|
||||||
|
|
||||||
err := mxRegisterUser(username)
|
err := mx.RegisterUser(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if mxE, ok := err.(*mxlib.MxError); !ok || mxE.ErrCode != "M_USER_IN_USE" {
|
if mxE, ok := err.(*mxlib.MxError); !ok || mxE.ErrCode != "M_USER_IN_USE" {
|
||||||
log.Printf("Could not register %s: %s", username, err)
|
log.Printf("Could not register %s: %s", username, err)
|
||||||
|
@ -170,7 +170,7 @@ func dbGetMxUser(protocol string, userId connector.UserID) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
mxid := fmt.Sprintf("@%s:%s", username, config.MatrixDomain)
|
mxid := fmt.Sprintf("@%s:%s", username, config.MatrixDomain)
|
||||||
mxProfileDisplayname(mxid, fmt.Sprintf("%s (%s)", userId, protocol))
|
mx.ProfileDisplayname(mxid, fmt.Sprintf("%s (%s)", userId, protocol))
|
||||||
|
|
||||||
user = DbUserMap{
|
user = DbUserMap{
|
||||||
Protocol: protocol,
|
Protocol: protocol,
|
||||||
|
|
|
@ -1,275 +0,0 @@
|
||||||
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(invite []string, as_user string) (string, error) {
|
|
||||||
rq := CreateDirectRoomRequest{
|
|
||||||
Preset: "private_chat",
|
|
||||||
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)
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
package appservice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
|
||||||
)
|
|
||||||
|
|
||||||
func roomAlias(protocol string, id RoomID) string {
|
|
||||||
return fmt.Sprintf("_ezbr__%s__%s", safeStringForId(string(id)), protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
func userMxId(protocol string, id UserID) string {
|
|
||||||
return fmt.Sprintf("_ezbr__%s__%s", safeStringForId(string(id)), protocol)
|
|
||||||
}
|
|
||||||
|
|
||||||
func safeStringForId(in string) string {
|
|
||||||
id2 := strings.ReplaceAll(in, "#", "")
|
|
||||||
id2 = strings.ReplaceAll(id2, "@", "__")
|
|
||||||
id2 = strings.ReplaceAll(id2, ":", "_")
|
|
||||||
return id2
|
|
||||||
}
|
|
|
@ -24,20 +24,24 @@ type Config struct {
|
||||||
var registration *mxlib.Registration
|
var registration *mxlib.Registration
|
||||||
var config *Config
|
var config *Config
|
||||||
|
|
||||||
|
var mx *mxlib.Client
|
||||||
|
|
||||||
func Start(r *mxlib.Registration, c *Config) (chan error, error) {
|
func Start(r *mxlib.Registration, c *Config) (chan error, error) {
|
||||||
registration = r
|
registration = r
|
||||||
config = c
|
config = c
|
||||||
|
|
||||||
|
mx = mxlib.NewClient(c.Server, r.AsToken)
|
||||||
|
|
||||||
err := InitDb()
|
err := InitDb()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = mxRegisterUser(registration.SenderLocalpart)
|
err = mx.RegisterUser(registration.SenderLocalpart)
|
||||||
if mxe, ok := err.(*mxlib.MxError); !ok || mxe.ErrCode != "M_USER_IN_USE" {
|
if mxe, ok := err.(*mxlib.MxError); !ok || mxe.ErrCode != "M_USER_IN_USE" {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = mxProfileDisplayname(ezbrMxId(), fmt.Sprintf("Easybridge (%s)", EASYBRIDGE_SYSTEM_PROTOCOL))
|
err = mx.ProfileDisplayname(ezbrMxId(), fmt.Sprintf("Easybridge (%s)", EASYBRIDGE_SYSTEM_PROTOCOL))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -134,7 +138,7 @@ func handleTxnEvent(e *mxlib.Event) error {
|
||||||
ev.Room = room.RoomID
|
ev.Room = room.RoomID
|
||||||
return acct.Conn.Send(ev)
|
return acct.Conn.Send(ev)
|
||||||
} else {
|
} else {
|
||||||
mxRoomKick(e.RoomId, e.Sender, fmt.Sprintf("Not present in %s on %s, please talk with Easybridge to rejoin", room.RoomID, room.Protocol))
|
mx.RoomKick(e.RoomId, e.Sender, fmt.Sprintf("Not present in %s on %s, please talk with Easybridge to rejoin", room.RoomID, room.Protocol))
|
||||||
return fmt.Errorf("not joined %s on %s", room.RoomID, room.Protocol)
|
return fmt.Errorf("not joined %s on %s", room.RoomID, room.Protocol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -146,7 +150,7 @@ func handleTxnEvent(e *mxlib.Event) error {
|
||||||
if pm_room := dbIsPmRoom(e.RoomId); pm_room != nil {
|
if pm_room := dbIsPmRoom(e.RoomId); pm_room != nil {
|
||||||
// If leaving a PM room, we must delete it
|
// If leaving a PM room, we must delete it
|
||||||
them_mx := userMxId(pm_room.Protocol, pm_room.UserID)
|
them_mx := userMxId(pm_room.Protocol, pm_room.UserID)
|
||||||
mxRoomLeaveAs(e.RoomId, them_mx)
|
mx.RoomLeaveAs(e.RoomId, them_mx)
|
||||||
db.Delete(pm_room)
|
db.Delete(pm_room)
|
||||||
return nil
|
return nil
|
||||||
} else if room := dbIsPublicRoom(e.RoomId); room != nil {
|
} else if room := dbIsPublicRoom(e.RoomId); room != nil {
|
||||||
|
@ -157,7 +161,7 @@ func handleTxnEvent(e *mxlib.Event) error {
|
||||||
return nil
|
return nil
|
||||||
// TODO: manage autojoin list, remove this room
|
// TODO: manage autojoin list, remove this room
|
||||||
} else {
|
} else {
|
||||||
mxRoomKick(e.RoomId, e.Sender, fmt.Sprintf("Not present in %s on %s, please talk with Easybridge to rejoin", room.RoomID, room.Protocol))
|
mx.RoomKick(e.RoomId, e.Sender, fmt.Sprintf("Not present in %s on %s, please talk with Easybridge to rejoin", room.RoomID, room.Protocol))
|
||||||
return fmt.Errorf("not joined %s on %s", room.RoomID, room.Protocol)
|
return fmt.Errorf("not joined %s on %s", room.RoomID, room.Protocol)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
51
appservice/util.go
Normal file
51
appservice/util.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package appservice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
. "git.deuxfleurs.fr/Deuxfleurs/easybridge/connector"
|
||||||
|
)
|
||||||
|
|
||||||
|
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, 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 = mx.SendMessageAs(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...))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
func roomAlias(protocol string, id RoomID) string {
|
||||||
|
return fmt.Sprintf("_ezbr__%s__%s", safeStringForId(string(id)), protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func userMxId(protocol string, id UserID) string {
|
||||||
|
return fmt.Sprintf("_ezbr__%s__%s", safeStringForId(string(id)), protocol)
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeStringForId(in string) string {
|
||||||
|
id2 := strings.ReplaceAll(in, "#", "")
|
||||||
|
id2 = strings.ReplaceAll(id2, "@", "__")
|
||||||
|
id2 = strings.ReplaceAll(id2, ":", "_")
|
||||||
|
return id2
|
||||||
|
}
|
BIN
easybridge.jpg
Normal file
BIN
easybridge.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 39 KiB |
254
mxlib/client.go
Normal file
254
mxlib/client.go
Normal file
|
@ -0,0 +1,254 @@
|
||||||
|
package mxlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
Server string
|
||||||
|
Token string
|
||||||
|
httpClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClient(server string, token string) *Client {
|
||||||
|
tr := &http.Transport{
|
||||||
|
MaxIdleConns: 10,
|
||||||
|
IdleConnTimeout: 30 * time.Second,
|
||||||
|
DisableCompression: true,
|
||||||
|
}
|
||||||
|
return &Client{
|
||||||
|
Server: server,
|
||||||
|
Token: token,
|
||||||
|
httpClient: &http.Client{Transport: tr},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) GetApiCall(endpoint string, response interface{}) error {
|
||||||
|
log.Debugf("Matrix GET request: %s\n", endpoint)
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", mx.Server+endpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return mx.DoAndParse(req, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) PutApiCall(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", mx.Server+endpoint, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
return mx.DoAndParse(req, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) PostApiCall(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", mx.Server+endpoint, bytes.NewBuffer(body))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
req.Header.Add("Content-Type", "application/json")
|
||||||
|
|
||||||
|
return mx.DoAndParse(req, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) DoAndParse(req *http.Request, response interface{}) error {
|
||||||
|
req.Header.Add("Authorization", "Bearer "+mx.Token)
|
||||||
|
|
||||||
|
resp, err := mx.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 (mx *Client) RegisterUser(username string) error {
|
||||||
|
req := RegisterRequest{
|
||||||
|
Username: username,
|
||||||
|
}
|
||||||
|
var rep RegisterResponse
|
||||||
|
return mx.PostApiCall("/_matrix/client/r0/register?kind=user", &req, &rep)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) ProfileDisplayname(userid string, displayname string) error {
|
||||||
|
req := ProfileDisplaynameRequest{
|
||||||
|
Displayname: displayname,
|
||||||
|
}
|
||||||
|
var rep struct{}
|
||||||
|
err := mx.PutApiCall(fmt.Sprintf("/_matrix/client/r0/profile/%s/displayname?user_id=%s",
|
||||||
|
url.QueryEscape(userid), url.QueryEscape(userid)),
|
||||||
|
&req, &rep)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) DirectoryRoom(alias string) (string, error) {
|
||||||
|
var rep DirectoryRoomResponse
|
||||||
|
err := mx.GetApiCall("/_matrix/client/r0/directory/room/"+url.QueryEscape(alias), &rep)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return rep.RoomId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) CreateRoom(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 := mx.PostApiCall("/_matrix/client/r0/createRoom", &rq, &rep)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return rep.RoomId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) CreateDirectRoomAs(invite []string, as_user string) (string, error) {
|
||||||
|
rq := CreateDirectRoomRequest{
|
||||||
|
Preset: "private_chat",
|
||||||
|
Topic: "",
|
||||||
|
Invite: invite,
|
||||||
|
CreationContent: map[string]interface{}{
|
||||||
|
"m.federate": false,
|
||||||
|
},
|
||||||
|
PowerLevels: map[string]interface{}{
|
||||||
|
"invite": 100,
|
||||||
|
},
|
||||||
|
IsDirect: true,
|
||||||
|
}
|
||||||
|
var rep CreateRoomResponse
|
||||||
|
err := mx.PostApiCall("/_matrix/client/r0/createRoom?user_id="+url.QueryEscape(as_user), &rq, &rep)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return rep.RoomId, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) RoomInvite(room string, user string) error {
|
||||||
|
rq := RoomInviteRequest{
|
||||||
|
UserId: user,
|
||||||
|
}
|
||||||
|
var rep struct{}
|
||||||
|
err := mx.PostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/invite", &rq, &rep)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) RoomKick(room string, user string, reason string) error {
|
||||||
|
rq := RoomKickRequest{
|
||||||
|
UserId: user,
|
||||||
|
Reason: reason,
|
||||||
|
}
|
||||||
|
var rep struct{}
|
||||||
|
err := mx.PostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/kick", &rq, &rep)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) RoomJoinAs(room string, user string) error {
|
||||||
|
rq := struct{}{}
|
||||||
|
var rep RoomJoinResponse
|
||||||
|
err := mx.PostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/join?user_id="+url.QueryEscape(user), &rq, &rep)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) RoomLeaveAs(room string, user string) error {
|
||||||
|
rq := struct{}{}
|
||||||
|
var rep struct{}
|
||||||
|
err := mx.PostApiCall("/_matrix/client/r0/rooms/"+url.QueryEscape(room)+"/leave?user_id="+url.QueryEscape(user), &rq, &rep)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) SendAs(room string, event_type string, content map[string]interface{}, user string) error {
|
||||||
|
txn_id := time.Now().UnixNano()
|
||||||
|
var rep RoomSendResponse
|
||||||
|
err := mx.PutApiCall(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 (mx *Client) SendMessageAs(room string, typ string, body string, user string) error {
|
||||||
|
content := map[string]interface{}{
|
||||||
|
"msgtype": typ,
|
||||||
|
"body": body,
|
||||||
|
}
|
||||||
|
return mx.SendAs(room, "m.room.message", content, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) PutStateAs(room string, event_type string, key string, content map[string]interface{}, as_user string) error {
|
||||||
|
var rep RoomSendResponse
|
||||||
|
err := mx.PutApiCall(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 (mx *Client) RoomNameAs(room string, name string, as_user string) error {
|
||||||
|
content := map[string]interface{}{
|
||||||
|
"name": name,
|
||||||
|
}
|
||||||
|
return mx.PutStateAs(room, "m.room.name", "", content, as_user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mx *Client) RoomTopicAs(room string, topic string, as_user string) error {
|
||||||
|
content := map[string]interface{}{
|
||||||
|
"topic": topic,
|
||||||
|
}
|
||||||
|
return mx.PutStateAs(room, "m.room.topic", "", content, as_user)
|
||||||
|
}
|
Loading…
Reference in a new issue