예제 #1
0
def on_torrents_list_selection(update: Update, context: CallbackContext):
    logger.info('torrents list menu button from %s: %s', update.message.from_user.first_name, context.match[0])

    qbfilter = context.match[0]
    if qbfilter.startswith('/'):
        # remove the "/" if the category has been used as command
        qbfilter = qbfilter.replace('/', '')

    logger.info('torrents status: %s', qbfilter)

    torrents = qb.torrents(filter=qbfilter, sort='dlspeed', reverse=False) or []
    if qbfilter == 'tostart':
        all_torrents = qb.torrents(filter='all')
        completed_torrents = [t.hash for t in qb.torrents(filter='completed')]
        active_torrents = [t.hash for t in qb.torrents(filter='active')]

        torrents = [t for t in all_torrents if t.hash not in completed_torrents and t.hash not in active_torrents]

    logger.info('qbittirrent request returned %d torrents', len(torrents))

    if not torrents:
        update.message.reply_html('There is no torrent to be listed for <i>{}</i>'.format(qbfilter))
        return

    if qbfilter == 'completed':
        base_string = TORRENT_STRING_COMPLETED  # use a shorter string with less info for completed torrents
    else:
        base_string = TORRENT_STRING_COMPACT

    strings_list = [base_string.format(**torrent.dict()) for torrent in torrents]

    for strings_chunk in u.split_text(strings_list):
        update.message.reply_html('\n'.join(strings_chunk))
def on_json_command(update: Update, context: CallbackContext):
    logger.info('/json command from %s', update.message.from_user.first_name)

    torrents = qb.torrents(filter='all', get_torrent_generic_properties=True)

    logger.info('qbittirrent request returned %d torrents', len(torrents))

    if not torrents:
        update.message.reply_html('There is no torrent')
        return

    update.message.reply_text("Sending file, it might take a while...")

    result_dict = defaultdict(list)
    for torrent in torrents:
        torrent_dict = torrent.dict()
        torrent_dict["_trackers"] = torrent.trackers()
        result_dict[torrent.state].append(torrent_dict)

    file_path = os.path.join('downloads', f'{update.message.message_id}.json')

    with open(file_path, 'w+') as f:
        json.dump(result_dict, f, indent=2)

    update.message.reply_document(open(file_path, 'rb'),
                                  caption='#torrents_list',
                                  timeout=60 * 10)

    os.remove(file_path)
예제 #3
0
def refresh_active_torrents(_, update):
    logger.info('refresh active torrents inline button used by %s',
                update.effective_user.first_name)

    torrents = qb.torrents(filter='active', sort='dlspeed',
                           reverse=False) or []

    if not torrents:
        update.callback_query.answer('Cannot refresh: no torrents')
        return

    strings_list = [
        TORRENT_STRING_COMPACT.format(**torrent.dict()) for torrent in torrents
    ]

    # we assume the list doesn't require more than one message
    try:
        update.callback_query.edit_message_text('\n'.join(strings_list),
                                                reply_markup=kb.REFRESH_ACTIVE,
                                                parse_mode=ParseMode.HTML)
    except BadRequest as br:
        logger.error(
            'Telegram error when refreshing the active torrents list: %s',
            br.message)
        update.callback_query.answer('Error: {}'.format(br.message))
        return

    update.callback_query.answer('Refreshed')
예제 #4
0
def on_torrents_list_selection(update: Update, context: CallbackContext):
    logger.info('torrents list menu button from %s: %s',
                update.message.from_user.first_name, context.match[0])

    qbfilter = context.match[0].replace("/", "")
    logger.info('torrents status: %s', qbfilter)

    update.message.reply_html(
        f"Listing torrents with status <code>{qbfilter}</code> (migth take some seconds):"
    )

    torrents = qb.torrents(filter=qbfilter,
                           sort='dlspeed',
                           reverse=False,
                           get_torrent_generic_properties=False) or []
    logger.info('qbittirrent request returned %d torrents', len(torrents))

    if not torrents:
        update.message.reply_html(
            'There is no torrent to be listed for <i>{}</i>'.format(qbfilter))
        return

    if qbfilter == 'completed':
        base_string = TORRENT_STRING_COMPLETED  # use a shorter string with less info for completed torrents
    else:
        base_string = TORRENT_STRING_COMPACT

    strings_list = [
        base_string.format(**torrent.dict()) for torrent in torrents
    ]

    for strings_chunk in u.split_text(strings_list):
        update.message.reply_html('\n'.join(strings_chunk))
예제 #5
0
def on_remove_dead_trackers_command(update: Update, context: CallbackContext):
    logger.info('remove dead trackers from %s',
                update.message.from_user.first_name)

    torrents = qb.torrents(filter='all', get_torrent_generic_properties=False)

    removed_trackers = 0
    affected_torrents = 0
    for torrent in torrents:
        trackers = torrent.trackers()

        urls_to_remove = []
        for tracker in trackers:
            if tracker["status"] != 4:
                # status 4: "Tracker has been contacted, but it is not working (or doesn't send proper replies)"
                continue

            urls_to_remove.append(tracker["url"])

        if urls_to_remove:
            torrent.remove_trackers(urls_to_remove)
            removed_trackers += len(urls_to_remove)
            affected_torrents += 1

    update.message.reply_text(
        f"Removed {removed_trackers} trackers from {affected_torrents} torrents"
    )
