Index: trunk/README.md
===================================================================
--- trunk/README.md	(revision 4)
+++ trunk/README.md	(revision 4)
@@ -0,0 +1,1 @@
+required: requests, feedparser
Index: trunk/example/example.py
===================================================================
--- trunk/example/example.py	(revision 4)
+++ trunk/example/example.py	(revision 4)
@@ -0,0 +1,50 @@
+#-*- coding: utf-8 -*-
+from feed2telegram import Feed2Telegram
+
+url   = '<url>'
+token = '<telegram-bot-token>'
+
+f2t = None
+
+def getIterator(feed):
+	return feed.entries
+
+def getMessage(entry):
+	return entry['link']
+
+def createInstance(threaded):
+	global f2t
+
+	f2t = Feed2Telegram(url,
+						token,
+						getIterator,
+						getMessage,
+						check_interval=60*60,
+						send_error=True,
+						continue_on_error=True,
+						threaded=threaded)
+
+# only for example ...
+def interrupt(signum, frame):
+	f2t.stop()
+
+def runWithThreading():
+	import signal
+	signal.signal(signal.SIGINT, interrupt)
+
+	createInstance(True)
+
+	f2t.start() # returned
+	f2t.join() # blocked
+
+def withoutThreading():
+	createInstance(False)
+
+	f2t.start() # blocked
+
+def main():
+	withoutThreading()
+
+if __name__ == '__main__':
+	main()
+
Index: trunk/feed2telegram.py
===================================================================
--- trunk/feed2telegram.py	(revision 4)
+++ trunk/feed2telegram.py	(revision 4)
@@ -0,0 +1,186 @@
+#-*- 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<token>/<method>'
+
+		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('<token>', self.token).replace('<method>', 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()
+
Index: trunk/implements/hikarinoakariost_feeder.py
===================================================================
--- trunk/implements/hikarinoakariost_feeder.py	(revision 4)
+++ trunk/implements/hikarinoakariost_feeder.py	(revision 4)
@@ -0,0 +1,13 @@
+#-*- coding: utf-8 -*-
+from feed2telegram import Feed2Telegram
+
+url   = 'http://hikarinoakariost.info/feed'
+token = '' # @hikarinoakariost_feeder_bot
+
+Feed2Telegram(url,
+			  token,
+			  lambda feed : feed.entries,
+			  lambda entry: entry['link'],
+			  continue_on_error=True
+			 ).start()
+
