| 
				
			 | 
			
			
				@@ -4,6 +4,7 @@ import ( 
			 | 
		
	
		
			
			| 
				4
			 | 
			
				4
			 | 
			
			
				 	"flag" 
			 | 
		
	
		
			
			| 
				5
			 | 
			
				5
			 | 
			
			
				 	"fmt" 
			 | 
		
	
		
			
			| 
				6
			 | 
			
				6
			 | 
			
			
				 	"os" 
			 | 
		
	
		
			
			| 
				
			 | 
			
				7
			 | 
			
			
				+	"regexp" 
			 | 
		
	
		
			
			| 
				7
			 | 
			
				8
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				8
			 | 
			
				9
			 | 
			
			
				 	"config" 
			 | 
		
	
		
			
			| 
				9
			 | 
			
				10
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -13,9 +14,9 @@ import ( 
			 | 
		
	
		
			
			| 
				13
			 | 
			
				14
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				14
			 | 
			
				15
			 | 
			
			
				 // Fixed variables 
			 | 
		
	
		
			
			| 
				15
			 | 
			
				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
			 | 
			
				20
			 | 
			
			
				 	// ErrInvalidRoomMembersNb is fired if the number of joined members in the 
			 | 
		
	
		
			
			| 
				20
			 | 
			
				21
			 | 
			
			
				 	// room isn't 3 (i.e.: the user, their friend, and the Facebook bot). 
			 | 
		
	
		
			
			| 
				21
			 | 
			
				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,12 +30,12 @@ var ( 
			 | 
		
	
		
			
			| 
				29
			 | 
			
				30
			 | 
			
			
				 	InfoNameUpdated = "Room's name updated" 
			 | 
		
	
		
			
			| 
				30
			 | 
			
				31
			 | 
			
			
				 	// InfoProcessIsOver is displayed once the whole process is over, just before 
			 | 
		
	
		
			
			| 
				31
			 | 
			
				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
			 | 
			
				36
			 | 
			
			
				 // Command line flags 
			 | 
		
	
		
			
			| 
				36
			 | 
			
				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
			 | 
			
				39
			 | 
			
			
				 	configFile = flag.String("config", "config.yaml", "Configuration file") 
			 | 
		
	
		
			
			| 
				39
			 | 
			
				40
			 | 
			
			
				 ) 
			 | 
		
	
		
			
			| 
				40
			 | 
			
				41
			 | 
			
			
				  
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -55,44 +56,57 @@ func main() { 
			 | 
		
	
		
			
			| 
				55
			 | 
			
				56
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				56
			 | 
			
				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
			 | 
			
				63
			 | 
			
			
				 		flag.Usage() 
			 | 
		
	
		
			
			| 
				61
			 | 
			
				64
			 | 
			
			
				 		os.Exit(1) 
			 | 
		
	
		
			
			| 
				62
			 | 
			
				65
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				63
			 | 
			
				66
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				67
			 | 
			
			
				+	// Load the configuration from the configuration file. 
			 | 
		
	
		
			
			| 
				64
			 | 
			
				68
			 | 
			
			
				 	cfg, err := config.Parse(*configFile) 
			 | 
		
	
		
			
			| 
				65
			 | 
			
				69
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				66
			 | 
			
				70
			 | 
			
			
				 		panic(err) 
			 | 
		
	
		
			
			| 
				67
			 | 
			
				71
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				68
			 | 
			
				72
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				73
			 | 
			
			
				+	// Compute the room's ID along with the current user's. 
			 | 
		
	
		
			
			| 
				69
			 | 
			
				74
			 | 
			
			
				 	roomID := fmt.Sprintf("!%s:%s", *localpart, cfg.Matrix.ServerName) 
			 | 
		
	
		
			
			| 
				70
			 | 
			
				75
			 | 
			
			
				 	userID := fmt.Sprintf("@%s:%s", cfg.Matrix.Localpart, cfg.Matrix.ServerName) 
			 | 
		
	
		
			
			| 
				71
			 | 
			
				76
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				77
			 | 
			
			
				+	// Load the Matrix client from configuration data. 
			 | 
		
	
		
			
			| 
				72
			 | 
			
				78
			 | 
			
			
				 	cli, err := gomatrix.NewClient(cfg.Matrix.HomeserverURL, userID, cfg.Matrix.AccessToken) 
			 | 
		
	
		
			
			| 
				73
			 | 
			
				79
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				74
			 | 
			
				80
			 | 
			
			
				 		logrus.Panic(err) 
			 | 
		
	
		
			
			| 
				75
			 | 
			
				81
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				76
			 | 
			
				82
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				83
			 | 
			
			
				+	// Retrieve the list of joined members in the room. 
			 | 
		
	
		
			
			| 
				77
			 | 
			
				84
			 | 
			
			
				 	membersResp, err := cli.JoinedMembers(roomID) 
			 | 
		
	
		
			
			| 
				78
			 | 
			
				85
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				79
			 | 
			
				86
			 | 
			
			
				 		logrus.Panic(err) 
			 | 
		
	
		
			
			| 
				80
			 | 
			
				87
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				81
			 | 
			
				88
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				89
			 | 
			
			
				+	// Retrieve the current user's own display  name. 
			 | 
		
	
		
			
			| 
				82
			 | 
			
				90
			 | 
			
			
				 	displayNameResp, err := cli.GetOwnDisplayName() 
			 | 
		
	
		
			
			| 
				83
			 | 
			
				91
			 | 
			
			
				 	if err != nil { 
			 | 
		
	
		
			
			| 
				84
			 | 
			
				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
			 | 
			
				97
			 | 
			
			
				 	if len(membersResp.Joined) != 3 { 
			 | 
		
	
		
			
			| 
				88
			 | 
			
				98
			 | 
			
			
				 		logrus.Error(ErrInvalidRoomMembersNb) 
			 | 
		
	
		
			
			| 
				89
			 | 
			
				99
			 | 
			
			
				 		os.Exit(1) 
			 | 
		
	
		
			
			| 
				90
			 | 
			
				100
			 | 
			
			
				 	} 
			 | 
		
	
		
			
			| 
				91
			 | 
			
				101
			 | 
			
			
				  
			 | 
		
	
		
			
			| 
				
			 | 
			
				102
			 | 
			
			
				+	// Iterate over the slice of joined members. 
			 | 
		
	
		
			
			| 
				92
			 | 
			
				103
			 | 
			
			
				 	var avatarURL, displayName string 
			 | 
		
	
		
			
			| 
				93
			 | 
			
				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
			 | 
			
				107
			 | 
			
			
				 		if member.DisplayName != nil && *(member.DisplayName) != displayNameResp.DisplayName { 
			 | 
		
	
		
			
			| 
				95
			 | 
			
				108
			 | 
			
			
				 			displayName = *(member.DisplayName) 
			 | 
		
	
		
			
			| 
				
			 | 
			
				109
			 | 
			
			
				+			// If there's also an avatar set for the friend, use it. 
			 | 
		
	
		
			
			| 
				96
			 | 
			
				110
			 | 
			
			
				 			if member.AvatarURL != nil { 
			 | 
		
	
		
			
			| 
				97
			 | 
			
				111
			 | 
			
			
				 				avatarURL = *(member.AvatarURL) 
			 | 
		
	
		
			
			| 
				98
			 | 
			
				112
			 | 
			
			
				 			} 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -104,6 +118,8 @@ func main() { 
			 | 
		
	
		
			
			| 
				104
			 | 
			
				118
			 | 
			
			
				 		"avatar_url":   avatarURL, 
			 | 
		
	
		
			
			| 
				105
			 | 
			
				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
			 | 
			
				123
			 | 
			
			
				 	if len(avatarURL) > 0 { 
			 | 
		
	
		
			
			| 
				108
			 | 
			
				124
			 | 
			
			
				 		if _, err := cli.SendStateEvent( 
			 | 
		
	
		
			
			| 
				109
			 | 
			
				125
			 | 
			
			
				 			roomID, 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -117,9 +133,13 @@ func main() { 
			 | 
		
	
		
			
			| 
				117
			 | 
			
				133
			 | 
			
			
				 		} 
			 | 
		
	
		
			
			| 
				118
			 | 
			
				134
			 | 
			
			
				 		logrus.Info(InfoAvatarUpdated) 
			 | 
		
	
		
			
			| 
				119
			 | 
			
				135
			 | 
			
			
				 	} else { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				136
			 | 
			
			
				+		// Else print a warning so the user can see it clearly. 
			 | 
		
	
		
			
			| 
				120
			 | 
			
				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
			 | 
			
				143
			 | 
			
			
				 	if len(displayName) > 0 { 
			 | 
		
	
		
			
			| 
				124
			 | 
			
				144
			 | 
			
			
				 		if _, err := cli.SendStateEvent( 
			 | 
		
	
		
			
			| 
				125
			 | 
			
				145
			 | 
			
			
				 			roomID, 
			 | 
		
	
	
		
			
			| 
				
			 | 
			
			
				@@ -133,8 +153,12 @@ func main() { 
			 | 
		
	
		
			
			| 
				133
			 | 
			
				153
			 | 
			
			
				 		} 
			 | 
		
	
		
			
			| 
				134
			 | 
			
				154
			 | 
			
			
				 		logrus.Info(InfoNameUpdated) 
			 | 
		
	
		
			
			| 
				135
			 | 
			
				155
			 | 
			
			
				 	} else { 
			 | 
		
	
		
			
			| 
				
			 | 
			
				156
			 | 
			
			
				+		// Else print a warning so the user can see it clearly. 
			 | 
		
	
		
			
			| 
				136
			 | 
			
				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
			 | 
			
				163
			 | 
			
			
				 	logrus.Info(InfoProcessIsOver) 
			 | 
		
	
		
			
			| 
				140
			 | 
			
				164
			 | 
			
			
				 } 
			 |