Esempio n. 1
0
async def zip(event, session):
    """Create 1.5GB zips with all files collectd in this chat."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type,
                                          event.message)

    chat_path = get_chat_path(subscriber.chat_name)
    if not os.path.exists(chat_path):
        return "No files for this chat yet."

    zip_dir = init_zip_dir(subscriber.chat_name)

    text = f"Zipping started, this might take some time. Please don't issue this command again until I'm finished."
    await event.respond(text)

    create_zips(subscriber.chat_name, zip_dir, chat_path)

    text = "Zipping is completed. I'll now start uploading."
    await event.respond(text)

    for zip_file in os.listdir(zip_dir):
        zip_file_path = os.path.join(zip_dir, zip_file)
        await archive.send_file(event.message.to_id, zip_file_path)

    shutil.rmtree(zip_dir)

    return "All files are uploaded :)"
Esempio n. 2
0
async def stop(event, session):
    """Stop the bot."""
    to_id, to_type = get_peer_information(event.message.to_id)

    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)
    subscriber.active = False
    session.add(subscriber)

    return "Files won't be archived any longer."
Esempio n. 3
0
async def start(event, session):
    """Start the bot."""
    to_id, to_type = get_peer_information(event.message.to_id)

    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)
    subscriber.active = True
    session.add(subscriber)

    return 'Files posted in this chat will now be archived.'
Esempio n. 4
0
async def process_message(session,
                          subscriber,
                          message,
                          event,
                          full_scan=False):
    """Process a single message. Check if it has a file we want to download."""
    to_id, to_type = get_peer_information(message.to_id)

    try:
        # If this message is forwarded, get the original sender.
        if message.forward and message.forward.sender_id is not None:
            user_id = message.forward.sender_id
            user = await archive.get_entity(user_id)

            # A channel can be a sender as well, early return if the sender is no User
            if not isinstance(user, types.User):
                return

        else:
            # Ignore messages with no sent user
            if message.from_id is None:
                return

            user_id = message.from_id
            user = await archive.get_entity(message.from_id)

        # Check if we should accept this message
        if not await should_accept_message(event, message, user, subscriber):
            return

        # Create a new file. If it's not possible or not wanted, return None
        new_file = await create_file(session, event, subscriber, message, user,
                                     full_scan)
        if new_file is None:
            return None

        # Download the file
        success = await message.download_media(str(new_file.file_path))

        # Download succeeded, if the result is not None
        if success is not None:
            # Mark the file as succeeded
            new_file.success = True
        session.commit()

    except ValueError:
        # Handle broadcast channels those have None for user_id:
        if user_id is None:
            user_id = subscriber.chat_name
        user = UnknownUser(user_id)

    # We got a flood wait error. Wait for the specified time and recursively retry
    except FloodWaitError as e:
        time.sleep(e.seconds + 1)
        process_message(session, subscriber, message, event, full_scan)
        return
Esempio n. 5
0
async def process(event, session):
    """Check if we received any files."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)

    try:
        await process_message(session, subscriber, event.message, event)
    except BadMessageError:
        # Ignore bad message errors
        return
