Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion backend/community_manager/handlers/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ async def handle_chat_action(event: events.ChatAction.Event):
if not telegram_chat_service.check_exists(event.chat_id):
logger.debug(
"Chat doesn't exist, but bot was not added to the chat: %d. Skipping event...",
event.chat_id,
)
return

Expand All @@ -43,7 +44,9 @@ async def handle_chat_action(event: events.ChatAction.Event):
session, telethon_client=event.client
)
logo_path = await telegram_chat_action.fetch_and_push_profile_photo(
event.chat
event.chat,
# We definitely know here that the new photo was set - no need to fetch the current value
current_logo_path=None,
)
if logo_path:
logger.debug(
Expand Down
34 changes: 25 additions & 9 deletions backend/core/actions/chat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,9 @@ async def _load_participants(self, chat_identifier: int) -> None:
last_name=participant_user.last_name,
username=participant_user.username,
is_premium=participant_user.premium or False,
language_code=participant_user.lang_code
or core_settings.default_language,
language_code=(
participant_user.lang_code or core_settings.default_language
),
)
)
self.telegram_chat_user_service.create_or_update(
Expand All @@ -192,26 +193,33 @@ async def index(self, chat: ChatPeerType) -> None:
logger.info(f"Creating a new chat invite link for the chat {chat_id!r}...")
invite_link = await self.telethon_service.get_invite_link(chat)
self.telegram_chat_service.refresh_invite_link(chat_id, invite_link.link)
logger.info(f"Chat {chat_id!r} created successfully")
await self._load_participants(telegram_chat.id)

async def fetch_and_push_profile_photo(self, chat: ChatPeerType) -> Path | None:
async def fetch_and_push_profile_photo(
self,
chat: ChatPeerType,
current_logo_path: str | None,
) -> Path | None:
"""
Fetches the profile photo of a chat and uploads it for hosting. This function
handles the download of the profile photo from the given chat and then pushes
it to a CDN service for further access. If the profile photo exists, it will
be returned as a Path object; otherwise, None is returned.

:param chat: The chat from which the profile photo is to be fetched.
:param current_logo_path: The current logo path in the database.
:return: The local path of the fetched profile photo or None
"""
logo_path = await self.telethon_service.download_profile_photo(chat)
logo_path = await self.telethon_service.download_profile_photo(
entity=chat,
current_logo_path=current_logo_path,
)
if logo_path:
await self.cdn_service.upload_file(
file_path=logo_path,
object_name=logo_path.name,
)
logger.info(f"Profile photo for chat {chat.id!r} uploaded")
logger.info(f"New profile photo for chat {chat.id!r} uploaded")
return logo_path

async def _create(
Expand All @@ -230,7 +238,9 @@ async def _create(
:return: A DTO containing the details of the created Telegram chat.
:raises TelegramChatAlreadyExists: If the chat already exists in the database.
"""
logo_path = await self.fetch_and_push_profile_photo(chat)
logo_path = await self.fetch_and_push_profile_photo(
chat, current_logo_path=None
)
try:
chat_id = get_peer_id(chat, add_mark=True)
telegram_chat = self.telegram_chat_service.create(
Expand Down Expand Up @@ -325,7 +335,7 @@ async def _refresh(self, chat: TelegramChat) -> TelegramChat:
:raises TelegramChatNotSufficientPrivileges: If the bot lacks functionality privileges within the chat
"""
try:
chat_entity, logo_path = await self._get_chat_data(chat.id)
chat_entity = await self._get_chat_data(chat.id)

except (
TelegramChatNotSufficientPrivileges, # happens when bot has no rights to function in the chat
Expand All @@ -343,10 +353,16 @@ async def _refresh(self, chat: TelegramChat) -> TelegramChat:
self.telegram_chat_service.delete(chat_id=chat.id)
raise

logo_path = await self.fetch_and_push_profile_photo(
chat_entity, current_logo_path=chat.logo_path
)

chat = self.telegram_chat_service.update(
chat=chat,
entity=chat_entity,
logo_path=logo_path.name,
# If a new logo was downloaded - use it,
# otherwise fallback to the current one
logo_path=logo_path.name if logo_path else chat.logo_path,
)
await self.index(chat_entity)
logger.info(f"Chat {chat.id!r} refreshed successfully")
Expand Down
4 changes: 2 additions & 2 deletions backend/core/dtos/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ class UserInitDataPO(BaseModel):

class TelegramUserDTO(BaseModel):
id: int
first_name: str
first_name: str = ""
last_name: str | None = None
username: str | None = None
is_premium: bool
is_premium: bool = False
language_code: str
photo_url: str | None = None
allow_write_to_pm: bool = True
Expand Down
23 changes: 15 additions & 8 deletions backend/core/services/supertelethon.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import datetime
import logging
from pathlib import Path
from typing import AsyncGenerator
Expand Down Expand Up @@ -94,23 +93,31 @@ async def revoke_chat_invite(self, chat_id: int, link: str) -> None:
)
)

async def download_profile_photo(self, entity: Channel) -> Path | None:
async def download_profile_photo(
self,
entity: Channel,
current_logo_path: str | None = None,
) -> Path | None:
if not entity.photo or isinstance(entity.photo, ChatPhotoEmpty):
logger.debug(f"Chat {entity.id!r} does not have a logo. Skipping")
return None

# Adding timestamp allows to bypass the cache of the image to reflect the change
logo_path = f"{entity.id}-{int(datetime.datetime.now().timestamp())}.jpg"
# Adding timestamp allows bypassing the cache of the image to reflect the change
new_file_name = f"{entity.photo.photo_id}.png"

if current_logo_path and current_logo_path == new_file_name:
logger.debug(f"Logo for chat {entity.id} is up-to-date. Skipping download.")
return None

with open(CHAT_LOGO_PATH / logo_path, "wb") as f:
with open(CHAT_LOGO_PATH / new_file_name, "wb") as f:
await self.client.download_profile_photo(entity, f)

# To avoid redundant files, we clean the old versions of the logo
# To avoid redundant files, we remove the previous file
clean_old_versions(
path=CHAT_LOGO_PATH, prefix=f"{entity.id}-", current_file=logo_path
path=CHAT_LOGO_PATH, prefix=current_logo_path, current_file=new_file_name
)

return CHAT_LOGO_PATH / logo_path
return CHAT_LOGO_PATH / new_file_name

async def promote_user(
self, chat_id: int, telegram_user_id: int, custom_title: str
Expand Down
2 changes: 0 additions & 2 deletions backend/core/utils/custom_rules/telegram_usernames.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ def _inner(nfts: list[NftItem]) -> list[NftItem]:

telegram_username = TelegramUsername(nft.blockchain_metadata.name)

print(telegram_username.username, target_length, len(telegram_username))

if len(telegram_username) <= target_length:
valid_nfts.append(nft)

Expand Down