repos
/
irgramd
/ annotate_shade
telegram, irc: Set topic in IRC with Telegram channel/chat description
Annotate for file irc.py
2022-02-20 E.
1
# irgramd: IRC-Telegram gateway
01:25:27 '
2
# irc.py: IRC server side implementation
'
3
#
'
4
# Copyright (c) 2019 Peter Bui <pbui@bx612.space>
2023-03-29 E.
5
# Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
2022-02-20 E.
6
#
01:25:27 '
7
# Use of this source code is governed by a MIT style license that
'
8
# can be found in the LICENSE file included in this project.
2020-11-19 E.
9
19:41:24 '
10
import collections
'
11
import logging
'
12
import re
'
13
import socket
2021-01-27 E.
14
import string
19:51:33 '
15
import time
2020-11-19 E.
16
19:41:24 '
17
import tornado.ioloop
'
18
'
19
# Local modules
'
20
2024-11-02 E.
21
from include import VERSION, CHAN_MAX_LENGTH, NICK_MAX_LENGTH, MAX_LINE
2021-01-27 E.
22
from irc_replies import irc_codes
2022-01-24 E.
23
from utils import chunks, set_replace, split_lines
2022-03-02 E.
24
from service import service
2023-06-25 E.
25
from exclam import exclam
2021-01-27 E.
26
19:51:33 '
27
# Constants
'
28
2021-02-08 E.
29
SRV = None
2021-03-04 E.
30
ALL_PARAMS = 16
2021-01-27 E.
31
VALID_IRC_NICK_FIRST_CHARS = string.ascii_letters + '[]\`_^{|}'
19:51:33 '
32
VALID_IRC_NICK_CHARS = VALID_IRC_NICK_FIRST_CHARS + string.digits + '-'
2020-11-19 E.
33
19:41:24 '
34
# IRC Regular Expressions
'
35
2021-02-17 E.
36
PREFIX = r'(?ai)(:[^ ]+ +|)'
2021-02-18 E.
37
IRC_JOIN_RX = re.compile(PREFIX + r'JOIN( +:| +|\n)(?P<channels>[^\n ]+|)')
2021-11-23 E.
38
IRC_LIST_RX = re.compile(PREFIX + r'LIST( +:| +|\n)(?P<channels>[^\n ]+|)')
2021-03-04 E.
39
IRC_MODE_RX = re.compile(PREFIX + r'MODE( +|\n)(?P<target>[^ ]+( +|\n)|)(?P<mode>[^ ]+( +|\n)|)(?P<arguments>[^\n]+|)')
2021-02-18 E.
40
IRC_MOTD_RX = re.compile(PREFIX + r'MOTD( +:| +|\n)(?P<target>[^\n ]+|)')
23:48:08 '
41
IRC_NAMES_RX = re.compile(PREFIX + r'NAMES( +:| +|\n)(?P<channels>[^\n ]+|)')
'
42
IRC_NICK_RX = re.compile(PREFIX + r'NICK( +:| +|\n)(?P<nick>[^\n ]+|)')
2021-03-31 E.
43
IRC_PART_RX = re.compile(PREFIX + r'PART( +|\n)(?P<channels>[^ ]+|)( +:| +|\n|)(?P<reason>[^\n]+|)')
2021-02-18 E.
44
IRC_PASS_RX = re.compile(PREFIX + r'PASS( +:| +|\n)(?P<password>[^\n ]+|)')
2021-01-24 E.
45
IRC_PING_RX = re.compile(PREFIX + r'PING( +:| +|\n)(?P<payload>[^\n]+|)')
2022-02-03 E.
46
IRC_PRIVMSG_RX = re.compile(PREFIX + r'PRIVMSG( +|\n)(?P<target>[^ ]+)( +:| +|\n)(?P<message>[^\n]+|)')
2021-02-20 E.
47
IRC_QUIT_RX = re.compile(PREFIX + r'QUIT( +:| +|\n)(?P<reason>[^\n]+|)')
2021-02-19 E.
48
IRC_TOPIC_RX = re.compile(PREFIX + r'TOPIC( +:| +|\n)(?P<channel>[^\n ]+|)')
2021-01-24 E.
49
IRC_USER_RX = re.compile(PREFIX + r'USER( +|\n)(?P<username>[^ ]+) +[^ ]+ +[^ ]+( +:| +|\n)(?P<realname>[^\n]+|)')
2021-11-17 E.
50
IRC_USERHOST_RX = re.compile(PREFIX + r'USERHOST( +|\n)(?P<nick1>[^ ]+( +|\n)|)(?P<nick2>[^ ]+( +|\n)|)(?P<nick3>[^ ]+( +|\n)|)(?P<nick4>[^ ]+( +|\n)|)(?P<nick5>[^\n]+|)')
2021-02-21 E.
51
IRC_VERSION_RX = re.compile(PREFIX + r'VERSION( +:| +|\n)(?P<target>[^\n ]+|)')
2021-02-18 E.
52
IRC_WHO_RX = re.compile(PREFIX + r'WHO( +:| +|\n)(?P<target>[^\n ]+|)')
23:48:08 '
53
IRC_WHOIS_RX = re.compile(PREFIX + r'WHOIS( +:| +|\n)(?P<nicks>[^\n ]+|)')
2020-11-19 E.
54
19:41:24 '
55
# IRC Handler
'
56
'
57
class IRCHandler(object):
2021-12-12 E.
58
def __init__(self, settings):
2020-11-19 E.
59
self.logger = logging.getLogger()
2022-02-12 E.
60
self.hostname = socket.getfqdn()
2021-12-13 E.
61
self.conf = settings
2021-01-28 E.
62
self.users = {}
2020-11-19 E.
63
19:41:24 '
64
# Initialize IRC
'
65
self.initialize_irc()
'
66
2020-11-22 E.
67
22:57:38 '
68
async def run(self, stream, address):
2021-01-20 E.
69
user = IRCUser(stream, address)
00:13:37 '
70
2022-02-16 E.
71
self.logger.info('Running IRC client connection from %s:%s', address[0], address[1])
2020-11-19 E.
72
19:41:24 '
73
while True:
2021-01-29 E.
74
try:
00:24:25 '
75
message = await user.stream.read_until(b'\n')
'
76
except tornado.iostream.StreamClosedError:
2021-02-20 E.
77
user.stream = None
22:52:38 '
78
reason = user.close_reason if user.close_reason else ':Client disconnect'
'
79
await self.send_users_irc(user, 'QUIT', (reason,))
2022-02-16 E.
80
self.logger.info('Closing IRC client connection from %s:%s', address[0], address[1])
2021-01-29 E.
81
if user in self.users.values():
2021-01-29 E.
82
del self.users[user.irc_nick.lower()]
2021-02-04 E.
83
user.del_from_channels(self)
2021-01-29 E.
84
del user
00:24:25 '
85
break
2023-06-10 E.
86
message = message.decode(self.conf['char_in_encoding'], errors='replace').replace('\r','\n')
2020-11-19 E.
87
self.logger.debug(message)
2021-01-24 E.
88
2021-03-04 E.
89
for pattern, handler, register_required, num_params_required in self.irc_handlers:
2020-11-19 E.
90
matches = pattern.match(message)
19:41:24 '
91
if matches:
2021-01-24 E.
92
if user.registered or not register_required:
02:31:52 '
93
params = matches.groupdict()
2021-02-04 E.
94
# Remove possible extra characters in parameters
14:37:25 '
95
params = {x:y.strip() for x,y in params.items()}
2021-01-24 E.
96
num_params = len([x for x in params.values() if x])
02:31:52 '
97
num_params_expected = len(params.keys())
2021-03-04 E.
98
if num_params >= self.num_params_necessary(num_params_required,
00:08:27 '
99
num_params_expected):
2021-01-24 E.
100
await handler(user, **params)
02:31:52 '
101
else:
2021-01-31 E.
102
await self.reply_code(user, 'ERR_NEEDMOREPARAMS')
2021-02-13 E.
103
else:
23:38:08 '
104
await self.reply_code(user, 'ERR_NOTREGISTERED', ('',), '*')
2021-02-14 E.
105
break
00:02:09 '
106
'
107
if not matches and user.registered:
2021-01-31 E.
108
await self.reply_code(user, 'ERR_UNKNOWNCOMMAND')
2020-11-19 E.
109
2020-11-22 E.
110
def set_telegram(self, tg):
22:57:38 '
111
self.tg = tg
2022-03-08 E.
112
self.service = service(self.conf, self.tg)
2023-06-25 E.
113
self.exclam = exclam(self.tg)
2020-11-22 E.
114
2020-11-19 E.
115
# IRC
19:41:24 '
116
'
117
def initialize_irc(self):
2022-03-01 E.
118
self.irc_handlers = \
19:02:36 '
119
(
2021-03-04 E.
120
# pattern handle register_required num_params_required
2022-03-01 E.
121
(IRC_PRIVMSG_RX, self.handle_irc_privmsg, True, ALL_PARAMS),
19:02:36 '
122
(IRC_PING_RX, self.handle_irc_ping, True, ALL_PARAMS),
2021-03-04 E.
123
(IRC_JOIN_RX, self.handle_irc_join, True, ALL_PARAMS),
00:08:27 '
124
(IRC_MODE_RX, self.handle_irc_mode, True, 1),
'
125
(IRC_NAMES_RX, self.handle_irc_names, True, ALL_PARAMS),
'
126
(IRC_TOPIC_RX, self.handle_irc_topic, True, ALL_PARAMS),
2021-11-17 E.
127
(IRC_USERHOST_RX, self.handle_irc_userhost, True, 1),
2022-03-01 E.
128
(IRC_PART_RX, self.handle_irc_part, True, 1),
2021-03-04 E.
129
(IRC_WHO_RX, self.handle_irc_who, True, ALL_PARAMS),
00:08:27 '
130
(IRC_WHOIS_RX, self.handle_irc_whois, True, ALL_PARAMS),
2022-03-01 E.
131
(IRC_LIST_RX, self.handle_irc_list, True, 0),
19:02:36 '
132
(IRC_NICK_RX, self.handle_irc_nick, False, ALL_PARAMS),
'
133
(IRC_MOTD_RX, self.handle_irc_motd, True, 0),
'
134
(IRC_USER_RX, self.handle_irc_user, False, ALL_PARAMS),
'
135
(IRC_QUIT_RX, self.handle_irc_quit, False, 0),
'
136
(IRC_VERSION_RX, self.handle_irc_version, True, 0),
'
137
(IRC_PASS_RX, self.handle_irc_pass, False, ALL_PARAMS),
2020-11-19 E.
138
)
19:41:24 '
139
self.iid_to_tid = {}
'
140
self.irc_channels = collections.defaultdict(set)
2021-02-01 E.
141
self.irc_channels_ops = collections.defaultdict(set)
2021-02-04 E.
142
self.irc_channels_founder = collections.defaultdict(set)
2021-01-27 E.
143
self.start_time = time.strftime('%a %d %b %Y %H:%M:%S %z')
2020-11-19 E.
144
2023-06-14 E.
145
self.service_user = IRCUser(None, ('Services',''), self.conf['service_user'],
2022-02-26 E.
146
'Control', 'Telegram Service', is_service=True)
23:47:48 '
147
self.users[self.conf['service_user'].lower()] = self.service_user
2020-11-19 E.
148
2021-01-20 E.
149
async def send_irc_command(self, user, command):
2020-11-19 E.
150
self.logger.debug('Send IRC Command: %s', command)
19:41:24 '
151
command = command + '\r\n'
2023-06-10 E.
152
user.stream.write(command.encode(self.conf['char_out_encoding'], errors='replace'))
2021-01-20 E.
153
2021-01-27 E.
154
# IRC handlers
19:51:33 '
155
'
156
async def handle_irc_pass(self, user, password):
'
157
self.logger.debug('Handling PASS: %s %s', password)
'
158
'
159
if user.registered:
2021-01-31 E.
160
await self.reply_code(user, 'ERR_ALREADYREGISTRED')
2021-01-27 E.
161
else:
19:51:33 '
162
user.recv_pass = password
'
163
2021-01-20 E.
164
async def handle_irc_nick(self, user, nick):
2020-11-19 E.
165
self.logger.debug('Handling NICK: %s', nick)
19:41:24 '
166
2021-02-08 E.
167
ni = nick.lower()
2022-02-04 E.
168
current = user.irc_nick.lower() if user.irc_nick else None
18:18:34 '
169
if ni == current:
'
170
return
2021-01-28 E.
171
if not user.valid_nick(nick):
2021-02-08 E.
172
await self.reply_code(user, 'ERR_ERRONEUSNICKNAME', (nick,), '*')
23:04:54 '
173
elif ni in self.users.keys():
2021-01-31 E.
174
await self.reply_code(user, 'ERR_NICKNAMEINUSE', (nick,), '*')
2021-12-13 E.
175
elif user.pam_auth(nick, self.conf['pam'], self.conf['pam_group'], user.recv_pass) \
2022-01-22 E.
176
or user.local_auth(nick, self.conf['irc_nicks'], user.recv_pass, self.conf['irc_password'], self.conf['pam']):
2021-02-08 E.
177
if user.registered:
23:04:54 '
178
# rename
2021-02-20 E.
179
await self.send_users_irc(user, 'NICK', (nick,))
2021-02-21 E.
180
del self.users[current]
00:12:58 '
181
for ch in self.irc_channels.keys():
'
182
set_replace(self.irc_channels[ch], current, ni)
'
183
set_replace(self.irc_channels_ops[ch], current, ni)
'
184
set_replace(self.irc_channels_founder[ch], current, ni)
2021-01-27 E.
185
user.irc_nick = nick
2021-02-08 E.
186
self.users[ni] = user
2021-01-27 E.
187
if not user.registered and user.irc_username:
2022-03-08 E.
188
await self.register(user)
2021-01-27 E.
189
else:
2022-02-04 E.
190
if user.registered:
18:31:39 '
191
await self.reply_code(user, 'ERR_ERRONEUSNICKNAME', (nick,))
'
192
else:
'
193
await self.reply_code(user, 'ERR_PASSWDMISMATCH')
2021-01-20 E.
194
2021-11-17 E.
195
async def handle_irc_userhost(self, user, **nicks):
20:49:05 '
196
niv = nicks.values()
'
197
self.logger.debug('Handling USERHOST: %s', str(tuple(niv)))
'
198
'
199
reply = ''
'
200
sep = ''
'
201
away = '+'
'
202
for ni in niv:
'
203
n = ni.lower()
'
204
if n in self.users.keys():
'
205
usr = self.users[n]
'
206
oper = '*' if usr.oper else ''
'
207
reply += '{}{}{}={}{}@{}'.format(sep, usr.irc_nick, oper, away, usr.irc_username, usr.address)
'
208
if not sep: sep = ' '
'
209
if reply:
'
210
await self.reply_code(user, 'RPL_USERHOST', (reply,))
'
211
2021-01-20 E.
212
async def handle_irc_user(self, user, username, realname):
2020-11-19 E.
213
self.logger.debug('Handling USER: %s, %s', username, realname)
19:41:24 '
214
2021-01-20 E.
215
user.irc_username = username
00:13:37 '
216
user.irc_realname = realname
2021-01-27 E.
217
if user.irc_nick:
2022-03-08 E.
218
await self.register(user)
2020-11-19 E.
219
2021-02-12 E.
220
async def handle_irc_join(self, user, channels):
02:09:58 '
221
self.logger.debug('Handling JOIN: %s', channels)
'
222
'
223
if channels == '0':
2021-03-31 E.
224
for channel in self.irc_channels.keys():
01:21:58 '
225
if user.irc_nick in self.irc_channels[channel]:
'
226
await self.part_irc_channel(user, channel, '')
2021-02-12 E.
227
else:
02:09:58 '
228
for channel in channels.split(','):
'
229
if channel.lower() in self.irc_channels.keys():
2023-03-24 E.
230
await self.join_irc_channel(user, channel, full_join=True)
2021-02-12 E.
231
else:
2021-02-14 E.
232
await self.reply_code(user, 'ERR_NOSUCHCHANNEL', (channel,))
2021-01-20 E.
233
2021-03-31 E.
234
async def handle_irc_part(self, user, channels, reason):
01:21:58 '
235
self.logger.debug('Handling PART: %s, %s', channels, reason)
'
236
'
237
for channel in channels.split(','):
'
238
chan = channel.lower()
'
239
if chan in self.irc_channels.keys():
'
240
if user.irc_nick in self.irc_channels[chan]:
'
241
await self.part_irc_channel(user, channel, reason)
'
242
else:
'
243
await self.reply_code(user, 'ERR_NOTONCHANNEL', (channel,))
'
244
else:
'
245
await self.reply_code(user, 'ERR_NOSUCHCHANNEL', (channel,))
'
246
2021-11-23 E.
247
async def handle_irc_list(self, user, channels):
23:27:12 '
248
self.logger.debug('Handling LIST: %s', channels)
'
249
'
250
if channels:
'
251
chans = channels.split(',')
'
252
else:
'
253
chans = self.irc_channels.keys()
'
254
'
255
await self.reply_code(user, 'RPL_LISTSTART')
'
256
for channel in chans:
'
257
chan = channel.lower()
'
258
if chan in self.irc_channels.keys():
'
259
real_chan = self.get_realcaps_name(chan)
'
260
users_count = len(self.irc_channels[chan])
2022-01-30 E.
261
topic = await self.tg.get_channel_topic(chan, [None])
2024-11-02 E.
262
await self.reply_code(user, 'RPL_LIST', (real_chan, users_count, topic[:MAX_LINE]))
2021-11-23 E.
263
await self.reply_code(user, 'RPL_LISTEND')
23:27:12 '
264
2021-02-14 E.
265
async def handle_irc_names(self, user, channels):
00:55:27 '
266
self.logger.debug('Handling NAMES: %s', channels)
'
267
'
268
for channel in channels.split(','):
'
269
await self.irc_namelist(user, channel)
'
270
2021-03-04 E.
271
async def handle_irc_mode(self, user, target, mode, arguments):
00:08:27 '
272
self.logger.debug('Handling MODE: %s, %s, %s', target, mode, arguments)
'
273
2022-02-12 E.
274
is_user = False
00:29:36 '
275
is_channel = False
'
276
'
277
tgt = target.lower()
'
278
if tgt in self.users.keys():
'
279
if tgt == user.irc_nick:
'
280
is_user = True
'
281
else:
'
282
await self.reply_code(user, 'ERR_USERSDONTMATCH')
'
283
elif tgt[0] == '#':
'
284
if tgt in self.irc_channels.keys():
'
285
is_channel = True
'
286
else:
'
287
await self.reply_code(user, 'ERR_NOSUCHCHANNEL', (target,))
'
288
else:
'
289
await self.reply_code(user, 'ERR_NOSUCHNICK', (target,))
'
290
2021-03-04 E.
291
if not mode:
2022-02-12 E.
292
if is_user:
00:29:36 '
293
await self.mode_user(user, user, False)
'
294
if is_channel:
'
295
await self.mode_channel(user, target, False)
'
296
elif mode == 'b' and is_channel:
'
297
await self.reply_code(user, 'RPL_ENDOFBANLIST', (target,))
2021-03-04 E.
298
2021-02-17 E.
299
async def handle_irc_motd(self, user, target):
20:24:00 '
300
self.logger.debug('Handling MOTD: %s', target)
'
301
2022-02-12 E.
302
if not target or target == self.gethostname(user):
2021-02-17 E.
303
await self.send_motd(user)
20:24:00 '
304
else:
'
305
await self.reply_code(user, 'ERR_NOSUCHSERVER', (target,))
'
306
2021-02-19 E.
307
async def handle_irc_topic(self, user, channel):
00:05:45 '
308
self.logger.debug('Handling TOPIC: %s', channel)
'
309
'
310
chan = channel.lower()
'
311
real_chan = self.get_realcaps_name(chan)
2022-01-30 E.
312
await self.irc_channel_topic(user, real_chan, [None])
2021-02-19 E.
313
2021-01-20 E.
314
async def handle_irc_ping(self, user, payload):
2020-11-19 E.
315
self.logger.debug('Handling PING: %s', payload)
2021-02-08 E.
316
2022-02-12 E.
317
await self.reply_command(user, SRV, 'PONG', (self.gethostname(user), payload))
2020-11-19 E.
318
2021-02-04 E.
319
async def handle_irc_who(self, user, target):
23:38:22 '
320
self.logger.debug('Handling WHO: %s', target)
'
321
tgt = target.lower()
'
322
if tgt in self.irc_channels.keys():
'
323
users = self.irc_channels[tgt]
2021-02-14 E.
324
chan = self.get_realcaps_name(tgt)
2021-02-04 E.
325
elif tgt in self.users.keys():
23:38:22 '
326
users = (self.users[tgt],)
'
327
chan = '*'
'
328
else:
'
329
await self.reply_code(user, 'ERR_NOSUCHSERVER', (target,))
'
330
return
'
331
for usr in users:
'
332
if not isinstance(usr,IRCUser):
'
333
usr = self.users[usr.lower()]
'
334
op = self.get_irc_op(usr.irc_nick, chan)
'
335
await self.reply_code(user, 'RPL_WHOREPLY', (chan, usr.irc_username,
2022-02-12 E.
336
usr.address, self.gethostname(user), usr.irc_nick, op, usr.irc_realname
2021-02-04 E.
337
))
23:38:22 '
338
await self.reply_code(user, 'RPL_ENDOFWHO', (chan,))
'
339
2021-02-07 E.
340
async def handle_irc_whois(self, user, nicks):
2021-02-21 E.
341
self.logger.debug('Handling WHOIS: %s', nicks)
2021-02-07 E.
342
for nick in nicks.split(','):
02:14:36 '
343
ni = nick.lower()
'
344
if ni in self.users.keys():
'
345
usr = self.users[ni]
2022-02-27 E.
346
real_ni = usr.irc_nick
2021-02-07 E.
347
await self.reply_code(user, 'RPL_WHOISUSER', (real_ni, usr.irc_username, usr.address, usr.irc_realname))
2022-02-12 E.
348
await self.reply_code(user, 'RPL_WHOISSERVER', (real_ni, self.gethostname(user)))
2021-02-07 E.
349
chans = usr.get_channels(self)
02:14:36 '
350
if chans: await self.reply_code(user, 'RPL_WHOISCHANNELS', (real_ni, chans))
'
351
idle = await self.tg.get_telegram_idle(ni)
'
352
if idle != None: await self.reply_code(user, 'RPL_WHOISIDLE', (real_ni, idle))
'
353
if usr.oper: await self.reply_code(user, 'RPL_WHOISOPERATOR', (real_ni,))
'
354
if usr.stream: await self.reply_code(user, 'RPL_WHOISACCOUNT', (real_ni,
2022-02-05 E.
355
'{}|{}!{}@Telegram'.format(self.tg.tg_username,
02:30:31 '
356
await self.tg.get_telegram_display_name_me(), self.tg.id
2021-02-07 E.
357
)))
02:14:36 '
358
if await self.tg.is_bot(ni):
'
359
await self.reply_code(user, 'RPL_WHOISBOT', (real_ni,))
2022-02-26 E.
360
elif usr.tls or (not usr.stream and not usr.is_service):
2021-02-07 E.
361
proto = 'TLS' if usr.tls else 'MTProto'
2022-02-12 E.
362
server = self.gethostname(user) if usr.stream else 'Telegram'
2021-02-07 E.
363
await self.reply_code(user, 'RPL_WHOISSECURE', (real_ni, proto, server))
2022-02-26 E.
364
if usr.is_service:
23:47:48 '
365
await self.reply_code(user, 'RPL_WHOISSERVICE', (real_ni,))
2021-02-07 E.
366
await self.reply_code(user, 'RPL_ENDOFWHOIS', (real_ni,))
02:14:36 '
367
else:
'
368
await self.reply_code(user, 'ERR_NOSUCHNICK', (nick,))
'
369
2021-02-21 E.
370
async def handle_irc_version(self, user, target):
22:35:32 '
371
self.logger.debug('Handling VERSION: %s', target)
'
372
'
373
tgt = target.lower()
2022-02-12 E.
374
if not tgt or tgt == self.gethostname(user) or tgt in self.users.keys():
02:02:43 '
375
await self.reply_code(user, 'RPL_VERSION', (VERSION, self.gethostname(user)))
2021-02-21 E.
376
await self.send_isupport(user)
22:35:32 '
377
else:
'
378
await self.reply_code(user, 'ERR_NOSUCHSERVER', (target,))
'
379
2022-02-03 E.
380
async def handle_irc_privmsg(self, user, target, message):
20:11:10 '
381
self.logger.debug('Handling PRIVMSG: %s, %s', target, message)
'
382
'
383
tgl = target.lower()
2022-02-26 E.
384
if self.service_user.irc_nick.lower() == tgl:
2022-03-19 E.
385
reply = await self.service.parse_command(message, user.irc_nick)
2022-03-06 E.
386
for reply_line in reply:
2022-03-18 E.
387
await self.send_msg(self.service_user, None, reply_line, user)
2022-02-26 E.
388
return
2023-04-11 E.
389
defered_send = None
2022-02-03 E.
390
# Echo channel messages from IRC to other IRC connections
20:11:10 '
391
# because they won't receive event from Telegram
2023-04-11 E.
392
# used defered_send function when id is known
2022-02-03 E.
393
if tgl in self.irc_channels.keys():
2023-04-11 E.
394
chan = tgl
07:45:58 '
395
defered_send = self.send_msg_others
'
396
defered_target = chan
'
397
else:
'
398
chan = None
2022-02-03 E.
399
20:11:10 '
400
if tgl == user.irc_nick:
'
401
tgt = self.tg.tg_username.lower()
2022-01-27 E.
402
# Echo message to the user him/herself in IRC
22:40:06 '
403
# because no event will be received from Telegram
2023-04-11 E.
404
# used defered_send function when id is known
07:45:58 '
405
defered_send = self.send_msg
'
406
defered_target = None
2022-01-27 E.
407
else:
2022-02-03 E.
408
tgt = tgl
2021-02-02 E.
409
2022-02-04 E.
410
if tgt in self.iid_to_tid:
2023-06-15 E.
411
message = self.tg.replace_mentions(message, me_nick='', received=False)
2022-02-04 E.
412
telegram_id = self.iid_to_tid[tgt]
2023-06-25 E.
413
if message[0] == '!':
22:17:22 '
414
cont, tg_msg = await self.exclam.command(message, telegram_id, user)
'
415
else:
'
416
tg_msg = await self.tg.telegram_client.send_message(telegram_id, message)
'
417
cont = True
'
418
if cont:
'
419
mid = self.tg.mid.num_to_id_offset(telegram_id, tg_msg.id)
'
420
text = '[{}] {}'.format(mid, message)
'
421
self.tg.to_cache(tg_msg.id, mid, text, message, user, chan, media=None)
'
422
'
423
if defered_send:
'
424
await defered_send(user, defered_target, text)
2022-02-04 E.
425
else:
17:21:49 '
426
await self.reply_code(user, 'ERR_NOSUCHNICK', (target,))
2020-11-19 E.
427
2021-02-20 E.
428
async def handle_irc_quit(self, user, reason):
22:52:38 '
429
self.logger.debug('Handling TOPIC: %s', reason)
'
430
'
431
await self.reply_command(user, SRV, 'ERROR', (':Client disconnect',))
'
432
user.close_reason = ':' + reason
'
433
user.stream.close()
'
434
2021-01-24 E.
435
# IRC functions
2022-03-08 E.
436
async def register(self, user):
2023-06-14 E.
437
self.logger.info('Registered IRC user "%s" from %s:%s', user.irc_nick, user.address, user.port)
23:15:43 '
438
2022-03-08 E.
439
user.registered = True
20:57:51 '
440
await self.send_greeting(user)
'
441
await self.send_help(user)
2022-03-08 E.
442
await self.check_telegram_auth(user)
2021-01-24 E.
443
2022-03-18 E.
444
async def send_msg(self, source, target, message, selfuser=None):
2022-01-24 E.
445
messages = split_lines(message)
2022-02-03 E.
446
tgt = target.lower() if target else ''
20:11:10 '
447
is_chan = tgt in self.irc_channels.keys()
'
448
# source None (False): it's self Telegram user, see [1]
2022-01-24 E.
449
source_mask = source.get_irc_mask() if source else ''
21:35:55 '
450
for msg in messages:
2022-03-18 E.
451
if selfuser:
21:08:29 '
452
irc_users = (selfuser,)
'
453
elif is_chan:
2022-02-03 E.
454
irc_users = (u for u in self.users.values() if u.stream and u.irc_nick in self.irc_channels[tgt])
20:11:10 '
455
else:
'
456
irc_users = (u for u in self.users.values() if u.stream)
'
457
'
458
for irc_user in irc_users:
'
459
await self.send_privmsg(irc_user, source_mask, target, msg)
'
460
'
461
async def send_msg_others(self, source, target, message):
'
462
source_mask = source.get_irc_mask()
'
463
is_chan = target in self.irc_channels.keys()
'
464
if is_chan:
'
465
irc_users = (u for u in self.users.values() if u.stream and u.irc_nick != source.irc_nick
'
466
and u.irc_nick in self.irc_channels[target])
'
467
else:
'
468
irc_users = (u for u in self.users.values() if u.stream and u.irc_nick != source.irc_nick)
'
469
'
470
for irc_user in irc_users:
'
471
await self.send_privmsg(irc_user, source_mask, target, message)
'
472
2023-11-26 E.
473
async def send_action(self, source, target, message):
19:07:44 '
474
action_message = '\x01ACTION {}\x01'.format(message)
'
475
await self.send_msg(source, target, action_message)
'
476
2022-02-03 E.
477
async def send_privmsg(self, user, source_mask, target, msg):
20:11:10 '
478
# reference [1]
'
479
src_mask = source_mask if source_mask else user.get_irc_mask()
'
480
# target None (False): it's private, not a channel
'
481
tgt = target if target else user.irc_nick
2023-03-28 E.
482
if self.tg.refwd_me:
21:32:36 '
483
msg = msg.format(user.irc_nick)
2023-06-10 E.
484
# replace self @username and other mentions for self messages sent by this instance of irgramd
20:31:22 '
485
msg = self.tg.replace_mentions(msg, user.irc_nick)
'
486
2022-02-03 E.
487
await self.send_irc_command(user, ':{} PRIVMSG {} :{}'.format(src_mask, tgt, msg))
2022-01-24 E.
488
2021-02-08 E.
489
async def reply_command(self, user, prfx, comm, params):
2022-02-12 E.
490
prefix = self.gethostname(user) if prfx == SRV else prfx.get_irc_mask()
2021-02-08 E.
491
p = len(params)
23:04:54 '
492
if p == 1:
'
493
fstri = ':{} {} {}'
'
494
else:
'
495
fstri = ':{} {}' + ((p - 1) * ' {}') + ' :{}'
'
496
await self.send_irc_command(user, fstri.format(prefix, comm, *params))
'
497
2021-01-31 E.
498
async def reply_code(self, user, code, params=None, client=None):
2021-01-24 E.
499
num, tail = irc_codes[code]
2021-01-31 E.
500
if params:
00:39:07 '
501
nick = client if client else user.irc_nick
'
502
rest = tail.format(*params)
2022-02-12 E.
503
stri = ':{} {} {} {}'.format(self.gethostname(user), num, nick, rest)
2021-01-31 E.
504
else:
2022-02-12 E.
505
stri = ':{} {} {} :{}'.format(self.gethostname(user), num, user.irc_nick, tail)
2021-01-31 E.
506
await self.send_irc_command(user, stri)
2021-01-27 E.
507
19:51:33 '
508
async def send_greeting(self, user):
2021-01-31 E.
509
await self.reply_code(user, 'RPL_WELCOME', (user.irc_nick,))
2022-02-12 E.
510
await self.reply_code(user, 'RPL_YOURHOST', (self.gethostname(user), VERSION))
2021-01-31 E.
511
await self.reply_code(user, 'RPL_CREATED', (self.start_time,))
2022-02-12 E.
512
await self.reply_code(user, 'RPL_MYINFO', (self.gethostname(user), VERSION))
2021-02-21 E.
513
await self.send_isupport(user)
2021-01-27 E.
514
await self.send_motd(user)
2021-03-04 E.
515
await self.mode_user(user, user, True)
2021-01-27 E.
516
19:51:33 '
517
async def send_motd(self, user):
2022-02-12 E.
518
await self.reply_code(user, 'RPL_MOTDSTART', (self.gethostname(user),))
2021-01-31 E.
519
await self.reply_code(user, 'RPL_MOTD', ('Welcome to the irgramd server',))
00:39:07 '
520
await self.reply_code(user, 'RPL_MOTD', ('',))
'
521
await self.reply_code(user, 'RPL_MOTD', ('This is not a normal IRC server, it\'s a gateway that',))
'
522
await self.reply_code(user, 'RPL_MOTD', ('allows connecting from an IRC client (the program that',))
'
523
await self.reply_code(user, 'RPL_MOTD', ('you are [probably] using right now) to the Telegram instant',))
'
524
await self.reply_code(user, 'RPL_MOTD', ('messaging network as a regular user account (not bot)',))
'
525
await self.reply_code(user, 'RPL_MOTD', ('',))
'
526
await self.reply_code(user, 'RPL_MOTD', ('irgramd is an open source project that you can find on',))
2022-02-04 E.
527
await self.reply_code(user, 'RPL_MOTD', ('darcs repository: https://src.presi.org/darcs/irgramd',))
2021-01-31 E.
528
await self.reply_code(user, 'RPL_MOTD', ('git repository: https://github.com/prsai/irgramd',))
00:39:07 '
529
await self.reply_code(user, 'RPL_ENDOFMOTD')
2021-01-27 E.
530
2021-02-21 E.
531
async def send_isupport(self, user):
2024-09-07 E.
532
await self.reply_code(user, 'RPL_ISUPPORT', (CHAN_MAX_LENGTH, NICK_MAX_LENGTH))
2021-02-21 E.
533
2022-03-08 E.
534
async def send_help(self, user):
2024-10-06 E.
535
for line in self.service.initial_help():
2022-03-18 E.
536
await self.send_msg(self.service_user, None, line, user)
2022-03-08 E.
537
2022-03-08 E.
538
async def check_telegram_auth(self, user):
2022-03-19 E.
539
await self.tg.auth_checked.wait()
2022-03-08 E.
540
if not self.tg.authorized and not self.tg.ask_code:
21:58:55 '
541
for line in (
'
542
'----',
'
543
'Your Telegram account is not authorized yet,',
'
544
'you must supply the code that Telegram sent to your phone',
'
545
'or another client that is currently connected',
'
546
'use /msg {} code <code>'.format(self.service_user.irc_nick),
'
547
'e.g. /msg {} code 12345'.format(self.service_user.irc_nick),
'
548
):
'
549
await self.send_msg(self.service_user, user.irc_nick, line)
'
550
2021-02-20 E.
551
async def send_users_irc(self, prfx, command, params):
22:52:38 '
552
for usr in [x for x in self.users.values() if x.stream]:
'
553
await self.reply_command(usr, prfx, command, params)
'
554
2021-03-04 E.
555
async def mode_user(self, user, usr, com):
00:08:27 '
556
modes = ''
'
557
if usr.oper: modes += 'o'
'
558
if usr.tls: modes += 'S'
'
559
if modes: modes = '+' + modes
'
560
if com:
'
561
await self.reply_command(user, usr, 'MODE', (usr.irc_nick, modes))
'
562
else:
'
563
await self.reply_code(user, 'RPL_UMODEIS', (modes,))
'
564
'
565
async def mode_channel(self, user, channel, com):
'
566
modes = '+nt'
'
567
if com:
'
568
await self.reply_command(user, user, 'MODE', (channel, modes))
'
569
else:
'
570
await self.reply_code(user, 'RPL_CHANNELMODEIS', (channel, modes,''))
'
571
2023-03-24 E.
572
async def join_irc_channel(self, user, channel, full_join):
2021-02-12 E.
573
entity_cache = [None]
2021-02-01 E.
574
chan = channel.lower()
2021-02-12 E.
575
real_chan = self.get_realcaps_name(chan)
02:09:58 '
576
'
577
if full_join: self.irc_channels[chan].add(user.irc_nick)
'
578
'
579
# Notify IRC users in this channel
'
580
for usr in [self.users[x.lower()] for x in self.irc_channels[chan] if self.users[x.lower()].stream]:
'
581
await self.reply_command(usr, user, 'JOIN', (real_chan,))
'
582
'
583
if not full_join:
'
584
return
'
585
2021-02-04 E.
586
op = self.get_irc_op(self.tg.tg_username, channel)
23:26:44 '
587
if op == '@': self.irc_channels_ops[chan].add(user.irc_nick)
'
588
elif op == '~': self.irc_channels_founder[chan].add(user.irc_nick)
2020-11-19 E.
589
2021-02-12 E.
590
date = await self.tg.get_channel_creation(channel, entity_cache)
02:09:58 '
591
await self.reply_code(user, 'RPL_CREATIONTIME', (real_chan, date))
'
592
await self.irc_channel_topic(user, real_chan, entity_cache)
'
593
await self.irc_namelist(user, real_chan)
'
594
2021-03-31 E.
595
async def part_irc_channel(self, user, channel, reason):
01:21:58 '
596
chan = channel.lower()
'
597
real_chan = self.get_realcaps_name(chan)
'
598
'
599
# Notify IRC users in this channel
'
600
for usr in [self.users[x.lower()] for x in self.irc_channels[chan] if self.users[x.lower()].stream]:
'
601
await self.reply_command(usr, user, 'PART', (real_chan, reason))
'
602
'
603
self.irc_channels[chan].remove(user.irc_nick)
'
604
self.irc_channels_ops[chan].discard(user.irc_nick)
'
605
self.irc_channels_founder[chan].discard(user.irc_nick)
'
606
2022-01-30 E.
607
async def irc_channel_topic(self, user, channel, entity_cache):
2021-02-19 E.
608
chan = channel.lower()
00:05:45 '
609
topic = await self.tg.get_channel_topic(chan, entity_cache)
'
610
timestamp = await self.tg.get_channel_creation(chan, entity_cache)
2023-03-16 E.
611
if self.irc_channels_founder[chan]:
00:06:33 '
612
founder = list(self.irc_channels_founder[chan])[0]
'
613
else:
'
614
founder = self.service_user.irc_nick
2024-11-02 E.
615
await self.reply_code(user, 'RPL_TOPIC', (channel, topic[:MAX_LINE]))
2021-02-12 E.
616
await self.reply_code(user, 'RPL_TOPICWHOTIME', (channel, founder, timestamp))
02:09:58 '
617
'
618
async def irc_namelist(self, user, channel):
'
619
nicks = [self.get_irc_op(x, channel) + x for x in self.irc_channels[channel.lower()]]
'
620
status = '='
2020-11-19 E.
621
for chunk in chunks(nicks, 25, ''):
2021-02-12 E.
622
await self.reply_code(user, 'RPL_NAMREPLY', (status, channel, ' '.join(chunk)))
02:09:58 '
623
await self.reply_code(user, 'RPL_ENDOFNAMES', (channel,))
2020-11-19 E.
624
2021-02-04 E.
625
def get_irc_op(self, nick, channel):
23:26:44 '
626
chan = channel.lower()
'
627
if chan in self.irc_channels.keys():
'
628
if nick in self.irc_channels_ops[chan]:
'
629
return '@'
'
630
if nick in self.irc_channels_founder[chan]:
'
631
return '~'
'
632
return ''
'
633
2021-02-12 E.
634
def get_realcaps_name(self, name):
02:09:58 '
635
# name must be in lower
'
636
return self.tg.tid_to_iid[self.iid_to_tid[name]]
'
637
2021-03-04 E.
638
def num_params_necessary(self, num_params_required, num_params_expected):
00:08:27 '
639
if num_params_required == ALL_PARAMS:
'
640
npn = num_params_expected
'
641
else:
'
642
npn = num_params_required
'
643
return npn
'
644
2022-02-12 E.
645
def gethostname(self, user):
02:02:43 '
646
return 'localhost' if user.from_localhost else self.hostname
'
647
2021-01-20 E.
648
class IRCUser(object):
2022-02-26 E.
649
def __init__(self, stream, address, irc_nick=None, username='', realname=None, is_service=False):
2021-01-20 E.
650
self.stream = stream
2021-01-31 E.
651
self.address = address[0]
2023-06-14 E.
652
self.port = str(address[1])
2022-02-12 E.
653
self.from_localhost = True if address[0].split('.')[0] == '127' else False
2021-01-31 E.
654
self.irc_nick = irc_nick
2021-02-10 E.
655
self.irc_username = str(username)
2021-01-31 E.
656
self.irc_realname = realname
2021-01-29 E.
657
self.registered = False
2021-01-27 E.
658
self.password = ''
2021-01-24 E.
659
self.recv_pass = ''
2021-02-07 E.
660
self.oper = False
02:14:36 '
661
self.tls = False
'
662
self.bot = None
2022-02-26 E.
663
self.is_service = is_service
2021-02-20 E.
664
self.close_reason = ''
2021-01-28 E.
665
2021-02-01 E.
666
def get_irc_mask(self):
18:48:42 '
667
return '{}!{}@{}'.format(self.irc_nick, self.irc_username, self.address)
'
668
2021-02-07 E.
669
def get_channels(self, irc):
02:14:36 '
670
res = ''
'
671
for chan in irc.irc_channels.keys():
'
672
if self.irc_nick in irc.irc_channels[chan]:
'
673
res += irc.get_irc_op(self.irc_nick, chan) + chan + ' '
'
674
return res
'
675
2021-01-28 E.
676
def valid_nick(self, nick):
13:07:44 '
677
if len(nick) <= NICK_MAX_LENGTH and nick[0] in VALID_IRC_NICK_FIRST_CHARS:
'
678
for x in nick[1:]:
'
679
if x not in VALID_IRC_NICK_CHARS:
2021-02-16 E.
680
return False
18:44:01 '
681
return True
'
682
else: return False
2021-02-04 E.
683
23:26:44 '
684
def del_from_channels(self, irc, channels=None):
'
685
for chan in channels if channels else irc.irc_channels.keys():
'
686
irc.irc_channels[chan].discard(self.irc_nick)
'
687
irc.irc_channels_ops[chan].discard(self.irc_nick)
'
688
irc.irc_channels_founder[chan].discard(self.irc_nick)
2021-12-13 E.
689
20:31:17 '
690
def pam_auth(self, nick, pam, pam_group, recv_pass):
'
691
if not pam: return False
'
692
'
693
# Check if user is in groups (main or others)
'
694
if pam_group:
'
695
import pwd
'
696
import grp
'
697
try:
'
698
user_group_id = pwd.getpwnam(nick).pw_gid
'
699
group_data = grp.getgrnam(pam_group)
'
700
pam_group_id = group_data.gr_gid
'
701
group_members = group_data.gr_mem
'
702
check_group = user_group_id == pam_group_id \
'
703
or nick in group_members
'
704
except:
'
705
check_group = False
'
706
if not check_group: return False
'
707
'
708
# Check user authentication (via PAM)
'
709
import PAM
'
710
def pam_conv(auth, query_list, userData):
'
711
resp = []
'
712
resp.append((recv_pass, 0))
'
713
return resp
'
714
p = PAM.pam()
'
715
p.start('passwd')
'
716
p.set_item(PAM.PAM_USER, nick)
'
717
p.set_item(PAM.PAM_CONV, pam_conv)
'
718
try:
'
719
p.authenticate()
'
720
p.acct_mgmt()
'
721
except:
'
722
return False
'
723
else:
'
724
return True
'
725
2022-01-22 E.
726
def local_auth(self, nick, nicks, recv_pass, irc_pass, pam):
21:08:56 '
727
return ( not pam
'
728
and nick in nicks
2021-12-13 E.
729
and recv_pass == irc_pass
20:31:17 '
730
)