Browse Source

Add some documentation

Brendan Abolivier 6 years ago
parent
commit
d22266b26b
Signed by: Brendan Abolivier <contact@brendanabolivier.com> GPG key ID: 8EF1500759F70623
4 changed files with 107 additions and 10 deletions
  1. 50
    0
      README.md
  2. 11
    0
      config.sample.yaml
  3. 15
    3
      src/config/config.go
  4. 31
    7
      src/fixer/main.go

+ 50
- 0
README.md View File

1
+# Fixer for Matrix's Facebook Messenger puppet bridge
2
+
3
+I'm using the [Matrix↔️Facebook Messenger puppet bridge](https://github.com/matrix-hacks/matrix-puppet-facebook) to talk with my friends on Facebook using [Riot](https://riot.im) plugged to my [Matrix](https://matrix.org) homeserver.
4
+
5
+When I'm having a 1:1 chat with a Facebook friend, the bridge creates a with the friend, the appservice's bot and myself. Because of that, and also because it doesn't automatically set the room's avatar and name, I have troubles identifying what's happening in that room.
6
+
7
+This small tool will take the local part of the room ID created by the Matrix↔️Facebook Messenger bot once the friend has joined it, identify the friend, and grab their avatar and display name to set the room's.
8
+
9
+**Note: I'm not shaming anyone here, as the bridge is currently still in development. This is totally a temporary solution.**
10
+
11
+## Install
12
+
13
+You'll first need a Go install, and [gb](https://getgb.io/):
14
+
15
+```
16
+go get github.com/constabulary/gb/...
17
+```
18
+
19
+Then clone this repo on your computer (or server), walk into it, install its dependencies and build it:
20
+
21
+```
22
+git clone https://github.com/babolivier/matrix-puppet-facebook-1to1-fixer
23
+cd matrix-puppet-facebook-1to1-fixer
24
+gb vendor restore
25
+gb build
26
+```
27
+
28
+## Configure
29
+
30
+An example configuration is located in the [config.sample.yaml](config.sample.yaml) file.
31
+
32
+Copy it somewhere, and edit it accordingly with the key's comments.
33
+
34
+## Run
35
+
36
+You have to run this tool manually for each room you want to edit.
37
+
38
+You can run this tool by typing
39
+
40
+```
41
+./bin/fixer --room-id-localpart ROOM_ID_LOCALPART
42
+```
43
+
44
+where `ROOM_ID_LOCALPART` is the room's ID's localpart (which usually looks like something like `ZUFHhmRzEyUdzljKRz`).
45
+
46
+If you moved your configuration file to a different path than `./config.yaml`, you can specify the configuration file's path by appending the `--config CONFIG_PATH` to your command, where `CONFIG_PATH` is the path to your configuration file.
47
+
48
+## Stuff it doesn't do
49
+
50
+Because it's not supported by [gomatrix](https://github.com/matrix-org/gomatrix) yet, this tool won't set the room as direct chat, nor will it change its push notification settings. These features will be added as soon as gomatrix supports them.

+ 11
- 0
config.sample.yaml View File

1
+# Matrix-related settings
2
+matrix:
3
+  # The full URL of the homeserver to contact.
4
+  homeserver_url: https://matrix.org
5
+  # The server name of the homeserver to contact.
6
+  server_name: matrix.org
7
+  # The local part from the bridge user's Matrix ID. In the example shown in this
8
+  # sample file, the user's Matrix ID is @Alice:matrix.org.
9
+  localpart: Alice
10
+  # The bridge user's access token.
11
+  access_token: ACCESS_TOKEN

+ 15
- 3
src/config/config.go View File

6
 	"gopkg.in/yaml.v2"
6
 	"gopkg.in/yaml.v2"
7
 )
7
 )
8
 
8
 
9
+// Config represents the full configuration.
9
 type Config struct {
10
 type Config struct {
10
 	Matrix MatrixConfig `yaml:"matrix"`
11
 	Matrix MatrixConfig `yaml:"matrix"`
11
 }
12
 }
12
 
13
 
14
+// MatrixConfig represents the Matrix part of the configuration.
13
 type MatrixConfig struct {
15
 type MatrixConfig struct {
16
+	// HomeserverURL is the full URL of the homeserver to contact
17
+	// (e.g. https://matrix.org/).
14
 	HomeserverURL string `yaml:"homeserver_url"`
18
 	HomeserverURL string `yaml:"homeserver_url"`
15
-	ServerName    string `yaml:"server_name"`
16
-	Localpart     string `yaml:"localpart"`
17
-	AccessToken   string `yaml:"access_token"`
19
+	// ServerName is the server name of the homeserver to contact
20
+	// (e.g. matrix.org)0.
21
+	ServerName string `yaml:"server_name"`
22
+	// Localpart is the local part from the bridge user's Matrix ID (e.g. Alice).
23
+	Localpart string `yaml:"localpart"`
24
+	// AccessToken is the bridge user's access token.
25
+	AccessToken string `yaml:"access_token"`
18
 }
26
 }
