const mx = require('matrix-appservice-bridge'); const tmi = require('tmi.js'); // has to be defined here because of scope let bridge; let tmiListener; // Matrix part async function matrixEventHandler(request, context) { const event = request.getData(); if(event.type === 'm.room.message') { const message = event.content.body; const remoteUsers = await bridge.getUserStore().getRemoteUsersFromMatrixId(event.sender); // use the first user, multipe user mappings are unsupported const remoteUser = remoteUsers[0]; const tmiUsername = remoteUser.id; const tmiPassword = remoteUser.data.password; const remoteRooms = await bridge.getRoomStore().getLinkedRemoteRooms(event.room_id); remoteRooms.forEach(remoteRoom => { const tmiClient = new tmi.Client({ connection: { secure: true, }, identity: { username: tmiUsername, password: tmiPassword }, channels: [ remoteRoom.roomId ] }); // connect to tmi, send message and disconnect tmiClient.connect(); tmiClient.on('connected', () => { tmiClient.say(remoteRoom.roomId, message); tmiClient.disconnect(); }); }); } else if(event.content.membership === 'invite' && event.state_key === '@'+bridge.opts.registration.sender_localpart+':'+bridge.opts.domain) { // auto-join on invite bridge.getIntent().join(event.room_id); } }; new mx.Cli({ registrationPath: 'matritch-registration.yaml', generateRegistration: (reg, callback) => { reg.setId(mx.AppServiceRegistration.generateToken()); reg.setHomeserverToken(mx.AppServiceRegistration.generateToken()); reg.setAppServiceToken(mx.AppServiceRegistration.generateToken()); reg.setSenderLocalpart('_matritch'); reg.addRegexPattern('users', '@_matritch_.*', true); callback(reg); }, run: async (port, config) => { bridge = new mx.Bridge({ homeserverUrl: process.env.HOMESEVER || 'http://localhost:8008', domain: process.env.DOMAIN || 'localhost', registration: 'matritch-registration.yaml', controller: { onUserQuery: queriedUser => { // auto-provision users with no additonal data console.log(JSON.stringify(queriedUser)) return {}; }, onEvent: matrixEventHandler } }); console.log('Matrix-side listening on port %s', port); await bridge.run(port, config); connectTmiListener(); } }).run(); // Twitch part const localpartPrefix = bridge.opts.registration.namespaces.users[0].regex.slice(0, -2); const domain = bridge.opts.domain; async function tmiEventHandler(target, context, message, self) { const remoteUsers = await bridge.getUserStore().getRemoteUser(context.username) if(remoteUsers) { // ignore messages from briged users return; } const matrixRooms = await bridge.getRoomStore().getLinkedMatrixRooms(target); matrixRooms.forEach(matrixRoom => { const intent = bridge.getIntent(localpartPrefix+context.username+':'+domain); intent.sendText(matrixRoom.roomId, message); }); }; async function connectTmiListener() { const remoteRooms = await bridge.getRoomStore().select({ matrix_id: {$exists: true}, remote_id: {$exists: true} }); const channels = remoteRooms.map(remoteRoom => { return remoteRoom.remote_id; }); const tmiAnonymousOptions = { connection: { secure: true, reconnect: true }, channels: channels }; tmiListener = new tmi.Client(tmiAnonymousOptions); tmiListener.on('message', tmiEventHandler); tmiListener.connect(); }