Commit | Line | Data |
---|---|---|
1882b5bc | 1 | #!/usr/bin/env python3 |
149bc23e AS |
2 | # Copyright (C) 2019 Alex Schroeder <alex@gnu.org> |
3 | ||
4 | # This program is free software: you can redistribute it and/or modify it under | |
5 | # the terms of the GNU Affero General Public License as published by the Free | |
6 | # Software Foundation, either version 3 of the License, or (at your option) any | |
7 | # later version. | |
8 | # | |
9 | # This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | |
11 | # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more | |
12 | # details. | |
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License along | |
15 | # with this program. If not, see <https://www.gnu.org/licenses/>. | |
16 | ||
17 | import diaspy | |
6328099f | 18 | import subprocess |
149bc23e | 19 | import argparse |
6328099f | 20 | import shutil |
149bc23e AS |
21 | import cmd |
22 | import sys | |
23 | import os | |
24 | ||
25 | # Command abbreviations | |
26 | _ABBREVS = { | |
27 | "q": "quit", | |
f38b656c | 28 | "p": "preview", |
1badc74e | 29 | "c": "comments", |
bb8316e5 AS |
30 | "r": "reload", |
31 | "n": "notifications", | |
ebc699fd | 32 | "e": "edit", |
f38b656c | 33 | "d": "delete", |
149bc23e AS |
34 | } |
35 | ||
1882b5bc AS |
36 | _RC_PATHS = ( |
37 | "~/.config/jan-pona-mute/login", | |
ebc699fd | 38 | "~/.jan-pona-mute.d/login" |
1882b5bc AS |
39 | "~/.jan-pona-mute" |
40 | ) | |
41 | ||
ebc699fd AS |
42 | _NOTE_DIRS = ( |
43 | "~/.config/jan-pona-mute/notes", | |
44 | "~/.jan-pona-mute.d/notes" | |
45 | ) | |
46 | ||
6328099f | 47 | _PAGERS = ( |
ebc699fd | 48 | os.getenv("PAGER"), |
6328099f AS |
49 | "mdcat", |
50 | "fold", | |
51 | "cat" | |
52 | ) | |
53 | ||
ebc699fd AS |
54 | _EDITORS = ( |
55 | os.getenv("EDITOR"), | |
56 | "vi", | |
57 | "ed" | |
58 | ) | |
59 | ||
149bc23e | 60 | def get_rcfile(): |
ebc699fd AS |
61 | """Init file finder""" |
62 | for path in _RC_PATHS: | |
63 | path = os.path.expanduser(path) | |
64 | if os.path.exists(path): | |
65 | return path | |
149bc23e AS |
66 | return None |
67 | ||
ebc699fd AS |
68 | def get_notes_dir(): |
69 | """Notes directory finder""" | |
70 | dir = None | |
71 | for path in _NOTE_DIRS: | |
72 | path = os.path.expanduser(path) | |
73 | if os.path.isdir(path): | |
74 | dir = path | |
75 | break | |
76 | if dir == None: | |
77 | dir = os.path.expanduser(_NOTE_DIRS[0]) | |
78 | if not os.path.isdir(dir): | |
79 | os.makedirs(dir) | |
80 | return dir | |
81 | ||
82 | def get_binary(list): | |
83 | for cmd in list: | |
84 | if cmd != None: | |
85 | bin = shutil.which(cmd) | |
86 | if bin != None: | |
87 | return bin | |
88 | ||
6328099f | 89 | def get_pager(): |
ebc699fd AS |
90 | """Pager finder""" |
91 | return get_binary(_PAGERS) | |
92 | ||
93 | def get_editor(): | |
94 | """Editor finder""" | |
95 | return get_binary(_EDITORS) | |
6328099f | 96 | |
149bc23e AS |
97 | class DiasporaClient(cmd.Cmd): |
98 | ||
99 | prompt = "\x1b[38;5;255m" + "> " + "\x1b[0m" | |
6328099f | 100 | intro = "Welcome to Diaspora! Use the intro command for a quick introduction." |
149bc23e | 101 | |
8bf63019 AS |
102 | header_format = "\x1b[1;38;5;255m" + "%s" + "\x1b[0m" |
103 | ||
149bc23e AS |
104 | username = None |
105 | pod = None | |
106 | password = None | |
6328099f | 107 | pager = None |
ebc699fd | 108 | editor = None |
149bc23e AS |
109 | |
110 | connection = None | |
18e74209 | 111 | notifications = [] |
bec46251 AS |
112 | home = [] |
113 | numbers_refer_to = None | |
18e74209 | 114 | post = None |
bb8316e5 | 115 | post_cache = {} # key is self.post.uid, and notification.id |
f1bfb7bc | 116 | |
714a2f67 AS |
117 | undo = [] |
118 | ||
149bc23e AS |
119 | |
120 | # dict mapping user ids to usernames | |
121 | users = {} | |
122 | ||
123 | def get_username(self, guid): | |
124 | if guid in self.users: | |
125 | return self.users[guid] | |
126 | else: | |
127 | user = diaspy.people.User(connection = self.connection, guid = guid) | |
128 | self.users[guid] = user.handle() | |
129 | return self.users[guid] | |
130 | ||
6328099f | 131 | def do_intro(self, line): |
d87d6adf | 132 | """Start here.""" |
6328099f | 133 | print(""" |
bb8316e5 AS |
134 | Use the 'account' and 'password' commands to set up your connection, |
135 | then use the 'login' command to log in. If everything works as | |
136 | intended, use the 'save' command to save these commands to an init | |
137 | file. | |
6328099f | 138 | |
bec46251 AS |
139 | Once you've listed things such as notifications or the home stream, |
140 | enter a number to select the corresponding item. | |
6328099f AS |
141 | """) |
142 | ||
149bc23e AS |
143 | def do_account(self, account): |
144 | """Set username and pod using the format username@pod.""" | |
145 | try: | |
146 | (self.username, self.pod) = account.split('@') | |
1882b5bc | 147 | print("Username and pod set: %s@%s" % (self.username, self.pod)) |
149bc23e AS |
148 | except ValueError: |
149 | print("The account must contain an @ character, e.g. kensanata@pluspora.com.") | |
150 | print("Use the account comand to set the account.") | |
151 | ||
152 | def do_info(self, line): | |
153 | """Get some info about things. By default, it is info about yourself.""" | |
154 | print("Info about yourself:") | |
1882b5bc AS |
155 | print("Username: %s" % self.username) |
156 | print("Password: %s" % ("None" if self.password == None else "set")) | |
157 | print("Pod: %s" % self.pod) | |
6328099f | 158 | print("Pager: %s" % self.pager) |
ebc699fd | 159 | print("Editor: %s" % self.editor) |
bec46251 | 160 | print("Cache: %s posts" % len(self.post_cache)) |
149bc23e AS |
161 | |
162 | def do_password(self, password): | |
163 | """Set the password.""" | |
164 | self.password = (None if self.password == "" else password) | |
1882b5bc AS |
165 | print("Password %s" % ("unset" if self.password == "" else "set")) |
166 | ||
167 | def do_save(self, line): | |
d87d6adf | 168 | """Save your login information to the init file.""" |
1882b5bc | 169 | if self.username == None or self.pod == None: |
ebc699fd | 170 | print("Use the 'account' command to set username and pod.") |
1882b5bc | 171 | elif self.password == None: |
ebc699fd | 172 | print("Use the 'password' command.") |
1882b5bc AS |
173 | else: |
174 | rcfile = get_rcfile() | |
175 | if rcfile == None: | |
ebc699fd | 176 | rfile = _RC_PATHS[0] |
1882b5bc AS |
177 | seen_account = False |
178 | seen_password = False | |
179 | seen_login = False | |
180 | changed = False | |
181 | file = [] | |
182 | with open(rcfile, "r") as fp: | |
183 | for line in fp: | |
184 | words = line.strip().split() | |
185 | if words: | |
186 | if words[0] == "account": | |
187 | seen_account = True | |
188 | account = "%s@%s" % (self.username, self.pod) | |
189 | if len(words) > 1 and words[1] != account: | |
190 | line = "account %s\n" % account | |
191 | changed = True | |
192 | elif words[0] == "password": | |
193 | seen_password = True | |
194 | if len(words) > 1 and words[1] != self.password: | |
195 | line = "password %s\n" % self.password | |
196 | changed = True | |
197 | elif words[0] == "login": | |
198 | if seen_account and seen_password: | |
199 | seen_login = True | |
200 | else: | |
201 | # skip login if no account or no password given | |
202 | line = None | |
203 | changed = True | |
204 | if line != None: | |
205 | file.append(line) | |
206 | if not seen_account: | |
207 | file.append("account %s@%s\n" % (self.username, self.pod)) | |
208 | changed = True | |
209 | if not seen_password: | |
210 | file.append("password %s\n" % self.password) | |
211 | changed = True | |
212 | if not seen_login: | |
213 | file.append("login\n") | |
214 | changed = True | |
215 | if changed: | |
216 | if os.path.isfile(rcfile): | |
217 | os.rename(rcfile, rcfile + "~") | |
218 | if not os.path.isdir(os.path.dirname(rcfile)): | |
219 | os.makedirs(os.path.dirname(rcfile)) | |
220 | with open(rcfile, "w") as fp: | |
221 | fp.write("".join(file)) | |
222 | print("Wrote %s" % rcfile) | |
223 | else: | |
224 | print("No changes made, %s left unchanged" % rcfile) | |
149bc23e AS |
225 | |
226 | def do_login(self, line): | |
227 | """Login.""" | |
228 | if line != "": | |
1882b5bc AS |
229 | self.onecmd("account %s" % line) |
230 | if self.username == None or self.pod == None: | |
ebc699fd | 231 | print("Use the 'account' command to set username and pod.") |
1882b5bc | 232 | elif self.password == None: |
ebc699fd | 233 | print("Use the 'password' command.") |
1882b5bc AS |
234 | else: |
235 | self.connection = diaspy.connection.Connection( | |
236 | pod = "https://%s" % self.pod, username = self.username, password = self.password) | |
237 | try: | |
238 | self.connection.login() | |
239 | self.onecmd("notifications") | |
240 | except diaspy.errors.LoginError: | |
ebc699fd | 241 | print("Login failed.") |
149bc23e | 242 | |
6328099f AS |
243 | def do_pager(self, pager): |
244 | """Set the pager, e.g. to cat""" | |
245 | self.pager = pager | |
246 | print("Pager set: %s" % self.pager) | |
247 | ||
ebc699fd AS |
248 | def do_editor(self, editor): |
249 | """Set the editor, e.g. to ed""" | |
250 | self.editor = editor | |
251 | print("Editor set: %s" % self.editor) | |
252 | ||
8bf63019 AS |
253 | def header(self, line): |
254 | """Wrap line in header format.""" | |
255 | return self.header_format % line | |
256 | ||
149bc23e | 257 | def do_notifications(self, line): |
bb8316e5 AS |
258 | """List notifications. Use 'notifications reload' to reload them.""" |
259 | if line == "" and self.notifications: | |
260 | print("Redisplaying the notifications in the cache.") | |
261 | print("Use the 'reload' argument to reload them.") | |
262 | elif line == "reload" or not self.notifications: | |
263 | if self.connection == None: | |
264 | print("Use the 'login' command, first.") | |
265 | return | |
266 | self.notifications = diaspy.notifications.Notifications(self.connection).last() | |
ebc699fd AS |
267 | else: |
268 | print("The 'notifications' command only takes the optional argument 'reload'.") | |
269 | return | |
bb8316e5 AS |
270 | if self.notifications: |
271 | for n, notification in enumerate(self.notifications): | |
272 | print(self.header("%2d. %s %s") % (n+1, notification.when(), notification)) | |
273 | print("Enter a number to select the notification.") | |
bec46251 | 274 | self.numbers_refer_to = 'notifications' |
bb8316e5 AS |
275 | else: |
276 | print("There are no notifications. 😢") | |
149bc23e AS |
277 | |
278 | ### The end! | |
279 | def do_quit(self, *args): | |
280 | """Exit jan-pona-mute.""" | |
281 | print("Be safe!") | |
282 | sys.exit() | |
283 | ||
284 | def default(self, line): | |
285 | if line.strip() == "EOF": | |
286 | return self.onecmd("quit") | |
287 | ||
288 | # Expand abbreviated commands | |
289 | first_word = line.split()[0].strip() | |
290 | if first_word in _ABBREVS: | |
291 | full_cmd = _ABBREVS[first_word] | |
292 | expanded = line.replace(first_word, full_cmd, 1) | |
293 | return self.onecmd(expanded) | |
294 | ||
bb8316e5 AS |
295 | # Finally, see if it's a notification and show it |
296 | self.do_show(line) | |
1882b5bc | 297 | |
bb8316e5 | 298 | def do_show(self, line): |
18e74209 | 299 | """Show the post given by the index number. |
bec46251 AS |
300 | The index number must refer to the current list of notifications |
301 | or the home stream.""" | |
302 | if not self.notifications and not self.home: | |
303 | print("Use the 'login' command to load notifications.") | |
bb8316e5 AS |
304 | return |
305 | if line == "": | |
bec46251 | 306 | print("Please specify a number.") |
bb8316e5 | 307 | return |
1882b5bc | 308 | try: |
bb8316e5 | 309 | n = int(line.strip()) |
bec46251 AS |
310 | if self.numbers_refer_to == 'notifications': |
311 | notification = self.notifications[n-1] | |
312 | self.show(notification) | |
313 | self.load(notification.about()) | |
314 | elif self.numbers_refer_to == 'home': | |
315 | self.post = self.home[n-1] | |
316 | else: | |
317 | print("Internal error: not sure what numbers '%s' refer to." % self.numbers_refer_to) | |
318 | return | |
bb8316e5 AS |
319 | except ValueError: |
320 | print("The 'show' command takes a notification number but '%s' is not a number" % line) | |
321 | return | |
1882b5bc | 322 | except IndexError: |
6328099f | 323 | print("Index too high!") |
1882b5bc AS |
324 | return |
325 | ||
18e74209 AS |
326 | print() |
327 | self.show(self.post) | |
1882b5bc | 328 | |
bb8316e5 AS |
329 | if(self.post.comments): |
330 | print() | |
88c1a828 AS |
331 | if len(self.post.comments) == 1: |
332 | print("There is 1 comment.") | |
333 | else: | |
334 | print("There are %d comments." % len(self.post.comments)) | |
bb8316e5 AS |
335 | print("Use the 'comments' command to list the latest comments.") |
336 | ||
337 | def load(self, id): | |
338 | """Load the post belonging to the id (from a notification), | |
339 | or get it from the cache.""" | |
340 | if id in self.post_cache: | |
341 | self.post = self.post_cache[id] | |
ebc699fd | 342 | print("Retrieved post from the cache.") |
bb8316e5 AS |
343 | else: |
344 | print("Loading...") | |
345 | self.post = diaspy.models.Post(connection = self.connection, id = id) | |
346 | self.post_cache[id] = self.post | |
347 | return self.post | |
348 | ||
349 | def do_reload(self, line): | |
350 | """Reload the current post.""" | |
351 | if self.post == None: | |
352 | print("Use the 'show' command to show a post, first.") | |
353 | return | |
354 | print("Reloading...") | |
355 | self.post = diaspy.models.Post(connection = self.connection, id = self.post.id) | |
356 | self.post_cache[id] = self.post | |
357 | ||
1882b5bc AS |
358 | def show(self, item): |
359 | """Show the current item.""" | |
6328099f | 360 | if self.pager: |
88c1a828 | 361 | subprocess.run(self.pager, input = str(item), text = True) |
6328099f | 362 | else: |
88c1a828 | 363 | print(str(item)) |
6328099f | 364 | |
18e74209 | 365 | def do_comments(self, line): |
bb8316e5 AS |
366 | """Show the comments for the current post. |
367 | Use the 'all' argument to show them all. Use a numerical argument to | |
368 | show that many. The default is to load the last five.""" | |
18e74209 | 369 | if self.post == None: |
bb8316e5 | 370 | print("Use the 'show' command to show a post, first.") |
18e74209 AS |
371 | return |
372 | if self.post.comments == None: | |
373 | print("The current post has no comments.") | |
374 | return | |
375 | ||
376 | n = 5 | |
377 | comments = self.post.comments | |
378 | ||
379 | if line == "all": | |
380 | n = None | |
381 | elif line != "": | |
6328099f AS |
382 | try: |
383 | n = int(line.strip()) | |
384 | except ValueError: | |
ebc699fd AS |
385 | print("The 'comments' command takes a number as its argument, or 'all'.") |
386 | print("The default is to show the last 5 comments.") | |
6328099f | 387 | return |
6328099f | 388 | |
ebc699fd AS |
389 | if n == None: |
390 | start = 0 | |
391 | else: | |
392 | # n is from the back | |
393 | start = max(len(comments) - n, 0) | |
18e74209 | 394 | |
42716c49 | 395 | if comments: |
ebc699fd | 396 | for n, comment in enumerate(comments[start:], start): |
42716c49 AS |
397 | print() |
398 | print(self.header("%2d. %s %s") % (n+1, comment.when(), comment.author())) | |
399 | print() | |
400 | self.show(comment) | |
401 | else: | |
402 | print("There are no comments on the selected post.") | |
1882b5bc | 403 | |
714a2f67 | 404 | def do_comment(self, line): |
ebc699fd AS |
405 | """Leave a comment on the current post. |
406 | If you just use a number as your comment, it will refer to a note. | |
407 | Use the 'edit' command to edit notes.""" | |
714a2f67 | 408 | if self.post == None: |
bb8316e5 | 409 | print("Use the 'show' command to show a post, first.") |
714a2f67 | 410 | return |
ebc699fd | 411 | try: |
9200e1e1 | 412 | # if the comment is just a number, use a note to post |
ebc699fd AS |
413 | n = int(line.strip()) |
414 | notes = self.get_notes() | |
415 | if notes: | |
416 | try: | |
7e2b259d | 417 | line = self.read_note(notes[n-1]) |
ebc699fd AS |
418 | print("Using note #%d: %s" % (n, notes[n-1])) |
419 | except IndexError: | |
420 | print("Use the 'list notes' command to list valid numbers.") | |
421 | return | |
422 | else: | |
423 | print("There are no notes to use.") | |
424 | return | |
425 | except ValueError: | |
9200e1e1 AS |
426 | # in which case we'll simply comment with the line |
427 | pass | |
714a2f67 AS |
428 | comment = self.post.comment(line) |
429 | self.post.comments.add(comment) | |
ebc699fd | 430 | self.undo.append("delete comment %s from %s" % (comment.id, self.post.id)) |
714a2f67 AS |
431 | print("Comment posted.") |
432 | ||
f1bfb7bc AS |
433 | def do_delete(self, line): |
434 | """Delete a comment.""" | |
435 | words = line.strip().split() | |
436 | if words: | |
437 | if words[0] == "comment": | |
ebc699fd | 438 | if len(words) == 4: |
b5d7f34e AS |
439 | post = self.post_cache[words[3]] |
440 | post.delete_comment(words[1]) | |
441 | comments = [c.id for c in post.comments if c.id != id] | |
442 | post.comments = diaspy.models.Comments(comments) | |
ebc699fd AS |
443 | print("Comment deleted.") |
444 | return | |
b5d7f34e AS |
445 | if self.post == None: |
446 | print("Use the 'show' command to show a post, first.") | |
447 | return | |
ebc699fd AS |
448 | if len(words) == 2: |
449 | try: | |
450 | n = int(words[1]) | |
451 | comment = self.post.comments[n-1] | |
452 | id = comment.id | |
453 | except ValueError: | |
454 | print("Deleting a comment requires an integer.") | |
455 | return | |
456 | except IndexError: | |
457 | print("Use the 'comments' command to find valid comment numbers.") | |
458 | return | |
459 | # not catching any errors from diaspy | |
460 | self.post.delete_comment(id) | |
461 | # there is no self.post.comments.remove(id) | |
462 | comments = [c.id for c in self.post.comments if c.id != id] | |
463 | self.post.comments = diaspy.models.Comments(comments) | |
464 | print("Comment deleted.") | |
465 | return | |
466 | else: | |
467 | print("Deleting a comment requires a comment id and a post id, or a number.") | |
bb8316e5 | 468 | print("delete comment <comment id> from <post id>") |
ebc699fd AS |
469 | print("delete comment 5") |
470 | return | |
471 | if words[0] == "note": | |
472 | if len(words) != 2: | |
473 | print("Deleting a note requires a number.") | |
474 | return | |
475 | try: | |
476 | n = int(words[1]) | |
477 | except ValueError: | |
478 | print("Deleting a note requires an integer.") | |
f1bfb7bc | 479 | return |
ebc699fd AS |
480 | notes = self.get_notes() |
481 | if notes: | |
482 | try: | |
483 | os.unlink(self.get_note_path(notes[n-1])) | |
484 | print("Deleted note #%d: %s" % (n, notes[n-1])) | |
485 | except IndexError: | |
486 | print("Use the 'list notes' command to list valid numbers.") | |
487 | else: | |
488 | print("There are no notes to delete.") | |
f1bfb7bc | 489 | else: |
ebc699fd | 490 | print("Things to delete: comment, note.") |
f1bfb7bc AS |
491 | return |
492 | else: | |
493 | print("Delete what?") | |
494 | ||
495 | def do_undo(self, line): | |
496 | """Undo an action.""" | |
497 | if line != "": | |
ebc699fd | 498 | print("The 'undo' command does not take an argument.") |
f1bfb7bc AS |
499 | return |
500 | if not self.undo: | |
501 | print("There is nothing to undo.") | |
502 | return | |
503 | return self.onecmd(self.undo.pop()) | |
504 | ||
ebc699fd AS |
505 | def do_edit(self, line): |
506 | """Edit a note with a given name.""" | |
507 | if line == "": | |
508 | print("Edit takes the name of a note as an argument.") | |
509 | return | |
510 | file = self.get_note_path(line) | |
511 | if self.editor: | |
512 | subprocess.run([self.editor, file]) | |
cc59946e | 513 | self.onecmd("notes") |
ebc699fd AS |
514 | else: |
515 | print("Use the 'editor' command to set an editor.") | |
516 | ||
517 | def do_notes(self, line): | |
518 | """List notes""" | |
519 | if line != "": | |
520 | print("The 'notes' command does not take an argument.") | |
521 | return | |
522 | notes = self.get_notes() | |
523 | if notes: | |
524 | for n, note in enumerate(notes): | |
525 | print(self.header("%2d. %s") % (n+1, note)) | |
526 | else: | |
527 | print("Use 'edit' to create a note.") | |
528 | else: | |
529 | print("Things to list: notes.") | |
530 | ||
531 | def get_notes(self): | |
532 | """Get the list of notes.""" | |
533 | return os.listdir(get_notes_dir()) | |
534 | ||
535 | def get_note_path(self, filename): | |
536 | """Get the correct path for a note.""" | |
537 | return os.path.join(get_notes_dir(), filename) | |
538 | ||
7e2b259d AS |
539 | def read_note(self, filename): |
540 | """Get text of a note.""" | |
541 | with open(self.get_note_path(filename), mode = 'r', encoding = 'utf-8') as fp: | |
542 | return fp.read() | |
543 | ||
f38b656c AS |
544 | def do_preview(self, line): |
545 | """Preview a note using your pager. | |
546 | Use the 'pager' command to set your pager to something like 'mdcat'.""" | |
547 | if line == "": | |
548 | print("The 'preview' command the number of a note as an argument.") | |
549 | print("Use the 'notes' command to list all your notes.") | |
550 | return | |
551 | try: | |
552 | n = int(line.strip()) | |
553 | notes = self.get_notes() | |
554 | if notes: | |
555 | try: | |
556 | self.show(self.read_note(notes[n-1])) | |
557 | except IndexError: | |
558 | print("Use the 'list notes' command to list valid numbers.") | |
559 | return | |
560 | else: | |
561 | print("There are no notes to preview.") | |
562 | return | |
563 | except ValueError: | |
564 | print("The 'preview' command takes a number as its argument.") | |
565 | return | |
566 | ||
bec46251 AS |
567 | def do_home(self, line): |
568 | """Show the main stream containing the combined posts of the | |
569 | followed users and tags and the community spotlights posts if | |
570 | the user enabled those.""" | |
571 | if line == "": | |
572 | if self.home: | |
573 | print("Redisplaying the cached statuses of the home stream.") | |
574 | print("Use the 'reload' argument to reload them.") | |
575 | print("Use the 'all' argument to show them all.") | |
576 | print("Use a number to show only that many.") | |
577 | print("The default is 5.") | |
578 | else: | |
579 | print("Loading...") | |
580 | self.home = diaspy.streams.Stream(self.connection) | |
581 | self.home.fill() | |
582 | for post in self.home: | |
583 | if post.id not in self.post_cache: | |
584 | self.post_cache[post.id] = post | |
585 | elif line == "reload": | |
586 | if self.connection == None: | |
587 | print("Use the 'login' command, first.") | |
588 | return | |
589 | if self.home: | |
590 | print("Reloading...") | |
591 | self.home.update() | |
592 | line = "" | |
593 | else: | |
594 | self.home = diaspy.streams.Stream(self.connection) | |
595 | self.home.fill() | |
596 | ||
597 | n = 5 | |
598 | posts = self.home | |
599 | ||
600 | if line == "all": | |
601 | n = None | |
602 | elif line != "": | |
603 | try: | |
604 | n = int(line.strip()) | |
605 | except ValueError: | |
606 | print("The 'home' command takes a number as its argument, or 'reload' or 'all'.") | |
607 | print("The default is to show the last 5 posts.") | |
608 | return | |
609 | ||
610 | if n == None: | |
611 | start = 0 | |
612 | else: | |
613 | # n is from the back | |
614 | start = max(len(posts) - n, 0) | |
615 | ||
616 | if posts: | |
617 | for n, post in enumerate(posts[start:], start): | |
618 | print() | |
619 | print(self.header("%2d. %s %s") % (n+1, post.data()["created_at"], post.author())) | |
620 | print() | |
621 | self.show(post) | |
622 | ||
623 | print("Enter a number to select the post.") | |
624 | self.numbers_refer_to = 'home' | |
625 | else: | |
626 | print("The people you follow have nothing to say.") | |
627 | print("The tags you follow are empty. 😢") | |
628 | ||
149bc23e AS |
629 | # Main function |
630 | def main(): | |
631 | ||
632 | # Parse args | |
633 | parser = argparse.ArgumentParser(description='A command line Diaspora client.') | |
1882b5bc AS |
634 | parser.add_argument('--no-init-file', dest='init_file', action='store_const', |
635 | const=False, default=True, help='Do not load a init file') | |
149bc23e AS |
636 | args = parser.parse_args() |
637 | ||
638 | # Instantiate client | |
1882b5bc AS |
639 | c = DiasporaClient() |
640 | ||
641 | # Process init file | |
6328099f | 642 | seen_pager = False |
ebc699fd | 643 | seen_editor = False |
1882b5bc AS |
644 | if args.init_file: |
645 | rcfile = get_rcfile() | |
646 | if rcfile: | |
647 | print("Using init file %s" % rcfile) | |
648 | with open(rcfile, "r") as fp: | |
649 | for line in fp: | |
650 | line = line.strip() | |
6328099f AS |
651 | if line != "": |
652 | c.cmdqueue.append(line) | |
653 | if not seen_pager: | |
654 | seen_pager = line.startswith("pager "); | |
ebc699fd AS |
655 | if not seen_editor: |
656 | seen_editor = line.startswith("editor "); | |
1882b5bc | 657 | else: |
ebc699fd | 658 | print("Use the 'save' command to save your login sequence to an init file.") |
149bc23e | 659 | |
6328099f AS |
660 | if not seen_pager: |
661 | # prepend | |
662 | c.cmdqueue.insert(0, "pager %s" % get_pager()) | |
ebc699fd AS |
663 | if not seen_editor: |
664 | # prepend | |
665 | c.cmdqueue.insert(0, "editor %s" % get_editor()) | |
6328099f | 666 | |
149bc23e AS |
667 | # Endless interpret loop |
668 | while True: | |
669 | try: | |
670 | c.cmdloop() | |
671 | except KeyboardInterrupt: | |
672 | print("") | |
673 | ||
674 | if __name__ == '__main__': | |
675 | main() |