source: feed2telegram/trunk/feed2telegram.py@ 12

Last change on this file since 12 was 12, checked in by cheese, 6 years ago

#1 change feedparser.parse to requests for timeout

File size: 5.2 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, chat_id):
42 self.token = token
43 self.chatId = 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 Datetime:
71 def __init__(self):
72 self.datetime = None
73
74 def setRfc2822(self, value):
75 parsed = email.utils.parsedate(value)
76 self.datetime = datetime.datetime(*parsed[:6])
77 return self
78
79 def toTimestamp(self):
80 timetuple = self.datetime.timetuple()
81 return time.mktime(timetuple)
82
83class Feed:
84 def __init__(self, url):
85 self.url = url
86 self.etag = None
87 self.modified = None
88 self.feed = None
89
90 self.save_dir = '.changes'
91
92 if os.path.isfile(self.filepath):
93 self.load()
94
95 @property
96 def filepath(self):
97 domain = urlparse.urlparse(self.url).netloc
98 return os.path.join(self.save_dir, domain)
99
100 def save(self):
101 data = {
102 'url': self.url,
103 'etag': self.etag,
104 'modified': self.modified
105 }
106 jsonstr = json.dumps(data, indent=2)
107
108 if not os.path.isdir(self.save_dir):
109 os.makedirs(self.save_dir)
110
111 with open(self.filepath, 'w') as f:
112 f.write(jsonstr)
113
114 def load(self):
115 with open(self.filepath, 'r') as f:
116 jsonobj = json.load(f)
117
118 self.etag = jsonobj['etag']
119 self.modified = jsonobj['modified']
120
121 def get(self):
122 try:
123 resp = requests.get(self.url, timeout=10.0)
124 except requests.ReadTimeout as e:
125 raise
126
127 # self.feed = feedparser.parse(self.url)# , etag=self.etag, modified=self.modified)
128 self.feed = feedparser.parse(resp.content)
129
130 try:
131 self.etag = self.feed.etag
132 except:
133 self.etag = None
134
135 try:
136 self.modified = self.feed.modified
137 except:
138 convert = lambda rfc2822: Datetime().setRfc2822(rfc2822).toTimestamp()
139 mtimes = [convert(entry.published) for entry in self.feed.entries]
140
141 if len(mtimes) > 0:
142 self.modified = self.feed.entries[mtimes.index(max(mtimes))].published
143
144 return self.feed
145
146class Feed2Telegram:
147 def __init__(self,
148 feed_url, # required
149 telegram_token, # required
150 chat_id, # required
151 callback_get_message, # required
152 callback_get_entries=lambda entries: reversed(entries),
153 check_interval=60*60,
154 new_entries_only=True,
155 send_error=True,
156 continue_on_error=False,
157 threaded=False):
158 self.url = feed_url
159 self.feed = Feed(self.url)
160
161 self.token = telegram_token
162 self.chat_id = chat_id
163 self.telegram = Telegram(self.token, self.chat_id)
164
165 self.get_message = callback_get_message
166 self.get_entries = callback_get_entries
167
168 self.new_entries_only = new_entries_only
169 self.send_error = send_error
170 self.continue_on_error = continue_on_error
171
172 self.thread = None
173 if threaded:
174 self.thread = threading.Thread(target=self.run)
175
176 self.stop_event = threading.Event()
177 self.last_modified = None
178
179 def isSendingEntry(self, entry):
180 if self.new_entries_only:
181 if self.last_modified:
182 published = Datetime().setRfc2822(entry.published).toTimestamp()
183 last = Datetime().setRfc2822(self.last_modified).toTimestamp()
184
185 if published > last:
186 return True
187 else:
188 return True
189
190 return False
191
192 def once(self):
193 self.last_modified = self.feed.modified
194 feed = self.feed.get()
195
196 for entry in self.get_entries(feed.entries):
197 if self.stop_event.is_set():
198 break
199
200 if not self.isSendingEntry(entry):
201 continue
202
203 message = self.get_message(entry)
204 self.telegram.sendMessage(message)
205
206 self.feed.save()
207
208 def run(self):
209 while not self.stop_event.is_set():
210
211 try:
212 self.once()
213
214 except:
215 if self.send_error:
216 e = traceback.format_exc()
217 sys.stderr.write(e)
218# self.telegram.sendMessage(e)
219
220 if not self.continue_on_error:
221 raise
222
223 finally:
224 if not self.stop_event.is_set() and self.continue_on_error:
225 self.stop_event.wait(60 * 60)
226
227 def start(self):
228 self.stop_event.clear()
229
230 if self.thread:
231 self.thread.run()
232 else:
233 self.run()
234
235 def join(self, timeout=None):
236 if self.thread:
237 self.thread.join(timeout)
238
239 def stop(self):
240 self.stop_event.set()
241
Note: See TracBrowser for help on using the repository browser.