Skip to content

Commit

Permalink
Comments counter
Browse files Browse the repository at this point in the history
  • Loading branch information
lavadk committed Apr 5, 2021
1 parent 3832f38 commit e96b09a
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 26 deletions.
110 changes: 87 additions & 23 deletions liker/button/comment_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from liker.state.space_state import SpaceState
from liker.button import markup_utils
from liker.button.markup_synchronizer import MarkupSynchronizer
from liker.setup import constants

logger = logging.getLogger(__file__)

Expand All @@ -25,12 +26,9 @@ def message(self, message: types.Message) -> bool:
if self._check_forward_from_channel(message):
return True

# TODO: handle replies
# In replies to messages
# reply_to_message.forward_from_chat.id = ? -1001380133596
# reply_to_message.forward_from_message_id = 99
# print('Point #1')
# print(message)
if self._check_reply_to_channel_post(message):
return True

return False

def _check_forward_from_channel(self, message: types.Message) -> bool:
Expand All @@ -43,31 +41,97 @@ def _check_forward_from_channel(self, message: types.Message) -> bool:
return False

enabled_channel_ids = self.enabled_chats.enabled_channel_ids()
from_channel_id = message.forward_from_chat.id
if from_channel_id not in enabled_channel_ids:
channel_id = message.forward_from_chat.id
if channel_id not in enabled_channel_ids:
logger.debug('Check forward: ignoring message as it is not from enabled channel')
return False

from_message_id = message.forward_from_message_id
channel_message_id = message.forward_from_message_id
thread_message_id = message.message_id

channel_state = self.space_state.ensure_channel_state(str(from_channel_id))
reply_markup_str = channel_state.markup_queue.try_get(str(from_message_id))
if reply_markup_str is None:
reply_markup_str = channel_state.markup_trail.try_get(str(from_message_id))
if reply_markup_str is None:
logger.error(f'Was not able to get cached reply markup for {from_channel_id}')
reply_markup = self._try_find_reply_markup(channel_id=channel_id, message_id=channel_message_id)
if reply_markup is None:
logger.error(f'Was not able to get cached reply markup for {channel_id}')
return True

