diff options
Diffstat (limited to 'appservice')
-rw-r--r-- | appservice/account.go | 18 | ||||
-rw-r--r-- | appservice/db.go | 10 | ||||
-rw-r--r-- | appservice/matrix.go | 275 | ||||
-rw-r--r-- | appservice/names.go | 23 | ||||
-rw-r--r-- | appservice/server.go | 14 | ||||
-rw-r--r-- | appservice/util.go | 51 |
6 files changed, 74 insertions, 317 deletions
diff --git a/appservice/account.go b/appservice/account.go index e507230..311f266 100644 --- a/appservice/account.go +++ b/appservice/account.go @@ -81,7 +81,7 @@ func (a *Account) joinedInternal(roomId RoomID) error { 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") { err = nil } @@ -107,7 +107,7 @@ func (a *Account) leftInternal(roomId RoomID) error { 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") { err = nil } @@ -130,7 +130,7 @@ func (a *Account) userInfoUpdatedInternal(user UserID, info *UserInfo) error { } 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 { err = err2 } @@ -167,7 +167,7 @@ func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *Ro } 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 { err = err2 } @@ -175,7 +175,7 @@ func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *Ro if info.Name != "" { 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 { err = err2 } @@ -211,7 +211,7 @@ func (a *Account) eventInternal(event *Event) error { return err } - err = mxRoomInvite(mx_room_id, mx_user_id) + err = mx.RoomInvite(mx_room_id, mx_user_id) if err != nil { if strings.Contains(err.Error(), "already in the room") { err = nil @@ -219,7 +219,7 @@ func (a *Account) eventInternal(event *Event) error { 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 { log.Printf("%s join %s %s", a.Protocol, event.Author, 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 mxRoomLeaveAs(mx_room_id, mx_user_id) + return mx.RoomLeaveAs(mx_room_id, mx_user_id) } else { log.Printf("%s msg %s %s", a.Protocol, event.Author, event.Room) mx_room_id := "" @@ -249,6 +249,6 @@ func (a *Account) eventInternal(event *Event) error { 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) } } diff --git a/appservice/db.go b/appservice/db.go index 6423b95..5cefd30 100644 --- a/appservice/db.go +++ b/appservice/db.go @@ -88,13 +88,13 @@ func dbGetMxRoom(protocol string, roomId connector.RoomID) (string, error) { if must_create { alias := roomAlias(protocol, roomId) // 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 err != nil { 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 { log.Printf("Could not create room for %s: %s", name, err) return "", err @@ -125,7 +125,7 @@ func dbGetMxPmRoom(protocol string, them connector.UserID, themMxId string, usMx if must_create { 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 { log.Printf("Could not create room for %s: %s", name, err) return "", err @@ -161,7 +161,7 @@ func dbGetMxUser(protocol string, userId connector.UserID) (string, error) { if must_create { username := userMxId(protocol, userId) - err := mxRegisterUser(username) + err := mx.RegisterUser(username) if err != nil { if mxE, ok := err.(*mxlib.MxError); !ok || mxE.ErrCode != "M_USER_IN_USE" { 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) - mxProfileDisplayname(mxid, fmt.Sprintf("%s (%s)", userId, protocol)) + mx.ProfileDisplayname(mxid, fmt.Sprintf("%s (%s)", userId, protocol)) user = DbUserMap{ Protocol: protocol, diff --git a/appservice/matrix.go b/appservice/matrix.go deleted file mode 100644 index 6b99992..0000000 --- a/appservice/matrix.go +++ /dev/null @@ -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) -} diff --git a/appservice/names.go b/appservice/names.go deleted file mode 100644 index 9ed00f8..0000000 --- a/appservice/names.go +++ /dev/null @@ -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 -} diff --git a/appservice/server.go b/appservice/server.go index aa43935..d3ecb43 100644 --- a/appservice/server.go +++ b/appservice/server.go @@ -24,20 +24,24 @@ type Config struct { var registration *mxlib.Registration var config *Config +var mx *mxlib.Client + func Start(r *mxlib.Registration, c *Config) (chan error, error) { registration = r config = c + mx = mxlib.NewClient(c.Server, r.AsToken) + err := InitDb() if err != nil { 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" { 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 { return nil, err } @@ -134,7 +138,7 @@ func handleTxnEvent(e *mxlib.Event) error { ev.Room = room.RoomID return acct.Conn.Send(ev) } 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) } } else { @@ -146,7 +150,7 @@ func handleTxnEvent(e *mxlib.Event) error { if pm_room := dbIsPmRoom(e.RoomId); pm_room != nil { // If leaving a PM room, we must delete it them_mx := userMxId(pm_room.Protocol, pm_room.UserID) - mxRoomLeaveAs(e.RoomId, them_mx) + mx.RoomLeaveAs(e.RoomId, them_mx) db.Delete(pm_room) return nil } else if room := dbIsPublicRoom(e.RoomId); room != nil { @@ -157,7 +161,7 @@ func handleTxnEvent(e *mxlib.Event) error { return nil // TODO: manage autojoin list, remove this room } 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) } } else { diff --git a/appservice/util.go b/appservice/util.go new file mode 100644 index 0000000..4175fb0 --- /dev/null +++ b/appservice/util.go @@ -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 +} |