예제 #6
0
def on_json_command(update: Update, context: CallbackContext):
    logger.info('/json command from %s', update.message.from_user.first_name)

    torrents = qb.torrents(filter='all')

    logger.info('qbittirrent request returned %d torrents', len(torrents))

    if not torrents:
        update.message.reply_html('There is no torrent')
        return

    result_dict = defaultdict(list)
    for torrent in torrents:
        result_dict[torrent.state].append(torrent.dict())

    file_path = os.path.join('downloads',
                             '{}.json'.format(update.message.message_id))

    with open(file_path, 'w+') as f:
        json.dump(result_dict, f, indent=4)

    update.message.reply_document(open(file_path, 'rb'),
                                  caption='#torrents_list',
                                  timeout=60 * 10)

    os.remove(file_path)
예제 #7
0
def on_priorities_command(update: Update, context: CallbackContext):
    logger.info('/priorities from %s', update.effective_user.first_name)

    torrents = qb.torrents(sort='priority', reverse=False)

    # filter out paused completed torrents
    non_completed_torrents = list()
    for torrent in torrents:
        if torrent.state in ('pausedUP', ):
            continue

        non_completed_torrents.append(torrent)
        if len(non_completed_torrents) == 25:
            # list must contain 25 torrents max
            break

    lines = [TORRENT_STRING.format(t=t) for t in non_completed_torrents]

    for strings_chunk in u.split_text(lines):
        update.message.reply_html('\n'.join(strings_chunk))
예제 #8
0
def on_atm_list_command(update: Update, context: CallbackContext):
    logger.info('/atmyes or /atmno command used by %s', update.effective_user.first_name)

    torrents = qb.torrents()

    atm_enabled = update.message.text.lower().endswith("atmyes")

    base_string = "• <code>{short_name}</code> ({size_pretty}, {state_pretty}) [<a href=\"{info_deeplink}\">info</a>]"
    strings_list = [torrent.string(base_string=base_string) for torrent in torrents if torrent['auto_tmm'] is atm_enabled]

    update.message.reply_html(
        f"There are <b>{len(strings_list)}/{len(torrents)}</b> torrents with "
        f"Automatic Torrent Management {'enabled' if atm_enabled else 'disabled'}:"
    )

    if not strings_list:
        update.message.reply_text("-")
        return

    for strings_chunk in u.split_text(strings_list):
        update.message.reply_html('\n'.join(strings_chunk))
예제 #9
0
def get_quick_info_text(sort_active_by_dl_speed=True):
    if sort_active_by_dl_speed:
        active_torrents_sort = 'dlspeed'
    else:
        active_torrents_sort = 'progress'

    active_torrents = qb.torrents(filter='active', sort=active_torrents_sort, reverse=False)
    completed_torrents = qb.torrents(filter='completed')

    total_active_count = 0
    total_completed_count = 0

    if not active_torrents:
        active_torrents_strings_list = ['no active torrent']
    else:
        total_active_count = len(active_torrents)  # non-filtered count

        active_torrents_filtered = list()
        active_torrents_without_traffic_count = 0
        active_torrents_fetching_metadata_count = 0

        for active_torrent in active_torrents:
            if active_torrent.state in ('metaDL',):
                active_torrents_fetching_metadata_count += 1
            elif active_torrent.state in ('stalledDL',):
                # for some reasons, sometime in the active list we find also torrents in this state
                active_torrents_without_traffic_count += 1
            elif active_torrent.state in ('forcedDL', 'forcedUP') and active_torrent.generic_speed <= 0:
                # count torrents that are not generating traffic and that have been force-started
                active_torrents_without_traffic_count += 1
            else:
                active_torrents_filtered.append(active_torrent)

        active_torrents_strings_list = [TORRENT_STRING_COMPACT.format(**t.dict()) for t in active_torrents_filtered]

        # the list contains the strings to concatenate as the last row of the active torrents list
        other_torrents_counts_string = list()
        if active_torrents_without_traffic_count > 0:
            text = '<b>{}</b> stalled'.format(active_torrents_without_traffic_count)
            other_torrents_counts_string.append(text)
        if active_torrents_fetching_metadata_count > 0:
            text = '<b>{}</b> fetching metadata'.format(active_torrents_fetching_metadata_count)
            other_torrents_counts_string.append(text)

        if other_torrents_counts_string:
            active_torrents_strings_list.append('• ' + ', '.join(other_torrents_counts_string))

    if completed_torrents:
        total_completed_count = len(completed_torrents)
        completed_torrents_strings_list = ['• {}'.format(t.short_name) for t in completed_torrents]
    else:
        completed_torrents_strings_list = ['no completed torrent']

    # shorten the message if it's too long to send
    completed_torrents_string_len = sum(map(len, completed_torrents_strings_list))
    active_torrents_string_len = sum(map(len, active_torrents_strings_list))
    if (completed_torrents_string_len + active_torrents_string_len) > MAX_MESSAGE_LENGTH:
        # we assume the longest one between the two is the completed torrents list
        completed_torrents_strings_list = ['list too long, use /completed to see completed torrents']

    schedule_info = qb.get_schedule()
    if not schedule_info:
        schedule_string = '<b>Schedule</b>: off'
    else:
        schedule_string = '<b>Schedule</b>: on, from {from_hour} to {to_hour} ({days})'.format(**schedule_info)

    alt_speed_info = qb.get_alt_speed(human_readable=True)
    alt_speed_string = '<b>Alt speed is {status}</b> (down: {alt_dl_limit}/s, up: {alt_up_limit}/s)'.format(
        **alt_speed_info
    )

    current_speed = qb.get_speed()
    current_speed_string = '<b>Current speed</b>: down: {0}/s, up: {1}/s'.format(*current_speed)

    text = QUICK_INFO_TEXT.format(
        total_completed_count=total_completed_count,
        completed='\n'.join(completed_torrents_strings_list),
        total_active_count=total_active_count,
        active='\n'.join(active_torrents_strings_list),
        schedule=schedule_string,
        alt_speed=alt_speed_string,
        current_speed=current_speed_string,
        last_refresh=datetime.datetime.now().strftime('%d/%m/%Y %H:%M:%S')
    )

    return text