19
 
27
 
28
+// Parse reads the file located at the provided path then proceeds to create and
29
+// fill in a Config instance.
20
 func Parse(path string) (cfg *Config, err error) {
30
 func Parse(path string) (cfg *Config, err error) {
21
 	cfg = new(Config)
31
 	cfg = new(Config)
22
 
32
 
33
+	// Load the content from the configuration file.
23
 	content, err := ioutil.ReadFile(path)
34
 	content, err := ioutil.ReadFile(path)
24
 	if err != nil {
35
 	if err != nil {
25
 		return
36
 		return
26
 	}
37
 	}
27
 
38
 
39
+	// Parse the YAML content from the configuration file.
28
 	err = yaml.Unmarshal(content, cfg)
40
 	err = yaml.Unmarshal(content, cfg)
29
 	return
41
 	return
30
 }
42
 }

+ 31
- 7
src/fixer/main.go View File

4
 	"flag"
4
 	"flag"
5
 	"fmt"
5
 	"fmt"
6
 	"os"
6
 	"os"
7
+	"regexp"
7
 
8
 
8
 	"config"
9
 	"config"
9
 
10
 
13
 
14
 
14
 // Fixed variables
15
 // Fixed variables
15
 var (
16
 var (
16
-	// ErrEmptyRoomID is fired if no room ID localpart has been provided, and is
17
-	// followed by the command's usage.
18
-	ErrEmptyRoomID = fmt.Errorf("The room ID localpart cannot be empty")
17
+	// ErrRoomIDEmptyOrInvalid is fired if no room ID localpart has been provided
18
+	// or is invalid, and is followed by the command's usage.
19
+	ErrRoomIDEmptyOrInvalid = fmt.Errorf("The room ID localpart is either empty or invalid")
19
 	// ErrInvalidRoomMembersNb is fired if the number of joined members in the
20
 	// ErrInvalidRoomMembersNb is fired if the number of joined members in the
20
 	// room isn't 3 (i.e.: the user, their friend, and the Facebook bot).
21
 	// room isn't 3 (i.e.: the user, their friend, and the Facebook bot).
21
 	ErrInvalidRoomMembersNb = fmt.Errorf("Invalid number of members in the room: either the friend hasn't joined yet, or there's more than one friend in the room")
22
 	ErrInvalidRoomMembersNb = fmt.Errorf("Invalid number of members in the room: either the friend hasn't joined yet, or there's more than one friend in the room")
29
 	InfoNameUpdated = "Room's name updated"
30
 	InfoNameUpdated = "Room's name updated"
30
 	// InfoProcessIsOver is displayed once the whole process is over, just before
31
 	// InfoProcessIsOver is displayed once the whole process is over, just before
31
 	// exiting.
32
 	// exiting.
32
-	InfoProcessIsOver = "The room has been fully updated. Don't forget to mark it as direct chat in Riot, and to edit its push rules."
33
+	InfoProcessIsOver = "The room has been fully updated. Don't forget to mark it as direct chat in Riot, and to edit its notification rules."
33
 )
34
 )
34
 
35
 
35
 // Command line flags
36
 // Command line flags
36
 var (
37
 var (
37
-	localpart  = flag.String("room-id-localpart", "", "Room ID localpart")
38
+	localpart  = flag.String("room-id-localpart", "", "Room ID localpart (i.e. 'ZUFHhmRzEyUdzljKRz')")
38
 	configFile = flag.String("config", "config.yaml", "Configuration file")
39
 	configFile = flag.String("config", "config.yaml", "Configuration file")
39
 )
40
 )
40
 
41
 
55
 
56
 
56
 	flag.Parse()
57
 	flag.Parse()
57
 
58
 
58
-	if len(*localpart) == 0 {
59
-		logrus.Error(ErrEmptyRoomID)
59
+	// We need the room ID's localpart to be non-empty and only composed of letters.
60
+	roomIDLocalpartRgxp := regexp.MustCompile("^[a-zA-Z]+")
61
+	if len(*localpart) == 0 || !roomIDLocalpartRgxp.Match([]byte(*localpart)) {
62
+		logrus.Error(ErrRoomIDEmptyOrInvalid)
60
 		flag.Usage()
63
 		flag.Usage()
61
 		os.Exit(1)
64
 		os.Exit(1)
62
 	}
65
 	}
63
 
66
 
67
+	// Load the configuration from the configuration file.
64
 	cfg, err := config.Parse(*configFile)
68
 	cfg, err := config.Parse(*configFile)
65
 	if err != nil {
69
 	if err != nil {
66
 		panic(err)
70
 		panic(err)
67
 	}
71
 	}
68
 
72
 
73
+	// Compute the room's ID along with the current user's.
69
 	roomID := fmt.Sprintf("!%s:%s", *localpart, cfg.Matrix.ServerName)
74
 	roomID := fmt.Sprintf("!%s:%s", *localpart, cfg.Matrix.ServerName)
70
 	userID := fmt.Sprintf("@%s:%s", cfg.Matrix.Localpart, cfg.Matrix.ServerName)
