mirror of
				https://github.com/NohamR/TransmissionBot.git
				synced 2025-11-04 08:09:30 +00:00 
			
		
		
		
	bugfixes and performance enhancements
* disabled checking reactions as their added; that was slow. * reload transmissionrpc client (when there's no repeating messages) so the bot can handle when transmission restarts or becomes unavailable.
This commit is contained in:
		
							parent
							
								
									d6cd84dbcf
								
							
						
					
					
						commit
						8442665ecd
					
				
							
								
								
									
										127
									
								
								bot.py
									
									
									
									
									
								
							
							
						
						
									
										127
									
								
								bot.py
									
									
									
									
									
								
							@ -83,6 +83,7 @@ REPEAT_MSGS = {}
 | 
			
		||||
 | 
			
		||||
client = Bot(command_prefix=BOT_PREFIX)
 | 
			
		||||
TSCLIENT = None
 | 
			
		||||
MAKE_CLIENT_FAILED = False
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger('transmission_bot')
 | 
			
		||||
logger.setLevel(logging.INFO)
 | 
			
		||||
@ -135,7 +136,7 @@ class TSClient(transmissionrpc.Client):
 | 
			
		||||
	helper functionality.
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	def get_torrents_by(self, sort_by=None, filter_by=None, reverse=False, filter_regex=None, id_list=None):
 | 
			
		||||
	def get_torrents_by(self, sort_by=None, filter_by=None, reverse=False, filter_regex=None, id_list=None, num_results=None):
 | 
			
		||||
		"""This method will call get_torrents and then perform any sorting or filtering
 | 
			
		||||
		actions requested on the returned torrent set.
 | 
			
		||||
 | 
			
		||||
@ -147,12 +148,13 @@ class TSClient(transmissionrpc.Client):
 | 
			
		||||
		:return: Sorted and filter torrent list
 | 
			
		||||
		:rtype: transmissionrpc.Torrent[]
 | 
			
		||||
		"""
 | 
			
		||||
		if id_list:
 | 
			
		||||
			torrents = self.get_torrents(ids=id_list)
 | 
			
		||||
		else:
 | 
			
		||||
			torrents = self.get_torrents()
 | 
			
		||||
			if filter_regex:
 | 
			
		||||
				regex = re.compile(filter_regex, re.IGNORECASE)
 | 
			
		||||
				torrents = [tor for tor in torrents if regex.search(tor.name)]
 | 
			
		||||
		if id_list:
 | 
			
		||||
			torrents = [tor for tor in torrents if tor.id in id_list]
 | 
			
		||||
			if filter_by:
 | 
			
		||||
				for f in filter_by.split():
 | 
			
		||||
					if f in filter_names:
 | 
			
		||||
@ -180,6 +182,8 @@ class TSClient(transmissionrpc.Client):
 | 
			
		||||
						sort_by = "ratio"
 | 
			
		||||
			if sort_by:
 | 
			
		||||
				torrents = sort_torrents_by(torrents, key=getattr(Sort, sort_by), reverse=reverse)
 | 
			
		||||
			if num_results and num_results < len(torrents):
 | 
			
		||||
				torrents = torrents[-num_results:]
 | 
			
		||||
		return torrents
 | 
			
		||||
		
 | 
			
		||||
def make_client():
 | 
			
		||||
@ -191,16 +195,26 @@ def make_client():
 | 
			
		||||
	:param args: Optional CLI args passed in.
 | 
			
		||||
	:return:
 | 
			
		||||
	"""
 | 
			
		||||
	global MAKE_CLIENT_FAILED
 | 
			
		||||
	try:
 | 
			
		||||
		return TSClient(
 | 
			
		||||
		tsclient = TSClient(
 | 
			
		||||
			TSCLIENT_CONFIG['host'],
 | 
			
		||||
			port=TSCLIENT_CONFIG['port'],
 | 
			
		||||
			user=TSCLIENT_CONFIG['user'],
 | 
			
		||||
			password=TSCLIENT_CONFIG['password']
 | 
			
		||||
		)
 | 
			
		||||
		MAKE_CLIENT_FAILED = False
 | 
			
		||||
		return tsclient
 | 
			
		||||
	except:
 | 
			
		||||
		MAKE_CLIENT_FAILED = True
 | 
			
		||||
		return None
 | 
			
		||||
 | 
			
		||||
		
 | 
			
		||||
def reload_client():
 | 
			
		||||
	global TSCLIENT
 | 
			
		||||
	TSCLIENT = make_client()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Filter(object):
 | 
			
		||||
	"""A set of filtering operations that can be used against a list of torrent objects"""
 | 
			
		||||
 | 
			
		||||
@ -399,7 +413,7 @@ def stop_torrents(torrents=[], reason=DEFAULT_REASON):
 | 
			
		||||
				TSCLIENT.stop_torrent(torrent.hashString)
 | 
			
		||||
			logger.info("Paused: {} {}\n\tReason: {}\n\tDry run: {}".format(torrent.name, torrent.hashString, reason, DRYRUN))
 | 
			
		||||
 | 
			
		||||
def resume_torrents(torrents=[], reason=DEFAULT_REASON):
 | 
			
		||||
def resume_torrents(torrents=[], reason=DEFAULT_REASON, start_all=False):
 | 
			
		||||
	""" Stop (pause) a list of torrents from the client.
 | 
			
		||||
 | 
			
		||||
	:param client: Transmission RPC Client
 | 
			
		||||
@ -412,6 +426,11 @@ def resume_torrents(torrents=[], reason=DEFAULT_REASON):
 | 
			
		||||
	:type dry_run: bool
 | 
			
		||||
	:return:
 | 
			
		||||
	"""
 | 
			
		||||
	if start_all:
 | 
			
		||||
		if not DRYRUN:
 | 
			
		||||
			TSCLIENT.start_all()
 | 
			
		||||
		logger.info("Resumed: all transfers\n\tReason: {}\n\tDry run: {}".format(torrent.name, torrent.hashString, reason, DRYRUN))
 | 
			
		||||
	else:
 | 
			
		||||
		for torrent in (torrents if len(torrents) > 0 else TSCLIENT.get_torrents()):
 | 
			
		||||
			if torrent.status == "stopped":
 | 
			
		||||
				if not DRYRUN:
 | 
			
		||||
@ -451,8 +470,7 @@ def add_torrent(torStr):
 | 
			
		||||
 | 
			
		||||
@client.event
 | 
			
		||||
async def on_ready():
 | 
			
		||||
	global TSCLIENT
 | 
			
		||||
	TSCLIENT = make_client()
 | 
			
		||||
	reload_client()
 | 
			
		||||
	if TSCLIENT is None:
 | 
			
		||||
		print("Failed to create transmissionrpc client")
 | 
			
		||||
	else:
 | 
			
		||||
@ -578,13 +596,21 @@ async def add(context, *, content = ""):
 | 
			
		||||
		torStr = []
 | 
			
		||||
		for t in torFileList:
 | 
			
		||||
			# await context.message.channel.send('Adding torrent from file: {}\n Please wait...'.format(t["name"]))
 | 
			
		||||
			try:
 | 
			
		||||
				tor = add_torrent(t["content"])
 | 
			
		||||
			except:
 | 
			
		||||
				await context.message.channel.send('‼️ Error communicating with Transmission ‼️')
 | 
			
		||||
				return
 | 
			
		||||
			torStr.append("From file: {}".format(tor.name))
 | 
			
		||||
			
 | 
			
		||||
		for t in content.strip().split(" "):
 | 
			
		||||
			if len(t) > 5:
 | 
			
		||||
				# await context.message.channel.send('Adding torrent from link: {}\n Please wait...'.format(t))
 | 
			
		||||
				try:
 | 
			
		||||
					tor = add_torrent(t)
 | 
			
		||||
				except:
 | 
			
		||||
					await context.message.channel.send('‼️ Error communicating with Transmission ‼️')
 | 
			
		||||
					return
 | 
			
		||||
				torStr.append("From link: {}".format(tor.name))
 | 
			
		||||
				
 | 
			
		||||
		if len(torStr) > 0:
 | 
			
		||||
@ -705,6 +731,8 @@ async def summary(context, *, content="", repeat_msg_key=None):
 | 
			
		||||
	global REPEAT_MSGS
 | 
			
		||||
	if await CommandPrecheck(context):
 | 
			
		||||
		if not repeat_msg_key:
 | 
			
		||||
			if len(REPEAT_MSGS) == 0:
 | 
			
		||||
				reload_client()
 | 
			
		||||
			try:
 | 
			
		||||
				await context.message.delete()
 | 
			
		||||
			except:
 | 
			
		||||
@ -746,6 +774,25 @@ async def summary(context, *, content="", repeat_msg_key=None):
 | 
			
		||||
				await msg.add_reaction(stateEmoji[i+stateEmojiFilterStartNum])
 | 
			
		||||
			elif summaryData[1][i] == 0 and stateEmoji[i+stateEmojiFilterStartNum] in msgRxns:
 | 
			
		||||
				await msg.clear_reaction(stateEmoji[i+stateEmojiFilterStartNum])
 | 
			
		||||
			# if not repeat_msg_key:
 | 
			
		||||
			# 	cache_msg = await context.message.channel.fetch_message(msg.id)
 | 
			
		||||
			# 	for r in cache_msg.reactions:
 | 
			
		||||
			# 		if r.count > 1:
 | 
			
		||||
			# 			async for user in r.users():
 | 
			
		||||
			# 				if user.id in WHITELIST:
 | 
			
		||||
			# 					if str(r.emoji) == '📜':
 | 
			
		||||
			# 						await msg.clear_reactions()
 | 
			
		||||
			# 						await legend(context)
 | 
			
		||||
			# 						return
 | 
			
		||||
			# 					elif str(r.emoji) == '🔄':
 | 
			
		||||
			# 						await msg.clear_reaction('🔄')
 | 
			
		||||
			# 						await repeat_command(summary, context=context, content=content, msg_list=[msg])
 | 
			
		||||
			# 						return
 | 
			
		||||
			# 					elif str(r.emoji) in stateEmoji[stateEmojiFilterStartNum-1:] and user.id == context.message.author.id:
 | 
			
		||||
			# 						await msg.clear_reactions()
 | 
			
		||||
			# 						await list_transfers(context, content=torStateFilters[str(r.emoji)])
 | 
			
		||||
			# 						return
 | 
			
		||||
		
 | 
			
		||||
		cache_msg = await context.message.channel.fetch_message(msg.id)
 | 
			
		||||
		for r in cache_msg.reactions:
 | 
			
		||||
			if r.count > 1:
 | 
			
		||||
@ -767,7 +814,10 @@ async def summary(context, *, content="", repeat_msg_key=None):
 | 
			
		||||
							await repeat_command(summary, context=context, content=content, msg_list=[msg])
 | 
			
		||||
							return
 | 
			
		||||
						elif str(r.emoji) in stateEmoji[stateEmojiFilterStartNum-1:] and user.id == context.message.author.id:
 | 
			
		||||
							if repeat_msg_key:
 | 
			
		||||
								await msg.clear_reaction(str(r.emoji))
 | 
			
		||||
							else:
 | 
			
		||||
								await msg.clear_reactions()
 | 
			
		||||
							await list_transfers(context, content=torStateFilters[str(r.emoji)])
 | 
			
		||||
							return
 | 
			
		||||
		
 | 
			
		||||
@ -783,11 +833,17 @@ async def summary(context, *, content="", repeat_msg_key=None):
 | 
			
		||||
			pass
 | 
			
		||||
		else:
 | 
			
		||||
			if str(reaction.emoji) in stateEmoji[stateEmojiFilterStartNum-1:] and str(reaction.emoji) not in ignoreEmoji:
 | 
			
		||||
				if repeat_msg_key:
 | 
			
		||||
					await msg.clear_reaction(str(reaction.emoji))
 | 
			
		||||
				else:
 | 
			
		||||
					await msg.clear_reactions()
 | 
			
		||||
				await list_transfers(context, content=torStateFilters[str(reaction.emoji)])
 | 
			
		||||
				return
 | 
			
		||||
			elif str(reaction.emoji) == '📜':
 | 
			
		||||
				if repeat_msg_key:
 | 
			
		||||
					await msg.clear_reaction('📜')
 | 
			
		||||
				else:
 | 
			
		||||
					await msg.clear_reactions()
 | 
			
		||||
				await legend(context)
 | 
			
		||||
				return
 | 
			
		||||
			elif str(reaction.emoji) == '❎':
 | 
			
		||||
@ -813,8 +869,8 @@ async def summary(context, *, content="", repeat_msg_key=None):
 | 
			
		||||
								await legend(context)
 | 
			
		||||
								return
 | 
			
		||||
							elif str(r.emoji) == '❎':
 | 
			
		||||
								await msg.clear_reactions()
 | 
			
		||||
								REPEAT_MSGS[repeat_msg_key]['do_repeat'] = False
 | 
			
		||||
								await msg.clear_reactions()
 | 
			
		||||
								return
 | 
			
		||||
							elif str(r.emoji) == '🖨':
 | 
			
		||||
								await msg.clear_reaction('🖨')
 | 
			
		||||
@ -915,6 +971,7 @@ def torList(torrents, author_name="Torrent Transfers",title=None,description=Non
 | 
			
		||||
def torGetListOpsFromStr(listOpStr):
 | 
			
		||||
	filter_by = None
 | 
			
		||||
	sort_by = None
 | 
			
		||||
	num_results = None
 | 
			
		||||
	splitcontent = listOpStr.split(" ")
 | 
			
		||||
	
 | 
			
		||||
	if "--filter" in splitcontent:
 | 
			
		||||
@ -943,16 +1000,28 @@ def torGetListOpsFromStr(listOpStr):
 | 
			
		||||
			del splitcontent[ind+1]
 | 
			
		||||
		del splitcontent[ind]
 | 
			
		||||
		
 | 
			
		||||
	if "-N" in splitcontent:
 | 
			
		||||
		ind = splitcontent.index("-N")
 | 
			
		||||
		if len(splitcontent) > ind + 1:
 | 
			
		||||
			try:
 | 
			
		||||
				num_results = int(splitcontent[ind+1])
 | 
			
		||||
			except:
 | 
			
		||||
				num_results = -1
 | 
			
		||||
			del splitcontent[ind+1]
 | 
			
		||||
		del splitcontent[ind]
 | 
			
		||||
	
 | 
			
		||||
	filter_regex = " ".join(splitcontent).strip()
 | 
			
		||||
	if filter_regex == "":
 | 
			
		||||
		filter_regex = None
 | 
			
		||||
	
 | 
			
		||||
	if filter_by is not None and filter_by not in filter_names_full:
 | 
			
		||||
		return -1, None, None
 | 
			
		||||
		return -1, None, None, None
 | 
			
		||||
	if sort_by is not None and sort_by not in sort_names:
 | 
			
		||||
		return None, -1, None
 | 
			
		||||
		return None, -1, None, None
 | 
			
		||||
	if num_results is not None and num_results <= 0:
 | 
			
		||||
		return None, None, None, -1
 | 
			
		||||
		
 | 
			
		||||
	return filter_by, sort_by, filter_regex
 | 
			
		||||
	return filter_by, sort_by, filter_regex, num_results
 | 
			
		||||
 | 
			
		||||
async def repeat_command(command, context, content="", msg_list=[]):
 | 
			
		||||
	global REPEAT_MSGS
 | 
			
		||||
@ -1001,24 +1070,28 @@ async def list_transfers(context, *, content="", repeat_msg_key=None):
 | 
			
		||||
		filter_by = None
 | 
			
		||||
		sort_by = None
 | 
			
		||||
		filter_regex = None
 | 
			
		||||
		num_results = None
 | 
			
		||||
		if not id_list:
 | 
			
		||||
			filter_by, sort_by, filter_regex = torGetListOpsFromStr(content)
 | 
			
		||||
			if filter_by == -1:
 | 
			
		||||
			filter_by, sort_by, filter_regex, num_results = torGetListOpsFromStr(content)
 | 
			
		||||
			if filter_by is not None and filter_by == -1:
 | 
			
		||||
				await context.message.channel.send("Invalid filter specified. Choose one of {}".format(str(filter_names_full)))
 | 
			
		||||
				return
 | 
			
		||||
			if sort_by == -1:
 | 
			
		||||
			if sort_by is not None and sort_by == -1:
 | 
			
		||||
				await context.message.channel.send("Invalid sort specified. Choose one of {}".format(str(sort_names)))
 | 
			
		||||
				return
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
			if num_results is not None and num_results <= 0:
 | 
			
		||||
				await context.message.channel.send("Must specify integer greater than 0 for `-N`!")
 | 
			
		||||
				return
 | 
			
		||||
		
 | 
			
		||||
		if not repeat_msg_key:
 | 
			
		||||
			if len(REPEAT_MSGS) == 0:
 | 
			
		||||
				reload_client()
 | 
			
		||||
			try:
 | 
			
		||||
				await context.message.delete()
 | 
			
		||||
			except:
 | 
			
		||||
				pass
 | 
			
		||||
		
 | 
			
		||||
		torrents = TSCLIENT.get_torrents_by(sort_by=sort_by, filter_by=filter_by, filter_regex=filter_regex, id_list=id_list)
 | 
			
		||||
		torrents = TSCLIENT.get_torrents_by(sort_by=sort_by, filter_by=filter_by, filter_regex=filter_regex, id_list=id_list, num_results=num_results)
 | 
			
		||||
		
 | 
			
		||||
		embeds = torList(torrents, title="{} transfer{} matching '`{}`'".format(len(torrents),'' if len(torrents)==1 else 's',content))
 | 
			
		||||
		
 | 
			
		||||
@ -1127,21 +1200,27 @@ async def modify(context, *, content=""):
 | 
			
		||||
			filter_by = None
 | 
			
		||||
			sort_by = None
 | 
			
		||||
			filter_regex = None
 | 
			
		||||
			num_results = None
 | 
			
		||||
			if not id_list:
 | 
			
		||||
				filter_by, sort_by, filter_regex = torGetListOpsFromStr(content)
 | 
			
		||||
				if filter_by == -1:
 | 
			
		||||
				filter_by, sort_by, filter_regex, num_results = torGetListOpsFromStr(content)
 | 
			
		||||
				if filter_by is not None and filter_by == -1:
 | 
			
		||||
					await context.message.channel.send("Invalid filter specified. Choose one of {}".format(str(filter_names_full)))
 | 
			
		||||
					return
 | 
			
		||||
				if sort_by == -1:
 | 
			
		||||
				if sort_by is not None and sort_by == -1:
 | 
			
		||||
					await context.message.channel.send("Invalid sort specified. Choose one of {}".format(str(sort_names)))
 | 
			
		||||
					return
 | 
			
		||||
				if num_results is not None and num_results <= 0:
 | 
			
		||||
					await context.message.channel.send("Must specify integer greater than 0 for `-N`!")
 | 
			
		||||
					return
 | 
			
		||||
 | 
			
		||||
			try:
 | 
			
		||||
				await context.message.delete()
 | 
			
		||||
			except:
 | 
			
		||||
				pass
 | 
			
		||||
			
 | 
			
		||||
			torrents = TSCLIENT.get_torrents_by(filter_by=filter_by, sort_by=sort_by, filter_regex=filter_regex, id_list=id_list)
 | 
			
		||||
			if len(REPEAT_MSGS) == 0:
 | 
			
		||||
				reload_client()
 | 
			
		||||
			torrents = TSCLIENT.get_torrents_by(filter_by=filter_by, sort_by=sort_by, filter_regex=filter_regex, id_list=id_list, num_results=num_results)
 | 
			
		||||
 | 
			
		||||
			if len(torrents) > 0:
 | 
			
		||||
				ops = ["pause","resume","remove","removedelete","verify"]
 | 
			
		||||
@ -1177,6 +1256,7 @@ async def modify(context, *, content=""):
 | 
			
		||||
		
 | 
			
		||||
		for i in opEmoji:
 | 
			
		||||
			await msgs[-1].add_reaction(i)
 | 
			
		||||
			
 | 
			
		||||
		cache_msg = await context.message.channel.fetch_message(msg.id)
 | 
			
		||||
		for reaction in cache_msg.reactions:
 | 
			
		||||
			if reaction.count > 1:
 | 
			
		||||
@ -1372,11 +1452,12 @@ async def help(context, *, content=""):
 | 
			
		||||
			if content in ["l","list"]:
 | 
			
		||||
				embed = discord.Embed(title='List transfers', color=0xb51a00)
 | 
			
		||||
				embed.set_author(name="List current transfers with sorting, filtering, and search options", icon_url=LOGO_URL)
 | 
			
		||||
				embed.add_field(name="Usage", value='`{0}list [--filter FILTER] [--sort SORT] [NAME]`'.format(BOT_PREFIX), inline=False)
 | 
			
		||||
				embed.add_field(name="Usage", value='`{0}list [--filter FILTER] [--sort SORT] [-N NUM_RESULTS] [NAME]`'.format(BOT_PREFIX), inline=False)
 | 
			
		||||
				embed.add_field(name="Filtering", value='`--filter FILTER` or `-f FILTER`\n`FILTER` is one of `{}`'.format(str(filter_names_full)), inline=False)
 | 
			
		||||
				embed.add_field(name="Sorting", value='`--sort SORT` or `-s SORT`\n`SORT` is one of `{}`'.format(str(sort_names)), inline=False)
 | 
			
		||||
				embed.add_field(name="Specify number of results to show", value='`-N NUM_RESULTS`\n`NUM_RESULTS` is an integer greater than 0', inline=False)
 | 
			
		||||
				embed.add_field(name="Searching by name", value='`NAME` is a regular expression used to search transfer names (no enclosing quotes; may contain spaces)', inline=False)
 | 
			
		||||
				embed.add_field(name="Examples", value="*List all transfers:* `{0}list`\n*Search using phrase 'ubuntu':* `{0}l ubuntu`\n*List downloading transfers:* `{0}l -f downloading`\n*Sort transfers by age:* `{0}list --sort age`".format(BOT_PREFIX), inline=False)
 | 
			
		||||
				embed.add_field(name="Examples", value="*List all transfers:* `{0}list`\n*Search using phrase 'ubuntu':* `{0}l ubuntu`\n*List downloading transfers:* `{0}l -f downloading`\n*List 10 most recently added transfers (sort transfers by age and specify number):* `{0}list --sort age -N 10`".format(BOT_PREFIX), inline=False)
 | 
			
		||||
				await context.message.channel.send(embed=embed)
 | 
			
		||||
			elif content in ["a","add"]:
 | 
			
		||||
				embed = discord.Embed(title='Add transfer', description="If multiple torrents are added, separate them by spaces", color=0xb51a00)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user