at 23.11-beta 7.3 kB view raw
1import ../make-test-python.nix ({ pkgs, ... }: 2 let 3 homeserverUrl = "http://homeserver:8008"; 4 in 5 { 6 name = "matrix-appservice-irc"; 7 meta = { 8 maintainers = pkgs.matrix-appservice-irc.meta.maintainers; 9 }; 10 11 nodes = { 12 homeserver = { pkgs, ... }: { 13 # We'll switch to this once the config is copied into place 14 specialisation.running.configuration = { 15 services.matrix-synapse = { 16 enable = true; 17 settings = { 18 database.name = "sqlite3"; 19 app_service_config_files = [ "/registration.yml" ]; 20 21 enable_registration = true; 22 23 # don't use this in production, always use some form of verification 24 enable_registration_without_verification = true; 25 26 listeners = [ { 27 # The default but tls=false 28 bind_addresses = [ 29 "0.0.0.0" 30 ]; 31 port = 8008; 32 resources = [ { 33 "compress" = true; 34 "names" = [ "client" ]; 35 } { 36 "compress" = false; 37 "names" = [ "federation" ]; 38 } ]; 39 tls = false; 40 type = "http"; 41 } ]; 42 }; 43 }; 44 45 networking.firewall.allowedTCPPorts = [ 8008 ]; 46 }; 47 }; 48 49 ircd = { pkgs, ... }: { 50 services.ngircd = { 51 enable = true; 52 config = '' 53 [Global] 54 Name = ircd.ircd 55 Info = Server Info Text 56 AdminInfo1 = _ 57 58 [Channel] 59 Name = #test 60 Topic = a cool place 61 62 [Options] 63 PAM = no 64 ''; 65 }; 66 networking.firewall.allowedTCPPorts = [ 6667 ]; 67 }; 68 69 appservice = { pkgs, ... }: { 70 services.matrix-appservice-irc = { 71 enable = true; 72 registrationUrl = "http://appservice:8009"; 73 74 settings = { 75 homeserver.url = homeserverUrl; 76 homeserver.domain = "homeserver"; 77 78 ircService.servers."ircd" = { 79 name = "IRCd"; 80 port = 6667; 81 dynamicChannels = { 82 enabled = true; 83 aliasTemplate = "#irc_$CHANNEL"; 84 }; 85 }; 86 }; 87 }; 88 89 networking.firewall.allowedTCPPorts = [ 8009 ]; 90 }; 91 92 client = { pkgs, ... }: { 93 environment.systemPackages = [ 94 (pkgs.writers.writePython3Bin "do_test" 95 { 96 libraries = [ pkgs.python3Packages.matrix-nio ]; 97 flakeIgnore = [ 98 # We don't live in the dark ages anymore. 99 # Languages like Python that are whitespace heavy will overrun 100 # 79 characters.. 101 "E501" 102 ]; 103 } '' 104 import sys 105 import socket 106 import functools 107 from time import sleep 108 import asyncio 109 110 from nio import AsyncClient, RoomMessageText, JoinResponse 111 112 113 async def matrix_room_message_text_callback(matrix: AsyncClient, msg: str, _r, e): 114 print("Received matrix text message: ", e) 115 if msg in e.body: 116 print("Received hi from IRC") 117 await matrix.close() 118 exit(0) # Actual exit point 119 120 121 class IRC: 122 def __init__(self): 123 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 124 sock.connect(("ircd", 6667)) 125 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) 126 sock.send(b"USER bob bob bob :bob\n") 127 sock.send(b"NICK bob\n") 128 self.sock = sock 129 130 def join(self, room: str): 131 self.sock.send(f"JOIN {room}\n".encode()) 132 133 def privmsg(self, room: str, msg: str): 134 self.sock.send(f"PRIVMSG {room} :{msg}\n".encode()) 135 136 def expect_msg(self, body: str): 137 buffer = "" 138 while True: 139 buf = self.sock.recv(1024).decode() 140 buffer += buf 141 if body in buffer: 142 return 143 144 145 async def run(homeserver: str): 146 irc = IRC() 147 148 matrix = AsyncClient(homeserver) 149 response = await matrix.register("alice", "foobar") 150 print("Matrix register response: ", response) 151 152 response = await matrix.join("#irc_#test:homeserver") 153 print("Matrix join room response:", response) 154 assert isinstance(response, JoinResponse) 155 room_id = response.room_id 156 157 irc.join("#test") 158 # FIXME: what are we waiting on here? Matrix? IRC? Both? 159 # 10s seem bad for busy hydra machines. 160 sleep(10) 161 162 # Exchange messages 163 print("Sending text message to matrix room") 164 response = await matrix.room_send( 165 room_id=room_id, 166 message_type="m.room.message", 167 content={"msgtype": "m.text", "body": "hi from matrix"}, 168 ) 169 print("Matrix room send response: ", response) 170 irc.privmsg("#test", "hi from irc") 171 172 print("Waiting for the matrix message to appear on the IRC side...") 173 irc.expect_msg("hi from matrix") 174 175 callback = functools.partial( 176 matrix_room_message_text_callback, matrix, "hi from irc" 177 ) 178 matrix.add_event_callback(callback, RoomMessageText) 179 180 print("Waiting for matrix message...") 181 await matrix.sync_forever() 182 183 exit(1) # Unreachable 184 185 186 if __name__ == "__main__": 187 asyncio.run(run(sys.argv[1])) 188 '' 189 ) 190 ]; 191 }; 192 }; 193 194 testScript = '' 195 import pathlib 196 import os 197 198 start_all() 199 200 ircd.wait_for_unit("ngircd.service") 201 ircd.wait_for_open_port(6667) 202 203 with subtest("start the appservice"): 204 appservice.wait_for_unit("matrix-appservice-irc.service") 205 appservice.wait_for_open_port(8009) 206 207 with subtest("copy the registration file"): 208 appservice.copy_from_vm("/var/lib/matrix-appservice-irc/registration.yml") 209 homeserver.copy_from_host( 210 str(pathlib.Path(os.environ.get("out", os.getcwd())) / "registration.yml"), "/" 211 ) 212 homeserver.succeed("chmod 444 /registration.yml") 213 214 with subtest("start the homeserver"): 215 homeserver.succeed( 216 "/run/current-system/specialisation/running/bin/switch-to-configuration test >&2" 217 ) 218 219 homeserver.wait_for_unit("matrix-synapse.service") 220 homeserver.wait_for_open_port(8008) 221 222 with subtest("ensure messages can be exchanged"): 223 client.succeed("do_test ${homeserverUrl} >&2") 224 ''; 225 })