at master 9.5 kB view raw
1{ pkgs, ... }: 2let 3 homeserverDomain = "server"; 4 homeserverUrl = "http://server:8008"; 5 username = "alice"; 6 instagramBotUsername = "instagrambot"; 7 facebookBotUsername = "facebookbot"; 8in 9{ 10 name = "mautrix-meta-sqlite"; 11 meta.maintainers = pkgs.mautrix-meta.meta.maintainers; 12 13 nodes = { 14 server = 15 { config, pkgs, ... }: 16 { 17 services.matrix-synapse = { 18 enable = true; 19 settings = { 20 database.name = "sqlite3"; 21 22 enable_registration = true; 23 24 # don't use this in production, always use some form of verification 25 enable_registration_without_verification = true; 26 27 listeners = [ 28 { 29 # The default but tls=false 30 bind_addresses = [ 31 "0.0.0.0" 32 ]; 33 port = 8008; 34 resources = [ 35 { 36 "compress" = true; 37 "names" = [ "client" ]; 38 } 39 { 40 "compress" = false; 41 "names" = [ "federation" ]; 42 } 43 ]; 44 tls = false; 45 type = "http"; 46 } 47 ]; 48 }; 49 }; 50 51 services.mautrix-meta.instances.facebook = { 52 enable = true; 53 54 settings = { 55 homeserver = { 56 address = homeserverUrl; 57 domain = homeserverDomain; 58 }; 59 60 appservice = { 61 port = 8009; 62 63 bot.username = facebookBotUsername; 64 }; 65 66 bridge.permissions."@${username}:server" = "user"; 67 }; 68 }; 69 70 services.mautrix-meta.instances.instagram = { 71 enable = true; 72 73 settings = { 74 homeserver = { 75 address = homeserverUrl; 76 domain = homeserverDomain; 77 }; 78 79 appservice = { 80 port = 8010; 81 82 bot.username = instagramBotUsername; 83 }; 84 85 bridge.permissions."@${username}:server" = "user"; 86 }; 87 }; 88 89 networking.firewall.allowedTCPPorts = [ 8008 ]; 90 }; 91 92 client = 93 { pkgs, ... }: 94 { 95 environment.systemPackages = [ 96 (pkgs.writers.writePython3Bin "register_user" 97 { 98 libraries = [ pkgs.python3Packages.matrix-nio ]; 99 flakeIgnore = [ 100 # We don't live in the dark ages anymore. 101 # Languages like Python that are whitespace heavy will overrun 102 # 79 characters.. 103 "E501" 104 ]; 105 } 106 '' 107 import sys 108 import asyncio 109 110 from nio import AsyncClient 111 112 113 async def run(username: str, homeserver: str): 114 matrix = AsyncClient(homeserver) 115 116 response = await matrix.register(username, "foobar") 117 print("Matrix register response: ", response) 118 119 120 if __name__ == "__main__": 121 asyncio.run(run(sys.argv[1], sys.argv[2])) 122 '' 123 ) 124 (pkgs.writers.writePython3Bin "do_test" 125 { 126 libraries = [ pkgs.python3Packages.matrix-nio ]; 127 flakeIgnore = [ 128 # We don't live in the dark ages anymore. 129 # Languages like Python that are whitespace heavy will overrun 130 # 79 characters.. 131 "E501" 132 ]; 133 } 134 '' 135 import sys 136 import functools 137 import asyncio 138 139 from nio import AsyncClient, RoomMessageNotice, RoomCreateResponse, RoomInviteResponse 140 141 142 async def message_callback(matrix: AsyncClient, msg: str, _r, e): 143 print("Received matrix text message: ", e) 144 assert msg in e.body 145 exit(0) # Success! 146 147 148 async def run(username: str, bot_username: str, homeserver: str): 149 matrix = AsyncClient(homeserver, f"@{username}:${homeserverDomain}") 150 151 response = await matrix.login("foobar") 152 print("Matrix login response: ", response) 153 154 # Open a DM with the bridge bot 155 response = await matrix.room_create() 156 print("Matrix create room response:", response) 157 assert isinstance(response, RoomCreateResponse) 158 room_id = response.room_id 159 160 response = await matrix.room_invite(room_id, f"@{bot_username}:${homeserverDomain}") 161 assert isinstance(response, RoomInviteResponse) 162 163 callback = functools.partial( 164 message_callback, matrix, "Hello, I'm an Instagram bridge bot." 165 ) 166 matrix.add_event_callback(callback, RoomMessageNotice) 167 168 print("Waiting for matrix message...") 169 await matrix.sync_forever(timeout=30000) 170 171 172 if __name__ == "__main__": 173 asyncio.run(run(sys.argv[1], sys.argv[2], sys.argv[3])) 174 '' 175 ) 176 ]; 177 }; 178 }; 179 180 testScript = '' 181 def extract_token(data): 182 stdout = data[1] 183 stdout = stdout.strip() 184 line = stdout.split('\n')[-1] 185 return line.split(':')[-1].strip("\" '\n") 186 187 def get_token_from(token, file): 188 data = server.execute(f"cat {file} | grep {token}") 189 return extract_token(data) 190 191 def get_as_token_from(file): 192 return get_token_from("as_token", file) 193 194 def get_hs_token_from(file): 195 return get_token_from("hs_token", file) 196 197 config_yaml = "/var/lib/mautrix-meta-facebook/config.yaml" 198 registration_yaml = "/var/lib/mautrix-meta-facebook/meta-registration.yaml" 199 200 start_all() 201 202 with subtest("wait for bridges and homeserver"): 203 # bridge 204 server.wait_for_unit("mautrix-meta-facebook.service") 205 server.wait_for_unit("mautrix-meta-instagram.service") 206 207 # homeserver 208 server.wait_for_unit("matrix-synapse.service") 209 210 server.wait_for_open_port(8008) 211 # Bridges only open the port after they contact the homeserver 212 server.wait_for_open_port(8009) 213 server.wait_for_open_port(8010) 214 215 with subtest("register user"): 216 client.succeed("register_user ${username} ${homeserverUrl} >&2") 217 218 with subtest("ensure messages can be exchanged"): 219 client.succeed("do_test ${username} ${facebookBotUsername} ${homeserverUrl} >&2") 220 client.succeed("do_test ${username} ${instagramBotUsername} ${homeserverUrl} >&2") 221 222 with subtest("ensure as_token and hs_token stays same after restart"): 223 generated_as_token_facebook = get_as_token_from(config_yaml) 224 generated_hs_token_facebook = get_hs_token_from(config_yaml) 225 226 generated_as_token_facebook_registration = get_as_token_from(registration_yaml) 227 generated_hs_token_facebook_registration = get_hs_token_from(registration_yaml) 228 229 # Indirectly checks the as token is not set to something like empty string or "null" 230 assert len(generated_as_token_facebook) > 20, f"as_token ({generated_as_token_facebook}) is too short, something went wrong" 231 assert len(generated_hs_token_facebook) > 20, f"hs_token ({generated_hs_token_facebook}) is too short, something went wrong" 232 233 assert generated_as_token_facebook == generated_as_token_facebook_registration, f"as_token should be the same in registration ({generated_as_token_facebook_registration}) and configuration ({generated_as_token_facebook}) files" 234 assert generated_hs_token_facebook == generated_hs_token_facebook_registration, f"hs_token should be the same in registration ({generated_hs_token_facebook_registration}) and configuration ({generated_hs_token_facebook}) files" 235 236 server.systemctl("restart mautrix-meta-facebook") 237 server.systemctl("restart mautrix-meta-instagram") 238 239 server.wait_for_open_port(8009) 240 server.wait_for_open_port(8010) 241 242 new_as_token_facebook = get_as_token_from(config_yaml) 243 new_hs_token_facebook = get_hs_token_from(config_yaml) 244 245 assert generated_as_token_facebook == new_as_token_facebook, f"as_token should stay the same after restart inside the configuration file (is: {new_as_token_facebook}, was: {generated_as_token_facebook})" 246 assert generated_hs_token_facebook == new_hs_token_facebook, f"hs_token should stay the same after restart inside the configuration file (is: {new_hs_token_facebook}, was: {generated_hs_token_facebook})" 247 248 new_as_token_facebook = get_as_token_from(registration_yaml) 249 new_hs_token_facebook = get_hs_token_from(registration_yaml) 250 251 assert generated_as_token_facebook == new_as_token_facebook, f"as_token should stay the same after restart inside the registration file (is: {new_as_token_facebook}, was: {generated_as_token_facebook})" 252 assert generated_hs_token_facebook == new_hs_token_facebook, f"hs_token should stay the same after restart inside the registration file (is: {new_hs_token_facebook}, was: {generated_hs_token_facebook})" 253 254 with subtest("ensure messages can be exchanged after restart"): 255 client.succeed("do_test ${username} ${instagramBotUsername} ${homeserverUrl} >&2") 256 client.succeed("do_test ${username} ${facebookBotUsername} ${homeserverUrl} >&2") 257 ''; 258}