summaryrefslogtreecommitdiff
path: root/relay.py
blob: 7fac5fa49e59db599081274026ccf7a59b1678a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
#!/usr/bin/python3
#
# miniirc-based relay bot
#
# © 2018 by luk3yx
#

import miniirc, sys
from miniirc import IRC

# Variables
debug    = True

# Network list
networks = {
   'LibreIRC': {
       'ip':       'irc.andrewyu.org',
       'port':     6697,
       'nick':     'LibreRelay',
       'ignored':  {},
       '#librespeech': 'librespeech',
   },
   'Libera': {
       'ip':       'irc.libera.chat',
       'port':     6697,
       'nick':     'LibreRelay',
       'ignored':  {},
       '#botwar': 'librespeech',
   },
}


# Format strings
# formatstrings = {
#     'PRIVMSG': '<{host[0]}@{network}> {msg}',
#     'ACTION':  '* {host[0]}@{network} {msg}',
#     'JOIN':    '--> {host[0]}@{network} ({host[1]}@{host[2]}) has joined',
#     'PART':    '<-- {host[0]}@{network} ({host[1]}@{host[2]}) has left ({msg})',
#     'KICK':    '<-- {victim} has been kicked by {kicker} ({msg})',
#     'NICK':    ' -- {host[0]}@{network} ({host[1]}@{host[2]}) is now known as'
#         '{msg}',
#     'QUIT':    '<-- {victim} ({host[1]}@{host[2]}) has quit ({msg})'
# }

# This looks messy, but it creates nice coloured/colored strings.
_uh = '\x036(\x0310{{host[1]}}@{{host[2]}}\x036)'
_ = '\x03{0}{1}\x03 {{host[0]}}@{{network}} ' + _uh + \
    '\x03{0} has {2} \x036(\x0310{{msg}}\x036)'
_hn = '{host[0]}@{network}'
formatstrings = {
    'PRIVMSG': '<' + _hn + '> {msg}',
    'ACTION':  '* ' + _hn + ' {msg}',
    'JOIN':    _.format(3, '-->', 'joined').replace(' \x036(\x0310{msg}\x036)',
        ''),
    'PART':    _.format(4, '<--', 'left'),
    'KICK':    _.format(4, '<--', 'been yaykicked by {kicker}').replace(' ' +
        _uh.format(), '').replace('{host[0]}', '{victim}'),
    'NICK':    _.format(6, ' --', '{NICK}').replace('({msg})',
        '{msg}').replace('has {NICK}', 'is now known as'),
    'QUIT':    _.format(4, '<--', 'quit IRC')
}
del _
del _hn
del _uh

# Welcome!
print('Welcome to miniirc-relay!', file=sys.stderr)
is_channel = lambda channel : type(channel) == str and not channel[:1].isalnum()

# Parse the networks list and connect to any new IRC networks
_ircs   = {}
relayed = {}
def parse_networks():
    print('Parsing networks...', file=sys.stderr)
    relayed.clear()
    for name in networks:
        network = networks[name]

        channels = set()
        for channel in network:
            if is_channel(channel):
                lchan = channel.lower()
                if channel != lchan:
                    network[lchan] = network[channel]
                    del network[channel]
                    channel = lchan
                del lchan
                channels.add(channel)
                id = network[channel]
                if id not in relayed:
                    relayed[id] = {}
                relayed[id][name] = channel

        if not network.get(IRC):
            print('Connecting to {}...'.format(repr(name)), file=sys.stderr)
            network[IRC] = IRC(network['ip'], network['port'], network['nick'],
                channels, ns_identity = network.get('ns_identity'),
                debug = debug)
            network[IRC].debug('Channels on {}: {}'.format(repr(name),channels))
            _ircs[network[IRC]] = name
    print('Done.', file=sys.stderr)

# Check to see if a user is ignored.
def is_ignored(hostmask, network):
    if not networks.get(network) or not networks[network].get('ignored'):
        return
    for name in hostmask:
        if name.lower() in networks[network]['ignored']:
            networks[network][IRC].debug('Ignoring message from ' +
                repr(hostmask))
            return True
    return False

# Send a message to networks
def relay_message(irc, msg, channel=None):
    if not msg:
        return
    network = _ircs.get(irc)

    if channel:
        channel = channel.lower()
        irc.debug('Sending message from', channel, 'on', network)
        if not is_channel(channel) or channel not in networks[network]:
            return
        id = networks[network][channel]
        if id not in relayed:
            print('WARNING: Non-existent channel ID detected!', file=sys.stderr)
            parse_networks()
        to_relay = relayed[id]
        for n in to_relay:
            if n != network and networks[n].get(IRC):
                irc.debug('Relaying from', network, 'to', n, 'via', networks[n][IRC])
                networks[n][IRC].msg(to_relay[n], msg)
    else:
        for n in networks:
            if n != network and networks[n].get(IRC):
                for channel in networks[n]:
                    if is_channel(channel):
                        networks[n][IRC].msg(channel, msg)


# Handle PRIVMSGs
@miniirc.Handler('PRIVMSG', colon=False)
def handle_privmsg(irc, hostmask, args):
    text = args[-1]
    msg = None
    net = _ircs.get(irc)
    if is_ignored(hostmask, net):
        return

    if text == '\x01ACTION\x01' or (text.startswith('\x01ACTION ')
      and text.endswith('\x01')):
        msg = formatstrings['ACTION'].format(host=hostmask, network=net,
            msg=text[8:-1])
    elif not text.startswith('\x01'):
        msg = formatstrings['PRIVMSG'].format(host=hostmask, network=net,
            msg=text)

    relay_message(irc, msg, args[0])

# Handle JOINs
@miniirc.Handler('JOIN', colon=False)
def handle_join(irc, hostmask, args):
    net = _ircs.get(irc)
    if is_ignored(hostmask, net):
        return
    if net:
        msg = formatstrings['JOIN'].format(host=hostmask, network=net)
        relay_message(irc, msg, args[0])

# Handle PARTs
@miniirc.Handler('PART', colon=False)
def handle_part(irc, hostmask, args):
    net = _ircs.get(irc)
    if is_ignored(hostmask, net):
        return
    if net:
        msg = formatstrings['PART'].format(host=hostmask, network=net,
            msg=args[-1])
        relay_message(irc, msg, args[0])

# Handle PARTs
@miniirc.Handler('KICK', colon=False)
def handle_part(irc, hostmask, args):
    net = _ircs.get(irc)
    if is_ignored(hostmask, net):
        return
    if net:
        msg = formatstrings['KICK'].format(victim=args[-2], network=net,
            kicker=hostmask[0], msg=args[-1])
        relay_message(irc, msg, args[0])

# Handle QUITs
@miniirc.Handler('QUIT', colon=False)
def handle_quit(irc, hostmask, args):
    net = _ircs.get(irc)
    if is_ignored(hostmask, net):
        return
    if net:
        msg = formatstrings['QUIT'].format(host=hostmask, network=net,
            msg=args[-1] if args else '')
        relay_message(irc, msg)

# Handle NICKs
@miniirc.Handler('NICK', colon=False)
def handle_quit(irc, hostmask, args):
    net = _ircs.get(irc)
    if is_ignored(hostmask, net):
        return
    if net:
        msg = formatstrings['NICK'].format(host=hostmask, network=net,
            newnick=args[0])
        relay_message(irc, msg)

# Parse the network list
parse_networks()