patch 4b63ac15d2b61f2c0da0686ba1936c79d7136444 Author: E. Bosch Date: Fri Feb 12 03:09:58 CET 2021 * irc: Fix JOIN command diff -rN -u old-irgramd/irc.py new-irgramd/irc.py --- old-irgramd/irc.py 2024-10-23 06:25:24.264047213 +0200 +++ new-irgramd/irc.py 2024-10-23 06:25:24.268047207 +0200 @@ -29,7 +29,7 @@ IRC_PING_RX = re.compile(PREFIX + r'PING( +:| +|\n)(?P[^\n]+|)') IRC_PRIVMSG_RX = re.compile(PREFIX + r'PRIVMSG( +|\n)(?P[^ ]+)( +:| +|\n)(?P[^\n]+|)') IRC_USER_RX = re.compile(PREFIX + r'USER( +|\n)(?P[^ ]+) +[^ ]+ +[^ ]+( +:| +|\n)(?P[^\n]+|)') -IRC_JOIN_RX = re.compile(PREFIX + r'JOIN( +|\n)(?P[^ ]+)') +IRC_JOIN_RX = re.compile(PREFIX + r'JOIN( +|\n)(?P[^ ]+)') IRC_WHO_RX = re.compile(PREFIX + r'WHO( +:| +|\n)(?P[^\n]+|)') IRC_WHOIS_RX = re.compile(PREFIX + r'WHOIS( +:| +|\n)(?P[^\n]+|)') @@ -153,10 +153,18 @@ user.registered = True await self.send_greeting(user) - async def handle_irc_join(self, user, channel): - self.logger.debug('Handling JOIN: %s', channel) + async def handle_irc_join(self, user, channels): + self.logger.debug('Handling JOIN: %s', channels) - await self.join_irc_channel(user, channel, True) + if channels == '0': + #part all + pass + else: + for channel in channels.split(','): + if channel.lower() in self.irc_channels.keys(): + await self.join_irc_channel(user, channel, True) + else: + await reply_code(user, 'ERR_NOSUCHCHANNEL', (channel,)) async def handle_irc_ping(self, user, payload): self.logger.debug('Handling PING: %s', payload) @@ -267,35 +275,41 @@ await self.reply_code(user, 'RPL_ENDOFMOTD') async def join_irc_channel(self, user, channel, full_join=False): + entity_cache = [None] chan = channel.lower() - self.irc_channels[chan].add(user.irc_nick) - op = self.get_irc_op(self.tg.tg_username, channel) - if op == '@': self.irc_channels_ops[chan].add(user.irc_nick) - elif op == '~': self.irc_channels_founder[chan].add(user.irc_nick) + real_chan = self.get_realcaps_name(chan) - # Join Channel - await self.send_irc_command(user, ':{} JOIN :{}'.format( - user.get_irc_mask(), channel - )) + if full_join: self.irc_channels[chan].add(user.irc_nick) + + # Notify IRC users in this channel + for usr in [self.users[x.lower()] for x in self.irc_channels[chan] if self.users[x.lower()].stream]: + await self.reply_command(usr, user, 'JOIN', (real_chan,)) if not full_join: return - # Get all users from channel - tid = self.iid_to_tid[channel.lower()] - nicks = self.irc_channels[channel.lower()] - - # Set channel topic - topic = (await self.tg.telegram_client.get_entity(tid)).title - await self.send_irc_command(user, ':{} TOPIC {} :{}'.format( - user.get_irc_mask(), channel, topic - )) + op = self.get_irc_op(self.tg.tg_username, channel) + if op == '@': self.irc_channels_ops[chan].add(user.irc_nick) + elif op == '~': self.irc_channels_founder[chan].add(user.irc_nick) - # Send NAMESLIST + date = await self.tg.get_channel_creation(channel, entity_cache) + await self.reply_code(user, 'RPL_CREATIONTIME', (real_chan, date)) + await self.irc_channel_topic(user, real_chan, entity_cache) + await self.irc_namelist(user, real_chan) + + async def irc_channel_topic(self, user, channel, entity_cache): + topic = await self.tg.get_channel_topic(channel, entity_cache) + timestamp = await self.tg.get_channel_creation(channel, entity_cache) + founder = list(self.irc_channels_founder[channel.lower()])[0] + await self.reply_code(user, 'RPL_TOPIC', (channel, topic)) + await self.reply_code(user, 'RPL_TOPICWHOTIME', (channel, founder, timestamp)) + + async def irc_namelist(self, user, channel): + nicks = [self.get_irc_op(x, channel) + x for x in self.irc_channels[channel.lower()]] + status = '=' for chunk in chunks(nicks, 25, ''): - await self.send_irc_command(user, ':{} 353 {} = {} :{}'.format( - self.hostname, user.irc_nick, channel, ' '.join(chunk) - )) + await self.reply_code(user, 'RPL_NAMREPLY', (status, channel, ' '.join(chunk))) + await self.reply_code(user, 'RPL_ENDOFNAMES', (channel,)) async def part_irc_channel(self, user, channel): self.irc_channels[channel].remove(user.irc_nick) @@ -312,6 +326,10 @@ return '~' return '' + def get_realcaps_name(self, name): + # name must be in lower + return self.tg.tid_to_iid[self.iid_to_tid[name]] + class IRCUser(object): def __init__(self, stream, address, irc_nick=None, username=None, realname=None): self.stream = stream diff -rN -u old-irgramd/irc_replies.py new-irgramd/irc_replies.py --- old-irgramd/irc_replies.py 2024-10-23 06:25:24.264047213 +0200 +++ new-irgramd/irc_replies.py 2024-10-23 06:25:24.268047207 +0200 @@ -13,15 +13,20 @@ 'RPL_WHOISIDLE': ('317', '{} {} :seconds idle'), 'RPL_ENDOFWHOIS': ('318', '{} :End of WHOIS command'), 'RPL_WHOISCHANNELS': ('319', '{} :{}'), + 'RPL_CREATIONTIME': ('329', '{} {}'), 'RPL_WHOISACCOUNT': ('330', '{} {} :Telegram name'), + 'RPL_TOPIC': ('332', '{} :{}'), + 'RPL_TOPICWHOTIME': ('333', '{} {} {}'), 'RPL_WHOISBOT': ('335', '{} :is a Telegram bot'), 'RPL_WHOREPLY': ('352', '{} {} {} {} {} H{} :0 {}'), + 'RPL_NAMREPLY': ('353', '{} {} :{}'), + 'RPL_ENDOFNAMES': ('366', '{} :End of NAME reply'), 'RPL_MOTDSTART': ('375', ':- {} Message of the day - '), 'RPL_MOTD': ('372', ':- {}'), 'RPL_ENDOFMOTD': ('376', 'End of MOTD command'), 'ERR_NOSUCHNICK': ('401', '{} :Nick not found'), 'ERR_NOSUCHSERVER': ('402', '{} :Target not found'), - 'ERR_NOSUCHCHANNEL': ('403', 'No such channel'), + 'ERR_NOSUCHCHANNEL': ('403', '{} :Channel not found'), 'ERR_CANNOTSENDTOCHAN': ('404', 'Cannot send to channel'), 'ERR_TOOMANYCHANNELS': ('405', 'Too many channels'), 'ERR_WASNOSUCHNICK': ('406', 'There was no such nick'), diff -rN -u old-irgramd/telegram.py new-irgramd/telegram.py --- old-irgramd/telegram.py 2024-10-23 06:25:24.264047213 +0200 +++ new-irgramd/telegram.py 2024-10-23 06:25:24.268047207 +0200 @@ -2,6 +2,7 @@ import logging import os import datetime +import re import telethon from telethon import types as tgty @@ -10,6 +11,10 @@ from include import CHAN_MAX_LENGHT, NICK_MAX_LENGTH from irc import IRCUser +# Constants + +TL_TYPES_IDENT = re.compile(r"") + # Configuration # GET API_ID and API_HASH from https://my.telegram.org/apps @@ -29,6 +34,7 @@ self.authorized = False self.id = None self.tg_username = None + self.channels_date = {} async def initialize_telegram(self): # Setup media folder @@ -188,15 +194,43 @@ self.irc.users[irc_nick].bot = bot return bot - def get_tid(self, irc_nick, tid=None): + async def get_channel_topic(self, channel, entity_cache=[None]): + tid = self.get_tid(channel) + if entity_cache[0]: + entity = entity_cache[0] + else: + entity = await self.telegram_client.get_entity(tid) + entity_cache[0] = entity + entity_type = self.get_entity_type(entity) + return 'Telegram ' + entity_type + ' ' + str(tid) + ': ' + entity.title + + async def get_channel_creation(self, channel, entity_cache=[None]): + tid = self.get_tid(channel) + if tid in self.channels_date.keys(): + timestamp = self.channels_date[tid] + else: + if entity_cache[0]: + entity = entity_cache[0] + else: + entity = await self.telegram_client.get_entity(tid) + entity_cache[0] = entity + timestamp = entity.date.timestamp() + self.channels_date[tid] = timestamp + return int(timestamp) + + def get_tid(self, irc_item, tid=None): + it = irc_item.lower() if tid: pass - elif irc_nick in self.irc.iid_to_tid: - tid = self.irc.iid_to_tid[irc_nick.lower()] + elif it in self.irc.iid_to_tid: + tid = self.irc.iid_to_tid[it] else: tid = self.id return tid + def get_entity_type(self, entity): + return TL_TYPES_IDENT.match(str(type(entity))).groups()[0] + async def handle_telegram_message(self, event): self.logger.debug('Handling Telegram Message: %s', event)