Esempio n. 6
0
async def process(event, session):
    """Main entry for processing messages and downloading files."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type,
                                          event.message)

    try:
        await process_message(session, subscriber, event.message, event)
    except BadMessageError:
        # Ignore bad message errors
        return
Esempio n. 7
0
async def create_file(session, event, subscriber, message, user, full_scan):
    """Create a file object from a message."""
    to_id, to_type = get_peer_information(message.to_id)

    file_type, file_id = await get_file_information(event, message, subscriber,
                                                    user, full_scan)
    if not file_type:
        return None

    # Check if this exact file from the same message is already downloaded.
    # This is a hard constraint which shouldn't be violated.
    if File.exists(session, subscriber, file_id):
        return None

    # The file path is depending on the media type.
    # In case such a file already exists and duplicate files are disabled, the function returns None.
    # In that case we will return early.
    file_path, file_name = get_file_path(subscriber, get_username(user),
                                         message)

    # Don't check zipped files from ourselves.
    # Otherwise we would double in size on each /scan_chat /zip command combination
    me = await event.client.get_me()
    splitted = file_name.rsplit('.', maxsplit=2)
    if user.id == me.id and len(splitted) == 3 and splitted[1] == '7z':
        return None

    if file_path is None:
        # Inform the user about duplicate files
        if subscriber.verbose:
            text = f"File with name {file_name} already exists."
            await event.respond(text)

        sentry.captureMessage("File already exists",
                              extra={
                                  'file_path': file_path,
                                  'file_name': file_name,
                                  'chat': subscriber.chat_name,
                                  'user': get_username(user)
                              },
                              tags={'level': 'info'})
        return None

    # The file path is depending on the media type.
    new_file = File(file_id, to_id, user.id, subscriber, to_type, message.id,
                    file_type, file_name, file_path)

    session.add(new_file)
    session.commit()

    return new_file
Esempio n. 8
0
async def scan_chat(event, session):
    """Check if we received any files."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type,
                                          event.message)

    async for message in archive.iter_messages(event.message.to_id):
        try:
            await process_message(session, subscriber, message, event)
        except BadMessageError:
            # Ignore bad message errors
            return

    return "Chat scan successful."
