source: feed2telegram/trunk/feed2telegram.py@ 11

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

#1 fix default argument and add once() method

File size: 5.3 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 self.feed = feedparser.parse(self.url)# , etag=self.etag, modified=self.modified)
123
124 try:
125 self.etag = self.feed.etag
126 except:
127 self.etag = None
128
129 try:
130 self.modified = self.feed.modified
131 except:
132 convert = lambda rfc2822: Datetime().setRfc2822(rfc2822).toTimestamp()
133 mtimes = [convert(entry.published) for entry in self.feed.entries]
134
135 if len(mtimes) > 0:
136 self.modified = self.feed.entries[mtimes.index(max(mtimes))].published
137
138 return self.feed
139
140class Feed2Telegram:
141 def __init__(self,
142 feed_url, # required
143 telegram_token, # required
144 chat_id, # required
145 callback_get_message, # required
146 callback_get_entries=lambda entries: reversed(entries),
147 check_interval=60*60,
148 new_entries_only=True,
149 send_error=True,
150 continue_on_error=False,
151 threaded=False):
152 self.url = feed_url
153 self.feed = Feed(self.url)
154
155 self.token = telegram_token
156 self.chat_id = chat_id
157 self.telegram = Telegram(self.token, self.chat_id)
158
159 self.get_message = callback_get_message
160 self.get_entries = callback_get_entries
161
162 self.new_entries_only = new_entries_only
163 self.send_error = send_error
164 self.continue_on_error = continue_on_error
165
166 self.thread = None
167 if threaded:
168 self.thread = threading.Thread(target=self.run)
169
170 self.stop_event = threading.Event()
171 self.last_modified = None
172
173 def isSendingEntry(self, entry):
174 if self.new_entries_only:
175 if self.last_modified:
176 published = Datetime().setRfc2822(entry.published).toTimestamp()
177 last = Datetime().setRfc2822(self.last_modified).toTimestamp()
178
179 if published > last:
180 return True
181 else:
182 return True
183
184 return False
185
186 def once(self):
187 self.last_modified = self.feed.modified
188 feed = self.feed.get()
189
190 for entry in self.get_entries(feed.entries):
191 if self.stop_event.is_set():
192 break
193
194 if not self.isSendingEntry(entry):
195 continue
196
197 message = self.get_message(entry)
198 self.telegram.sendMessage(message)
199
200 self.feed.save()
201
202 def run(self):
203 while not self.stop_event.is_set():
204
205 try:
206 self.once()
207
208 except:
209 if self.send_error:
210 e = traceback.format_exc()
211 sys.stderr.write(e)
212# self.telegram.sendMessage(e)
213
214 if not self.continue_on_error:
215 raise
216
217 finally:
218 if not self.stop_event.is_set() and self.continue_on_error:
219 self.stop_event.wait(60 * 60)
220
221 def start(self):
222 self.stop_event.clear()
223
224 if self.thread:
225 self.thread.run()
226 else:
227 self.run()
228
229 def join(self, timeout=None):
230 if self.thread:
231 self.thread.join(timeout)
232
233 def stop(self):
234 self.stop_event.set()
235
Note: See TracBrowser for help on using the repository browser.