지난번엔 텔레그램API를 활용해
토렌트 다운로드가 완료되면 텔레그램으로 알람을 받는 기능을 사용해 보았는데요.
이번엔 아예 텔레그램으로 토렌트 검색 및 다운로드 받는 기능을 구현해봤습니다.
자~ 곰탱이 봇! 일명 곰봇입니다~!
아래는 GomBot.py 소스입니다.
파이썬으로 작성되어 있으며,
최신본은 https://github.com/zeroidle/GomBot 에서 확인할 수 있습니다
#!/usr/bin/env python3.5 #-*- coding: utf-8 -*- import telepot import time import logging import json import pprint import feedparser import urllib import sys from _ctypes import Array #reload(sys) #sys.setdefaultencoding('utf-8') class GomBot(telepot.Bot): token='' admin_id=[] public_room=[] menu = {} def __init__(self): self.stdin_path = '/dev/null' self.stdout_path = '/dev/tty' self.stderr_path = '/dev/tty' self.pidfile_path = '/var/run/gombot.pid' self.pidfile_timeout = 5 super(GomBot, self).__init__(self.token) self._answerer = telepot.helper.Answerer(self) def run(self): try: self.message_loop(self.handle) log.debug('Listening ...') while 1: tc = Transmission() arr = Transmission.garbage_collection(tc) if len(arr)>0: #노티할애가 있으면 pms = Plexmediaserver() if not pms.refresh(2): log.debug("refresh 실패로 완료된 시드유지함") return str = "\n".join(arr) for room in self.public_room: self.sendMessage(room, "%s\n 준비되었습니다."%str) time.sleep(10) except Exception as e: log.exception("Main loop error") def handle(self, msg): flavor = 'normal' # normal message if flavor == 'normal': content_type, chat_type, chat_id = telepot.glance(msg) log.info('Normal Message:%s %s %s', content_type, chat_type, chat_id) log.debug(json.dumps(msg, ensure_ascii=False)) command = msg['text'] from_id = msg['from']['id'] chat_id = msg['chat']['id'] log.debug(command) if not command.startswith('/'): # 명령커맨드일 경우 return keyword = command[1:].split(' ') if keyword[0] == "셧다운": log.debug("셧다운 권한확인") if from_id in self.admin_id: self.sendMessage(chat_id,"모든 서버를 셧다운 합니다.") else: log.debug(" 권한 없는 사용자(%d)가 셧다운 시도" % from_id) self.sendMessage(chat_id,"권한이 없습니다.") elif keyword[0] == "하이": self.sendMessage(chat_id,"반갑구만 반가워요") elif keyword[0] == "검색": # 토렌트 검색해야지 if chat_id in self.public_room: # 채팅방이 공방이면 self.sendMessage(chat_id, "공개방입니다.\n 봇을 따로 소환해 검색하세요") return result = self.get_search_list(' '.join(keyword[1:])) self.set_menu(chat_id, from_id,result) elif keyword[0] == '받기': # 토렌트 검색해야지 idx = int(keyword[1].split('.')[0]) - 1 menu = self.menu[chat_id][idx] log.debug("다운로드주소 : %s" % menu['link']) tc = Transmission() dn_path = tc.get_dnpath(menu['title']) log.debug ("title: %s "% menu['title']) log.debug ("link: %s "% menu['link']) log.debug ("다운로드경로는 %s" % dn_path) to = tc.add_torrent(menu['link'],download_dir=dn_path) if (to): log.debug("downloading : %s %s" % (to.id, to.name)) self.sendMessage(chat_id,'%s 다운로딩' % menu['title']) else: self.sendMessage(chat_id,'다운로드 실패') elif keyword[0] == '확인': # 토렌트 진행상황 확인 tc = Transmission() torrents = tc.get_torrents() if len(torrents) == 0: self.sendMessage(chat_id, "다운로드중인 파일은 없습니다.") return str = '' for torrent in torrents: str = str + "%s - %s peers %.2f %%\n" % (torrent.name[:20],torrent.peersConnected, torrent.percentDone*100) self.sendMessage(chat_id, str) elif keyword[0] == "갱신": pms = Plexmediaserver() arr = [1,2] for num in arr: pms.refresh(num) else: str = {"/검색 [검색어] - 검색어를 토렌트사이트에서 검색합니다.", "/확인 - 토렌트 다운로드 진행상황을 확인합니다.", "/갱신 - Plex Media Server 라이브러리를 갱신합니다."} str = "\n".join(str) self.sendMessage(chat_id,str) # inline query - need `/setinline` elif flavor == 'inline_query': query_id, from_id, query_string = telepot.glance(msg, flavor=flavor) print('Inline Query:', query_id, from_id, query_string) def compute_answer(): # Compose your own answers articles = [{'type': 'article', 'id': 'abc', 'title': query_string, 'message_text': query_string}] return articles self._answerer.answer(msg, compute_answer) # chosen inline result - need `/setinlinefeedback` elif flavor == 'chosen_inline_result': result_id, from_id, query_string = telepot.glance(msg, flavor=flavor) print('Chosen Inline Result:', result_id, from_id, query_string) # Remember the chosen answer to do better next time #else: # raise telepot.BadFlavor(msg) def get_search_list(self, keyword): from bs4 import BeautifulSoup import urllib.request log.debug(keyword) url = Transmission.url % urllib.request.quote(keyword) headers = { 'User-Agent' : 'Mozilla/5.0' } req = urllib.request.Request(url, None, headers) handle = urllib.request.urlopen(req) data = handle.read() soup = BeautifulSoup(data,"lxml") count = 0 arrData = [] for item in soup.findAll("item"): count = count + 1 if (count>10): break arr = {} arr['title'] = item.title.next_element arr['link'] = item.link.next_element log.debug("%d 제목 : %s" % (count,arr['title'])) arrData.append(arr) return arrData def set_menu(self,chat_id, from_id, searchresult): self.menu[chat_id] = [] for (i,entry) in enumerate(searchresult): arr = {} arr['title'] = entry['title'] arr['link'] = entry['link'] self.menu[chat_id].append(arr) self.set_keyboard(chat_id, searchresult) def set_keyboard(self,chat_id, searchresult): output_list =[] for (i,entry) in enumerate(searchresult): title = "/받기 " + str(i+1) + ". " + entry['title'] temp_list = [] temp_list.append(title[:50]) log.debug(title) output_list.append(temp_list) show_keyboard = {'keyboard': output_list} self.sendMessage(chat_id,'선택해주세요',reply_markup=show_keyboard) class Plexmediaserver(): #LD_LIBRARY_PATH=/usr/lib/plexmediaserver #curl http://192.168.0.20:32400/library/sections/2/refresh host = '' port = '' def __init__(selfself): log.debug("plex init") def refresh(self, section_id): try: refresh_url = "http://%s:%s/library/sections/%s/refresh" % (self.host, self.port, section_id) log.debug("Plex Media Server - %d refresh", section_id) log.debug(refresh_url) f = urllib.request.urlopen(refresh_url) return True except: return False import transmissionrpc class Transmission(transmissionrpc.Client): shost = '' sport = '' uname = '' upass = '' url = '' mediapath = '' tvdir = '' moviedir='' tc = '' def __init__(self): super(Transmission, self).__init__(self.shost, self.sport, self.uname,self.upass) def garbage_collection(self): torrents = self.get_torrents() arr = [] for torrent in torrents: if torrent.percentDone == 1: # Downloaded self.remove_torrent(torrent.id) log.debug("Downloaded - %d %s"%(torrent.id, torrent.name)) ext_str = torrent.downloadDir.replace(self.mediapath,'') arr.append("%s (%s)"%(torrent.name,ext_str)) return arr def get_dir(self): import os list = os.listdir(self.mediapath+self.tvdir) log.debug(json.dumps(list, ensure_ascii=False)) return list def get_dnpath(self, filename): title = filename.split('.')[0] log.debug(title +" 제목을 기준으로 확인중") list = self.get_dir() found = False for n in list: if n.replace(' ','') in title.replace(' ',''): #문자열이 존재하면 dn_dir = self.mediapath+self.tvdir+"/"+n+"/" log.info("기존 디렉토리 찾음 " + n) return dn_dir if (self.is_tv(title)): # TV구만 dn_dir = self.mediapath+self.tvdir+"/"+title+"/" else: # 영화라 치자 dn_dir = self.mediapath+self.moviedir+"/"+title+"/" log.info(dn_dir+"에 다운로드 합니다.") return dn_dir def is_tv(self,title): found = False list = self.get_dir() if (not found): log.debug("_"+title + "_ TVDB를 참조합니다.") # TV프로그램인지 확인해보자 import urllib import xml.etree.ElementTree as ET TVDB_TITLE='http://thetvdb.com/api/GetSeries.php?seriesname=%s&language=ko' url = TVDB_TITLE % (urllib.parse.quote(title)) rss = ET.parse(urllib.request.urlopen(url)).getroot() seriesid = -1 for element in rss.findall("Series"): seriesid = element.findtext("seriesid") dn_dir = self.mediapath+self.tvdir+"/"+title+"/" log.debug(dn_dir) if (seriesid != -1): found = True else: log.debug("찾지 못했습니다." + url) return found def loadConf(): fp = open(conf_file,'r') conf = json.loads(fp.read()) fp.close() GomBot.token = conf['telegram']['token'] GomBot.admin_id = conf['telegram']['admin_id'] GomBot.public_room = conf['telegram']['public_room'] Transmission.shost = conf['transmission']['host'] Transmission.sport = conf['transmission']['port'] Transmission.uname = conf['transmission']['user'] Transmission.upass = conf['transmission']['pass'] Transmission.url = conf['transmission']['url'] Transmission.mediapath = conf['transmission']['mediapath'] Transmission.tvdir = conf['transmission']['tvdir'] Transmission.moviedir = conf['transmission']['moviedir'] Plexmediaserver.host = conf['plexmediaserver']['host'] Plexmediaserver.port = conf['plexmediaserver']['port'] import time from daemon import runner # python-daemon2 import logging.handlers #load configuration conf_file = 'gombot-settings.json' loadConf() bot = GomBot() log = logging.getLogger("GomBot") log.setLevel(logging.DEBUG) formatter = logging.Formatter("%(asctime)s %(name)s:%(levelname)s %(message)s (%(filename)s:%(lineno)s)", datefmt='%Y-%m-%d %H:%M:%S') handler = logging.handlers.RotatingFileHandler("GomBot.log", maxBytes=10240, backupCount=1) handler.setFormatter(formatter) handler2 = logging.StreamHandler() handler2.setFormatter(formatter) log.addHandler(handler) log.addHandler(handler2) if (len(sys.argv)>1 and sys.argv[1] == "foreground"): log.info("Foreground mode start") log.setLevel(logging.DEBUG) log.debug("Debug mode setted.") bot.run() exit() daemon_runner = runner.DaemonRunner(bot) daemon_runner.daemon_context.files_preserve=[handler.stream] daemon_runner.do_action()