group_id = message.chat.id
short_group_id = telegram_utils.get_short_chat_id(group_id)
comments_url = f'https://t.me/c/{short_group_id}/999999999?thread={thread_message_id}'
reply_markup = InlineKeyboardMarkup.de_json(reply_markup_str)
reply_markup = markup_utils.add_url_button_to_markup(reply_markup=reply_markup,
text='💬',
url=comments_url)
self.markup_synchronizer.add(channel_id=from_channel_id,
message_id=from_message_id,
reply_markup = self._ensure_comment_button(reply_markup=reply_markup,
group_id=group_id,
thread_message_id=thread_message_id)
self.markup_synchronizer.add(channel_id=channel_id,
message_id=channel_message_id,
reply_markup=reply_markup,
to_top=True)
return True

def _check_reply_to_channel_post(self, message: types.Message) -> bool:
ref_message: types.Message = message.reply_to_message
if ref_message is None:
return False

group_id = message.chat.id
if ref_message.forward_from_chat is not None:
channel_id = ref_message.forward_from_chat.id
channel_message_id = ref_message.forward_from_message_id
thread_message_id = ref_message.message_id
else:
channel_id = self.enabled_chats.try_get_channel_id_for_linked_chat_id(group_id)
if channel_id is None:
return False
channel_state = self.space_state.ensure_channel_state(str(channel_id))
ref_message_trail = channel_state.comment_trail.try_get(str_message_id=str(ref_message.message_id))
if ref_message_trail is None:
return False
channel_message_id = ref_message_trail['channel_message_id']
thread_message_id = ref_message_trail['thread_message_id']

enabled_channel_ids = self.enabled_chats.enabled_channel_ids()
if channel_id not in enabled_channel_ids:
return False

reply_markup = self._try_find_reply_markup(channel_id=channel_id, message_id=channel_message_id)
if reply_markup is None:
logger.warning(f'Ignoring a reply message as there is not cached reply markup: channel {channel_id}, '
f'channel message {channel_message_id}')
return True

reply_markup = self._ensure_comment_button(reply_markup=reply_markup,
group_id=group_id,
thread_message_id=thread_message_id)
markup_utils.change_reaction_counter(reply_markup,
reaction=constants.COMMENT_TEXT,
delta=1)
self.markup_synchronizer.add(channel_id=channel_id,
message_id=channel_message_id,
reply_markup=reply_markup,
to_top=True)

comment_message_id = message.message_id
comment_dict = {
'channel_message_id': channel_message_id,
'thread_message_id': thread_message_id,
}
channel_state = self.space_state.ensure_channel_state(str(channel_id))
channel_state.comment_trail.add(str_message_id=str(comment_message_id),
comment_dict=comment_dict)
return True

@staticmethod
def _ensure_comment_button(reply_markup: InlineKeyboardMarkup,
group_id: int,
thread_message_id: int) -> InlineKeyboardMarkup:
if not markup_utils.markup_has_button(reply_markup, constants.COMMENT_TEXT):
short_group_id = telegram_utils.get_short_chat_id(group_id)
comments_url = f'https://t.me/c/{short_group_id}/999999999?thread={thread_message_id}'
reply_markup = markup_utils.add_url_button_to_markup(reply_markup=reply_markup,
text=constants.COMMENT_TEXT,
url=comments_url)
return reply_markup

def _try_find_reply_markup(self, channel_id, message_id):
channel_state = self.space_state.ensure_channel_state(str(channel_id))
reply_markup_str = channel_state.markup_queue.try_get(str(message_id))
if reply_markup_str is None:
reply_markup_str = channel_state.markup_trail.try_get(str(message_id))
result = None if (reply_markup_str is None) else InlineKeyboardMarkup.de_json(reply_markup_str)
return result
14 changes: 11 additions & 3 deletions liker/button/markup_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ def build_reply_markup(enabled_reactions: list,

def change_reaction_counter(reply_markup: types.InlineKeyboardMarkup, reaction: str, delta: int):
for btn in iterate_markup_buttons(reply_markup):
_handler, _case_id, button_response = telegram_utils.decode_button_data(btn.callback_data)

if reaction == button_response:
if reaction in btn.text:
num_str = btn.text.replace(reaction, '').strip()

num = _num_str_to_number(num_str)
Expand Down Expand Up @@ -92,3 +90,13 @@ def add_url_button_to_markup(reply_markup: types.InlineKeyboardMarkup,
new_b = types.InlineKeyboardButton(text, url)
new_buttons = list(iterate_markup_buttons(reply_markup)) + [new_b]
return markup_from_buttons(new_buttons)


def markup_has_button(reply_markup: types.InlineKeyboardMarkup, text: str):
result = False
for btn in iterate_markup_buttons(reply_markup):
result = text in btn.text
if result:
break
return result

2 changes: 2 additions & 0 deletions liker/setup/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

BUTTON_HANDLER = 'lik'

COMMENT_TEXT = '💬'

REACTION_HASH_BYTES = 4

ABUSE_PERIOD_SECONDS = 600
Expand Down
2 changes: 2 additions & 0 deletions liker/state/channel_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from liker.state.reaction_hashes import ReactionHashes
from liker.state.markup_queue import MarkupQueue
from liker.state.markup_trail import MarkupTrail
from liker.state.comment_trail import CommentTrail


class ChannelState(Preserver):
Expand All @@ -16,3 +17,4 @@ def __init__(self, state_dir: Path, str_channel_id: str):

self.markup_queue = MarkupQueue(self.state)
self.markup_trail = MarkupTrail(self.state)
self.comment_trail = CommentTrail(self.state)
40 changes: 40 additions & 0 deletions liker/state/comment_trail.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import logging
import inject
from jsonstore import JsonStore
from typing import Optional
from typeguard import typechecked
from tengine import Config

logger = logging.getLogger(__file__)


class CommentTrail:
config = inject.attr(Config)

def __init__(self, state: JsonStore):
self.state = state

def ensure_trail(self):
if 'comment_trail' not in self.state:
self.state['comment_trail'] = {}
return self.state['comment_trail']

def update_trail(self, trail):
self.state['comment_trail'] = trail

@typechecked
def add(self, str_message_id: str, comment_dict: dict):
trail = self.ensure_trail()
if str_message_id in trail:
del trail[str_message_id]
trail_max_len = self.config['comment_trail']
trail_items = [(str_message_id, comment_dict)] + list(trail.items())
trail_items = trail_items[:trail_max_len]
trail = dict(trail_items)
self.update_trail(trail)

@typechecked
def try_get(self, str_message_id: str) -> Optional[dict]:
trail = self.ensure_trail()
comment_dict = trail.get(str_message_id, None)
return comment_dict
9 changes: 9 additions & 0 deletions liker/state/enabled_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,12 @@ def enabled_channel_ids(self) -> List[int]:
str_arr = self.state.__dict__['_data'].keys()
arr = [int(x) for x in str_arr]
return arr

def try_get_channel_id_for_linked_chat_id(self, linked_chat_id) -> Optional[int]:
result = None
for ch_id in self.enabled_channel_ids():
ch_dict = self.get_channel_dict(str(ch_id))
if ch_dict['linked_chat_id'] == linked_chat_id:
result = ch_id
break
return result

0 comments on commit e96b09a

Please sign in to comment.