module main import time import setup import irc import matrix import rpc import appsvc import db import chat import regex import util import bridge struct Main { mut: irc &irc.IrcActor appsvc &appsvc.AppsvcActor matrix &matrix.Actor rpc &rpc.Actor config setup.Config db db.Db chat &chat.Actor aliases chat.Aliases } fn main() { config := setup.config() mut main := &Main{ irc: irc.setup() appsvc: appsvc.setup(config) matrix: matrix.init(config) rpc: rpc.init(config) config: config db: db.init(config) chat: chat.setup(config) } go main.listen_out() go main.matrix.setup() go main.appsvc.listen() go main.rpc.listen() go main.matrix.listen() go main.irc.listen() irchosts := main.db.select_all('irc_servers') for row in irchosts { ircnet := irc.network_from_db(row) main.irc.networks.add(ircnet) } println('loaded $irchosts.len irc networks') aliases := main.db.select_all('bridge_nicks') for alias in aliases { main.aliases.add(&chat.Alias{ matrix: alias[0], irc: alias[1] }) } println('loaded $aliases.len matrix/irc aliases') main.mainloop() } fn (mut self Main) mainloop() { for { select { irc_m := <-self.irc.cin { self.irc_do(irc_m) } matrix := <-self.matrix.cin { self.matrix_do(matrix) } appsvc := <-self.appsvc.out { self.as_do(appsvc) } rpc := <-self.rpc.out { self.rpc_do(rpc) } chat_payload := <-self.chat.cin { self.chat_do(chat_payload) } > 60 * time.minute { println('$time.now()') } } } } fn (mut self Main) chat_do(payload chat.Payload) { println(payload) match payload { chat.MakeIrcUser { mut ircnet := self.irc.networks.by_name(payload.network_hostname) or { println('chat_do() not found: $payload.network_hostname') return } mut puppet := self.irc.new_ghost(mut ircnet, payload.nick) self.irc.puppets.add(mut puppet) } else {} } } fn (mut self Main) matrix_do(payload matrix.Payload) { match payload { matrix.Connect { joined_room_ids := self.matrix.joined_rooms() or { println('$err') return } mut matrix_rooms := []&matrix.Room{} for jroom_id in joined_room_ids { db_room := self.db.select_by_field('matrix_rooms', 'room_id', jroom_id) mut mroom := &matrix.Room{} if db_room.len > 0 { mroom = matrix.room_from_db(db_room[0]) } else { println('warning: joined room $jroom_id not found in db') mroom = &matrix.Room{ id: jroom_id name: util.StringOrNone(&util.None{}) } } println('matrix room loaded $mroom') matrix_rooms << mroom } self.matrix.joined_rooms.replace(matrix_rooms) println('Matrix $self.config.matrix_host sync_rooms') self.sync_matrix_rooms() println('Matrix $self.config.matrix_host SETUP done') alias := &chat.Alias{ matrix: self.matrix.whoami irc: matrix.split(self.matrix.whoami)[1] } self.aliases.add(alias) self.admin_say('* vbridge started. I am $self.matrix.whoami') // start IRC setup after matrix println('connecting $self.irc.networks.networks.len irc networks') go self.irc.connect_all() } matrix.MakeUser { // register user, join room, and try again if _ := self.matrix.register(payload) { self.admin_say('registered $payload') self.process_out_user(payload.user_id) } else { self.admin_say('matrix registration error for $payload: $err') } } matrix.JoinRoom { self.matrix.join_as(payload.name, payload.room) or {} self.process_out_user(payload.name) } else {} } } fn (mut self Main) irc_do(irc_m irc.Payload) { match irc_m { irc.PrivMsg { println('<- $irc_m') if irc_m.channel == irc_m.puppet.network.nick { room := irc_m.nick[1..].split('!')[0] self.command(chat.System.irc, irc_m.puppet.network.name, irc_m.nick, room, irc_m.message[1..]) } else { // when bridgebot hears it, repeat in matrix if irc_m.puppet.nick == irc_m.puppet.network.nick { partial_irc_nick := irc_m.nick.split('!')[0] if _ := self.irc.puppets.by_net_nick(irc_m.puppet.network, partial_irc_nick) { println('info: $partial_irc_nick is a ghost. not relaying.') } else { full_channel := '$irc_m.channel:$irc_m.puppet.network.name' if room_name := bridge.matching_room(full_channel, mut self.db) { if mroom := self.matrix.joined_rooms.find_room_by_name(room_name) { matrix_name := self.name_convert(chat.System.irc, partial_irc_nick) self.chat.say(chat.System.matrix, matrix_name, '', mroom.id, irc_m.message) } else { println('warning: no matrix.joined_rooms for $room_name') } } else { println('warning: no bridge found for $full_channel msg dropped: $irc_m.message') } } } } } irc.Connect { user_id := self.name_convert(.irc, irc_m.puppet.nick) msg := '${irc_m.puppet.nick}($user_id) connected to $irc_m.puppet.network' self.matrix_say(user_id, msg) self.sync_irc_channels(irc_m.puppet) } irc.Disconnect { user_id := self.name_convert(.irc, irc_m.puppet.nick) msg := '$irc_m.puppet.nick disconnected from $irc_m.puppet.network.hostname use !irc connect' self.matrix_say(user_id, msg) } irc.Joined { user_id := self.name_convert(.irc, irc_m.puppet.nick) msg := '$irc_m.puppet.nick joined $irc_m.channel on $irc_m.puppet.network.name' self.matrix_say(user_id, msg) self.process_out_user(irc_m.puppet.nick) } irc.NickInUse { user_id := self.name_convert(.irc, irc_m.puppet.nick) msg := '$irc_m.puppet.nick is already in use on $irc_m.puppet.network.name' self.matrix_say(user_id, msg) mut puppet := self.irc.puppets.by_nick(irc_m.puppet.nick) or { return } // mut hack puppet.hangup() } } } fn (mut self Main) rpc_do(cmd rpc.Command) { self.command(chat.System.irc, 'rpc', 'rpc', 'rpc', '$cmd.verb ${cmd.params['line']}') } fn (mut self Main) as_do(cmd appsvc.Command) { // println('as_do: ${cmd.data['id']} ${cmd.data['type']}') match cmd.data['type'].str() { 'm.room.name' { println('m.room.name $cmd') } 'm.room.member' { println('m.room.member $cmd') c := cmd.data['content'].as_map() room_id := cmd.data['room_id'].str() whoami := cmd.data['state_key'].str() println('m.room.member user ${cmd.data['state_key']} room_id $room_id is ${c['membership']}') membership := c['membership'].str() match membership { 'invite' { room_alias := self.matrix.invited_by(cmd.data) println('matrix invite invited by $room_alias') match self.join_request_matrix(room_alias, room_id) { .saved { self.admin_say('recorded $room_id') self.sync_matrix_rooms() } .already_joined { self.admin_say('already joined $room_id') } } } 'join' { mut msg := '' if room := self.matrix.joined_rooms.find_room_by_id(room_id) { if room.name is string { room_name := room.name // VBUG msg = '$whoami joined ${room_name}.' if room_name.starts_with('@') { msg = msg + ' (DM room)' } else { if channel := bridge.matching_room(room_name, mut self.db) { msg = msg + ' (bridged to $channel)' } else { msg = msg + ' (not bridged. Use !bridge)' } } self.admin_say(msg) self.process_out_user(whoami) } } } 'leave' { mut msg := '$whoami left room ' if room := self.matrix.joined_rooms.find_room_by_id(room_id) { if room.name is string { msg = msg + room.name } else { msg = msg + '$room_id (found room, missing name)' } } else { msg = msg + '$room_id (unrecognized room)' } // self.admin_say(msg) } else {} } } 'm.room.message' { event_id := cmd.data['event_id'].str() c := cmd.data['content'].as_map() body := c['body'].str() room_id := cmd.data['room_id'].str() sender := cmd.data['sender'].str() if sender != self.matrix.whoami { println('<- $sender ${c['msgtype']} $room_id $event_id "$body"') // if not a puppet matrix user if _ := self.matrix_name_match(sender) { println('msg is from matrix puppet ${sender}. skipping') } else { // if room has a human name if alias_room := self.matrix.joined_rooms.by_id(room_id) { irc_nick := self.name_convert(chat.System.matrix, sender) if alias_room.name is string { if alias_room.name.starts_with('@') { if body.starts_with('!') { self.command(chat.System.matrix, 'matrix', sender, room_id, body[1..]) } } else { if room := bridge.matching_room(alias_room.name, mut self.db) { // repeat in irc saybody := match c['msgtype'].str() { 'm.text' { body } 'm.emote' { util.ctcp_encode('ACTION', body) } else { 'unknown matrix msgtype ${c['msgtype']}' } } room_parts := room.split(':') sayparts := saybody.split('\n') for saypart in sayparts { self.chat.say(chat.System.irc, irc_nick, room_parts[1], room_parts[0], saypart) } } else { println('as_do/m.room.message: no bridge found for ${alias_room}. msg dropped: $body') } } } else { println('warning: room_id $room_id is unrecognized. msg dropped: $body') } } } } else { println('$sender -> ${c['msgtype']} $room_id $event_id "$body"') } } else { println('unknown type') } } } fn (mut self Main) command(system chat.System, network string, name string, room_id string, message string) { parts := message.trim_space().split(' ') println('COMMAND $system $network $name, $room_id $parts') match parts[0] { 'help' { self.chat.say(system, '', network, room_id, 'commands: (each command gives its own help)') self.chat.say(system, '', network, room_id, '!status !irc !matrix !bridge') } 'status' { self.status_report(system, network, room_id) } 'matrix' { mut help_screen := false if parts.len > 1 { help_screen = self.command_matrix(system, network, room_id, parts) } else { help_screen = true } if help_screen { self.chat.say(system, '', network, room_id, '!matrix <status | join | leave>') } } 'irc' { if parts.len > 1 { self.command_irc(system, name, network, room_id, parts) } else { self.chat.say(system, '', network, room_id, '!irc <add | status | delete | connect | join | part>') } } 'bridge' { mut help_screen := false if parts.len > 1 { help_screen = self.command_bridge(system, network, room_id, parts) } else { help_screen = true } if help_screen { self.chat.say(system, '', network, room_id, '!bridge <add | list | del>') } } else { self.chat.say(system, '', network, room_id, 'unknown command ${parts[0]}. try !help') } } } fn (mut self Main) status_report(system chat.System, network string, room string) { mut msg := '$self.matrix.host is $self.matrix.conn_state in $self.matrix.joined_rooms.len() rooms. $self.irc.networks.networks.len irc networks connected.' self.chat.say(system, '', network, room, msg) } fn (mut self Main) matrix_status(system chat.System, network string, room string) { mut msg := '$self.matrix.host is $self.matrix.conn_state in $self.matrix.joined_rooms.len() rooms. ' self.chat.say(system, '', network, room, msg) for r in self.matrix.joined_rooms.rooms { self.chat.say(system, '', network, room, 'matrix: room $r') } } fn (mut self Main) command_matrix(system chat.System, network string, room_id string, parts []string) bool { cmd := parts[1] match cmd { 'status' { self.matrix_status(system, network, room_id) } 'join' { if parts.len > 1 { room := parts[1] mut msg := '' if self.irc.is_room(room) { } else if self.matrix.is_room(room) { if alias_room := self.matrix.room_alias(room) { match self.join_request_matrix(room, alias_room.id) { .saved { msg = 'matrix room $room ($alias_room.id) added' go self.sync_matrix_rooms() } .already_joined { msg = 'matrix room $room already added' } } } else { match err { matrix.RoomAliasErrNotFound { msg = 'join failed: no room_alias exists for ${room}.' } else { msg = 'join failed: room_alias failure: ($err)' } } } } else { msg = 'join: unknown room format: $room' } self.chat.say(system, '', network, room_id, msg) } else { help_msg := '!join <#<irc_channel_name>[:<irc_server_hostname>] | #<matrix_room:server.com>>' self.chat.say(system, '', network, room_id, help_msg) self.chat.say(system, '', network, room_id, 'exmaple: !join #chatirc') self.chat.say(system, '', network, room_id, 'exmaple: !join #chat:matrix.org') } } 'leave' { if parts.len > 1 { room := parts[1] mut msg := '' if self.irc.is_room(room) { self.leave_request(chat.System.irc, network, room) msg = 'irc room $room removed' } else if self.matrix.is_room(room) { if alias_room := self.matrix.room_alias(room) { self.leave_request(chat.System.matrix, network, alias_room.id) msg = 'matrix room $alias_room removed' } else { match err { matrix.RoomAliasErrNotFound {} else {} } } } else { msg = 'leave: unknown room format: $room' } self.chat.say(system, '', network, room_id, msg) go self.sync_matrix_rooms() } else { self.chat.say(system, '', network, room_id, '!leave <#ircchannel or #matrix:room>') } } else { return true } } return false } fn (mut self Main) command_bridge(system chat.System, network string, room_id string, parts []string) bool { if parts.len > 1 { cmd := parts[1] match cmd { 'list' { rows := self.db.select_all('bridge_rooms') for row in rows { self.chat.say(system, '', network, room_id, 'bridge ${row[0]} <=> ${row[1]}') } } 'add' { if parts.len == 4 { left := parts[2] right := parts[3] matrix_server := left.split(':')[1] if matrix_server == self.config.matrix_host { irc_netname := right.split(':')[1] if _ := self.irc.networks.by_name(irc_netname) { bridge := bridge.Bridge{ left: left right: right } self.db.insert('bridge_rooms', bridge.to_db()) } else { self.chat.say(system, '', network, room_id, '$irc_netname is not a known irc network. use !irc add') } } else { self.chat.say(system, '', network, room_id, '$left must be a channel on $self.config.matrix_host') } } else { self.chat.say(system, '', network, room_id, '!bridge add #room:matrix.server #channel:ircnetwork') } } 'del' { if parts.len == 3 { self.db.delete_by_field('bridge_rooms', 'left', parts[2]) self.db.delete_by_field('bridge_rooms', 'right', parts[2]) } } else { return true } } return false } else { return true } } fn (mut self Main) command_irc(system chat.System, name string, network string, room_id string, parts []string) bool { mut default_action := false match parts[1] { 'add' { if parts.len == 4 { host := parts[2] nick := parts[3] mut irc_temp := irc.Network{ hostname: host nick: nick } self.db.insert('irc_servers', irc.network_to_db(irc_temp)) self.chat.say(system, '', network, room_id, 'host $host nick $nick added') // << copies ircnet, so find after add mut new_ircnet := self.irc.networks.add(irc_temp) mut bot_puppet := self.irc.new_ghost(mut new_ircnet, new_ircnet.nick) go bot_puppet.dial() } else { self.chat.say(system, '', network, room_id, 'usage: !irc add <host:port> <bridge nickname>') } } 'del' { if ircnet := self.irc.networks.by_name(parts[2]) { // VBUG if mut var := mut p_net := *ircnet self.db.delete_by_field('irc_servers', 'hostname', p_net.hostname) ircidx := self.irc.networks.by_hostname_idx(p_net.hostname) if ircidx >= 0 { mut puppets := self.irc.puppets.by_network(p_net.name) for mut puppet in puppets { puppet.hangup() } self.irc.networks.networks.delete(ircidx) } } else { println('server del $parts[2] not found') } } 'status' { for mut ircnet in self.irc.networks.networks { mut p_net := *ircnet self.chat.say(system, '', network, room_id, 'irc: network: $p_net') for mut puppet in self.irc.puppets.puppets { mut p_p := *puppet msg2 := '$p_net $p_p.nick $p_p.state $p_p.channels.channels.len channels' self.chat.say(system, '', network, room_id, 'irc: $msg2') for channel in p_p.channels.channels { msg3 := '$p_net $channel $p_p.nick' self.chat.say(system, '', network, room_id, 'irc: $msg3') } } } } 'nick' { if parts.len > 2 { if parts.len == 3 { nick := parts[2] old_nick := self.name_convert(chat.System.matrix, name) mut alias := self.aliases.match_matrix(name) or { alias := &chat.Alias{ matrix: name irc: nick } self.db.insert('bridge_nicks', chat.alias_to_db(alias)) self.aliases.add(alias) alias } if alias.irc != nick { self.chat.say(system, '', network, room_id, 'updated preferred irc nick for $name from $alias.irc to $nick') alias.irc = nick self.db.update_by_field('bridge_nicks', 'matrix', name, 'irc', nick) } mut puppet := self.irc.puppets.by_nick(alias.irc) or { d_msg := 'nick $alias.irc not found' self.chat.say(system, '', network, room_id, d_msg) return false } d_msg := 'nicksync $alias.irc == $puppet.nick' self.chat.say(system, '', network, room_id, d_msg) if old_nick == puppet.nick { msg := 'changing $puppet.network $puppet.nick to $nick' self.chat.say(system, '', network, room_id, msg) puppet.nick(nick) } } else { msg := '!irc nick <new_nick>' self.chat.say(system, '', network, room_id, msg) } } else { mut p_msg := '' nick := self.name_convert(chat.System.matrix, name) p_msg = 'your ($name) preferred nick is $nick' self.chat.say(system, '', network, room_id, p_msg) for ircnet in self.irc.networks.networks { puppets := self.irc.puppets.by_network(ircnet.name) for puppet in puppets { p_msg = '$ircnet $puppet.nick' self.chat.say(system, '', network, room_id, p_msg) } } } } 'connect' { for mut puppet in self.irc.puppets.puppets { mut p_pup := *puppet mut msg := 'checking irc connection for $p_pup.nick' self.chat.say(system, '', network, room_id, msg) if p_pup.state == .disconnected { msg = '$p_pup.nick disconnected. reconnecting to $p_pup.network' self.chat.say(system, '', network, room_id, msg) p_pup.dial() println('command_irc connect p_pup.state $p_pup.state') if p_pup.state == .connected { self.irc.comm(mut p_pup) p_pup.signin() } } } } 'join' { if parts.len == 4 { mut msg := '' network_name := parts[2] room := parts[3] if join_network := self.irc.networks.by_name(network_name) { match self.join_request_irc(join_network.name, room) { .saved { msg = 'irc room $room added' irc_nick := self.name_convert(system, name) if ghost := self.irc.puppets.by_net_nick(join_network, irc_nick) { msg = msg + '(joining)' // vbug to use go self.sync_irc_channels self.sync_irc_channels(ghost) } else { msg = msg + '($irc_nick not currently connected to $join_network.name)' } } .already_joined { msg = 'irc room $room already added' } } } else { msg = 'no network named ${network_name}. use !irc list' } self.chat.say(system, '', network, room_id, msg) } else { self.chat.say(system, '', network, room_id, 'usage: !irc join <network name> <#channel>') } } 'part' { if parts.len == 4 { mut msg := '' network_name := parts[2] room := parts[3] if join_network := self.irc.networks.by_name(network_name) { self.leave_request(chat.System.irc, join_network.name, room) msg = 'irc room $room $join_network.name removed' } else { msg = 'no network named ${network_name}. use !irc list' } self.chat.say(system, '', network, room_id, msg) } else { self.chat.say(system, '', network, room_id, 'usage: !irc join <network name> <#channel>') } } else { default_action = true } } return default_action } fn (mut self Main) listen_out() { for { select { outmsg := <-self.chat.out { self.process_out(outmsg) } > 1 * time.minute { if next_id := self.chat.queue.next_id() { println('listen_out timed out, found a msg.') self.process_out(next_id) } } } } } fn (mut self Main) process_out_user(user string) { println('process_out_user looking for $user') ids := self.chat.queue.by_name(user) for idx, id in ids { println('process_out_user found $user $id $idx/$ids.len') self.chat.out <- id break } } fn (mut self Main) process_out(id string) { if self.chat.queue.contains(id) { outmsg := self.chat.queue.get(id) println('process_out [$id/$self.chat.queue.len()] $outmsg') match outmsg.system { .irc { // TOOD remove ircnet_name := if outmsg.network.len == 0 { if self.irc.networks.networks.len > 0 { self.irc.networks.networks.first().hostname } else { 'noname' } } else { outmsg.network } match self.irc.say(ircnet_name, outmsg.name, outmsg.room, outmsg.message) { .good { println('process_out queue.delete($id)') self.chat.queue.delete(id) } .network_not_found {} .user_not_found { self.chat.cin <- chat.Payload(chat.MakeIrcUser{ network_hostname: ircnet_name nick: outmsg.name }) } .error {} } } .matrix { if room := self.matrix.joined_rooms.find_room_by_id(outmsg.room) { if self.matrix.owner == outmsg.name { self.matrix.room_say(room, outmsg.message) } else { match self.matrix.room_say_as(outmsg.name, room, outmsg.message) { .good { self.chat.queue.delete(id) if self.chat.queue.len() > 0 { println('process_out finished ${id}. remaining queue len $self.chat.queue.len()') } } .user_not_found { nick := self.name_convert(chat.System.matrix, outmsg.name) self.matrix.cin <- matrix.Payload(matrix.MakeUser{ name: nick user_id: outmsg.name }) } .not_in_room { p := matrix.Payload(matrix.JoinRoom{ name: outmsg.name room: outmsg.room }) match self.matrix.cin.try_push(p) { .success {} .not_ready { println('WARNING matrix.cin channel not ready. $self.matrix.cin.len entries') } .closed {} } } .error { println('matrix room_say_as error. retainig msg for retransmission') } } } } else { println('listen_out matrix.joined_rooms.find_room_by_id failed: $err.msg dropped: $outmsg.message ') println('process_out queue.delete($id)') self.chat.queue.delete(id) } } } } else { println('procesS_out dropping $id not in queue (len $self.chat.queue.len()') } } pub fn (mut self Main) leave_request(system chat.System, network string, room string) { table, field := match system { .irc { 'irc_channels', 'channel' } .matrix { 'matrix_rooms', 'room_id' } } self.db.delete_by_field(table, field, room) } enum JoinRequestResults { saved already_joined } pub fn (mut self Main) join_request_irc(network string, channel string) JoinRequestResults { self.db.insert('irc_channels', [ db.SqlValue{ name: 'channel', value: channel }, db.SqlValue{ name: 'netname' value: network }, ]) return JoinRequestResults.saved } pub fn (mut self Main) join_request_matrix(name string, room_id string) JoinRequestResults { self.db.insert('matrix_rooms', [db.SqlValue{ name: 'room_id', value: room_id }, db.SqlValue{ name: 'name' value: name }, ]) return JoinRequestResults.saved } pub fn (mut self Main) sync_matrix_rooms() { println('= sync_rooms') db_rooms := self.db.select_all('matrix_rooms').map(matrix.room_from_db(it)) println('sync_rooms: matrix db_rooms $db_rooms') println('sync_rooms: matrix joined_rooms $self.matrix.joined_rooms') needs_to_join := matrix.rooms_subtract(db_rooms, self.matrix.joined_rooms.pointers()) println('sync_rooms: matrix needs_to_join $needs_to_join') for room in needs_to_join { println('sync_rooms: joining $room.id') _, code := self.matrix.join(room.id) or { println('matrix.join fail $err') continue } if code == 200 { self.matrix.joined_rooms.add(room) self.admin_say('matrix: room $room joined') } else if code == 403 { println('sync_rooms: not invited to $room') self.leave_request(chat.System.matrix, '', room.id) } } for mut room in self.matrix.joined_rooms.rooms { if room.user_ids.len == 0 { room.user_ids = self.matrix.room_joined_members(room.id) or { continue } if room.name is string { if room.name.starts_with('@') { mut present := false for user_id in room.user_ids { if user_id == room.name { present = true } } if present == false { println('warning! $room does not include ${room.name}. sending invite.') self.matrix.room_invite(room, room.name) or {} } } } } } needs_to_leave := matrix.rooms_subtract(self.matrix.joined_rooms.pointers(), db_rooms) println('sync_rooms: matrix needs_to_leave $needs_to_leave') for room in needs_to_leave { self.matrix.leave(room.id) } } pub fn (mut self Main) sync_irc_channels(puppet irc.Puppet) { for row in self.db.select_all('irc_channels') { channel := row[0] netname := row[1] if puppet.network.name == netname { if puppet.state == .connected { if _ := puppet.find_channel(channel) { println('$puppet.network $puppet $channel already connected') } else { println('sync_irc_channels() $puppet.network $puppet.nick joining $netname $channel from db') mut puppet_mut := self.irc.puppets.by_nick(puppet.nick) or { continue } puppet_mut.join(channel) } } else { println('sync_irc_channels() $puppet.network $puppet.nick not connected. cannot join $channel') } } } } pub fn (mut self Main) matrix_name_match(name string) ?string { restr := '@$self.config.matrix_regex:$self.config.matrix_host' return regex_name_match(restr, name) } pub fn (mut self Main) irc_name_match(name string) bool { restr := self.config.irc_regex if rmatch := regex_name_match(restr, name) { println('irc_name_match $restr $name => $rmatch') return true } else { return false } } pub fn regex_name_match(restr string, name string) ?string { restr2 := restr.replace('|', '\\|') // pipe char helper hack mut re := regex.regex_opt(restr2) or { panic('regex_name_match regex parse fail for $restr') } _, _ := re.find(name) groups := re.get_group_list() group := groups[0] // always 1 group if group.end > 1 { return name[group.start..group.end] } else { return error('regex_name_match failed for $restr on $name') } } pub fn (mut self Main) name_convert(from_network chat.System, name string) string { return match from_network { .irc { alias := self.aliases.match_irc(name) or { matrix_user := regex_self_replace(self.config.matrix_regex, name) matrix_id := matrix.join([matrix_user, self.config.matrix_host]) alias := &chat.Alias{ matrix: matrix_id irc: name } self.matrix.user_displayname(matrix_id, name) or { println('ignoring user_displayname error for $matrix_id to $name') } self.aliases.add(alias) alias } alias.matrix } .matrix { alias := self.aliases.match_matrix(name) or { matrix_user := matrix.split(name)[1] irc_nick := regex_self_replace(self.config.irc_regex, matrix_user) alias := &chat.Alias{ matrix: name irc: irc_nick } // self.aliases.add(alias) alias } alias.irc } } } pub fn regex_self_replace(restr string, name string) string { newname := restr.replace_once('\(\.\*\)', name) println('regex_self_replace $restr $name => $newname') return newname } fn (mut self Main) nearest_matrix_channel(room_name string) ?string { if mroom := self.matrix.joined_rooms.room_by_partial_name(room_name.before(':')) { return mroom.id } else { return error('matchingmatrixchannelfail') } } fn (mut self Main) nearest_irc_channel(nick string, room &matrix.Room) ?string { if room.name is string { // VBUG: name := room.name { matrix_partial_name := room.name.before(':') println('matching_irc_channel matrix room name $room.name as irc channel name $matrix_partial_name') mut sname := '' // db search rows := self.db.select_by_field('irc_channels', 'channel', matrix_partial_name) if rows.len > 0 { println('matching_irc_channel found $matrix_partial_name in irc_channels db') sname = rows[0][0] } else { // connected channels search if ircc := self.irc.find_channel_by_name(nick, matrix_partial_name) { sname = ircc.name } else { return error('warning: matching_irc_channel found no irc channel for nick $nick matrix_room $matrix_partial_name') } } return sname } else { return error('matching_irc_channel: giving up on room $room has no name') } } pub fn (mut self Main) admin_say(msg string) { println('admin_say: $msg') if self.config.admin_room.len > 0 { if room := self.matrix.joined_rooms.find_room_by_name(self.config.admin_room) { self.chat.say(chat.System.matrix, '', self.config.matrix_host, room.id, msg) } else { println('warning admin_say has no room ${self.config.admin_room}. dropping msg $msg') } } else { self.matrix_say(self.config.matrix_owner, msg) } } pub fn (mut self Main) matrix_say(user string, msg string) { mut room := matrix.Room{} user_id := if user == self.matrix.whoami { self.config.matrix_owner } else { user } if this_room := self.matrix.joined_rooms.dm(user_id) { room = this_room } else { println('warning main.matrix_say has no private room with ${user_id}. creating room') room = self.matrix.room_create(user_id) or { println('main.matrix_say create_room $user_id failed. $err') return } println('warning main.matrix_say created room ${room}. inviting $user_id') self.matrix.room_invite(room, user_id) or { println('main.matrix_say invite $room $user_id failed. $err') return } } self.chat.say(chat.System.matrix, '', self.config.matrix_host, room.id, msg) }