Update copyright year in license --> to head
patch 86ef89f9f9220ecde8477db2aa673f94f32656ab
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Oct 26 20:43:28 CEST 2024
* telegram: Fix op and founder detection in channels
hunk ./telegram.py 184
- if isinstance(user.participant, tgty.ChatParticipantAdmin):
+ if isinstance(user.participant, tgty.ChatParticipantAdmin) or \
+ isinstance(user.participant, tgty.ChannelParticipantAdmin):
hunk ./telegram.py 188
- elif isinstance(user.participant, tgty.ChatParticipantCreator):
+ elif isinstance(user.participant, tgty.ChatParticipantCreator) or \
+ isinstance(user.participant, tgty.ChannelParticipantCreator):
patch 6d99fce9643fbdda1594f99331f4a4642ecc7f0f
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Oct 26 20:35:11 CEST 2024
* telegram: Fix handler for next reactions when the event is empty
hunk ./telegram.py 577
- react = max(reactions, key=lambda y: y.date)
- [_$_]
- if self.last_reaction != react.date:
+ react = max(reactions, key=lambda y: y.date) if reactions else None
+
+ if react and self.last_reaction != react.date:
patch efaf8df4b3fa314997615e2fc07a3decb3a7cfba
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Oct 26 13:01:02 CEST 2024
* exclam: Reorder handler list so commands are listed ordered in help
hunk ./exclam.py 21
- '!re': (self.handle_command_re, 2, 2, 2),
- '!ed': (self.handle_command_ed, 2, 2, 2),
hunk ./exclam.py 22
+ '!ed': (self.handle_command_ed, 2, 2, 2),
hunk ./exclam.py 24
- '!upl': (self.handle_command_upl, 1, 2, 2),
- '!reupl': (self.handle_command_reupl, 2, 3, 3),
+ '!re': (self.handle_command_re, 2, 2, 2),
hunk ./exclam.py 26
+ '!reupl': (self.handle_command_reupl, 2, 3, 3),
+ '!upl': (self.handle_command_upl, 1, 2, 2),
patch 0cfc7e59b24fb1a1b279fc593f8d04d0648e3880
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Oct 21 00:54:16 CEST 2024
* README update
hunk ./README.md 53
-- Reactions (receive, send)
+- Reactions (receive, send, remove)
patch abf1d31ddcf3cddd55844900065a3c3dd6bf9c67
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Oct 20 02:32:44 CEST 2024
* exclam: Add "-" parameter to "!react" to remove a reaction
hunk ./emoji2emoticon.py 92
-emo_inv = {}
+emo_inv = { '-': None }
hunk ./exclam.py 202
- reaction = [ tgty.ReactionEmoji(emoticon=utf8_emo) ]
+ reaction = [ tgty.ReactionEmoji(emoticon=utf8_emo) ] if utf8_emo else None
hunk ./exclam.py 219
- ' !react <compact_id> <emoticon reaction>',
+ ' !react <compact_id> <emoticon reaction>|-',
hunk ./exclam.py 222
+ 'Use - to remove a previous reaction.',
patch 5c1687a16a1e49af7b5f70e9449c447bbf7c9df9
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Oct 19 22:56:31 CEST 2024
* telegram: On emoticon->emoji conversions for reactions,
when several emoticons can be mapped to an emoji,
keep first elements that are more probable allowed for reactions
hunk ./emoji2emoticon.py 93
-for k in emo:
+for k in reversed(emo):
patch 7db9d9a07af099b00a32a6ee0197c400a52240e5
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Oct 13 23:00:24 CEST 2024
* telegram: Limit text length for reactions to twice as replies
hunk ./telegram.py 508
- if len(message_rendered) > self.quote_len and not msg.is_reply:
- text_old = '{}...'.format(message_rendered[:self.quote_len])
+ react_quote_len = self.quote_len * 2
+ if len(message_rendered) > react_quote_len:
+ text_old = '{}...'.format(message_rendered[:react_quote_len])
patch 42fe2f72d41ed36616e6a19b9388eec356679e38
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Oct 12 23:17:45 CEST 2024
* README update
hunk ./README.md 53
-- Reactions (receive)
+- Reactions (receive, send)
patch 873004141f83b60da113e8967a2148d9d33008be
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Oct 10 23:11:40 CEST 2024
* exclam: Add reaction (!react) command to send emoticon->emoji as Telegram reaction
hunk ./emoji2emoticon.py 89
- '\U0001f970': '":)e>"'
+ '\U0001f970': '":)e>"',
hunk ./emoji2emoticon.py 92
+emo_inv = {}
+for k in emo:
+ emo_inv[emo[k][1:-1]] = k
+
hunk ./exclam.py 10
-from telethon.errors.rpcerrorlist import MessageNotModifiedError, MessageAuthorRequiredError
+from telethon.tl.functions.messages import SendReactionRequest
+from telethon import types as tgty
+from telethon.errors.rpcerrorlist import MessageNotModifiedError, MessageAuthorRequiredError, ReactionInvalidError
hunk ./exclam.py 15
+from emoji2emoticon import emo_inv
hunk ./exclam.py 27
+ '!react': (self.handle_command_react, 2, 2, -1),
hunk ./exclam.py 193
+ )
+ return reply
+
+ async def handle_command_react(self, cid=None, act=None, help=None):
+ if not help:
+ id, chk_msg = await self.check_msg(cid)
+ if chk_msg is not None:
+ if act in emo_inv:
+ utf8_emo = emo_inv[act]
+ reaction = [ tgty.ReactionEmoji(emoticon=utf8_emo) ]
+ try:
+ update = await self.tg.telegram_client(SendReactionRequest(self.tmp_telegram_id, id, reaction=reaction))
+ except ReactionInvalidError:
+ reply = ('!react: Reaction not allowed',)
+ else:
+ self.tmp_tg_msg = update.updates[0].message
+ reply = True
+ else:
+ reply = ('!react: Unknown reaction',)
+ else:
+ reply = ('!react: Unknown message to react',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !react React to a message',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !react <compact_id> <emoticon reaction>',
+ 'React with <emoticon reaction> to a message with <compact_id>,',
+ 'irgramd will translate emoticon to closest emoji.',
patch 69a63d9f5f49ece35c5d552ba8b3081c8277490d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Oct 7 00:07:54 CEST 2024
* telegram, service: Move initial help to service module
add a line for "equivalent command"
hunk ./irc.py 535
- for line in (
- 'Welcome to irgramd service',
- 'use /msg {} help'.format(self.service_user.irc_nick),
- 'to get help',
- ):
+ for line in self.service.initial_help():
hunk ./service.py 28
+ def initial_help(self):
+ return (
+ 'Welcome to irgramd service',
+ 'use /msg {} help'.format(self.irc.service_user.irc_nick),
+ 'or equivalent in your IRC client',
+ 'to get help',
+ )
+
patch c1ffe716a42ea01ca345d7a756b685d7174f99c5
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Oct 6 23:59:23 CEST 2024
* telegram: Fix regression in delete reaction event
hunk ./telegram.py 520
- user = self.get_irc_user_from_telegram(event.sender_id)
+ user = self.get_irc_user_from_telegram(msg.sender_id)
hunk ./telegram.py 558
- if self.last_reaction == reaction.date:
- return
- self.last_reaction = reaction.date
+ if reaction:
+ if self.last_reaction == reaction.date:
+ return
+ self.last_reaction = reaction.date
patch beacde93a685dd954f9823dc0a6fea4594b2c1e4
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Sep 28 02:53:06 CEST 2024
* telegram: Avoid duplicated reactions events in some cases
hunk ./telegram.py 74
+ self.last_reaction = None
hunk ./telegram.py 558
+ if self.last_reaction == reaction.date:
+ return
+ self.last_reaction = reaction.date
hunk ./telegram.py 576
- id = event.msg_id
- msg = await self.telegram_client.get_messages(entity=event.peer, ids=id)
- mid = self.mid.num_to_id_offset(msg.peer_id, id)
- message = self.filters(msg.message)
- message_rendered = await self.render_text(msg, mid, upd_to_webpend=None)
-
- text_old, edition_react, user = self.format_reaction(msg, message_rendered, edition_case='react-add', reaction=react)
-
- text = '|React {}| {}'.format(text_old, edition_react)
-
- chan = await self.relay_telegram_message(msg, user, text)
-
- self.to_cache(id, mid, message, message_rendered, user, chan, msg.media)
- self.to_volatile_cache(self.prev_id, id, text, user, chan, current_date())
+ [_$_]
+ if self.last_reaction != react.date:
+ self.last_reaction = react.date
+ id = event.msg_id
+ msg = await self.telegram_client.get_messages(entity=event.peer, ids=id)
+ mid = self.mid.num_to_id_offset(msg.peer_id, id)
+ message = self.filters(msg.message)
+ message_rendered = await self.render_text(msg, mid, upd_to_webpend=None)
+
+ text_old, edition_react, user = self.format_reaction(msg, message_rendered, edition_case='react-add', reaction=react)
+
+ text = '|React {}| {}'.format(text_old, edition_react)
+
+ chan = await self.relay_telegram_message(msg, user, text)
+
+ self.to_cache(id, mid, message, message_rendered, user, chan, msg.media)
+ self.to_volatile_cache(self.prev_id, id, text, user, chan, current_date())
patch b2f8fe9251a26c43e16ba1aadff8d71e64a5a7e9
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Sep 27 11:05:53 CEST 2024
* telegram: Add handler for next reactions (2nd, 3rd, etc.) that don't come from
the same events as 1st (why?!)
hunk ./telegram.py 506
+ def format_reaction(self, msg, message_rendered, edition_case, reaction):
+ if len(message_rendered) > self.quote_len and not msg.is_reply:
+ text_old = '{}...'.format(message_rendered[:self.quote_len])
+ text_old = fix_braces(text_old)
+ else:
+ text_old = message_rendered
+
+ if edition_case == 'react-add':
+ user = self.get_irc_user_from_telegram(reaction.peer_id.user_id)
+ emoji = reaction.reaction.emoticon
+ react_action = '+'
+ react_icon = e.emo[emoji] if emoji in e.emo else emoji
+ elif edition_case == 'react-del':
+ user = self.get_irc_user_from_telegram(event.sender_id)
+ react_action = '-'
+ react_icon = ''
+ return text_old, '{}{}'.format(react_action, react_icon), user
+
hunk ./telegram.py 558
- if len(message_rendered) > self.quote_len and not event.message.is_reply:
- text_old = '{}...'.format(message_rendered[:self.quote_len])
- text_old = fix_braces(text_old)
- else:
- text_old = message_rendered
-
- if edition_case == 'react-add':
- user = self.get_irc_user_from_telegram(reaction.peer_id.user_id)
- emoji = reaction.reaction.emoticon
- react_action = '+'
- react_icon = e.emo[emoji] if emoji in e.emo else emoji
- elif edition_case == 'react-del':
- user = self.get_irc_user_from_telegram(event.sender_id)
- react_action = '-'
- react_icon = ''
- edition_react = '{}{}'.format(react_action, react_icon)
+ text_old, edition_react, user = self.format_reaction(event.message, message_rendered, edition_case, reaction)
hunk ./telegram.py 567
+ async def handle_next_reaction(self, event):
+ self.logger.debug('Handling Telegram Next Reaction (2nd, 3rd, ...): %s', pretty(event))
+
+ reactions = event.reactions.recent_reactions
+ react = max(reactions, key=lambda y: y.date)
+ id = event.msg_id
+ msg = await self.telegram_client.get_messages(entity=event.peer, ids=id)
+ mid = self.mid.num_to_id_offset(msg.peer_id, id)
+ message = self.filters(msg.message)
+ message_rendered = await self.render_text(msg, mid, upd_to_webpend=None)
+
+ text_old, edition_react, user = self.format_reaction(msg, message_rendered, edition_case='react-add', reaction=react)
+
+ text = '|React {}| {}'.format(text_old, edition_react)
+
+ chan = await self.relay_telegram_message(msg, user, text)
+
+ self.to_cache(id, mid, message, message_rendered, user, chan, msg.media)
+ self.to_volatile_cache(self.prev_id, id, text, user, chan, current_date())
+
hunk ./telegram.py 610
+ elif isinstance(update, tgty.UpdateMessageReactions):
+ await self.handle_next_reaction(update)
+
patch 8770a66d55d4d1c34e009fe5d0078f77c3be4d34
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Sep 25 01:36:06 CEST 2024
* telegram: Fix in reaction handler
hunk ./telegram.py 404
- elif react := next((x for x in reactions if x.date == msg.edit_date), None):
+ elif react := max(reactions, key=lambda y: y.date):
patch 7e550077a65e4737ca30a81e0decbcb4db0485a4
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Sep 20 23:50:13 CEST 2024
* telegram: Don't truncate text for reactions of replies
hunk ./telegram.py 540
- if len(message_rendered) > self.quote_len:
+ if len(message_rendered) > self.quote_len and not event.message.is_reply:
patch f9ff84bf789b6bd5109c5953590ca41c438fe123
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Sep 15 23:50:10 CEST 2024
* telegram: Minor improvement in debug of relay methods
hunk ./telegram.py 662
- self.logger.debug('Handling Telegram Private Message: %s, %s', user, message)
+ self.logger.debug('Relaying Telegram Private Message: %s, %s', user, message)
hunk ./telegram.py 670
- self.logger.debug('Handling Telegram Channel Message: %s', pretty(message) or text)
-
hunk ./telegram.py 676
+ self.logger.debug('Relaying Telegram Channel Message: %s, %s', chan, text)
+
patch 1d527812923bdf50653bb371185bec70c2abad40
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Sep 15 01:23:24 CEST 2024
* telegram: Improve a bit reactions handler
hunk ./telegram.py 392
- return react.updates[0].reactions.recent_reactions
+ updates = react.updates
+ r = next((x for x in updates if type(x) is tgty.UpdateMessageReactions), None)
+ return r.reactions.recent_reactions if r else None
patch b43b2bc6a4e9dcf0eaddb66ea3fd5abf7c95082b
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Sep 7 23:20:27 CEST 2024
* Fix typo in a constant
hunk ./include.py 13
-CHAN_MAX_LENGHT = 50
+CHAN_MAX_LENGTH = 50
hunk ./irc.py 21
-from include import VERSION, CHAN_MAX_LENGHT, NICK_MAX_LENGTH
+from include import VERSION, CHAN_MAX_LENGTH, NICK_MAX_LENGTH
hunk ./irc.py 532
- await self.reply_code(user, 'RPL_ISUPPORT', (CHAN_MAX_LENGHT, NICK_MAX_LENGTH))
+ await self.reply_code(user, 'RPL_ISUPPORT', (CHAN_MAX_LENGTH, NICK_MAX_LENGTH))
hunk ./telegram.py 22
-from include import CHAN_MAX_LENGHT, NICK_MAX_LENGTH
+from include import CHAN_MAX_LENGTH, NICK_MAX_LENGTH
patch 6aaf9a2af5898f8b6ec1027a3ccb7e85f6893f22
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Sep 1 01:01:19 CEST 2024
* Increase virtual version to 0.2
Remove alpha status
hunk ./README.md 16
-**irgramd is under active development in alpha state, though usable, several
+**irgramd is under active development, though usable, several
hunk ./include.py 11
-VERSION = '0.1'
+VERSION = '0.2'
patch a39c65dc932ee95b44b5a759cad3e413177fc5aa
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Aug 30 21:53:13 CEST 2024
* telegram: Add a cache of "volatile" events (delete, edit, react) to be shown in history
hunk ./telegram.py 71
+ self.volatile_cache = collections.OrderedDict()
+ self.prev_id = {}
hunk ./telegram.py 413
- if len(self.cache) >= 10000:
- self.cache.popitem(last=False)
+ self.limit_cache(self.cache)
hunk ./telegram.py 423
+ def to_volatile_cache(self, prev_id, id, ev, user, chan, date):
+ if chan in prev_id:
+ prid = prev_id[chan] if chan else prev_id[user]
+ self.limit_cache(self.volatile_cache)
+ elem = {
+ 'id': id,
+ 'rendered_event': ev,
+ 'user': user,
+ 'channel': chan,
+ 'date': date,
+ }
+ if prid not in self.volatile_cache:
+ self.volatile_cache[prid] = [elem]
+ else:
+ self.volatile_cache[prid].append(elem)
+
+ def limit_cache(self, cache):
+ if len(cache) >= 10000:
+ cache.popitem(last=False)
+
hunk ./telegram.py 560
+ self.to_volatile_cache(self.prev_id, id, text, user, chan, current_date())
hunk ./telegram.py 572
+ self.to_volatile_cache(self.prev_id, deleted_id, text, user, chan, current_date())
hunk ./telegram.py 595
+ await self.history_search_volatile(history, msg.id)
hunk ./telegram.py 598
+ peer = chan if chan else user
+ self.prev_id[peer] = msg.id
hunk ./telegram.py 638
+ async def history_search_volatile(self, history, id):
+ if history:
+ if id in self.volatile_cache:
+ for item in self.volatile_cache[id]:
+ user = item['user']
+ text = item['rendered_event']
+ chan = item['channel']
+ date = item['date']
+ text_send = self.set_history_timestamp(text, history=True, date=date, action=False)
+ await self.relay_telegram_message(None, user, text_send, chan)
+
patch 95e72ac9b26835162b8ba997c5ff99edfd5d464e
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Aug 30 19:00:53 CEST 2024
* utils: Small optimization in pretty()
hunk ./utils.py 46
+class LOGL:
+ debug = False
+
hunk ./utils.py 230
+ if levelu == 'DEBUG':
+ LOGL.debug = True
hunk ./utils.py 241
- return object.stringify() if object else object
+ return object.stringify() if LOGL.debug and object else object
patch 799dcf8a6a7c8346af93e7f17841baf08db70e7c
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Aug 30 01:58:06 CEST 2024
* utils: Add current_date() shortcut
hunk ./telegram.py 12
-import datetime
hunk ./telegram.py 24
-from utils import sanitize_filename, add_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces, format_timestamp, pretty
+from utils import sanitize_filename, add_filename, is_url_equiv, extract_url, get_human_size, get_human_duration
+from utils import get_highlighted, fix_braces, format_timestamp, pretty, current_date
hunk ./telegram.py 289
- current = datetime.datetime.now(datetime.timezone.utc)
+ current = current_date()
hunk ./utils.py 155
- delta = datetime.datetime.now(datetime.timezone.utc) - date
+ delta = current_date() - date
hunk ./utils.py 167
+def current_date():
+ return datetime.datetime.now(datetime.timezone.utc)
+
patch 6c991a90a37dcc5a96906992b5c4df41e7f68991
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Aug 29 23:06:59 CEST 2024
* Add trailing commas (and some spacing)
hunk ./exclam.py 62
- 'channel/chat.'
+ 'channel/chat.',
hunk ./exclam.py 88
- '<new_message> replaces the current message.'
+ '<new_message> replaces the current message.',
hunk ./exclam.py 168
- 'irgramd local directory or be an external HTTP/HTTPS URL.'
+ 'irgramd local directory or be an external HTTP/HTTPS URL.',
hunk ./exclam.py 188
- 'HTTP/HTTPS URL.'
+ 'HTTP/HTTPS URL.',
hunk ./service.py 252
- 'reset the number of mentions to you on <peer>.'
+ 'reset the number of mentions to you on <peer>.',
hunk ./telegram.py 32
- 3: '149.154.175.117'
+ 3: '149.154.175.117',
hunk ./telegram.py 105
- (self.handle_raw, telethon.events.Raw),
+ (self.handle_raw , telethon.events.Raw),
hunk ./telegram.py 108
- (self.handle_telegram_edited, telethon.events.MessageEdited),
+ (self.handle_telegram_edited , telethon.events.MessageEdited),
hunk ./telegram.py 419
- 'media': media
+ 'media': media,
patch 6057cbb6c30094c80cba9f6326a5b513a9ab540c
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Aug 25 01:21:01 CEST 2024
* utils, telegram: Add pretty() function to print readable objects in debug
hunk ./telegram.py 25
-from utils import sanitize_filename, add_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces, format_timestamp
+from utils import sanitize_filename, add_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces, format_timestamp, pretty
hunk ./telegram.py 484
- self.logger.debug('Handling Telegram Message Edited: %s', event)
+ self.logger.debug('Handling Telegram Message Edited: %s', pretty(event))
hunk ./telegram.py 541
- self.logger.debug('Handling Telegram Message Deleted: %s', event)
+ self.logger.debug('Handling Telegram Message Deleted: %s', pretty(event))
hunk ./telegram.py 555
- self.logger.debug('Handling Telegram Raw Event: %s', update)
+ self.logger.debug('Handling Telegram Raw Event: %s', pretty(update))
hunk ./telegram.py 563
- self.logger.debug('Handling Telegram Message: %s', event or message)
+ self.logger.debug('Handling Telegram Message: %s', pretty(event or message))
hunk ./telegram.py 631
- self.logger.debug('Handling Telegram Channel Message: %s', message or text)
+ self.logger.debug('Handling Telegram Channel Message: %s', pretty(message) or text)
hunk ./telegram.py 647
- self.logger.debug('Handling Telegram Chat Action: %s', event)
+ self.logger.debug('Handling Telegram Chat Action: %s', pretty(event))
hunk ./utils.py 231
+
+def pretty(object):
+ return object.stringify() if object else object
patch 0fd4392905932f144e14844f164d801a22b68467
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Aug 18 14:01:26 CEST 2024
* exclam: Add re-upload (!reupl) command to upload files/media as a reply to a message
hunk ./exclam.py 23
+ '!reupl': (self.handle_command_reupl, 2, 3, 3),
hunk ./exclam.py 148
- async def handle_command_upl(self, file=None, caption=None, help=None):
+ async def handle_command_upl(self, file=None, caption=None, help=None, re_id=None):
hunk ./exclam.py 155
- self.tmp_tg_msg = await self.tg.telegram_client.send_file(self.tmp_telegram_id, file_path, caption=caption)
+ self.tmp_tg_msg = await self.tg.telegram_client.send_file(self.tmp_telegram_id, file_path, caption=caption, reply_to=re_id)
hunk ./exclam.py 158
- reply = ('!upl: Error uploading',)
+ cmd = '!reupl' if re_id else '!upl'
+ reply = ('{}: Error uploading'.format(cmd),)
hunk ./exclam.py 169
+ )
+ return reply
+
+ async def handle_command_reupl(self, cid=None, file=None, caption=None, help=None):
+ if not help:
+ id, chk_msg = await self.check_msg(cid)
+ if chk_msg is not None:
+ reply = await self.handle_command_upl(file, caption, re_id=id)
+ else:
+ reply = ('!reupl: Unknown message to reply',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !reupl Reply to a message with an upload',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !reupl <compact_id> <file name/URL> [<optional caption>]',
+ 'Reply with the upload of <file name/URL> to a message with',
+ '<compact_id> on current channel/chat. The file must be',
+ 'present in "upload" irgramd local directory or be an external',
+ 'HTTP/HTTPS URL.'
patch 477b15fc239d17cccf626d042db2f323e1fa1b4b
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Aug 18 13:58:56 CEST 2024
* exclam: Check valid range for message IDs
hunk ./exclam.py 40
- if id is None:
+ if id is None or id < -2147483648 or id > 2147483647:
patch 35206dbcb8c561df88e525160e5d6a50dad07481
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Aug 15 01:30:54 CEST 2024
* Handle replies to deleted messages (maybe this case is only given from history)
hunk ./telegram.py 696
- replied_msg = replied.message
- cid = self.mid.num_to_id_offset(replied.peer_id, replied.id)
+ if replied:
+ replied_msg = replied.message
+ cid = self.mid.num_to_id_offset(replied.peer_id, replied.id)
+ replied_user = self.get_irc_user_from_telegram(replied.sender_id)
+ else:
+ replied_id = message.reply_to.reply_to_msg_id
+ cid = self.mid.num_to_id_offset(message.peer_id, replied_id)
+ if replied_id in self.cache:
+ text = self.cache[replied_id]['text']
+ replied_user = self.cache[replied_id]['user']
+ sp = ' '
+ else:
+ text = ''
+ replied_user = ''
+ sp = ''
+ replied_msg = '|Deleted|{}{}'.format(sp, text)
hunk ./telegram.py 718
- replied_user = self.get_irc_user_from_telegram(replied.sender_id)
hunk ./telegram.py 721
+ elif replied_user == '':
+ replied_nick = ''
patch ca113b48abe430eb11d500a5eb086edd15f23b0d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 28 19:45:06 CEST 2024
* Update copyright year in LICENSE
hunk ./LICENSE 4
-Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
+Copyright (c) 2020-2024 E. Bosch <presidev@AT@gmail.com>
patch 3ad99bad13beb07c34a9a992b026ed923f39b047
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 28 13:16:24 CEST 2024
* README update
hunk ./README.md 119
+## Inspired by
+
+- [telegramircd]
+- [ibotg]
+- [bitlbee]
+
hunk ./README.md 146
+[telegramircd]: https://github.com/prsai/telegramircd
+[ibotg]: https://github.com/prsai/ibotg
+[bitlbee]: https://www.bitlbee.org
patch c5d6314d751bbaca2ba4d24f4af0465af751ac40
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 28 00:28:22 CEST 2024
* utils: Fix when a filename has no extension
hunk ./utils.py 5
-# Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
+# Copyright (c) 2020-2024 E. Bosch <presidev@AT@gmail.com>
hunk ./utils.py 96
- ext = aux[1]
+ try:
+ ext = aux[1]
+ except:
+ ext = ''
patch 5d8ba95f7bbee459c4a9c7a0d524894ae8836c83
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Apr 27 20:32:49 CEST 2024
* exclam: Add command indicator to error messages
hunk ./exclam.py 53
- reply = ('Unknown message to reply',)
+ reply = ('!re: Unknown message to reply',)
hunk ./exclam.py 75
- reply = ('Not the author of the message to edit',)
+ reply = ('!ed: Not the author of the message to edit',)
hunk ./exclam.py 97
- reply = ('Not possible to delete',)
+ reply = ('!del: Not possible to delete',)
hunk ./exclam.py 134
- reply = ('Unknown chat to forward',)
+ reply = ('!fwd: Unknown chat to forward',)
hunk ./exclam.py 157
- reply = ('Error uploading',)
+ reply = ('!upl: Error uploading',)
patch b287d9843e3a4854c7ab0dc15558546ab7fd4c86
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 21 21:19:29 CEST 2024
* README update
hunk ./README.md 48
-- Media in messages (receive, download)
+- Media in messages (receive, download, upload)
patch d523591db91c8bf0ceb6ea65bac6358e5080f35a
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 21 20:59:04 CEST 2024
* exclam: !upl: Add support for HTTP/HTTPS URL for file upload
hunk ./exclam.py 150
- file_path = os.path.join(self.tg.telegram_upload_dir, file)
+ if file[:8] == 'https://' or file[:7] == 'http://':
+ file_path = file
+ else:
+ file_path = os.path.join(self.tg.telegram_upload_dir, file)
hunk ./exclam.py 163
- ' !upl <file name> [<optional caption>]',
- 'Upload the file referenced by <file name> to current ',
+ ' !upl <file name/URL> [<optional caption>]',
+ 'Upload the file referenced by <file name/URL> to current',
hunk ./exclam.py 166
- 'local directory.'
+ 'irgramd local directory or be an external HTTP/HTTPS URL.'
patch 18c87eca10fbc53fd73e8adf6b4da64dd4f24109
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Apr 19 01:11:38 CEST 2024
* service: Disable by now the help for subcommands "archive" and "delete" from
command "dialog" as they are not really implemented yet
hunk ./service.py 94
- ' archive <id> Archive the dialog specified by id',
- ' delete <id> Delete the dialog specified by id',
+# ' archive <id> Archive the dialog specified by id',
+# ' delete <id> Delete the dialog specified by id',
patch ca68ae9cfb315331dd08f8033ea2270e8a93e626
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 14 22:48:30 CEST 2024
* exclam: Add upload (!upl) command to upload files/media to chats/channels
Add "upload_dir" option to define the local directory to pick the files up,
by default "~/.cache/irgramd/upload"
hunk ./exclam.py 9
+import os
hunk ./exclam.py 22
+ '!upl': (self.handle_command_upl, 1, 2, 2),
hunk ./exclam.py 144
+ )
+ return reply
+
+ async def handle_command_upl(self, file=None, caption=None, help=None):
+ if not help:
+ try:
+ file_path = os.path.join(self.tg.telegram_upload_dir, file)
+ self.tmp_tg_msg = await self.tg.telegram_client.send_file(self.tmp_telegram_id, file_path, caption=caption)
+ reply = True
+ except:
+ reply = ('Error uploading',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !upl Upload a file to current channel/chat',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !upl <file name> [<optional caption>]',
+ 'Upload the file referenced by <file name> to current ',
+ 'channel/chat, the file must be present in "upload"',
+ 'local directory.'
hunk ./irgramd 113
+ tornado.options.define('upload_dir', default=None, metavar='PATH', help='Directory where files to upload are picked up, default "upload" in `cache_dir`')
hunk ./telegram.py 46
+ self.upload_dir = settings['upload_dir']
hunk ./telegram.py 81
+ # Setup upload folder
+ self.telegram_upload_dir = os.path.expanduser(self.upload_dir or os.path.join(self.cache_dir, 'upload'))
+ if not os.path.exists(self.telegram_upload_dir):
+ os.makedirs(self.telegram_upload_dir)
+
patch fa015f3a5b1ea9fe2b6c068491481d57837ecdc5
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 14 02:13:34 CEST 2024
* telegram: Use directory ".cache/irgramd/media" instead of
".config/irgramd/media" by default (relative to home directory)
Added "cache_dir" option to override the default "~/.cache/irgramd"
hunk ./irgramd 6
-# Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
+# Copyright (c) 2020-2024 E. Bosch <presidev@AT@gmail.com>
hunk ./irgramd 82
+ tornado.options.define('cache_dir', default='~/.cache/irgramd', metavar='PATH', help='Cache directory where telegram media is saved by default')
hunk ./irgramd 98
- tornado.options.define('media_dir', default=None, metavar='PATH', help='Directory where Telegram media files are downloaded, default "media" in `config_dir`')
+ tornado.options.define('media_dir', default=None, metavar='PATH', help='Directory where Telegram media files are downloaded, default "media" in `cache_dir`')
hunk ./telegram.py 41
+ self.cache_dir = settings['cache_dir']
hunk ./telegram.py 76
- self.telegram_media_dir = self.media_dir or os.path.join(self.config_dir, 'media')
+ self.telegram_media_dir = os.path.expanduser(self.media_dir or os.path.join(self.cache_dir, 'media'))
patch 170328f9bde0160c50f56b98cbf51c8726ab4d69
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 7 19:48:33 CEST 2024
* telegram: Fix a corner case in forward handler when saved_from_peer is not present
hunk ./telegram.py 5
-# Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
+# Copyright (c) 2020-2024 E. Bosch <presidev@AT@gmail.com>
hunk ./telegram.py 715
- if self.refwd_me:
- secondary_name = self.get_irc_user_from_telegram(message.fwd_from.saved_from_peer.user_id).irc_nick
+ if self.refwd_me and (saved_from_peer := message.fwd_from.saved_from_peer) is not None:
+ secondary_name = self.get_irc_user_from_telegram(saved_from_peer.user_id).irc_nick
patch 734e8c9f78627a6536e3ff52bd2db4879737830d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 7 19:08:52 CEST 2024
* README update
hunk ./README.md 50
-- Forwards (receive)
+- Forwards (receive, send)
hunk ./README.md 122
-Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
+Copyright (c) 2020-2024 E. Bosch <presidev@AT@gmail.com>
patch 4bb5866f7a3278949670e153444b9b1db74344ad
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 7 19:07:04 CEST 2024
* exclam: Add forward (!fwd) command to forward messages to other channels or chats
hunk ./exclam.py 4
-# Copyright (c) 2023 E. Bosch <presidev@AT@gmail.com>
+# Copyright (c) 2023, 2024 E. Bosch <presidev@AT@gmail.com>
hunk ./exclam.py 20
+ '!fwd': (self.handle_command_fwd, 2, 2, -1),
hunk ./exclam.py 108
+ )
+ return reply
+
+ async def handle_command_fwd(self, cid=None, chat=None, help=None):
+ if not help:
+ id, chk_msg = await self.check_msg(cid)
+ if chk_msg is not None:
+ async def send_fwd(tgt_ent, id):
+ from_ent = await self.tg.telegram_client.get_entity(self.tmp_telegram_id)
+ self.tmp_tg_msg = await self.tg.telegram_client.forward_messages(tgt_ent, id, from_ent)
+ return self.tmp_tg_msg
+
+ tgt = chat.lower()
+ if tgt in self.irc.iid_to_tid:
+ tgt_ent = await self.tg.telegram_client.get_entity(self.irc.iid_to_tid[tgt])
+ msg = await send_fwd(tgt_ent, id)
+ # echo fwded message
+ await self.tg.handle_telegram_message(event=None, message=msg)
+ reply = True
+ elif tgt in (u.irc_nick.lower() for u in self.irc.users.values() if u.stream):
+ tgt_ent = await self.tg.telegram_client.get_me()
+ await send_fwd(tgt_ent, id)
+ reply = True
+ else:
+ reply = ('Unknown chat to forward',)
+ else:
+ reply = ('Unknown message to forward',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !fwd Forward a message',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !fwd <compact_id> <chat>',
+ 'Forward a message with <compact_id> to <chat> channel/chat.'
patch aa90a8fae2fb0ed855e80f146dc88cbf7069a2dd
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Dec 31 01:26:30 CET 2023
* README update
hunk ./README.md 108
-In background (without logs):
-
- ./irgramd --logging=none &
+In background (with logs):
+
+ ./irgramd --log-file=irgramd.log &
patch f7068578a6b2806e38c5c5bfc1c03b8e59a14455
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Dec 20 01:50:56 CET 2023
* Fix logging system.
Remove logging options from tornado.log that were not working correctly in this setup
and use the new options from irgramd ("log_file" and "log_level").
Defer first logs to be included in log file opened later.
Improve option error handling.
hunk ./irgramd 23
+from utils import parse_loglevel
hunk ./irgramd 69
- logger = logging.getLogger()
+ # Remove tornado.log options (ugly hacks but these must not be defined)
+ tornado.options.options.logging = None
+ tornado_log_options = tuple(x for x in tornado.options.options._options.keys() if x != 'help' and x != 'logging')
+ for opt in tornado_log_options:
+ del tornado.options.options._options[opt]
+ # and reuse "--logging" to document empty "--" ;)
+ tornado.options.options._options['logging'].help = 'Stop parsing options'
+ for att in ('name', 'metavar', 'group_name', 'default'):
+ setattr(tornado.options.options._options['logging'], att, '')
+ # Define irgramd options
hunk ./irgramd 95
+ tornado.options.define('log_file', default=None, metavar='PATH', help='File where logs are appended, if not set will be stderr')
+ tornado.options.define('log_level', default='INFO', metavar='DEBUG|INFO|WARNING|ERROR|CRITICAL|NONE', help='The log level (and any higher to it) that will be logged')
hunk ./irgramd 112
- # parse cmd line first time to get --config and --config_dir
- tornado.options.parse_command_line()
+ try:
+ # parse cmd line first time to get --config and --config_dir
+ tornado.options.parse_command_line()
+ except Exception as exc:
+ print(exc)
+ exit(1)
hunk ./irgramd 122
- logger.info('Configuration Directory: %s', config_dir)
+ defered_logs = [(logging.INFO, 'Configuration Directory: %s', config_dir)]
hunk ./irgramd 127
- logger.info('Using configuration file: %s', config_file)
- tornado.options.parse_config_file(config_file)
+ defered_logs.append((logging.INFO, 'Using configuration file: %s', config_file))
+ try:
+ tornado.options.parse_config_file(config_file)
+ except Exception as exc:
+ print(exc)
+ exit(1)
hunk ./irgramd 134
- logger.warning('Configuration file not present, using only command line options and defaults')
+ defered_logs.append((logging.WARNING, 'Configuration file not present, using only command line options and defaults'))
hunk ./irgramd 141
+ # configure logging
+ loglevel = parse_loglevel(options['log_level'])
+ if loglevel == False:
+ print("Option 'log_level' requires one of these values: {}".format(tornado.options.options._options['log-level'].metavar))
+ exit(1)
+ logger_formats = { 'datefmt':'%Y-%m-%d %H:%M:%S', 'format':'[%(levelname).1s %(asctime)s %(module)s:%(lineno)d] %(message)s' }
+ logger = logging.getLogger()
+ if options['log_file']:
+ logging.basicConfig(filename=options['log_file'], level=loglevel, **logger_formats)
+ else:
+ logging.basicConfig(level=loglevel, **logger_formats)
+
+ for log in defered_logs:
+ logger.log(*log)
+
+ # main loop
hunk ./utils.py 16
+import logging
hunk ./utils.py 218
+
+def parse_loglevel(level):
+ levelu = level.upper()
+ if levelu == 'NONE':
+ l = None
+ elif levelu in ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'):
+ l = getattr(logging, levelu)
+ else:
+ l = False
+ return l
patch 987c7436cd18763a950f5799ef5fac6e7cb127e4
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Dec 18 21:18:42 CET 2023
* telegram, utils: Replace invalid characters in filenames with number sequences instead of just removing.
This will prevent some filename collisions in corner cases.
hunk ./utils.py 84
- return FILENAME_INVALID_CHARS.sub('', fn).strip('-').replace(' ','_')
+ cn = str(sanitize_filename.cn)
+ new_fn, ns = FILENAME_INVALID_CHARS.subn(cn, fn)
+ if ns:
+ sanitize_filename.cn += 1
+ return new_fn.strip('-').replace(' ','_')
+sanitize_filename.cn = 0
patch 3ba3cb2a3d628290c5d421637e779869c00fba7d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Dec 17 03:45:05 CET 2023
* telegram: Remove characters '#', '%' and '?' from filenames,
are not valid for static files in HTTP URLs.
hunk ./utils.py 19
-FILENAME_INVALID_CHARS = re.compile('[/{}<>()"\'\\|&]')
+FILENAME_INVALID_CHARS = re.compile('[/{}<>()"\'\\|&#%?]')
patch 59776df1810cf643a8b7d70ab0502afde6a5c47f
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Dec 17 02:49:18 CET 2023
* telegram: Add compact message IDs to filenames of media,
this will prevent most of the possible collisions of media with the same filename
hunk ./telegram.py 25
-from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces, format_timestamp
+from utils import sanitize_filename, add_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces, format_timestamp
hunk ./telegram.py 572
- text = await self.handle_webpage(upd_to_webpend, message)
+ text = await self.handle_webpage(upd_to_webpend, message, mid)
hunk ./telegram.py 579
- final_text = await self.handle_telegram_action(message)
+ final_text = await self.handle_telegram_action(message, mid)
hunk ./telegram.py 672
- async def handle_telegram_action(self, message):
+ async def handle_telegram_action(self, message, mid):
hunk ./telegram.py 679
- photo_url = await self.download_telegram_media(message)
+ photo_url = await self.download_telegram_media(message, mid)
hunk ./telegram.py 747
- return await self.handle_webpage(message.media.webpage, message)
+ return await self.handle_webpage(message.media.webpage, message, mid)
hunk ./telegram.py 834
- media_url_or_data = await self.download_telegram_media(message, filename, size, relay_attr)
+ media_url_or_data = await self.download_telegram_media(message, mid, filename, size, relay_attr)
hunk ./telegram.py 857
- async def handle_webpage(self, webpage, message):
+ async def handle_webpage(self, webpage, message, mid):
hunk ./telegram.py 859
- logo = await self.download_telegram_media(message)
+ logo = await self.download_telegram_media(message, mid)
hunk ./telegram.py 917
- async def download_telegram_media(self, message, filename=None, size=0, relay_attr=None):
+ async def download_telegram_media(self, message, mid, filename=None, size=0, relay_attr=None):
hunk ./telegram.py 921
- new_file = sanitize_filename(filename)
+ idd_file = add_filename(filename, mid)
+ new_file = sanitize_filename(idd_file)
hunk ./telegram.py 935
- new_file = str(self.media_cn) + filetype
+ gen_file = str(self.media_cn) + filetype
+ idd_file = add_filename(gen_file, mid)
+ new_file = sanitize_filename(idd_file)
hunk ./utils.py 86
+def add_filename(filename, add):
+ if add:
+ aux = filename.rsplit('.', 1)
+ name = aux[0]
+ ext = aux[1]
+ return '{}-{}.{}'.format(name, add, ext)
+ else:
+ return filename
+
patch 50155e1ef1b508f47640d4f570da84407efac9cb
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Dec 15 00:00:44 CET 2023
* telegram: Improve metadata shown for audio and recording/voice media,
and get filename from them
hunk ./telegram.py 731
- attrib_file = attrib_video = filename = None
+ attrib_file = attrib_av = filename = None
hunk ./telegram.py 735
- if isinstance(x, tgty.DocumentAttributeVideo):
- attrib_video = x
+ if isinstance(x, tgty.DocumentAttributeVideo) or isinstance(x, tgty.DocumentAttributeAudio):
+ attrib_av = x
hunk ./telegram.py 741
- return size, h_size, attrib_video, filename
+ return size, h_size, attrib_av, filename
hunk ./telegram.py 759
- elif message.audio: media_type = 'audio'
- elif message.voice: media_type = 'rec'
+ elif message.audio:
+ size, h_size, attrib_audio, filename = scan_doc_attributes(message.media.document)
+ dur = get_human_duration(attrib_audio.duration) if attrib_audio else ''
+ per = attrib_audio.performer or ''
+ tit = attrib_audio.title or ''
+ theme = ',{}/{}'.format(per, tit) if per or tit else ''
+ media_type = 'audio:{},{}{}'.format(h_size, dur, theme)
+ elif message.voice:
+ size, _, attrib_audio, filename = scan_doc_attributes(message.media.document)
+ dur = get_human_duration(attrib_audio.duration) if attrib_audio else ''
+ media_type = 'rec:{}'.format(dur)
hunk ./utils.py 133
- if s > 0: res += str(s) + 's'
+ if s > 0 or duration < 60: res += str(s) + 's'
patch 17b3e7fb0ee1d0153c40740e2934a2030bd9b0ba
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Dec 7 21:16:12 CET 2023
* README update
hunk ./README.md 35
-several Telegram accounts you will need to run several irgramd instances.
+several Telegram accounts you will need to run several irgramd instances. If
+all IRC clients are disconnected, irgramd will remain connected to Telegram.
+
+irgramd can also be seen as a kind of bouncer ([BNC]), with the difference
+that instead of talking IRC protocol on the client side, it talks Telegram
+protocol (MTProto), and can hide the IP and location of the IRC client (if
+executed in a different host).
hunk ./README.md 139
+[BNC]: https://en.wikipedia.org/wiki/BNC_(software)
patch ec320fd2c408d1239ba7e980e42318e1ab50bb21
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Dec 3 00:12:34 CET 2023
* Correct OpenStreetMap URL in irgramdrc.sample
hunk ./irgramdrc.sample 19
-#geo_url='https://osm.org/search?query={lat}%20{long}#map=15'
+#geo_url='https://osm.org/?mlat={lat}&mlon={long}&zoom=15'
patch 3976770a9424b2085ba1260328b3adb5ce6b1ebe
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Dec 2 20:41:44 CET 2023
* telegram: Add target nick in private messages sent from the self user
from another Telegram client
hunk ./service.py 80
- if id in self.tg.tid_to_iid.keys():
- name_in_irc = self.tg.tid_to_iid[id]
- else:
- name_in_irc = '<Unknown>'
+ name_in_irc = self.tg.get_irc_name_from_telegram_id(id)
+
hunk ./telegram.py 209
+ def get_irc_name_from_telegram_id(self, tid):
+ if tid in self.tid_to_iid.keys():
+ name_in_irc = self.tid_to_iid[tid]
+ else:
+ name_in_irc = '<Unknown>'
+ return name_in_irc
+
hunk ./telegram.py 588
- final_text = '[{}] {}{}'.format(mid, refwd_text, text)
+ target_mine = self.handle_target_mine(message.peer_id, user)
+
+ final_text = '[{}] {}{}{}'.format(mid, target_mine, refwd_text, text)
hunk ./telegram.py 835
+ def handle_target_mine(self, target, user):
+ # Add the target of messages sent by self user (me)
+ # received in other clients
+ target_id, target_type = self.get_peer_id_and_type(target)
+ if user is None and target_type == 'user' and target_id != self.id:
+ # self user^
+ # as sender
+ irc_id = self.get_irc_name_from_telegram_id(target_id)
+ target_mine = '[T: {}] '.format(irc_id)
+ else:
+ target_mine = ''
+ return target_mine
+
patch d9d472cb672a8172ee1d54241f20899754fb0640
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Nov 28 23:53:52 CET 2023
* README update
hunk ./README.md 49
+- Actions [pin message, channel photo] (receive)
patch 9c73d6fcff003a7d9b4f5f90df4f6dc71d84ae9d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Nov 28 22:38:01 CET 2023
* service: Add absolute ID as argument to get command
hunk ./service.py 109
- id = self.tg.mid.id_to_num_offset(peer_id, mid)
+ # If the ID starts with '=' is absolute ID, not compact ID
+ # character '=' is not used by compact IDs
+ if mid[0] == '=':
+ id = int(mid[1:])
+ else:
+ id = self.tg.mid.id_to_num_offset(peer_id, mid)
hunk ./service.py 128
- ' get <peer> <compact_id>',
- 'Get one message from peer with the compact ID',
+ ' get <peer> <compact_id|=ID>',
+ 'Get one message from peer with the compact or absolute ID',
patch c36c361baf23612e31780ba510278ceef611b7ec
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Nov 26 23:13:52 CET 2023
* telegram: Add support for the change of channel photo action
including download of the new photo
mapped to CTCP action on IRC
hunk ./telegram.py 668
+ elif isinstance(message.action, tgty.MessageActionChatEditPhoto):
+ _, media_type = self.scan_photo_attributes(message.action.photo)
+ photo_url = await self.download_telegram_media(message)
+ action_text = 'has changed chat [{}] {}'.format(media_type, photo_url)
hunk ./telegram.py 749
- ph_size = message.media.photo.sizes[-1]
- if isinstance(ph_size, tgty.PhotoSizeProgressive):
- size = ph_size.sizes[-1]
- else:
- size = ph_size.size
- if hasattr(ph_size, 'w') and hasattr(ph_size, 'h'):
- media_type = 'photo:{}x{}'.format(ph_size.w, ph_size.h)
- else:
- media_type = 'photo'
+ size, media_type = self.scan_photo_attributes(message.media.photo)
hunk ./telegram.py 867
+ def scan_photo_attributes(self, photo):
+ size = 0
+ sizes = photo.sizes
+ ph_size = sizes[-1]
+ if isinstance(ph_size, tgty.PhotoSizeProgressive):
+ size = ph_size.sizes[-1]
+ else:
+ for x in sizes:
+ if isinstance(x, tgty.PhotoSize):
+ if x.size > size:
+ size = x.size
+ ph_size = x
+ if hasattr(ph_size, 'w') and hasattr(ph_size, 'h'):
+ media_type = 'photo:{}x{}'.format(ph_size.w, ph_size.h)
+ else:
+ media_type = 'photo'
+
+ return size, media_type
+
patch 9c00648eaf7c048826dc4fe6bdbd509eabe57f47
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Nov 26 20:07:44 CET 2023
* telegram: Add support for pin message action
irc: Add support for sending CTCP actions (me) to map actions from Telegram
hunk ./irc.py 473
+ async def send_action(self, source, target, message):
+ action_message = '\x01ACTION {}\x01'.format(message)
+ await self.send_msg(source, target, action_message)
+
hunk ./telegram.py 556
- text_send = self.set_history_timestamp(text, history, msg.date)
+ text_send = self.set_history_timestamp(text, history, msg.date, msg.action)
hunk ./telegram.py 571
- if message.is_reply:
+ if message.action:
+ final_text = await self.handle_telegram_action(message)
+ return final_text
+ elif message.is_reply:
hunk ./telegram.py 585
- def set_history_timestamp(self, text, history, date):
+ def set_history_timestamp(self, text, history, date, action):
hunk ./telegram.py 588
- res = '{} {}'.format(timestamp, text)
+ if action:
+ res = '{} {}'.format(text, timestamp)
+ else:
+ res = '{} {}'.format(timestamp, text)
hunk ./telegram.py 598
+ action = (message and message.action)
hunk ./telegram.py 600
- await self.relay_telegram_private_message(user, text)
+ await self.relay_telegram_private_message(user, text, action)
hunk ./telegram.py 603
- chan = await self.relay_telegram_channel_message(message, user, text, channel)
+ chan = await self.relay_telegram_channel_message(message, user, text, channel, action)
hunk ./telegram.py 606
- async def relay_telegram_private_message(self, user, message):
+ async def relay_telegram_private_message(self, user, message, action=None):
hunk ./telegram.py 609
- await self.irc.send_msg(user, None, message)
-
- async def relay_telegram_channel_message(self, message, user, text, channel=None):
+ if action:
+ await self.irc.send_action(user, None, message)
+ else:
+ await self.irc.send_msg(user, None, message)
+
+ async def relay_telegram_channel_message(self, message, user, text, channel, action):
hunk ./telegram.py 622
- await self.irc.send_msg(user, chan, text)
+
+ if action:
+ await self.irc.send_action(user, chan, text)
+ else:
+ await self.irc.send_msg(user, chan, text)
+
hunk ./telegram.py 663
+ async def handle_telegram_action(self, message):
+ if isinstance(message.action, tgty.MessageActionPinMessage):
+ replied = await message.get_reply_message()
+ cid = self.mid.num_to_id_offset(replied.peer_id, replied.id)
+ action_text = 'has pinned message [{}]'.format(cid)
+ else:
+ action_text = ''
+ return action_text
+
patch 583b2fd653aa6786aba38cb9a00c6e990f3a1d11
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Nov 19 00:54:47 CET 2023
* telegram: Change add symbol in editions from "_" to "+"
hunk ./utils.py 180
- res += '_{}_ '.format(i[2:])
+ res += '+{}+ '.format(i[2:])
patch 8167f7dfd2712a9a103504fe7763e2a497adf723
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Nov 19 00:43:04 CET 2023
* telegram: Refactor download media code.
If named media files already downloaded, don't try to download again
hunk ./telegram.py 691
+ filename = None
+
+ def scan_doc_attributes(document):
+ attrib_file = attrib_video = filename = None
+ size = document.size
+ h_size = get_human_size(size)
+ for x in document.attributes:
+ if isinstance(x, tgty.DocumentAttributeVideo):
+ attrib_video = x
+ if isinstance(x, tgty.DocumentAttributeFilename):
+ attrib_file = x
+ filename = attrib_file.file_name if attrib_file else None
+
+ return size, h_size, attrib_video, filename
hunk ./telegram.py 733
- size = message.media.document.size
- h_size = get_human_size(size)
- attrib = next(x for x in message.media.document.attributes if isinstance(x, tgty.DocumentAttributeVideo))
- dur = get_human_duration(attrib.duration)
+ size, h_size, attrib_video, filename = scan_doc_attributes(message.media.document)
+ dur = get_human_duration(attrib_video.duration) if attrib_video else ''
hunk ./telegram.py 740
- size = message.media.document.size
- h_size = get_human_size(size)
+ size, h_size, _, filename = scan_doc_attributes(message.media.document)
hunk ./telegram.py 795
- if self.download and size > self.notice_size:
- await self.relay_telegram_message(message, user, '[{}] [{}] [Downloading]'.format(mid, media_type))
-
- media_url_or_data = await self.download_telegram_media(message)
+ relay_attr = (message, user, mid, media_type)
+ media_url_or_data = await self.download_telegram_media(message, filename, size, relay_attr)
hunk ./telegram.py 847
- async def download_telegram_media(self, message):
- local_path = None
- if self.download:
+ async def download_telegram_media(self, message, filename=None, size=0, relay_attr=None):
+ if not self.download:
+ return ''
+ if filename:
+ new_file = sanitize_filename(filename)
+ new_path = os.path.join(self.telegram_media_dir, new_file)
+ if os.path.exists(new_path):
+ local_path = new_path
+ else:
+ await self.notice_downloading(size, relay_attr)
+ local_path = await message.download_media(new_path)
+ if not local_path: return ''
+ else:
+ await self.notice_downloading(size, relay_attr)
hunk ./telegram.py 862
- if not local_path: return ''
-
- if message.document:
- new_file = sanitize_filename(os.path.basename(local_path))
- else:
+ if not local_path: return ''
hunk ./telegram.py 866
-
- new_path = os.path.join(self.telegram_media_dir, new_file)
+ new_path = os.path.join(self.telegram_media_dir, new_file)
+
hunk ./telegram.py 874
+ async def notice_downloading(self, size, relay_attr):
+ if relay_attr and size > self.notice_size:
+ message, user, mid, media_type = relay_attr
+ await self.relay_telegram_message(message, user, '[{}] [{}] [Downloading]'.format(mid, media_type))
+
patch 24a50b886f4cef2e1da003b74921982f5da0941c
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Oct 16 00:24:53 CEST 2023
* telegram: Add option "download_notice" to show the start of a big download
hunk ./irgramd 77
+ tornado.options.define('download_notice', default=10, metavar='SIZE (MiB)', help='Enable a notice when a download starts if its size is greater than SIZE, this is useful when a download takes some time to be completed')
hunk ./telegram.py 42
+ self.notice_size = settings['download_notice'] * 1048576
hunk ./telegram.py 555
- text = await self.render_text(msg, mid, upd_to_webpend)
+ text = await self.render_text(msg, mid, upd_to_webpend, user)
hunk ./telegram.py 563
- async def render_text(self, message, mid, upd_to_webpend, history=False):
+ async def render_text(self, message, mid, upd_to_webpend, user=None):
hunk ./telegram.py 567
- text = await self.handle_telegram_media(message)
+ text = await self.handle_telegram_media(message, user, mid)
hunk ./telegram.py 686
- async def handle_telegram_media(self, message):
+ async def handle_telegram_media(self, message, user, mid):
hunk ./telegram.py 690
+ size = 0
hunk ./telegram.py 707
- size = message.media.photo.sizes[-1]
- if hasattr(size, 'w') and hasattr(size, 'h'):
- media_type = 'photo:{}x{}'.format(size.w, size.h)
+ ph_size = message.media.photo.sizes[-1]
+ if isinstance(ph_size, tgty.PhotoSizeProgressive):
+ size = ph_size.sizes[-1]
hunk ./telegram.py 711
+ size = ph_size.size
+ if hasattr(ph_size, 'w') and hasattr(ph_size, 'h'):
+ media_type = 'photo:{}x{}'.format(ph_size.w, ph_size.h)
+ else:
hunk ./telegram.py 719
- size = get_human_size(message.media.document.size)
+ size = message.media.document.size
+ h_size = get_human_size(size)
hunk ./telegram.py 723
- media_type = 'video:{},{}'.format(size, dur)
+ media_type = 'video:{},{}'.format(h_size, dur)
hunk ./telegram.py 728
- size = get_human_size(message.media.document.size)
- media_type = 'file:{}'.format(size)
+ size = message.media.document.size
+ h_size = get_human_size(size)
+ media_type = 'file:{}'.format(h_size)
hunk ./telegram.py 784
+ if self.download and size > self.notice_size:
+ await self.relay_telegram_message(message, user, '[{}] [{}] [Downloading]'.format(mid, media_type))
+
patch 1fb88a2962a05a4a46ccec54dc3ae3b65f73ab78
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Oct 15 22:06:49 CEST 2023
* telegram: Add option "download_media" to control if media files must be downloaded
hunk ./irgramd 76
+ tornado.options.define('download_media', default=True, help='Enable download of any media (photos, documents, etc.), if not set only a message of media will be shown')
hunk ./telegram.py 41
+ self.download = settings['download_media']
hunk ./telegram.py 828
- local_path = await message.download_media(self.telegram_media_dir)
+ local_path = None
+ if self.download:
+ local_path = await message.download_media(self.telegram_media_dir)
patch a62e1a9973921a97198519dd5a74e1a4472b9364
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Oct 11 00:35:28 CEST 2023
* telegram: Support for showing question and options of polls
hunk ./README.md 48
+- Polls (receive, show)
hunk ./telegram.py 761
- media_url_or_data = ''
+ media_url_or_data = self.handle_poll(message.media.poll)
hunk ./telegram.py 779
+ def handle_poll(self, poll):
+ text = poll.question
+ for ans in poll.answers:
+ text += '\n* ' + ans.text
+ return text
+
patch 400207de8aa3ca0b2dfc8ff1eeaafeddc8221070
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Sep 17 20:57:11 CEST 2023
* README update
hunk ./README.md 49
+- History
patch 2ab08fbae8ec48e123e86a498c287e7afcd173e1
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Jul 22 20:32:19 CEST 2023
* telegram: Add compact ID to all replies
hunk ./telegram.py 647
+ space = ' '
hunk ./telegram.py 651
+ cid = self.mid.num_to_id_offset(replied.peer_id, replied.id)
hunk ./telegram.py 653
- replied_msg = '[{}]'.format(self.mid.num_to_id_offset(replied.peer_id, replied.id))
+ replied_msg = ''
+ space = ''
hunk ./telegram.py 665
- return '|Re {}: {}{}| '.format(replied_nick, replied_msg, trunc)
+ return '|Re {}: [{}]{}{}{}| '.format(replied_nick, cid, space, replied_msg, trunc)
patch 505cd255e7a1cea83385a48467c6d1d099b3bc5d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Jul 21 01:12:18 CEST 2023
* telegram: Add option for geo custom URL
hunk ./irgramd 77
+ tornado.options.define('geo_url', type=str, default=None, metavar='TEMPLATE_URL', help='Use custom URL for showing geo latitude/longitude location, eg. OpenStreetMap')
hunk ./irgramdrc.sample 18
+#geo url OpenStreetMap
+#geo_url='https://osm.org/search?query={lat}%20{long}#map=15'
+#geo url Google Maps
+#geo_url='https://maps.google.com/?q={lat},{long}'
hunk ./telegram.py 54
+ self.geo_url = settings['geo_url']
hunk ./telegram.py 741
- media_url_or_data = 'lat: {}, long: {}'.format(message.media.geo.lat, message.media.geo.long)
+ if self.geo_url:
+ geo_url = ' | ' + self.geo_url
+ else:
+ geo_url = ''
+ lat_long_template = 'lat: {lat}, long: {long}' + geo_url
+ media_url_or_data = lat_long_template.format(lat=message.media.geo.lat, long=message.media.geo.long)
patch f83be7c1c04d7dcb5ebd0119895eafbc6d784ecb
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Jul 20 04:46:06 CEST 2023
* README update
hunk ./README.md 45
-- Deletions (receive)
+- Deletions (receive, do)
patch ee2b02319b5759d67df9fcc08fdafbd57a8fcc80
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jul 17 02:37:21 CEST 2023
* exclam: Add delete (!del) command to delete messages
hunk ./exclam.py 19
+ '!del': (self.handle_command_del, 1, 1, -1),
hunk ./exclam.py 37
- chk_msg = await self.tg.telegram_client.get_messages(entity=self.tmp_telegram_id, ids=id)
+ if id is None:
+ chk_msg = None
+ else:
+ chk_msg = await self.tg.telegram_client.get_messages(entity=self.tmp_telegram_id, ids=id)
hunk ./exclam.py 85
+ )
+ return reply
+
+ async def handle_command_del(self, cid=None, help=None):
+ if not help:
+ id, del_msg = await self.check_msg(cid)
+ if del_msg is not None:
+ deleted = await self.tg.telegram_client.delete_messages(self.tmp_telegram_id, del_msg)
+ if deleted[0].pts_count == 0:
+ reply = ('Not possible to delete',)
+ else:
+ self.tmp_tg_msg = None
+ reply = None
+ else:
+ reply = ('Unknown message to delete',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !del Delete a message',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !del <compact_id>',
+ 'Delete a message with <compact_id> on current channel/chat'
patch 1de628ed1063db8129e0c142d5fe08907b27930a
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Jul 16 20:09:53 CEST 2023
* README update
hunk ./README.md 112
-Copyright (c) 2019 Peter Bui <pbui@bx612.space> [_$_]
+Copyright (c) 2019 Peter Bui <pbui@bx612.space> [_$_]
patch cbe1c8b9a949b8eb5b466496eb7eab3b2f3874bd
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Jul 16 20:02:03 CEST 2023
* README update
hunk ./README.md 46
-- Editions (receive)
+- Editions (receive, do)
hunk ./README.md 112
-Copyright (c) 2019 Peter Bui <pbui@bx612.space>
+Copyright (c) 2019 Peter Bui <pbui@bx612.space> [_$_]
patch fe0c125aa83d95b5472c0bc599e883c9b47213f6
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Jul 11 23:54:41 CEST 2023
* exclam: Fix indentation
hunk ./exclam.py 43
- self.tmp_tg_msg = await self.tg.telegram_client.send_message(self.tmp_telegram_id, msg, reply_to=id)
- reply = True
+ self.tmp_tg_msg = await self.tg.telegram_client.send_message(self.tmp_telegram_id, msg, reply_to=id)
+ reply = True
hunk ./exclam.py 46
- reply = ('Unknown message to reply',)
+ reply = ('Unknown message to reply',)
hunk ./exclam.py 62
- try:
- self.tmp_tg_msg = await self.tg.telegram_client.edit_message(ed_msg, new_msg)
- except MessageNotModifiedError:
- self.tmp_tg_msg = ed_msg
- reply = True
- except MessageAuthorRequiredError:
- reply = ('Not the author of the message to edit',)
- else:
- reply = True
+ try:
+ self.tmp_tg_msg = await self.tg.telegram_client.edit_message(ed_msg, new_msg)
+ except MessageNotModifiedError:
+ self.tmp_tg_msg = ed_msg
+ reply = True
+ except MessageAuthorRequiredError:
+ reply = ('Not the author of the message to edit',)
+ else:
+ reply = True
hunk ./exclam.py 72
- reply = ('Unknown message to edit',)
+ reply = ('Unknown message to edit',)
patch 74e097435eeb72086c6ed3f43a98ce3f5463d1c6
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Jul 11 01:48:40 CEST 2023
* exclam: Add edit (!ed) command to modify already sent messages
hunk ./exclam.py 9
+from telethon.errors.rpcerrorlist import MessageNotModifiedError, MessageAuthorRequiredError
+
hunk ./exclam.py 18
+ '!ed': (self.handle_command_ed, 2, 2, 2),
hunk ./exclam.py 33
+
+ async def check_msg(self, cid):
+ id = self.tg.mid.id_to_num_offset(self.tmp_telegram_id, cid)
+ chk_msg = await self.tg.telegram_client.get_messages(entity=self.tmp_telegram_id, ids=id)
+ return id, chk_msg
hunk ./exclam.py 41
- id = self.tg.mid.id_to_num_offset(self.tmp_telegram_id, cid)
- chk_msg = await self.tg.telegram_client.get_messages(entity=self.tmp_telegram_id, ids=id)
+ id, chk_msg = await self.check_msg(cid)
hunk ./exclam.py 55
+ )
+ return reply
+
+ async def handle_command_ed(self, cid=None, new_msg=None, help=None):
+ if not help:
+ id, ed_msg = await self.check_msg(cid)
+ if ed_msg is not None:
+ try:
+ self.tmp_tg_msg = await self.tg.telegram_client.edit_message(ed_msg, new_msg)
+ except MessageNotModifiedError:
+ self.tmp_tg_msg = ed_msg
+ reply = True
+ except MessageAuthorRequiredError:
+ reply = ('Not the author of the message to edit',)
+ else:
+ reply = True
+ else:
+ reply = ('Unknown message to edit',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !ed Edit a message',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !ed <compact_id> <new_message>',
+ 'Edit a message with <compact_id> on current channel/chat,',
+ '<new_message> replaces the current message.'
patch a8d4d79f7ef62a0938f42c2895c99e5d9341d50c
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jul 10 00:56:15 CEST 2023
* README update
hunk ./README.md 60
+## Instalation
+
+### From darcs
+
+ darcs clone https://src.presi.org/repos/darcs/irgramd
+ chmod +x irgramd/irgramd
+
+### From git
+
+ git clone https://github.com/prsai/irgramd.git
+ chmod +x irgramd/irgramd
+
+## Configuration
+
+From irgramd directory `./irgramd --help` will show all configuration
+options available, these options can be used directy in the command line or
+in a file.
+
+When used in command line the separator is `-` (dash) with two leading
+dashes, example: `--api-hash`.
+
+When used in a file the separator is `_` (underscore) without two leading
+dashes nor underscores, example: `api_hash`. The syntax of this file is just
+Python so strings are surrounded by quotes (`'`) and lists by brackets (`[]`).
+
+A sample of the configuration file is provided, copy it to the default
+configuration location:
+
+ mkdir -p ~/.config/irgramd
+ cp irgramd/irgramdrc.sample ~/.config/irgramd/irgramdrc
+
+And modified it with your API IDs and preferences.
+
+## Usage
+
+From irgramd directory, in foreground:
+
+ ./irgramd
+
+In background (without logs):
+
+ ./irgramd --logging=none &
+
hunk ./README.md 107
-(e.g. in Linux be in the shadow group or equivalent). The dependency is
+(e.g. in Linux be in the shadow group or equivalent). The dependency is
hunk ./README.md 112
-Copyright (c) 2019 Peter Bui <pbui@bx612.space> [_$_]
+Copyright (c) 2019 Peter Bui <pbui@bx612.space>
patch 93ee900b41a7ba1e912d3897d242f714e866de09
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jul 10 00:52:02 CEST 2023
* Add configuration file sample (irgramdrc.sample)
addfile ./irgramdrc.sample
hunk ./irgramdrc.sample 1
+api_id=XXXXXX
+api_hash='XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
+phone='XXXXXXXXXXX'
+#ask_code=True
+#irc_address='0.0.0.0'
+#tls=True
+#tls_cert='/path/to/certificate'
+#tls_key='/path/to/key'
+irc_nicks=['XXXXX', 'XXXXXX']
+media_url='https://server/token/'
+#pam=True
+#pam_group='XXXXX'
+#char_in_encoding='iso-8859-1'
+#char_out_encoding='iso-8859-1'
+#emoji_ascii=True
+hist_timestamp_format='[%m-%d %H:%M]'
+#timezone='Europe/Madrid'
patch 274ec21ecc7d6c159237df2648668f54b65d5332
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Jul 8 01:58:26 CEST 2023
* telegram, irc: Refactor and improve routine for conversion of mentions
hunk ./telegram.py 66
+ self.sorted_len_usernames = []
hunk ./telegram.py 136
+ self.add_sorted_len_usernames(self.tg_username)
hunk ./telegram.py 152
+ self.add_sorted_len_usernames(tg_ni)
hunk ./telegram.py 409
- def repl_mentioned(text, me_nick, received, mark, index, rargs):
- if text and text[index] == mark:
+ def repl_mentioned(text, me_nick, received, mark, repl_pref, repl_suff):
+ new_text = text
+
+ for user in self.sorted_len_usernames:
+ if user == self.tg_username:
+ if me_nick:
+ username = me_nick
+ else:
+ continue
+ else:
+ username = self.irc.users[user].irc_nick
+
hunk ./telegram.py 422
- subtext = text[1:]
- else:
- subtext = text[:-1]
- part = subtext.lower()
- if me_nick and part == self.tg_username:
- return replacement(me_nick, **rargs)
- if part in self.irc.users:
- return replacement(self.irc.users[part].irc_nick, **rargs)
- return text
-
- def replacement(nick, repl_pref, repl_suff):
- return '{}{}{}'.format(repl_pref, nick, repl_suff)
+ mention = mark + user
+ mention_case = mark + username
+ else: # sent
+ mention = user + mark
+ mention_case = username + mark
+ replcmnt = repl_pref + username + repl_suff
+
+ # Start of the text
+ for ment in (mention, mention_case):
+ if new_text.startswith(ment):
+ new_text = new_text.replace(ment, replcmnt, 1)
+
+ # Next words (with space as separator)
+ mention = ' ' + mention
+ mention_case = ' ' + mention_case
+ replcmnt = ' ' + replcmnt
+ new_text = new_text.replace(mention, replcmnt).replace(mention_case, replcmnt)
+
+ return new_text
hunk ./telegram.py 444
- index = 0
hunk ./telegram.py 446
- else:
+ else: # sent
hunk ./telegram.py 448
- index = -1
hunk ./telegram.py 452
- words = text.split(' ')
- words_replaced = [repl_mentioned(elem, me_nick, received, mark, index, rargs) for elem in words]
- text_replaced = ' '.join(words_replaced)
+ text_replaced = repl_mentioned(text, me_nick, received, mark, **rargs)
hunk ./telegram.py 462
+ def add_sorted_len_usernames(self, username):
+ self.sorted_len_usernames.append(username)
+ self.sorted_len_usernames.sort(key=lambda k: len(k), reverse=True)
+
patch 153aba3773e6ca3c7c505212ac1aeac19f15e68d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Jun 27 03:00:57 CEST 2023
* Fix typos in README
hunk ./README.md 64
-(e.g. in Linux be in the shadow group or equivalent).
-The dependency is totally optional, if not use, the module pyPAM is no
-needed.
+(e.g. in Linux be in the shadow group or equivalent). The dependency is
+totally optional, if not used, the module pyPAM is not needed.
patch 7225855530de2f42820f61b5a8f1083269aab749
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jun 26 22:55:35 CEST 2023
* README update
hunk ./README.md 58
+- [pyPAM] (optional, tested with v0.4.2-13.4 from deb, [legacy web](https://web.archive.org/web/20110316070059/http://www.pangalactic.org/PyPAM/))
+
+## Notes
+
+PAM authentication: it allows to authenticate IRC users from the system in
+Unix/Linux. The user that executes irgramd must have permissions to use PAM
+(e.g. in Linux be in the shadow group or equivalent).
+The dependency is totally optional, if not use, the module pyPAM is no
+needed.
hunk ./README.md 87
+[pyPAM]: https://packages.debian.org/bullseye/python3-pam
patch 1b816235b1b9baeae5f394f01c379bbb2e0136ce
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jun 26 22:23:59 CEST 2023
* README update
hunk ./README.md 43
-- Replies (receive)
+- Replies (receive, send)
patch ae837b8af1788904bfa4ed7430d331028475e8e3
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jun 26 00:29:50 CEST 2023
* Remove trailing spaces
hunk ./service.py 115
- reply = ('Message not found',) [_$_]
+ reply = ('Message not found',)
patch 5b2c938f7967a3345435fe21b2127999d0975f50
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Jun 26 00:17:22 CEST 2023
* Add exclam module to handle channel/chat commands begining with exclamation mark (!)
Implement reply (!re) as first exclam command
addfile ./exclam.py
hunk ./exclam.py 1
+# irgramd: IRC-Telegram gateway
+# exclam.py: IRC exclamation command handlers
+#
+# Copyright (c) 2023 E. Bosch <presidev@AT@gmail.com>
+#
+# Use of this source code is governed by a MIT style license that
+# can be found in the LICENSE file included in this project.
+
+from utils import command, HELP
+
+class exclam(command):
+ def __init__(self, telegram):
+ self.commands = \
+ { # Command Handler Arguments Min Max Maxsplit
+ '!re': (self.handle_command_re, 2, 2, 2),
+ }
+ self.tg = telegram
+ self.irc = telegram.irc
+ self.tmp_ircnick = None
+ self.tmp_telegram_id = None
+ self.tmp_tg_msg = None
+
+ async def command(self, message, telegram_id, user):
+ self.tmp_telegram_id = telegram_id
+ res = await self.parse_command(message, nick=None)
+ if isinstance(res, tuple):
+ await self.irc.send_msg(self.irc.service_user, None, res[0], user)
+ res = False
+ return res, self.tmp_tg_msg
+
+ async def handle_command_re(self, cid=None, msg=None, help=None):
+ if not help:
+ id = self.tg.mid.id_to_num_offset(self.tmp_telegram_id, cid)
+ chk_msg = await self.tg.telegram_client.get_messages(entity=self.tmp_telegram_id, ids=id)
+ if chk_msg is not None:
+ self.tmp_tg_msg = await self.tg.telegram_client.send_message(self.tmp_telegram_id, msg, reply_to=id)
+ reply = True
+ else:
+ reply = ('Unknown message to reply',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' !re Reply to a message',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' !re <compact_id> <message>',
+ 'Reply with <message> to a message with <compact_id> on current',
+ 'channel/chat.'
+ )
+ return reply
hunk ./irc.py 25
+from exclam import exclam
hunk ./irc.py 113
+ self.exclam = exclam(self.tg)
hunk ./irc.py 413
- tg_msg = await self.tg.telegram_client.send_message(telegram_id, message)
-
- mid = self.tg.mid.num_to_id_offset(telegram_id, tg_msg.id)
- text = '[{}] {}'.format(mid, message)
- self.tg.to_cache(tg_msg.id, mid, text, message, user, chan, media=None)
-
- if defered_send:
- await defered_send(user, defered_target, text)
+ if message[0] == '!':
+ cont, tg_msg = await self.exclam.command(message, telegram_id, user)
+ else:
+ tg_msg = await self.tg.telegram_client.send_message(telegram_id, message)
+ cont = True
+ if cont:
+ mid = self.tg.mid.num_to_id_offset(telegram_id, tg_msg.id)
+ text = '[{}] {}'.format(mid, message)
+ self.tg.to_cache(tg_msg.id, mid, text, message, user, chan, media=None)
+
+ if defered_send:
+ await defered_send(user, defered_target, text)
hunk ./service.py 9
-from utils import compact_date, command
+from utils import compact_date, command, HELP
hunk ./service.py 15
- { # Command Handler Arguments Min Max
- 'code': (self.handle_command_code, 1, 1),
- 'dialog': (self.handle_command_dialog, 1, 2),
- 'get': (self.handle_command_get, 2, 2),
- 'help': (self.handle_command_help, 0, 1),
- 'history': (self.handle_command_history, 1, 3),
- 'mark_read': (self.handle_command_mark_read, 1, 1),
+ { # Command Handler Arguments Min Max Maxsplit
+ 'code': (self.handle_command_code, 1, 1, -1),
+ 'dialog': (self.handle_command_dialog, 1, 2, -1),
+ 'get': (self.handle_command_get, 2, 2, -1),
+ 'help': (self.handle_command_help, 0, 1, -1),
+ 'history': (self.handle_command_history, 1, 3, -1),
+ 'mark_read': (self.handle_command_mark_read, 1, 1, -1),
hunk ./service.py 147
+ 'The commands begining with ! (exclamation) must be used directly',
+ 'in channels or chats. The following ! commands are available:',
+ )
+ for command in self.irc.exclam.commands.values():
+ handler = command[0]
+ help_text += await handler(help=HELP.brief)
+ help_text += \
+ (
hunk ./service.py 159
- elif help_command in self.commands.keys():
- handler = self.commands[help_command][0]
+ elif help_command in (all_commands := dict(**self.commands, **self.irc.exclam.commands)).keys():
+ handler = all_commands[help_command][0]
hunk ./service.py 261
-
-class HELP:
- desc = 1
- brief = 2
hunk ./utils.py 26
- words = line.split()
- command = words.pop(0).lower()
+ command = line.partition(' ')[0].lower()
hunk ./utils.py 29
- handler, min_args, max_args = self.commands[command]
+ handler, min_args, max_args, maxsplit = self.commands[command]
+ words = line.split(maxsplit=maxsplit)[1:]
hunk ./utils.py 41
+class HELP:
+ desc = 1
+ brief = 2
+
patch cbec6bc5a68bcadaaea6113355f12056d10cf577
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Jun 23 23:49:58 CEST 2023
* telegram: Fix: in forwards when the original user is unknown use the ID
hunk ./telegram.py 212
- user = self.get_irc_user_from_telegram(peer_id)
- if user is None:
- name = '{}'
- self.refwd_me = True
+ try:
+ user = self.get_irc_user_from_telegram(peer_id)
+ except:
+ name = str(peer_id)
hunk ./telegram.py 217
- name = user.irc_nick
+ if user is None:
+ name = '{}'
+ self.refwd_me = True
+ else:
+ name = user.irc_nick
patch dd182f24b1eb76b357ab4ab84363ca55669f7b97
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Jun 22 21:57:16 CEST 2023
* Move parse_command() to a new class, the code will be reused for the future
exclam module
hunk ./service.py 9
-from utils import compact_date
+from utils import compact_date, command
hunk ./service.py 12
-class service:
+class service(command):
hunk ./service.py 28
- async def parse_command(self, line, nick):
-
- words = line.split()
- command = words.pop(0).lower()
- self.tmp_ircnick = nick
- if command in self.commands.keys():
- handler, min_args, max_args = self.commands[command]
- num_words = len(words)
- if num_words < min_args or num_words > max_args:
- reply = ('Wrong number of arguments',)
- else:
- reply = await handler(*words)
- else:
- reply = ('Unknown command',)
-
- return reply
-
hunk ./utils.py 24
+class command:
+ async def parse_command(self, line, nick):
+ words = line.split()
+ command = words.pop(0).lower()
+ self.tmp_ircnick = nick
+ if command in self.commands.keys():
+ handler, min_args, max_args = self.commands[command]
+ num_words = len(words)
+ if num_words < min_args or num_words > max_args:
+ reply = ('Wrong number of arguments',)
+ else:
+ reply = await handler(*words)
+ else:
+ reply = ('Unknown command',)
+
+ return reply
+
patch 3ec451218b80bdd3c409b4aec91ae94ef4fa5c38
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Jun 15 20:08:03 CEST 2023
* telegram, irc: Add conversion of "mention:" (IRC style) to "@mention" in sent messages
hunk ./irc.py 409
+ message = self.tg.replace_mentions(message, me_nick='', received=False)
hunk ./telegram.py 398
- def replace_mentions(self, text, me_nick=''):
- def repl_mentioned(text, me_nick):
- if text and text[0] == '@':
- part = text[1:].lower()
+ def replace_mentions(self, text, me_nick='', received=True):
+ # For received replace @mention to ~mention~
+ # For sent replace mention: to @mention
+ rargs = {}
+ def repl_mentioned(text, me_nick, received, mark, index, rargs):
+ if text and text[index] == mark:
+ if received:
+ subtext = text[1:]
+ else:
+ subtext = text[:-1]
+ part = subtext.lower()
hunk ./telegram.py 410
- return replacement(me_nick)
+ return replacement(me_nick, **rargs)
hunk ./telegram.py 412
- return replacement(self.irc.users[part].irc_nick)
+ return replacement(self.irc.users[part].irc_nick, **rargs)
hunk ./telegram.py 415
- def replacement(nick):
- return '{}{}{}'.format('~',nick, '~')
-
- if text.find('@') != -1:
+ def replacement(nick, repl_pref, repl_suff):
+ return '{}{}{}'.format(repl_pref, nick, repl_suff)
+
+ if received:
+ mark = '@'
+ index = 0
+ rargs['repl_pref'] = '~'
+ rargs['repl_suff'] = '~'
+ else:
+ mark = ':'
+ index = -1
+ rargs['repl_pref'] = '@'
+ rargs['repl_suff'] = ''
+
+ if text.find(mark) != -1:
hunk ./telegram.py 431
- words_replaced = [repl_mentioned(elem, me_nick) for elem in words]
+ words_replaced = [repl_mentioned(elem, me_nick, received, mark, index, rargs) for elem in words]
patch eb5b15eba474680cab8764d0242229617d960e38
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Jun 15 01:15:43 CEST 2023
* irc: Add log for user registered (authorized in IRC)
hunk ./irc.py 143
- self.service_user = IRCUser(None, ('Services',), self.conf['service_user'],
+ self.service_user = IRCUser(None, ('Services',''), self.conf['service_user'],
hunk ./irc.py 430
+ self.logger.info('Registered IRC user "%s" from %s:%s', user.irc_nick, user.address, user.port)
+
hunk ./irc.py 645
+ self.port = str(address[1])
hunk ./telegram.py 148
- irc_user = IRCUser(None, ('Telegram',), tg_nick, user.id, self.get_telegram_display_name(user))
+ irc_user = IRCUser(None, ('Telegram',''), tg_nick, user.id, self.get_telegram_display_name(user))
patch 5da8c71bd90c689771ee2afddbb6f16a663c7c35
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Jun 14 00:43:05 CEST 2023
* emoji2emoticon: Add emoji "thinking face" to convert to ASCII
hunk ./emoji2emoticon.py 87
+ '\U0001f914': '":-L"',
patch 38a5f5ab57d1d819c40ed65f00e86c2cf6a9042f
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Jun 11 22:27:57 CEST 2023
* emoji2emoticon: Add emoji "shushing face" to convert to ASCII
hunk ./emoji2emoticon.py 87
+ '\U0001f92b': '":-o-m"',
patch 746b8dec8a7b53ef2750501c320aaa627d5ae7fe
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Jun 11 00:44:55 CEST 2023
* irc: Separate character encoding options as input and output
hunk ./irc.py 85
- message = message.decode(self.conf['char_encoding'], errors='replace').replace('\r','\n')
+ message = message.decode(self.conf['char_in_encoding'], errors='replace').replace('\r','\n')
hunk ./irc.py 150
- user.stream.write(command.encode(self.conf['char_encoding'], errors='replace'))
+ user.stream.write(command.encode(self.conf['char_out_encoding'], errors='replace'))
hunk ./irgramd 72
- tornado.options.define('char_encoding', default='utf-8', metavar='ENCODING', help='Character encoding for IRC')
+ tornado.options.define('char_in_encoding', default='utf-8', metavar='ENCODING', help='Character input encoding for IRC')
+ tornado.options.define('char_out_encoding', default='utf-8', metavar='ENCODING', help='Character output encoding for IRC')
patch 7300fb2b6fde17386971ea497343afae8526fb4c
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Jun 10 22:31:22 CEST 2023
* telegram, irc: Add conversion of mentions for self @username as well as
other mentions in self messages [saved messages]
hunk ./irc.py 471
+ # replace self @username and other mentions for self messages sent by this instance of irgramd
+ msg = self.tg.replace_mentions(msg, user.irc_nick)
+
hunk ./telegram.py 398
- def replace_mentions(self, text):
- def repl_mentioned(text):
+ def replace_mentions(self, text, me_nick=''):
+ def repl_mentioned(text, me_nick):
hunk ./telegram.py 402
+ if me_nick and part == self.tg_username:
+ return replacement(me_nick)
hunk ./telegram.py 405
- return '{}{}{}'.format('~', self.irc.users[part].irc_nick, '~')
+ return replacement(self.irc.users[part].irc_nick)
hunk ./telegram.py 408
+ def replacement(nick):
+ return '{}{}{}'.format('~',nick, '~')
+
hunk ./telegram.py 413
- words_replaced = [repl_mentioned(elem) for elem in words]
+ words_replaced = [repl_mentioned(elem, me_nick) for elem in words]
patch da6b06a9a974df34a72286bd8b93194e712490d5
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Jun 8 00:26:21 CEST 2023
* telegram: Refactor forward handle and related functions
Fix the use of saved_from_peer attribute and other improvements
hunk ./telegram.py 203
- def get_irc_nick_from_telegram_forward(self, fwd):
- if fwd.from_id is None:
+ async def get_irc_name_from_telegram_forward(self, fwd, saved):
+ from_id = fwd.saved_from_peer if saved else fwd.from_id
+ if from_id is None:
hunk ./telegram.py 207
- nick = fwd.from_name
+ # or was a broadcast from a channel (no user)
+ name = fwd.from_name
hunk ./telegram.py 210
- user = self.get_irc_user_from_telegram(fwd.from_id.user_id)
- if user is None:
- nick = '{}'
- self.refwd_me = True
+ peer_id, type = self.get_peer_id_and_type(from_id)
+ if type == 'user':
+ user = self.get_irc_user_from_telegram(peer_id)
+ if user is None:
+ name = '{}'
+ self.refwd_me = True
+ else:
+ name = user.irc_nick
hunk ./telegram.py 219
- nick = user.irc_nick
- return nick
+ try:
+ name = await self.get_irc_channel_from_telegram_id(peer_id)
+ except:
+ name = ''
+ return name
hunk ./telegram.py 330
+ def get_peer_id_and_type(self, peer):
+ if isinstance(peer, tgty.PeerChannel):
+ id = peer.channel_id
+ type = 'chan'
+ elif isinstance(peer, tgty.PeerChat):
+ id = peer.chat_id
+ type = 'chan'
+ elif isinstance(peer, tgty.PeerUser):
+ id = peer.user_id
+ type = 'user'
+ else:
+ id = peer
+ type = ''
+ return id, type
+
hunk ./telegram.py 617
- forwarded_nick = self.get_irc_nick_from_telegram_forward(message.fwd_from)
- forwarded_peer = message.fwd_from.saved_from_peer
- if isinstance(forwarded_peer, tgty.PeerChannel):
- dest = ' ' + await self.get_irc_channel_from_telegram_id(forwarded_peer.channel_id)
- elif isinstance(forwarded_peer, tgty.PeerChat):
- dest = ' ' + await self.get_irc_channel_from_telegram_id(forwarded_peer.chat_id)
+ space = space2 = ' '
+ if not (forwarded_peer_name := await self.get_irc_name_from_telegram_forward(message.fwd_from, saved=False)):
+ space = ''
+ saved_peer_name = await self.get_irc_name_from_telegram_forward(message.fwd_from, saved=True)
+ if saved_peer_name and saved_peer_name != forwarded_peer_name:
+ secondary_name = saved_peer_name
hunk ./telegram.py 626
- dest = ' ' + self.get_irc_user_from_telegram(forwarded_peer.user_id).irc_nick
+ secondary_name = self.get_irc_user_from_telegram(message.fwd_from.saved_from_peer.user_id).irc_nick
hunk ./telegram.py 628
- dest = ''
-
- return '|Fwd {}{}| '.format(forwarded_nick, dest)
+ secondary_name = ''
+ space2 = ''
+
+ return '|Fwd{}{}{}{}| '.format(space, forwarded_peer_name, space2, secondary_name)
patch d6b83b6ed9dfd2c1692e79da8b54046f96e059f5
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Jun 6 00:09:40 CEST 2023
* telegram: Add "media_dir" option to set the download media directory outside
of "config_dir" that didn't make sense, but keep it for compatibility if
"media_dir" is not set
hunk ./irgramd 81
+ tornado.options.define('media_dir', default=None, metavar='PATH', help='Directory where Telegram media files are downloaded, default "media" in `config_dir`')
hunk ./telegram.py 41
+ self.media_dir = settings['media_dir']
hunk ./telegram.py 71
- self.telegram_media_dir = os.path.join(self.config_dir, 'media')
+ self.telegram_media_dir = self.media_dir or os.path.join(self.config_dir, 'media')
patch 4c771fc4851d84b5ae451e1cd9280223c3c5b014
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Jun 1 19:55:14 CEST 2023
* telegram: Fix media contact attributes
hunk ./telegram.py 649
- if message.media.contact.first_name:
- media_url_or_data += message.media.contact.first_name + ' '
- if message.media.contact.last_name:
- media_url_or_data += message.media.contact.last_name + ' '
- if message.media.contact.phone_number:
- media_url_or_data += message.media.contact.phone_number
+ if message.media.first_name:
+ media_url_or_data += message.media.first_name + ' '
+ if message.media.last_name:
+ media_url_or_data += message.media.last_name + ' '
+ if message.media.phone_number:
+ media_url_or_data += message.media.phone_number
patch d147fb4ac2eb91490933ee7ba89ecffcde0255c1
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat May 20 23:39:03 CEST 2023
* telegram: Add filters for received messages, in these filters:
Include existing emoji to ASCII function
Add conversion of "@mention" to "~mention~" as "@user" is used to denote
channel operator in most IRC clients
hunk ./telegram.py 373
+ def replace_mentions(self, text):
+ def repl_mentioned(text):
+ if text and text[0] == '@':
+ part = text[1:].lower()
+ if part in self.irc.users:
+ return '{}{}{}'.format('~', self.irc.users[part].irc_nick, '~')
+ return text
+
+ if text.find('@') != -1:
+ words = text.split(' ')
+ words_replaced = [repl_mentioned(elem) for elem in words]
+ text_replaced = ' '.join(words_replaced)
+ else:
+ text_replaced = text
+ return text_replaced
+
+ def filters(self, text):
+ filtered = e.replace_mult(text, e.emo)
+ filtered = self.replace_mentions(filtered)
+ return filtered
+
hunk ./telegram.py 400
- message = e.replace_mult(event.message.message, e.emo)
+ message = self.filters(event.message.message)
hunk ./telegram.py 408
- t = e.replace_mult(self.cache[id]['text'], e.emo)
+ t = self.filters(self.cache[id]['text'])
hunk ./telegram.py 504
- final_text = e.replace_mult(final_text, e.emo)
+ final_text = self.filters(final_text)
patch 00e75e93c744727f1f30cb8e409d53b495c500ed
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue May 16 23:13:51 CEST 2023
* service: Refactor "get" command handle, make it robust when compact id has
not been initialized yet
hunk ./service.py 121
+ msg = None
hunk ./service.py 124
+ else: reply = ()
hunk ./service.py 127
- msg = await self.tg.telegram_client.get_messages(entity=peer_id, ids=id)
- if msg is None:
- reply = ('Message not found',)
- return reply
- await self.tg.handle_telegram_message(event=None, message=msg, history=True)
- reply = ()
+ if id is not None:
+ msg = await self.tg.telegram_client.get_messages(entity=peer_id, ids=id)
+ if msg is not None:
+ await self.tg.handle_telegram_message(event=None, message=msg, history=True)
+ else:
+ reply = ('Message not found',) [_$_]
patch 72bb23b64e2392ff01f7e060670e98093cfe0839
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon May 15 21:27:05 CEST 2023
* telegram: As messages are referenced by peer and id (in "get" and future
commands), make the compact IDs more compact with offsets relative to peers
(users or channels)
hunk ./irc.py 412
- mid = self.tg.mid.num_to_id_offset(tg_msg.id)
+ mid = self.tg.mid.num_to_id_offset(telegram_id, tg_msg.id)
hunk ./service.py 121
- id = self.tg.mid.id_to_num_offset(mid)
hunk ./service.py 124
+ id = self.tg.mid.id_to_num_offset(peer_id, mid)
hunk ./telegram.py 377
- mid = self.mid.num_to_id_offset(id)
+ mid = self.mid.num_to_id_offset(event.message.peer_id, id)
hunk ./telegram.py 441
- mid = self.mid.num_to_id_offset(deleted_id)
- text = 'Message id {} deleted not in cache'.format(mid)
+ text = 'Message id {} deleted not in cache'.format(deleted_id)
hunk ./telegram.py 458
- mid = self.mid.num_to_id_offset(msg.id)
+ mid = self.mid.num_to_id_offset(msg.peer_id, msg.id)
hunk ./telegram.py 557
- replied_msg = '[{}]'.format(self.mid.num_to_id_offset(replied.id))
+ replied_msg = '[{}]'.format(self.mid.num_to_id_offset(replied.peer_id, replied.id))
hunk ./telegram.py 740
- self.mesg_base = None
+ self.mesg_base = {}
hunk ./telegram.py 751
- def num_to_id_offset(self, num):
- if self.mesg_base is None:
- self.mesg_base = num
- return self.num_to_id(num - self.mesg_base)
+ def num_to_id_offset(self, peer, num):
+ peer_id = self.get_peer_id(peer)
+ if peer_id not in self.mesg_base:
+ self.mesg_base[peer_id] = num
+ return self.num_to_id(num - self.mesg_base[peer_id])
hunk ./telegram.py 766
- def id_to_num_offset(self, mid):
- if self.mesg_base is not None:
+ def id_to_num_offset(self, peer, mid):
+ peer_id = self.get_peer_id(peer)
+ if peer_id in self.mesg_base:
hunk ./telegram.py 770
- id = id_rel + self.mesg_base
+ id = id_rel + self.mesg_base[peer_id]
hunk ./telegram.py 773
+ return id
+
+ def get_peer_id(self, peer):
+ if isinstance(peer, tgty.PeerChannel):
+ id = peer.channel_id
+ elif isinstance(peer, tgty.PeerChat):
+ id = peer.chat_id
+ elif isinstance(peer, tgty.PeerUser):
+ id = peer.user_id
+ else:
+ id = peer
patch 3fe74b2337a9d66bddd47052220c413836cb05f9
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun May 14 00:39:42 CEST 2023
* service: Add "get" command to retrieve a specific message
hunk ./service.py 18
+ 'get': (self.handle_command_get, 2, 2),
hunk ./service.py 116
+ )
+ return reply
+
+ async def handle_command_get(self, peer=None, mid=None, help=None):
+ if not help:
+ id = self.tg.mid.id_to_num_offset(mid)
+ peer_id, reply = self.get_peer_id(peer.lower())
+ if reply: return reply
+
+ msg = await self.tg.telegram_client.get_messages(entity=peer_id, ids=id)
+ if msg is None:
+ reply = ('Message not found',)
+ return reply
+ await self.tg.handle_telegram_message(event=None, message=msg, history=True)
+ reply = ()
+ return reply
+
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' get Get a message by id and peer',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' get <peer> <compact_id>',
+ 'Get one message from peer with the compact ID',
hunk ./telegram.py 765
+
+ def id_to_num_offset(self, mid):
+ if self.mesg_base is not None:
+ id_rel = self.id_to_num(mid)
+ id = id_rel + self.mesg_base
+ else:
+ id = None
+ return id
patch 720468dce764675132030772fcb93f4bb5b00724
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon May 8 01:12:09 CEST 2023
* service: Add more indentation in brief help descriptions
hunk ./service.py 60
- reply = (' code Enter authorization code',)
+ reply = (' code Enter authorization code',)
hunk ./service.py 105
- reply = (' dialog Manage conversations (dialogs)',)
+ reply = (' dialog Manage conversations (dialogs)',)
hunk ./service.py 124
- help_text = (' help This help',)
+ help_text = (' help This help',)
hunk ./service.py 203
- reply = (' history Get messages from history',)
+ reply = (' history Get messages from history',)
hunk ./service.py 225
- reply = (' mark_read Mark messages as read',)
+ reply = (' mark_read Mark messages as read',)
patch 3ddecf1fe8d1534ab6af10d2b3602245541d4234
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon May 8 01:04:52 CEST 2023
* service: Add "mark_read" command to mark chats as read and reset mentions
hunk ./service.py 20
+ 'mark_read': (self.handle_command_mark_read, 1, 1),
hunk ./service.py 152
- def get_peer_id(tgt):
- if tgt in self.irc.users or tgt in self.irc.irc_channels:
- peer_id = self.tg.get_tid(tgt)
- reply = None
- else:
- peer_id = None
- reply = ('Unknown user or channel',)
- return peer_id, reply
-
hunk ./service.py 176
- peer_id, reply = get_peer_id(tgt)
+ peer_id, reply = self.get_peer_id(tgt)
hunk ./service.py 217
+ async def handle_command_mark_read(self, peer=None, help=None):
+ if not help:
+ peer_id, reply = self.get_peer_id(peer.lower())
+ if reply: return reply
+
+ await self.tg.telegram_client.send_read_acknowledge(peer_id, clear_mentions=True)
+ reply = ('',)
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' mark_read Mark messages as read',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' mark_read <peer>',
+ 'Mark all messages on <peer> (channel or user) as read, this also will',
+ 'reset the number of mentions to you on <peer>.'
+ )
+ return reply
+
+ def get_peer_id(self, tgt):
+ if tgt in self.irc.users or tgt in self.irc.irc_channels:
+ peer_id = self.tg.get_tid(tgt)
+ reply = None
+ else:
+ peer_id = None
+ reply = ('Unknown user or channel',)
+ return peer_id, reply
+
patch 940936aea4bf440b777cf726b9c88f21fc54f131
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun May 7 11:49:05 CEST 2023
* service: In dialog list: Use timezone option to convert the date for "last" field
hunk ./service.py 91
- last = compact_date(dialog.date)
+ last = compact_date(dialog.date, self.tg.timezone)
hunk ./utils.py 115
-def compact_date(date):
+def compact_date(date, tz):
hunk ./utils.py 117
+ date_local = date.astimezone(zoneinfo.ZoneInfo(tz))
hunk ./utils.py 120
- compact_date = date.strftime('%H:%M')
+ compact_date = date_local.strftime('%H:%M')
hunk ./utils.py 122
- compact_date = date.strftime('%d-%b')
+ compact_date = date_local.strftime('%d-%b')
hunk ./utils.py 124
- compact_date = date.strftime('%Y')
+ compact_date = date_local.strftime('%Y')
patch ecba573e04bccde171413d8064bd44c957952ad2
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun May 7 02:24:08 CEST 2023
* telegram: In highlight of editions, use dash (-) instead of dot (.) for
marking words deleted, that represents strikethrough.
Add some comments to the code
hunk ./utils.py 153
+ # deletion of words
hunk ./utils.py 155
- res += '.{}. '.format(i[2:])
+ res += '-{}- '.format(i[2:])
+ # addition of words
patch 5c25f44f55e071966feef397617ebfeea52dd88c
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun May 7 01:31:34 CEST 2023
* telegram: Add an option to enable and control format of timestamps for history messages
Add timezone option to convert timestamps
hunk ./README.md 54
-- [python] (>= v3.8)
+- [python] (>= v3.9)
hunk ./irgramd 76
+ tornado.options.define('hist_timestamp_format', metavar='DATETIME_FORMAT', help='Format string for timestamps in history, see https://www.strfti.me')
hunk ./irgramd 91
+ tornado.options.define('timezone', default='UTC', metavar='TIMEZONE', help='Timezone to use for dates (timestamps in history, last in dialogs, etc.)')
hunk ./service.py 206
- await self.tg.handle_telegram_message(event=None, message=msg)
+ await self.tg.handle_telegram_message(event=None, message=msg, history=True)
hunk ./telegram.py 25
-from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces
+from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces, format_timestamp
hunk ./telegram.py 51
+ self.hist_fmt = settings['hist_timestamp_format']
+ self.timezone = settings['timezone']
hunk ./telegram.py 453
- async def handle_telegram_message(self, event, message=None, upd_to_webpend=None):
+ async def handle_telegram_message(self, event, message=None, upd_to_webpend=None, history=False):
hunk ./telegram.py 461
- chan = await self.relay_telegram_message(msg, user, text)
+ text_send = self.set_history_timestamp(text, history, msg.date)
+ chan = await self.relay_telegram_message(msg, user, text_send)
hunk ./telegram.py 468
- async def render_text(self, message, mid, upd_to_webpend):
+ async def render_text(self, message, mid, upd_to_webpend, history=False):
hunk ./telegram.py 487
+ def set_history_timestamp(self, text, history, date):
+ if history and self.hist_fmt:
+ timestamp = format_timestamp(self.hist_fmt, self.timezone, date)
+ res = '{} {}'.format(timestamp, text)
+ else:
+ res = text
+ return res
+
hunk ./utils.py 14
+import zoneinfo
hunk ./utils.py 175
+
+def format_timestamp(format, tz, date):
+ date_local = date.astimezone(zoneinfo.ZoneInfo(tz))
+ return date_local.strftime(format)
patch 2f799847198dd8bd7070520f56803c063d74d524
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat May 6 21:04:28 CEST 2023
* telegram: Fix arguments in call to relay_telegram_message() in deleted handle
This should have been changed in patch "telegram: Change interface for received messages ..."
hunk ./telegram.py 437
- await self.relay_telegram_message(event=None, user=user, message=text, channel=chan)
+ await self.relay_telegram_message(message=None, user=user, text=text, channel=chan)
patch 18bfe4820f2f732b9af4d882c9e60191cf435256
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri May 5 00:23:04 CEST 2023
* telegram: Add support for retrieving history of messages, this is
implemented by service command "history"
hunk ./service.py 19
+ 'history': (self.handle_command_history, 1, 3),
hunk ./service.py 23
+ self.irc = telegram.irc
hunk ./service.py 149
+ async def handle_command_history(self, peer=None, limit='10', add_unread=None, help=None):
+ if not help:
+ def get_peer_id(tgt):
+ if tgt in self.irc.users or tgt in self.irc.irc_channels:
+ peer_id = self.tg.get_tid(tgt)
+ reply = None
+ else:
+ peer_id = None
+ reply = ('Unknown user or channel',)
+ return peer_id, reply
+
+ async def get_unread(tgt):
+ async for dialog in self.tg.telegram_client.iter_dialogs():
+ id, type = tgutils.resolve_id(dialog.id)
+ if id in self.tg.tid_to_iid.keys():
+ name = self.tg.tid_to_iid[id]
+ if tgt == name.lower():
+ count = dialog.unread_count
+ reply = None
+ break
+ else:
+ count = None
+ reply = ('Unknown unread',)
+ return count, reply
+
+ def conv_int(num_str):
+ if num_str.isdigit():
+ n = int(num_str)
+ err = None
+ else:
+ n = None
+ err = ('Invalid argument',)
+ return n, err
+
+ tgt = peer.lower()
+ peer_id, reply = get_peer_id(tgt)
+ if reply: return reply
+
+ if limit == 'unread':
+ add_unread = '0' if add_unread is None else add_unread
+ add_unread_int, reply = conv_int(add_unread)
+ if reply: return reply
+
+ li, reply = await get_unread(tgt)
+ if reply: return reply
+ li += add_unread_int
+ elif add_unread is not None:
+ reply = ('Wrong number of arguments',)
+ return reply
+ elif limit == 'all':
+ li = None
+ else:
+ li, reply = conv_int(limit)
+ if reply: return reply
+
+ his = await self.tg.telegram_client.get_messages(peer_id, limit=li)
+ for msg in reversed(his):
+ await self.tg.handle_telegram_message(event=None, message=msg)
+ reply = ()
+ return reply
+
+ else: # HELP.brief or HELP.desc (first line)
+ reply = (' history Get messages from history',)
+ if help == HELP.desc: # rest of HELP.desc
+ reply += \
+ (
+ ' history <peer> [<limit>|all|unread [<plusN>]]',
+ 'Get last <limit> number of messages already sent to <peer>',
+ '(channel or user). If not set <limit> is 10.',
+ 'Instead of <limit>, "unread" is for messages not marked as read,',
+ 'optionally <plusN> number of previous messages to the first unread.',
+ 'Instead of <limit>, "all" is for retrieving all available messages',
+ 'in <peer>.',
+ )
+ return reply
patch 5588befb5af1bef24d1309002c728e1cd2799fe6
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu May 4 22:04:15 CEST 2023
* telegram: Change event/message if statement by an expression
hunk ./telegram.py 454
- if event:
- msg = event.message
- else:
- msg = message
+ msg = event.message if event else message
patch 7fbbd73b77d52696b7d3ebdee564c07cea280f8a
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue May 2 23:37:17 CEST 2023
* telegram: Change interface for received messages, use message instead event
object if possible, this will be necessary to inject messages from future
history functionality
hunk ./telegram.py 378
- message_rendered = await self.render_text(event, mid, upd_to_webpend=None)
+ message_rendered = await self.render_text(event.message, mid, upd_to_webpend=None)
hunk ./telegram.py 447
- event = self.webpending.pop(update.webpage.id, None)
- if event:
- await self.handle_telegram_message(event, update.webpage)
-
- async def handle_telegram_message(self, event, upd_to_webpend=None):
- self.logger.debug('Handling Telegram Message: %s', event)
-
- user = self.get_irc_user_from_telegram(event.sender_id)
- mid = self.mid.num_to_id_offset(event.message.id)
-
- message = await self.render_text(event, mid, upd_to_webpend)
-
- chan = await self.relay_telegram_message(event, user, message)
-
- self.to_cache(event.message.id, mid, event.message.message, message, user, chan, event.message.media)
+ message = self.webpending.pop(update.webpage.id, None)
+ if message:
+ await self.handle_telegram_message(event=None, message=message, upd_to_webpend=update.webpage)
+
+ async def handle_telegram_message(self, event, message=None, upd_to_webpend=None):
+ self.logger.debug('Handling Telegram Message: %s', event or message)
+
+ if event:
+ msg = event.message
+ else:
+ msg = message
+
+ user = self.get_irc_user_from_telegram(msg.sender_id)
+ mid = self.mid.num_to_id_offset(msg.id)
+ text = await self.render_text(msg, mid, upd_to_webpend)
+ chan = await self.relay_telegram_message(msg, user, text)
+
+ self.to_cache(msg.id, mid, msg.message, text, user, chan, msg.media)
hunk ./telegram.py 468
- async def render_text(self, event, mid, upd_to_webpend):
+ async def render_text(self, message, mid, upd_to_webpend):
hunk ./telegram.py 470
- text = await self.handle_webpage(upd_to_webpend, event.message)
- elif event.message.media:
- text = await self.handle_telegram_media(event)
+ text = await self.handle_webpage(upd_to_webpend, message)
+ elif message.media:
+ text = await self.handle_telegram_media(message)
hunk ./telegram.py 474
- text = event.message.message
-
- if event.message.is_reply:
- refwd_text = await self.handle_telegram_reply(event)
- elif event.message.forward:
- refwd_text = await self.handle_telegram_forward(event)
+ text = message.message
+
+ if message.is_reply:
+ refwd_text = await self.handle_telegram_reply(message)
+ elif message.forward:
+ refwd_text = await self.handle_telegram_forward(message)
hunk ./telegram.py 483
- message = '[{}] {}{}'.format(mid, refwd_text, text)
- message = e.replace_mult(message, e.emo)
- return message
-
- async def relay_telegram_message(self, event, user, message, channel=None):
- private = (event and event.message.is_private) or (not event and not channel)
+ final_text = '[{}] {}{}'.format(mid, refwd_text, text)
+ final_text = e.replace_mult(final_text, e.emo)
+ return final_text
+
+ async def relay_telegram_message(self, message, user, text, channel=None):
+ private = (message and message.is_private) or (not message and not channel)
hunk ./telegram.py 490
- await self.relay_telegram_private_message(user, message)
+ await self.relay_telegram_private_message(user, text)
hunk ./telegram.py 493
- chan = await self.relay_telegram_channel_message(event, user, message, channel)
+ chan = await self.relay_telegram_channel_message(message, user, text, channel)
hunk ./telegram.py 501
- async def relay_telegram_channel_message(self, event, user, message, channel=None):
- self.logger.debug('Handling Telegram Channel Message: %s', event)
-
- if event:
- entity = await event.message.get_chat()
- chan = await self.get_irc_channel_from_telegram_id(event.message.chat_id, entity)
+ async def relay_telegram_channel_message(self, message, user, text, channel=None):
+ self.logger.debug('Handling Telegram Channel Message: %s', message or text)
+
+ if message:
+ entity = await message.get_chat()
+ chan = await self.get_irc_channel_from_telegram_id(message.chat_id, entity)
hunk ./telegram.py 509
- await self.irc.send_msg(user, chan, message)
+ await self.irc.send_msg(user, chan, text)
hunk ./telegram.py 545
- async def handle_telegram_reply(self, event):
+ async def handle_telegram_reply(self, message):
hunk ./telegram.py 547
- replied = await event.message.get_reply_message()
- message = replied.message
- if not message:
- message = '[{}]'.format(self.mid.num_to_id_offset(replied.id))
- elif len(message) > self.quote_len:
- message = message[:self.quote_len]
+ replied = await message.get_reply_message()
+ replied_msg = replied.message
+ if not replied_msg:
+ replied_msg = '[{}]'.format(self.mid.num_to_id_offset(replied.id))
+ elif len(replied_msg) > self.quote_len:
+ replied_msg = replied_msg[:self.quote_len]
hunk ./telegram.py 561
- return '|Re {}: {}{}| '.format(replied_nick, message, trunc)
-
- async def handle_telegram_forward(self, event):
- forwarded_nick = self.get_irc_nick_from_telegram_forward(event.message.fwd_from)
- forwarded_peer = event.forward.saved_from_peer
+ return '|Re {}: {}{}| '.format(replied_nick, replied_msg, trunc)
+
+ async def handle_telegram_forward(self, message):
+ forwarded_nick = self.get_irc_nick_from_telegram_forward(message.fwd_from)
+ forwarded_peer = message.fwd_from.saved_from_peer
hunk ./telegram.py 579
- async def handle_telegram_media(self, event):
- message = event.message
+ async def handle_telegram_media(self, message):
hunk ./telegram.py 593
- self.webpending[message.media.webpage.id] = event
+ self.webpending[message.media.webpage.id] = message
patch 0d2a17b54534226b14e45db9a6f5116bd2c3c41b
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 30 00:53:29 CEST 2023
* telegram: Make more robust the forward handler, take into account if the
user has a privacy option to only show the name
hunk ./telegram.py 200
+ def get_irc_nick_from_telegram_forward(self, fwd):
+ if fwd.from_id is None:
+ # telegram user has privacy options to show only the name
+ nick = fwd.from_name
+ else:
+ user = self.get_irc_user_from_telegram(fwd.from_id.user_id)
+ if user is None:
+ nick = '{}'
+ self.refwd_me = True
+ else:
+ nick = user.irc_nick
+ return nick
+
hunk ./telegram.py 560
- async def handle_telegram_forward(self, event): [_$_]
- forwarded_user = self.get_irc_user_from_telegram(event.forward.from_id.user_id)
- if forwarded_user is None:
- forwarded_nick = '{}'
- self.refwd_me = True
- else:
- forwarded_nick = forwarded_user.irc_nick
+ async def handle_telegram_forward(self, event):
+ forwarded_nick = self.get_irc_nick_from_telegram_forward(event.message.fwd_from)
patch 4f4de75d90e58e3985a7cbd8f6aad6ace683a406
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Apr 29 23:02:02 CEST 2023
* telegram: Add conversion of UTF-8 emojis to ASCII emoticons for all messages (not only reactions)
hunk ./emoji2emoticon.py 89
+
+def replace_mult(line, emo):
+ for utf_emo in emo:
+ if utf_emo in line:
+ line = line.replace(utf_emo, emo[utf_emo])
+ return line
hunk ./telegram.py 364
- message = event.message.message
+ message = e.replace_mult(event.message.message, e.emo)
hunk ./telegram.py 372
- t = self.cache[id]['text']
+ t = e.replace_mult(self.cache[id]['text'], e.emo)
hunk ./telegram.py 468
+ message = e.replace_mult(message, e.emo)
patch 0bfa5e100cf36ddf987dd40df98b4fdbaa90728f
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Apr 29 20:47:52 CEST 2023
* Add "emoji_ascii" option to control if emojis are converted to ASCII emoticons
hunk ./irgramd 75
+ tornado.options.define('emoji_ascii', default=False, help='Replace emoji with ASCII emoticons')
hunk ./telegram.py 26
-from emoji2emoticon import emo
+import emoji2emoticon as e
hunk ./telegram.py 51
+ if not settings['emoji_ascii']:
+ e.emo = {}
hunk ./telegram.py 402
- react_icon = emo[emoji] if emoji in emo else emoji
+ react_icon = e.emo[emoji] if emoji in e.emo else emoji
patch 22812451b9c8e65dc2d08c04bd4cbde65d8c9b23
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Apr 27 23:01:01 CEST 2023
* README update
hunk ./README.md 45
-- Deletes (receive)
+- Deletions (receive)
hunk ./README.md 52
+## Requirements
+
+- [python] (>= v3.8)
+- [telethon] (tested with v1.28.5)
+- [tornado] (tested with v6.1.0)
+- [aioconsole] (tested with v0.6.1)
+
hunk ./README.md 75
+[python]: https://www.python.org
+[tornado]: https://www.tornadoweb.org
+[aioconsole]: https://github.com/vxgmichel/aioconsole
patch e9b83df5a08d4ddb130b53ef26f389438e3dc75d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Apr 26 21:20:04 CEST 2023
* README update
hunk ./README.md 10
-**[irgramd primary repository] is in [darcs] version control system, [github
-repository] is synched as secondary**
-
-**irgramd is a fork from [pbui/irtelegramd] to resume the development**
-
-**irgramd is under active development in alpha state, several planned features
-are not implemented yet**
+**[irgramd primary repository] is in [darcs] version control system, github
+is used as [project management and secondary repository]**
+
+**irgramd was forked from [pbui/irtelegramd], was heavily modified and
+currently is a project on its own**
+
+**irgramd is under active development in alpha state, though usable, several
+planned features are not implemented yet**
hunk ./README.md 37
+## Features
+
+- Channels, groups and private chats
+- Users and channels mapped in IRC
+- Messages (receive, send)
+- Media in messages (receive, download)
+- Replies (receive)
+- Forwards (receive)
+- Deletes (receive)
+- Editions (receive)
+- Reactions (receive)
+- Dialogs management
+- Authentication and TLS for IRC
+- Multiple connections from IRC
+
hunk ./README.md 66
-[github repository]: https://github.com/prsai/irgramd
+[project management and secondary repository]: https://github.com/prsai/irgramd
patch 9fdc513df921e5f956b93feb4f061add2c55c548
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Apr 26 20:45:17 CEST 2023
* telegram: Rename option 'reply_length' as 'quote_length' and use it for quotes in reactions as well (not only for replies)
hunk ./irgramd 83
- tornado.options.define('reply_length', default=50, metavar='LENGTH', help='Max length of the text refered in replies, if longer is truncated')
+ tornado.options.define('quote_length', default=50, metavar='LENGTH', help='Max length of the text quoted in replies and reactions, if longer is truncated')
hunk ./telegram.py 25
-from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted
+from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted, fix_braces
hunk ./telegram.py 50
- self.reply_len = settings['reply_length']
+ self.quote_len = settings['quote_length']
hunk ./telegram.py 390
- if len(message_rendered) > 50:
- text_old = '{}...'.format(message_rendered[:50])
+ if len(message_rendered) > self.quote_len:
+ text_old = '{}...'.format(message_rendered[:self.quote_len])
+ text_old = fix_braces(text_old)
hunk ./telegram.py 532
- elif len(message) > self.reply_len:
- message = message[:self.reply_len]
+ elif len(message) > self.quote_len:
+ message = message[:self.quote_len]
hunk ./utils.py 166
+
+def fix_braces(text):
+ # Remove braces not closed, if the text was truncated
+ if text.endswith(' {...'):
+ subtext = text[:-5]
+ if not '{}' in subtext:
+ return '{}...'.format(subtext)
+ return text
patch 1d3ba8cce2f25104ebe89e202143ac27bfec87fa
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Apr 25 22:34:42 CEST 2023
* telegram: Add support for showing reactions to messages
Add conversion of UTF-8 emojis to ASCII emoticons
addfile ./emoji2emoticon.py
hunk ./emoji2emoticon.py 1
+# irgramd: IRC-Telegram gateway
+# emoji2emoticon.py: UTF-8 Emoji to ASCII emoticon replacement
+#
+# (C) Copyright 2019,2023 E. Bosch <presidev@AT@gmail.com>
+#
+# Use of this source code is governed by a MIT style license that
+# can be found in the LICENSE file included in this project.
+
+emo = {
+ '\U0000270c': '"V"',
+ '\U00002764': '"<3"',
+ '\U0001f389': '"<*``"',
+ '\U0001f44c': '"ok"',
+ '\U0001f44d': '"+1"',
+ '\U0001f44e': '"-1"',
+ '\U0001f44f': '"m_m"',
+ '\U0001f525': '"\^^^/"',
+ '\U0001f600': '":D"',
+ '\U0001f601': '"xD"',
+ '\U0001f602': '"x_D"',
+ '\U0001f603': '":D"',
+ '\U0001f604': '"xD"',
+ '\U0001f605': '"x`D"',
+ '\U0001f606': '"xD"',
+ '\U0001f607': '"O:)"',
+ '\U0001f608': '"}:)"',
+ '\U0001f609': '";)"',
+ '\U0001f60a': '"x)"',
+ '\U0001f60b': '"xP"',
+ '\U0001f60c': '":)"',
+ '\U0001f60d': '"E>)"',
+ '\U0001f60e': '"B)"',
+ '\U0001f60f': '"- -,"',
+ '\U0001f610': '":|"',
+ '\U0001f611': '":|"',
+ '\U0001f612': '"-. -."',
+ '\U0001f613': '":`|"',
+ '\U0001f614': '":|"',
+ '\U0001f615': '":/"',
+ '\U0001f616': '":S"',
+ '\U0001f617': '":*"',
+ '\U0001f618': '":**"',
+ '\U0001f619': '"x*"',
+ '\U0001f61a': '"x*"',
+ '\U0001f61b': '":P"',
+ '\U0001f61c': '";P"',
+ '\U0001f61d': '"xP"',
+ '\U0001f61e': '":("',
+ '\U0001f61f': '":(("',
+ '\U0001f620': '":("',
+ '\U0001f621': '":("',
+ '\U0001f622': '":_("',
+ '\U0001f623': '"x("',
+ '\U0001f624': '":<("',
+ '\U0001f625': '":`("',
+ '\U0001f626': '":(|"',
+ '\U0001f627': '":(||"',
+ '\U0001f628': '"||:("',
+ '\U0001f629': '"::("',
+ '\U0001f62a': '":`("',
+ '\U0001f62b': '"x("',
+ '\U0001f62c': '":E"',
+ '\U0001f62d': '":__(|"',
+ '\U0001f62e': '":O"',
+ '\U0001f62f': '":o"',
+ '\U0001f630': '":`O"',
+ '\U0001f631': '":O>"',
+ '\U0001f632': '"8-O"',
+ '\U0001f633': '":8|"',
+ '\U0001f634': '":.zz"',
+ '\U0001f635': '"6)"',
+ '\U0001f636': '":"',
+ '\U0001f637': '":W"',
+ '\U0001f638': '">:D"',
+ '\U0001f639': '":_D"',
+ '\U0001f63a': '">:D"',
+ '\U0001f63b': '">E>D"',
+ '\U0001f63c': '">- -,"',
+ '\U0001f63d': '">:*"',
+ '\U0001f63e': '">:("',
+ '\U0001f63f': '">:_("',
+ '\U0001f640': '">:(|"',
+ '\U0001f641': '":("',
+ '\U0001f642': '":)"',
+ '\U0001f643': '"(:"',
+ '\U0001f644': '"o o,"',
+ '\U0001f970': '":)e>"'
+ }
hunk ./irc.py 414
- self.tg.add_to_cache(tg_msg.id, mid, text, message, user, chan)
+ self.tg.to_cache(tg_msg.id, mid, text, message, user, chan, media=None)
hunk ./telegram.py 19
+from telethon.tl.functions.messages import GetMessagesReactionsRequest
hunk ./telegram.py 26
+from emoji2emoticon import emo
hunk ./telegram.py 316
- def add_to_cache(self, id, mid, message, proc_message, user, chan):
+ async def edition_case(self, msg):
+ def msg_edited(m):
+ return m.id in self.cache and \
+ ( m.message != self.cache[m.id]['text']
+ or m.media != self.cache[m.id]['media']
+ )
+ async def get_reactions(m):
+ react = await self.telegram_client(GetMessagesReactionsRequest(m.peer_id, id=[m.id]))
+ return react.updates[0].reactions.recent_reactions
+
+ react = None
+ if msg.reactions is None:
+ case = 'edition'
+ elif (reactions := await get_reactions(msg)) is None:
+ if msg_edited(msg):
+ case = 'edition'
+ else:
+ case = 'react-del'
+ elif react := next((x for x in reactions if x.date == msg.edit_date), None):
+ case = 'react-add'
+ else:
+ if msg_edited(msg):
+ case = 'edition'
+ else:
+ case = 'react-del'
+ react = None
+ return case, react
+
+ def to_cache(self, id, mid, message, proc_message, user, chan, media):
hunk ./telegram.py 352
- 'channel': chan
+ 'channel': chan,
+ 'media': media
hunk ./telegram.py 360
- user = self.get_irc_user_from_telegram(event.sender_id)
hunk ./telegram.py 365
- if id in self.cache:
- t = self.cache[id]['text']
- rt = self.cache[id]['rendered_text']
-
- ht, is_ht = get_highlighted(t, message)
-
- self.cache[id]['text'] = message
- self.cache[id]['rendered_text'] = message_rendered
+ edition_case, reaction = await self.edition_case(event.message)
+ if edition_case == 'edition':
+ action = 'Edited'
+ user = self.get_irc_user_from_telegram(event.sender_id)
+ if id in self.cache:
+ t = self.cache[id]['text']
+ rt = self.cache[id]['rendered_text']
+
+ ht, is_ht = get_highlighted(t, message)
+ else:
+ rt = fmid
+ is_ht = False
+
+ if is_ht:
+ edition_react = ht
+ text_old = fmid
+ else:
+ edition_react = message
+ text_old = rt
+ if user is None:
+ self.refwd_me = True
+
+ # Reactions
hunk ./telegram.py 389
- rt = fmid
- is_ht = False
-
- if is_ht:
- text_edited = ht
- text_old = fmid
- else:
- text_edited = message
- text_old = rt
- if user is None:
- self.refwd_me = True
-
- text = '|Edited {}| {}'.format(text_old, text_edited)
+ action = 'React'
+ if len(message_rendered) > 50:
+ text_old = '{}...'.format(message_rendered[:50])
+ else:
+ text_old = message_rendered
+
+ if edition_case == 'react-add':
+ user = self.get_irc_user_from_telegram(reaction.peer_id.user_id)
+ emoji = reaction.reaction.emoticon
+ react_action = '+'
+ react_icon = emo[emoji] if emoji in emo else emoji
+ elif edition_case == 'react-del':
+ user = self.get_irc_user_from_telegram(event.sender_id)
+ react_action = '-'
+ react_icon = ''
+ edition_react = '{}{}'.format(react_action, react_icon)
+
+ text = '|{} {}| {}'.format(action, text_old, edition_react)
+
hunk ./telegram.py 410
- if id not in self.cache:
- self.add_to_cache(id, mid, message, message_rendered, user, chan)
+ self.to_cache(id, mid, message, message_rendered, user, chan, event.message.media)
hunk ./telegram.py 445
- self.add_to_cache(event.message.id, mid, event.message.message, message, user, chan)
+ self.to_cache(event.message.id, mid, event.message.message, message, user, chan, event.message.media)
patch 5b3071c8f389341108b84aad6b91351a118cddb0
Author: E. Bosch <presidev@AT@gmail.com>
Date: Thu Apr 20 01:49:15 CEST 2023
* telegram: Relocate is_bot() method, to have get_* methods together
hunk ./telegram.py 246
- async def is_bot(self, irc_nick, tid=None):
- user = self.irc.users[irc_nick]
- if user.stream or user.is_service:
- bot = False
- else:
- bot = user.bot
- if bot == None:
- tid = self.get_tid(irc_nick, tid)
- tg_user = await self.telegram_client.get_entity(tid)
- bot = tg_user.bot
- user.bot = bot
- return bot
-
hunk ./telegram.py 301
+ async def is_bot(self, irc_nick, tid=None):
+ user = self.irc.users[irc_nick]
+ if user.stream or user.is_service:
+ bot = False
+ else:
+ bot = user.bot
+ if bot == None:
+ tid = self.get_tid(irc_nick, tid)
+ tg_user = await self.telegram_client.get_entity(tid)
+ bot = tg_user.bot
+ user.bot = bot
+ return bot
+
patch 515b30d8c49e9ec8619b0c0e3249d093e88a2a74
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 16 01:13:07 CEST 2023
* telegram: Add support for showing editions of messages, including highlight of differences
hunk ./irc.py 412
- text = '[{}] {}'.format(self.tg.mid.num_to_id_offset(tg_msg.id), message)
- self.tg.add_to_cache(tg_msg.id, None, text, user, chan)
+ mid = self.tg.mid.num_to_id_offset(tg_msg.id)
+ text = '[{}] {}'.format(mid, message)
+ self.tg.add_to_cache(tg_msg.id, mid, text, message, user, chan)
hunk ./telegram.py 24
-from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration
+from utils import sanitize_filename, is_url_equiv, extract_url, get_human_size, get_human_duration, get_highlighted
hunk ./telegram.py 90
+ (self.handle_telegram_edited, telethon.events.MessageEdited),
hunk ./telegram.py 314
- def add_to_cache(self, id, mid, message, user, chan):
+ def add_to_cache(self, id, mid, message, proc_message, user, chan):
hunk ./telegram.py 319
- 'rendered_text': message,
+ 'text': message,
+ 'rendered_text': proc_message,
hunk ./telegram.py 325
+ async def handle_telegram_edited(self, event):
+ self.logger.debug('Handling Telegram Message Edited: %s', event)
+
+ id = event.message.id
+ user = self.get_irc_user_from_telegram(event.sender_id)
+ mid = self.mid.num_to_id_offset(id)
+ fmid = '[{}]'.format(mid)
+ message = event.message.message
+ message_rendered = await self.render_text(event, mid, upd_to_webpend=None)
+
+ if id in self.cache:
+ t = self.cache[id]['text']
+ rt = self.cache[id]['rendered_text']
+
+ ht, is_ht = get_highlighted(t, message)
+
+ self.cache[id]['text'] = message
+ self.cache[id]['rendered_text'] = message_rendered
+ else:
+ rt = fmid
+ is_ht = False
+
+ if is_ht:
+ text_edited = ht
+ text_old = fmid
+ else:
+ text_edited = message
+ text_old = rt
+ if user is None:
+ self.refwd_me = True
+
+ text = '|Edited {}| {}'.format(text_old, text_edited)
+ chan = await self.relay_telegram_message(event, user, text)
+
+ if id not in self.cache:
+ self.add_to_cache(id, mid, message, message_rendered, user, chan)
+
hunk ./telegram.py 391
+ message = await self.render_text(event, mid, upd_to_webpend)
+
+ chan = await self.relay_telegram_message(event, user, message)
+
+ self.add_to_cache(event.message.id, mid, event.message.message, message, user, chan)
+
+ self.refwd_me = False
+
+ async def render_text(self, event, mid, upd_to_webpend):
hunk ./telegram.py 415
-
- chan = await self.relay_telegram_message(event, user, message)
-
- self.add_to_cache(event.message.id, mid, message, user, chan)
-
- self.refwd_me = False
+ return message
hunk ./utils.py 14
+import difflib
hunk ./utils.py 125
+
+def get_highlighted(a, b):
+ awl = len(a.split())
+ bwl = len(b.split())
+ delta_size = abs(awl - bwl)
+ highlighted = True
+
+ if not a:
+ res = '> {}'.format(b)
+ elif delta_size > 5:
+ res = b
+ highlighted = False
+ else:
+ al = a.split(' ')
+ bl = b.split(' ')
+ diff = difflib.ndiff(al, bl)
+ ld = list(diff)
+ res = ''
+ d = ''
+ eq = 0
+
+ for i in ld:
+ if i == '- ' or i[0] == '?':
+ continue
+ elif i == ' ' or i == '+ ':
+ res += ' '
+ continue
+ elif i[0] == '-':
+ res += '.{}. '.format(i[2:])
+ elif i[0] == '+':
+ res += '_{}_ '.format(i[2:])
+ else:
+ res += '{} '.format(i[2:])
+ eq += 1
+
+ delta_eq = bwl - eq
+ if delta_eq > 3:
+ res = b
+ highlighted = False
+
+ return res, highlighted
patch 7f549d8b7544443878875c310fea6f1fb6385364
Author: E. Bosch <presidev@AT@gmail.com>
Date: Wed Apr 12 00:43:22 CEST 2023
* telegram: Fix recognition of Telegram user, group, channel types
hunk ./service.py 79
- ' {:<11} {:<9} {:<9} {:4} {:<3} {:<4} {:<6} {}'.format(
+ ' {:<11} {:<9} {:<9} {:5} {:<3} {:<4} {:<6} {}'.format(
hunk ./service.py 86
- ty = 'User' if dialog.is_user else 'Chat' if dialog.is_group else 'Chan'
+ ty = self.tg.get_entity_type(dialog.entity, format='short')
hunk ./service.py 97
- reply += (' {:<11d} {:<9d} {:<9d} {:4} {:<3} {:<4} {:<6} {}'.format(
+ reply += (' {:<11d} {:<9d} {:<9d} {:5} {:<3} {:<4} {:<6} {}'.format(
hunk ./telegram.py 266
- entity_type = self.get_entity_type(entity)
+ entity_type = self.get_entity_type(entity, format='long')
hunk ./telegram.py 294
- def get_entity_type(self, entity):
- return type(entity).__name__
+ def get_entity_type(self, entity, format):
+ if isinstance(entity, tgty.User):
+ short = long = 'User'
+ elif isinstance(entity, tgty.Chat):
+ short = 'Chat'
+ long = 'Chat/Basic Group'
+ elif isinstance(entity, tgty.Channel):
+ if entity.broadcast:
+ short = 'Broad'
+ long = 'Broadcast Channel'
+ elif entity.megagroup:
+ short = 'Mega'
+ long = 'Super/Megagroup Channel'
+ elif entity.gigagroup:
+ short = 'Giga'
+ long = 'Broadcast Gigagroup Channel'
+
+ return short if format == 'short' else long
patch e664cfbb6157d6f9eab1db5d836213317fe1c2e5
Author: E. Bosch <presidev@AT@gmail.com>
Date: Tue Apr 11 09:45:58 CEST 2023
* Add compact message ids in messages sent from IRC that are echoed to IRC,
add those messages to cache
hunk ./irc.py 387
+ defered_send = None
hunk ./irc.py 390
+ # used defered_send function when id is known
hunk ./irc.py 392
- await self.send_msg_others(user, tgl, message)
+ chan = tgl
+ defered_send = self.send_msg_others
+ defered_target = chan
+ else:
+ chan = None
hunk ./irc.py 402
- await self.send_msg(user, None, message)
+ # used defered_send function when id is known
+ defered_send = self.send_msg
+ defered_target = None
hunk ./irc.py 410
- await self.tg.telegram_client.send_message(telegram_id, message)
+ tg_msg = await self.tg.telegram_client.send_message(telegram_id, message)
+
+ text = '[{}] {}'.format(self.tg.mid.num_to_id_offset(tg_msg.id), message)
+ self.tg.add_to_cache(tg_msg.id, None, text, user, chan)
+
+ if defered_send:
+ await defered_send(user, defered_target, text)
patch cee19ae7322cdc3235ad93d1666e9d44669ed16d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Mon Apr 10 23:31:19 CEST 2023
* telegram: Add support for showing deleted messages
hunk ./telegram.py 89
+ (self.handle_telegram_deleted , telethon.events.MessageDeleted),
hunk ./telegram.py 307
+ async def handle_telegram_deleted(self, event):
+ self.logger.debug('Handling Telegram Message Deleted: %s', event)
+
+ for deleted_id in event.original_update.messages:
+ if deleted_id in self.cache:
+ recovered_text = self.cache[deleted_id]['rendered_text']
+ text = '|Deleted| {}'.format(recovered_text)
+ user = self.cache[deleted_id]['user']
+ chan = self.cache[deleted_id]['channel']
+ await self.relay_telegram_message(event=None, user=user, message=text, channel=chan)
+ else:
+ mid = self.mid.num_to_id_offset(deleted_id)
+ text = 'Message id {} deleted not in cache'.format(mid)
+ await self.relay_telegram_private_message(self.irc.service_user, text)
+
hunk ./telegram.py 333
- if self.mid.mesg_base is None:
- self.mid.mesg_base = event.message.id
-
hunk ./telegram.py 334
- mid = self.mid.num_to_id(event.message.id - self.mid.mesg_base)
+ mid = self.mid.num_to_id_offset(event.message.id)
hunk ./telegram.py 421
- message = '[{}]'.format(self.mid.num_to_id(replied.id - self.mid.mesg_base))
+ message = '[{}]'.format(self.mid.num_to_id_offset(replied.id))
hunk ./telegram.py 621
+ def num_to_id_offset(self, num):
+ if self.mesg_base is None:
+ self.mesg_base = num
+ return self.num_to_id(num - self.mesg_base)
+
patch d4f07b39c63d6cfefbf3f3c7bd91fac6969ce39d
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sun Apr 9 01:07:35 CEST 2023
* telegram: Refactorize relay message functions, this will be necessary for deleted message support
hunk ./telegram.py 339
- if event.message.is_private:
+ chan = await self.relay_telegram_message(event, user, message)
+
+ self.add_to_cache(event.message.id, mid, message, user, chan)
+
+ self.refwd_me = False
+
+ async def relay_telegram_message(self, event, user, message, channel=None):
+ private = (event and event.message.is_private) or (not event and not channel)
+ if private:
hunk ./telegram.py 351
- chan = await self.relay_telegram_channel_message(event, user, message)
-
- self.add_to_cache(event.message.id, mid, message, user, chan)
-
- self.refwd_me = False
+ chan = await self.relay_telegram_channel_message(event, user, message, channel)
+ return chan
hunk ./telegram.py 359
- async def relay_telegram_channel_message(self, event, user, message):
+ async def relay_telegram_channel_message(self, event, user, message, channel=None):
hunk ./telegram.py 362
- entity = await event.message.get_chat()
- channel = await self.get_irc_channel_from_telegram_id(event.message.chat_id, entity)
- await self.irc.send_msg(user, channel, message)
- return channel
+ if event:
+ entity = await event.message.get_chat()
+ chan = await self.get_irc_channel_from_telegram_id(event.message.chat_id, entity)
+ else:
+ chan = channel
+ await self.irc.send_msg(user, chan, message)
+ return chan
patch 1a0d9f40779a40ea3c7e9967b71d8142ceba0081
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Apr 8 20:56:09 CEST 2023
* telegram: Rename some functions: use "relay" instead of "handle", to clarify
hunk ./telegram.py 340
- await self.handle_telegram_private_message(user, message)
+ await self.relay_telegram_private_message(user, message)
hunk ./telegram.py 343
- chan = await self.handle_telegram_channel_message(event, user, message)
+ chan = await self.relay_telegram_channel_message(event, user, message)
hunk ./telegram.py 349
- async def handle_telegram_private_message(self, user, message):
+ async def relay_telegram_private_message(self, user, message):
hunk ./telegram.py 354
- async def handle_telegram_channel_message(self, event, user, message):
+ async def relay_telegram_channel_message(self, event, user, message):
patch 677c32d4755b0de6d30000202b99de435ca742d3
Author: E. Bosch <presidev@AT@gmail.com>
Date: Sat Apr 8 12:30:10 CEST 2023
* telegram: Add message cache
hunk ./telegram.py 16
+import collections
hunk ./telegram.py 58
+ self.cache = collections.OrderedDict()
hunk ./telegram.py 296
+ def add_to_cache(self, id, mid, message, user, chan):
+ if len(self.cache) >= 10000:
+ self.cache.popitem(last=False)
+ self.cache[id] = {
+ 'mid': mid,
+ 'rendered_text': message,
+ 'user': user,
+ 'channel': chan
+ }
+
hunk ./telegram.py 341
+ chan = None
hunk ./telegram.py 343
- await self.handle_telegram_channel_message(event, user, message)
+ chan = await self.handle_telegram_channel_message(event, user, message)
+
+ self.add_to_cache(event.message.id, mid, message, user, chan)
hunk ./telegram.py 360
+ return channel
patch 69fd83158f7bf48b9ab8ffc3cf18d7938cbe2967
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Mar 31 22:46:58 CEST 2023
* telegram: Add logging in handle_raw()
hunk ./telegram.py 295
+ self.logger.debug('Handling Telegram Raw Event: %s', update)
+
patch d7fd113735674f001a113114fd8562b160458b86
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Mar 31 00:32:55 CEST 2023
* Remove obsolete dockerfile
hunk ./Dockerfile 1
-FROM alpine:latest
-MAINTAINER Peter Bui <pbui@yld.bx612.space>
-
-RUN apk update && \
- apk add python3 py3-pip
-
-RUN pip3 install telethon tornado==5.1.1
-
-RUN wget -O - https://gitlab.com/pbui/irtelegramd/-/archive/master/irtelegramd-master.tar.gz | tar xzvf -
-
-COPY irtelegramd.py /irtelegramd-master
-
-EXPOSE 6667
-ENTRYPOINT ["/irtelegramd-master/irtelegramd.py", "--address=0.0.0.0", "--config_dir=/var/lib/irtelegramd"]
rmfile ./Dockerfile
patch e1aef39b9e3f2cbe017b3c1ea324ccc9c330a73a
Author: E. Bosch <presidev@AT@gmail.com>
Date: Fri Mar 31 00:31:39 CEST 2023
* Update copyright year in license
hunk ./LICENSE 4
-Copyright (c) 2020-2022 E. Bosch <presidev@AT@gmail.com>
+Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>
hunk ./README.md 39
-Copyright (c) 2020-2022 E. Bosch <presidev@AT@gmail.com>
+Copyright (c) 2020-2023 E. Bosch <presidev@AT@gmail.com>