75
 	userID := fmt.Sprintf("@%s:%s", cfg.Matrix.Localpart, cfg.Matrix.ServerName)
71
 
76
 
77
+	// Load the Matrix client from configuration data.
72
 	cli, err := gomatrix.NewClient(cfg.Matrix.HomeserverURL, userID, cfg.Matrix.AccessToken)
78
 	cli, err := gomatrix.NewClient(cfg.Matrix.HomeserverURL, userID, cfg.Matrix.AccessToken)
73
 	if err != nil {
79
 	if err != nil {
74
 		logrus.Panic(err)
80
 		logrus.Panic(err)
75
 	}
81
 	}
76
 
82
 
83
+	// Retrieve the list of joined members in the room.
77
 	membersResp, err := cli.JoinedMembers(roomID)
84
 	membersResp, err := cli.JoinedMembers(roomID)
78
 	if err != nil {
85
 	if err != nil {
79
 		logrus.Panic(err)
86
 		logrus.Panic(err)
80
 	}
87
 	}
81
 
88
 
89
+	// Retrieve the current user's own display  name.
82
 	displayNameResp, err := cli.GetOwnDisplayName()
90
 	displayNameResp, err := cli.GetOwnDisplayName()
83
 	if err != nil {
91
 	if err != nil {
84
 		logrus.Panic(err)
92
 		logrus.Panic(err)
85
 	}
93
 	}
86
 
94
 
95
+	// Check if the number of joined members is three, as it should be with a
96
+	// 1:1 puppeted chat (the current user, their friend, and the AS bot).
87
 	if len(membersResp.Joined) != 3 {
97
 	if len(membersResp.Joined) != 3 {
88
 		logrus.Error(ErrInvalidRoomMembersNb)
98
 		logrus.Error(ErrInvalidRoomMembersNb)
89
 		os.Exit(1)
99
 		os.Exit(1)
90
 	}
100
 	}
91
 
101
 
102
+	// Iterate over the slice of joined members.
92
 	var avatarURL, displayName string
103
 	var avatarURL, displayName string
93
 	for _, member := range membersResp.Joined {
104
 	for _, member := range membersResp.Joined {
105
+		// The friend should be the only joined member who has a display name set
106
+		// which isn't the same as the current user's.
94
 		if member.DisplayName != nil && *(member.DisplayName) != displayNameResp.DisplayName {
107
 		if member.DisplayName != nil && *(member.DisplayName) != displayNameResp.DisplayName {
95
 			displayName = *(member.DisplayName)
108
 			displayName = *(member.DisplayName)
109
+			// If there's also an avatar set for the friend, use it.
96
 			if member.AvatarURL != nil {
110
 			if member.AvatarURL != nil {
97
 				avatarURL = *(member.AvatarURL)
111
 				avatarURL = *(member.AvatarURL)
98
 			}
112
 			}
104
 		"avatar_url":   avatarURL,
118
 		"avatar_url":   avatarURL,
105
 	}).Info("Found the friend")
119
 	}).Info("Found the friend")
106
 
120
 
121
+	// If the avatar has been found, set it as the room's avatar using a
122
+	// m.room.avatar state event.
107
 	if len(avatarURL) > 0 {
123
 	if len(avatarURL) > 0 {
108
 		if _, err := cli.SendStateEvent(
124
 		if _, err := cli.SendStateEvent(
109
 			roomID,
125
 			roomID,
117
 		}
133
 		}
118
 		logrus.Info(InfoAvatarUpdated)
134
 		logrus.Info(InfoAvatarUpdated)
119
 	} else {
135
 	} else {
136
+		// Else print a warning so the user can see it clearly.
120
 		logrus.Warn(WarnNoAvatar)
137
 		logrus.Warn(WarnNoAvatar)
121
 	}
138
 	}
122
 
139
 
140
+	// If the display name has been found, set it as the room's name using a
141
+	// m.room.name state event. This condition shouldn't be necessary, but heh,
142
+	// at least that might cover a potential regression from the bridge.
123
 	if len(displayName) > 0 {
143
 	if len(displayName) > 0 {
124
 		if _, err := cli.SendStateEvent(
144
 		if _, err := cli.SendStateEvent(
125
 			roomID,
145
 			roomID,
133
 		}
153
 		}
134
 		logrus.Info(InfoNameUpdated)
154
 		logrus.Info(InfoNameUpdated)
135
 	} else {
155
 	} else {
156
+		// Else print a warning so the user can see it clearly.
136
 		logrus.Warn(WarnNoDisplayName)
157
 		logrus.Warn(WarnNoDisplayName)
137
 	}
158
 	}
138
 
159
 
160
+	// Print a shiny message telling the user the process is over, but it's up
161
+	// to them to set the room as a direct chat and to update the room's push
162
+	// notification settings, since that's not supported by gomatrix.
139
 	logrus.Info(InfoProcessIsOver)
163
 	logrus.Info(InfoProcessIsOver)
140
 }
164
 }