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)
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')
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))
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" )
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)
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))
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))
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
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