예제 #10
0
def get_quick_info_text(sort_active_by_dl_speed=True):
    all_torrents = qb.torrents(filter='all',
                               get_torrent_generic_properties=False)
    states_counter = Counter([t.state for t in all_torrents])
    categories_counter = Counter([t.category for t in all_torrents])

    active_torrents_up = [
        torrent for torrent in all_torrents if torrent.state in ("uploading", )
    ]
    active_torrents_up.sort(key=lambda t: t['upspeed'])

    active_torrents_down = [
        torrent for torrent in all_torrents
        if torrent.state in ("downloading", )
    ]
    active_torrents_down.sort(key=lambda t: t['dlspeed'])

    active_torrents_down_strings_list = ['no active downloading torrents']
    active_torrents_up_strings_list = ['no active uploading torrents']
    states_count_string = 'none'
    categories_count_string = 'none'

    active_up_count = len(active_torrents_up)
    active_down_count = len(active_torrents_down)
    completed_count = len([t for t in all_torrents if t.progress == 1.00])

    if active_torrents_down:
        active_torrents_down_strings_list = [
            TORRENT_STRING_COMPACT.format(**t.dict())
            for t in active_torrents_down
        ]
    if active_torrents_up:
        active_torrents_up_strings_list = [
            TORRENT_STRING_COMPACT.format(**t.dict())
            for t in active_torrents_up
        ]

    states_count_list = list()
    for state, count in states_counter.most_common():
        states_count_list.append(f"{count} {STATES_DICT[state]}")
    if states_count_list:
        states_count_string = ', '.join(states_count_list)

    categories_count_list = list()
    for category, count in categories_counter.most_common():
        categories_count_list.append(f"{count} {category or 'not set'}")
    if categories_count_list:
        categories_count_string = ', '.join(categories_count_list)

    schedule_info = qb.get_schedule()
    if not schedule_info:
        schedule_string = '<b>Schedule</b>: off'
    else:
        schedule_string = '<b>Schedule</b>: on, from {from_hour} to {to_hour} ({days})'.format(
            **schedule_info)

    alt_speed_info = qb.get_alt_speed(human_readable=True)

    current_speed = qb.get_speed()
    speed_limit_global = qb.get_global_speed_limit()
    speed_limit_global_set = any(speed_limit_global)
    if alt_speed_info['status'] == 'on':
        current_speed_string = f'▲ <b>{current_speed[1]}/s</b> ({alt_speed_info["alt_up_limit"]}/s)\n' \
                               f'▼ <b>{current_speed[0]}/s</b> ({alt_speed_info["alt_dl_limit"]}/s)\n' \
                               f'alt speed is <b>on</b>'
    else:
        # add global limits in parenthesis only if they are set
        global_speed_limit_are_set = "\nsome global speed limits are set" if speed_limit_global_set else ""
        up_limit = f" ({speed_limit_global[1]}/s)" if speed_limit_global[
            1] else ""
        dl_limit = f" ({speed_limit_global[0]}/s)" if speed_limit_global[
            0] else ""
        current_speed_string = f'▲ <b>{current_speed[1]}/s</b>{up_limit}\n' \
                               f'▼ <b>{current_speed[0]}/s</b>{dl_limit}' \
                               f'{global_speed_limit_are_set}'

    text = QUICK_INFO_TEXT.format(
        completed_count=completed_count,
        states_count=states_count_string,
        categories_count=categories_count_string,
        active_down_count=active_down_count,
        active_up_count=active_up_count,
        active_up='\n'.join(active_torrents_up_strings_list),
        active_down='\n'.join(active_torrents_down_strings_list),
        schedule=schedule_string,
        current_speed=current_speed_string,
        last_refresh=datetime.datetime.now().strftime('%d/%m/%Y %H:%M:%S'))

    return text