88import codecs
99import argparse
1010from datetime import datetime
11+ from collections import defaultdict
1112from configparser import ConfigParser
1213
1314import yaml
1415from inoreader import InoreaderClient
15- from inoreader .filter import Filter
16+ from inoreader .filter import get_filter
1617
1718
1819APPID_ENV_NAME = 'INOREADER_APP_ID'
@@ -174,13 +175,7 @@ def fetch_unread(folder, tags, outfile, out_format):
174175
175176def add_filter_parser (subparsers ):
176177 parser = subparsers .add_parser ('filter' , help = 'Select articles and do something' )
177- parser .add_argument ("-f" , "--folder" , required = True , help = 'Folder which articles belong to' )
178178 parser .add_argument ("-r" , "--rules" , required = True , help = 'YAML file with your rules' )
179- parser .add_argument ("-a" , "--action" , default = 'read' ,
180- choices = ['read' , 'like' , 'tag' , 'broadcast' , 'star' ],
181- help = 'Action you want to perform, default: read' )
182- parser .add_argument ("-t" , "--tags" ,
183- help = "Tag(s) to be used when action is 'tag', seprate with comma" )
184179
185180
186181def apply_action (articles , client , action , tags ):
@@ -190,7 +185,7 @@ def apply_action(articles, client, action, tags):
190185
191186 for article in articles :
192187 print ("Add tags [{}] on article: {}" .format (tags , article .title ))
193- elif action == 'read ' :
188+ elif action == 'mark_as_read ' :
194189 client .mark_as_read (articles )
195190 for article in articles :
196191 print ("Mark article as read: {}" .format (article .title ))
@@ -208,35 +203,70 @@ def apply_action(articles, client, action, tags):
208203 print ("Starred article: {}" .format (article .title ))
209204
210205
211- def filter_articles (folder , rules_file , action , tags ):
206+ def filter_articles (rules_file ):
212207 client = get_client ()
213208 filters = []
214209 for rule in yaml .load (open (rules_file )):
210+ name = rule .get ('name' )
211+ folders = rule ['folders' ]
212+
213+ fields = []
214+ # only 'title' or 'content' is supported now
215+ for field in rule .get ('fields' , ['title' , 'content' ]):
216+ if field not in ('title' , 'content' ):
217+ continue
218+ fields .append (field )
219+ cur_filter = get_filter (rule ['filter' ])
220+
221+ actions = []
222+ # only 'mark_as_read', 'like', 'star', 'broadcast', 'tag' is supported now
223+ for action in rule .get ('actions' , [{'type' : 'mark_as_read' }]):
224+ if action ['type' ] not in ('mark_as_read' , 'like' , 'star' , 'broadcast' , 'tag' ):
225+ continue
226+ actions .append (action )
227+
215228 filters .append ({
216- 'field' : rule .get ('field' , 'title' ),
217- 'filter' : Filter .from_config (rule ),
229+ 'name' : name ,
230+ 'folders' : folders ,
231+ 'fields' : fields ,
232+ 'filter' : cur_filter ,
233+ 'actions' : actions
218234 })
219235
220- matched_articles = []
221- for idx , article in enumerate (client .fetch_unread (folder = folder )):
222- matched = False
223- for article_filter in filters :
224- if article_filter ['field' ] in ('title' , 'title_or_content' ) and \
225- article_filter ['filter' ].validate (article .title ):
236+ articles_by_foler = {} # folder -> articles
237+ matched_articles = defaultdict (list ) # action -> articles
238+ for rule in filters :
239+ articles = []
240+ for folder in rule ['folders' ]:
241+ if folder not in articles_by_foler :
242+ articles_by_foler [folder ] = list (client .fetch_unread (folder = folder ))
243+
244+ articles .extend (articles_by_foler [folder ])
245+
246+ # FIXME: deduplicate
247+ count = 0
248+ for article in articles :
249+ matched = False
250+ if 'title' in rule ['fields' ] and rule ['filter' ].validate (article .title ):
226251 matched = True
227- break
228- if article_filter ['field' ] in ('content' , 'title_or_content' ) and \
229- article_filter ['filter' ].validate (article .text ):
252+ if 'content' in rule ['fields' ] and rule ['filter' ].validate (article .text ):
230253 matched = True
231- break
232- if matched :
233- matched_articles .append (article )
234- if len (matched_articles ) == 10 :
235- apply_action (matched_articles , client , action , tags )
236- matched_articles = []
237254
238- if matched_articles :
239- apply_action (matched_articles , client , action , tags )
255+ if matched :
256+ for action in rule ['actions' ]:
257+ matched_articles [action ['type' ]].append ((article , action ))
258+
259+ count += 1
260+ print ("[{}] matched {} articles with filter: {}" .format (
261+ datetime .now (), count , rule ['name' ]))
262+
263+ for action_name in matched_articles :
264+ articles , actions = zip (* matched_articles [action_name ])
265+ if action_name != 'tag' :
266+ apply_action (articles , client , action_name , None )
267+ else :
268+ for article , action in zip (articles , actions ):
269+ apply_action ([article ], client , 'tag' , action ['tags' ])
240270
241271
242272def main ():
@@ -263,11 +293,7 @@ def main():
263293 elif args .command == 'fetch-unread' :
264294 fetch_unread (args .folder , args .tags , args .outfile , args .out_format )
265295 elif args .command == 'filter' :
266- if args .action == 'tag' and not args .tags :
267- print ("Need at least one tag when action is 'tag'!" )
268- sys .exit (1 )
269-
270- filter_articles (args .folder , args .rules , args .action , args .tags )
296+ filter_articles (args .rules )
271297
272298
273299if __name__ == '__main__' :
0 commit comments