source: feed2telegram/trunk/feed2telegram.py@ 8

Last change on this file since 8 was 8, checked in by cheese, 7 years ago

#1 fix bug for new entries

File size: 4.9 KB
Line 
1#-*- coding: utf-8 -*-
2import datetime
3import email
4import json
5import os
6import sys
7import threading
8import time
9import traceback
10import urlparse
11
12import feedparser
13import requests
14
15class Telegram:
16 class API:
17 URL = 'https://api.telegram.org/bot<token>/<method>'
18
19 def __init__(self, token, method, http_method='POST'):
20 self.token = token
21 self.method = method
22 self.http_method = http_method.upper()
23 self.param = {}
24
25 def setParam(self, **kwargs):
26 self.param.update(kwargs)
27 return self
28
29 @property
30 def url(self):
31 ret = Telegram.API.URL.replace('<token>', self.token).replace('<method>', self.method)
32 return ret
33
34 @property
35 def header(self):
36 header = {
37 'accept': 'application/x-www-form-urlencoded',
38 }
39 return header
40
41 def __init__(self, token):
42 self.token = token
43 self.chatId = self.getUpdates(limit=1)['result'][0]['message']['chat']['id']
44
45 def request(self, api):
46 if api.http_method == 'GET':
47 req = requests.get(api.url, headers=api.header, params=api.param)
48 else:# if api.http_method == 'POST':
49 req = requests.get(api.url, headers=api.header, data=api.param)
50
51 response = req.json()
52
53 if not response['ok']:
54 raise Exception(response['description'])
55
56 return response
57
58 def getUpdates(self, limit=None):
59 api = Telegram.API(token=self.token, method='getUpdates', http_method='GET')
60
61 if limit:
62 api.setParam(limit=limit)
63
64 return self.request(api)
65
66 def sendMessage(self, text):
67 api = Telegram.API(token=self.token, method='sendMessage').setParam(chat_id=self.chatId, text=text)
68 return self.request(api)
69
70class Feed:
71 def __init__(self, url):
72 self.url = url
73 self.etag = None
74 self.modified = None
75 self.feed = None
76
77 self.save_dir = '.changes'
78
79 if os.path.isfile(self.filepath):
80 self.load()
81
82 @property
83 def filepath(self):
84 domain = urlparse.urlparse(self.url).netloc
85 return os.path.join(self.save_dir, domain)
86
87 def save(self):
88 data = {
89 'url': self.url,
90 'etag': self.etag,
91 'modified': self.modified
92 }
93 jsonstr = json.dumps(data, indent=2)
94
95 if not os.path.isdir(self.save_dir):
96 os.makedirs(self.save_dir)
97
98 with open(self.filepath, 'w') as f:
99 f.write(jsonstr)
100
101 def load(self):
102 with open(self.filepath, 'r') as f:
103 jsonobj = json.load(f)
104
105 self.etag = jsonobj['etag']
106 self.modified = jsonobj['modified']
107
108 def get(self):
109 self.feed = feedparser.parse(self.url, etag=self.etag, modified=self.modified)
110
111 self.etag = self.feed.etag
112 self.modified = self.feed.modified
113
114 return self.feed
115
116class Datetime:
117 def __init__(self):
118 self.datetime = None
119
120 def setRfc2822(self, value):
121 parsed = email.utils.parsedate(value)
122 self.datetime = datetime.datetime(*parsed[:6])
123 return self
124
125 def toTimestamp(self):
126 timetuple = self.datetime.timetuple()
127 return time.mktime(timetuple)
128
129class Feed2Telegram:
130 def __init__(self,
131 feed_url, # required
132 telegram_token, # required
133 callback_get_message, # required
134 callback_get_entries=lambda entries: entries,
135 check_interval=60*60,
136 new_entries_only=False,
137 send_error=True,
138 continue_on_error=False,
139 threaded=False):
140 self.url = feed_url
141 self.feed = Feed(self.url)
142
143 self.token = telegram_token
144 self.telegram = Telegram(self.token)
145
146 self.get_message = callback_get_message
147 self.get_entries = callback_get_entries
148
149 self.new_entries_only = new_entries_only
150 self.send_error = send_error
151 self.continue_on_error = continue_on_error
152
153 self.thread = None
154 if threaded:
155 self.thread = threading.Thread(target=self.run)
156
157 self.stop_event = threading.Event()
158 self.last_modified = None
159
160 def isSendingEntry(self, entry):
161 if self.new_entries_only:
162 if self.last_modified:
163 published = Datetime().setRfc2822(entry.published).toTimestamp()
164 last = Datetime().setRfc2822(self.last_modified).toTimestamp()
165
166 if published > last:
167 return True
168 else:
169 return True
170
171 return False
172
173 def run(self):
174 while not self.stop_event.is_set():
175 try:
176 self.last_modified = self.feed.modified
177 feed = self.feed.get()
178
179 for entry in self.get_entries(feed.entries):
180 if self.stop_event.is_set():
181 break
182
183 if not self.isSendingEntry(entry):
184 continue
185
186 message = self.get_message(entry)
187 self.telegram.sendMessage(message)
188
189 self.feed.save()
190
191 except:
192 if self.send_error:
193 e = traceback.format_exc()
194 sys.stderr.write(e)
195 self.telegram.sendMessage(e)
196
197 if not self.continue_on_error:
198 raise
199
200 finally:
201 if not self.stop_event.is_set() and self.continue_on_error:
202 self.stop_event.wait(60 * 60)
203
204 def start(self):
205 self.stop_event.clear()
206
207 if self.thread:
208 self.thread.run()
209 else:
210 self.run()
211
212 def join(self, timeout=None):
213 if self.thread:
214 self.thread.join(timeout)
215
216 def stop(self):
217 self.stop_event.set()
218
Note: See TracBrowser for help on using the repository browser.