#-*- coding: utf-8 -*- import json import os import sys import threading import traceback import urlparse import feedparser import requests class Telegram: class API: URL = 'https://api.telegram.org/bot/' def __init__(self, token, method, http_method='POST'): self.token = token self.method = method self.http_method = http_method.upper() self.param = {} def setParam(self, **kwargs): self.param.update(kwargs) return self @property def url(self): ret = Telegram.API.URL.replace('', self.token).replace('', self.method) return ret @property def header(self): header = { 'accept': 'application/x-www-form-urlencoded', 'agent': ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) ' 'AppleWebKit/537.36 (KHTML, like Gecko) ' 'Chrome/59.0.3071.115 Safari/537.36'), } return header def __init__(self, token): self.token = token self.chatId = self.getUpdates(limit=1)['result'][0]['message']['chat']['id'] def request(self, api): if api.http_method == 'GET': req = requests.get(api.url, headers=api.header, params=api.param) else:# if api.http_method == 'POST': req = requests.get(api.url, headers=api.header, data=api.param) response = req.json() if not response['ok']: raise Exception(response['description']) return response def getUpdates(self, limit=None): api = Telegram.API(token=self.token, method='getUpdates', http_method='GET') if limit: api.setParam(limit=limit) return self.request(api) def sendMessage(self, text): api = Telegram.API(token=self.token, method='sendMessage').setParam(chat_id=self.chatId, text=text) return self.request(api) class Feed: def __init__(self, url): self.url = url self.etag = None self.modified = None self.feed = None self.save_dir = '.changes' if os.path.isfile(self.filepath): self.load() @property def filepath(self): domain = urlparse.urlparse(self.url).netloc return os.path.join(self.save_dir, domain) def save(self): data = { 'url': self.url, 'etag': self.etag, 'modified': self.modified } jsonstr = json.dumps(data, indent=2) if not os.path.isdir(self.save_dir): os.makedirs(self.save_dir) with open(self.filepath, 'w') as f: f.write(jsonstr) def load(self): with open(self.filepath, 'r') as f: jsonobj = json.load(f) self.etag = jsonobj['etag'] self.modified = jsonobj['modified'] def get(self): self.feed = feedparser.parse(self.url, etag=self.etag, modified=self.modified) self.etag = self.feed.etag self.modified = self.feed.modified return self.feed class Feed2Telegram: def __init__(self, feed_url, # required telegram_token, # required callback_get_iterator, # required, method returns iterable object from dictionary parsed by feedparser, prototype: def get_iterator(feed) callback_get_message, # required, method returns a message to send with an object of iterable object , prototype: def get_data(obj) check_interval=60*60, send_error=True, continue_on_error=False, threaded=False): self.url = feed_url self.feed = Feed(self.url) self.token = telegram_token self.telegram = Telegram(self.token) self.get_iterator = callback_get_iterator self.get_message = callback_get_message self.send_error = send_error self.continue_on_error = continue_on_error self.thread = None if threaded: self.thread = threading.Thread(target=self.run) self.stop_event = threading.Event() def run(self): while not self.stop_event.is_set(): try: feed = self.feed.get() for obj in self.get_iterator(feed): if self.stop_event.is_set(): break message = self.get_message(obj) self.telegram.sendMessage(message) else: self.feed.save() except: if self.send_error: e = traceback.format_exc() sys.stderr.write(e) self.telegram.sendMessage(e) if not self.continue_on_error: raise finally: if not self.stop_event.is_set() and self.continue_on_error: self.stop_event.wait(60 * 60) def start(self): self.stop_event.clear() if self.thread: self.thread.run() else: self.run() def join(self, timeout=None): if self.thread: self.thread.join(timeout) def stop(self): self.stop_event.set()