Esempio n. 9
0
async def scan_chat(event, session):
    """Scan the whole chat for files. Necessary for getting old files."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)

    await event.respond("Starting full chat scan.")
    async for message in archive.iter_messages(event.message.to_id):
        try:
            await process_message(session, subscriber, message, event, full_scan=True)
        except BadMessageError:
            # Ignore bad message errors
            return

    return "Full chat scan successful."
Esempio n. 10
0
async def set_name(event, session):
    """Set the name of the current chat (also affects the saving directory."""
    to_id, to_type = get_peer_information(event.message.to_id)
    new_chat_name = event.message.message.split(" ", maxsplit=1)[1].strip()
    # We already save to zips, prevent that
    if new_chat_name == "zips":
        return "Invalid chat name. Pick another."

    subscriber = Subscriber.get_or_create(
        session, to_id, to_type, event.message, chat_name=new_chat_name
    )

    old_chat_path = get_chat_path(subscriber.chat_name)
    new_chat_path = get_chat_path(new_chat_name)

    # Handle any command that tries to escape the download directory
    new_real_path = os.path.realpath(new_chat_path)
    target_real_path = os.path.realpath(config["download"]["target_dir"])
    if (
        not new_real_path.startswith(target_real_path)
        or new_real_path == target_real_path
    ):
        user = await archive.get_entity(types.PeerUser(event.message.from_id))
        sentry.captureMessage(
            "User tried to escape directory.",
            extra={
                "new_chat_name": new_chat_name,
                "chat": subscriber.chat_name,
                "user": get_username(user),
            },
            tags={"level": "info"},
        )

        return "Please stop fooling around and don't try to escape the directory. I have been notified about this."

    # Check whether we already have a chat with this name
    if (
        session.query(Subscriber)
        .filter(Subscriber.chat_name == new_chat_name)
        .one_or_none()
    ):
        return "Chat name already exists. Please choose another one."

    # Move the old directory to the new location
    elif old_chat_path != new_chat_path:
        subscriber.chat_name = new_chat_name
        if os.path.exists(old_chat_path):
            os.rename(old_chat_path, new_chat_path)
        return "Chat name changed."
Esempio n. 11
0
async def clear_history(event, session):
    """Stop the bot."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)

    chat_path = get_chat_path(subscriber.chat_name)
    for known_file in subscriber.files:
        session.delete(known_file)

    if os.path.exists(chat_path):
        shutil.rmtree(chat_path)

    session.commit()

    return "All files from this chat have been deleted."
Esempio n. 12
0
async def accepted_media_types(event, session):
    """Set query attributes."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)

    # Convert the incoming text into an boolean
    arguments = event.message.message.lower().split(' ')[1:]
    accepted_media = set()
    for argument in arguments:
        if argument in possible_media:
            accepted_media.add(argument)

    accepted_media = list(accepted_media)
    accepted_media.sort()

    subscriber.accepted_media = ' '.join(accepted_media)
    return f"Now accepting following media types: {accepted_media}."
Esempio n. 13
0
async def process_message(session, subscriber, message, event):
    """Process a single message."""
    to_id, to_type = get_peer_information(message.to_id)

    try:
        # If this message is forwarded, get the original sender.
        if message.forward:
            user_id = message.forward.sender_id
            user = await message.forward.get_sender()
        else:
            # Ignore messages with no sent user
            if message.from_id is None:
                return
            user_id = message.from_id
            user = await archive.get_entity(message.from_id)
    except ValueError:
        # Handle channels. Channels always have None for user_id:
        if user_id is None:
            user_id = subscriber.channel_name
        user = UnknownUser(user_id)

    # Check if we should accept this message
    if not await should_accept_message(event, message, user, subscriber):
        return

    # Ignore users with absolutely no name
    if user.last_name is None or user.first_name is None or user.username is None:
        return

    # Create a new file. If it's not possible or not wanted, return None
    new_file = await create_file(session, event, subscriber, message, user)
    if new_file is None:
        return None

    # Download the file
    success = await message.download_media(str(new_file.file_path))

    # Download succeeded, if the result is not None
    if success is not None:
        # Mark the file as succeeded
        new_file.success = True
    session.commit()
Esempio n. 14
0
async def set_name(event, session):
    """Set query attributes."""
    to_id, to_type = get_peer_information(event.message.to_id)
    new_channel_name = event.message.message.split(' ', maxsplit=1)[1].strip()
    if new_channel_name == 'zips':
        return "Invalid channel name. Pick another."

    subscriber = Subscriber.get_or_create(session,
                                          to_id,
                                          to_type,
                                          event.message,
                                          channel_name=new_channel_name)

    old_channel_path = get_channel_path(subscriber.channel_name)
    new_channel_path = get_channel_path(new_channel_name)

    new_real_path = os.path.realpath(new_channel_path)
    target_real_path = os.path.realpath(config.TARGET_DIR)
    if not new_real_path.startswith(target_real_path) or \
            new_real_path == target_real_path:
        user = await archive.get_entity(event.message.from_id)
        sentry.captureMessage("User tried to escape directory.",
                              extra={
                                  'new_channel_name': new_channel_name,
                                  'channel': subscriber.channel_name,
                                  'user': get_username(user)
                              },
                              tags={'level': 'info'})

        return "Please stop fooling around and try to escape the directory. I have been notified as well."

    if session.query(Subscriber) \
            .filter(Subscriber.channel_name == new_channel_name) \
            .one_or_none():
        return "Channel name already exists. Please choose another one."

    elif old_channel_path != new_channel_path:
        subscriber.channel_name = new_channel_name
        if os.path.exists(old_channel_path):
            os.rename(old_channel_path, new_channel_path)
        return "Channel name changed."
Esempio n. 15
0
async def process(event, session):
    """Main entry for processing messages and downloading files."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type, event.message)

    tries = 3
    current_try = 0
    while tries < current_try:
        try:
            await process_message(session, subscriber, event.message, event)
            return
        except BadMessageError:
            # Ignore bad message errors
            return
        except FloodWaitError as e:
            # We got a flood wait error. Wait for the specified time and recursively retry
            time.sleep(e.seconds + 1)
        except TimeoutError:
            # Timeout error. Just wait for 10 secs.
            time.sleep(10)

        current_try += 1
Esempio n. 16
0
async def info(event, session):
    """Send a the information about the current user settings."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type,
                                          event.message)
    return get_info_text(subscriber)
Esempio n. 17
0
async def info(event, session):
    """Send a help text."""
    to_id, to_type = get_peer_information(event.message.to_id)
    subscriber = Subscriber.get_or_create(session, to_id, to_type,
                                          event.message)
    return get_info_text(subscriber)