Note what we're doing during login
[jan-pona-mute.git] / jan-pona-mute.py
CommitLineData
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
17import diaspy
6328099f 18import subprocess
149bc23e 19import argparse
6328099f 20import shutil
149bc23e
AS
21import cmd
22import sys
23import os
24
1882b5bc
AS
25_RC_PATHS = (
26 "~/.config/jan-pona-mute/login",
ebc699fd 27 "~/.jan-pona-mute.d/login"
1882b5bc
AS
28 "~/.jan-pona-mute"
29)
30
ebc699fd
AS
31_NOTE_DIRS = (
32 "~/.config/jan-pona-mute/notes",
33 "~/.jan-pona-mute.d/notes"
34)
35
6328099f 36_PAGERS = (
ebc699fd 37 os.getenv("PAGER"),
6328099f
AS
38 "mdcat",
39 "fold",
40 "cat"
41)
42
ebc699fd
AS
43_EDITORS = (
44 os.getenv("EDITOR"),
45 "vi",
46 "ed"
47)
48
82f0d747
AS
49shortcuts = {
50 "q": "quit",
51 "p": "preview",
52 "c": "comments",
53 "r": "reload",
54 "n": "notifications",
55 "e": "edit",
56 "d": "delete",
57 "g": "notifications reload",
58}
59
149bc23e 60def 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
68def 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
82def 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 89def get_pager():
ebc699fd
AS
90 """Pager finder"""
91 return get_binary(_PAGERS)
92
93def get_editor():
94 """Editor finder"""
95 return get_binary(_EDITORS)
6328099f 96
149bc23e
AS
97class 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 = []
1ff8fa2c 112 home = None
bec46251 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
134Use the 'account' and 'password' commands to set up your connection,
135then use the 'login' command to log in. If everything works as
136intended, use the 'save' command to save these commands to an init
137file.
6328099f 138
bec46251
AS
139Once you've listed things such as notifications or the home stream,
140enter 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."""
82f0d747 154 print(self.header("Info"))
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 234 else:
1860d749 235 print("Setting up a connection...")
1882b5bc
AS
236 self.connection = diaspy.connection.Connection(
237 pod = "https://%s" % self.pod, username = self.username, password = self.password)
238 try:
1860d749 239 print("Logging in...")
1882b5bc 240 self.connection.login()
1882b5bc 241 except diaspy.errors.LoginError:
ebc699fd 242 print("Login failed.")
149bc23e 243
6328099f
AS
244 def do_pager(self, pager):
245 """Set the pager, e.g. to cat"""
246 self.pager = pager
247 print("Pager set: %s" % self.pager)
248
ebc699fd
AS
249 def do_editor(self, editor):
250 """Set the editor, e.g. to ed"""
251 self.editor = editor
252 print("Editor set: %s" % self.editor)
253
8bf63019
AS
254 def header(self, line):
255 """Wrap line in header format."""
256 return self.header_format % line
257
149bc23e 258 def do_notifications(self, line):
bb8316e5
AS
259 """List notifications. Use 'notifications reload' to reload them."""
260 if line == "" and self.notifications:
261 print("Redisplaying the notifications in the cache.")
262 print("Use the 'reload' argument to reload them.")
263 elif line == "reload" or not self.notifications:
264 if self.connection == None:
265 print("Use the 'login' command, first.")
266 return
267 self.notifications = diaspy.notifications.Notifications(self.connection).last()
ebc699fd
AS
268 else:
269 print("The 'notifications' command only takes the optional argument 'reload'.")
270 return
bb8316e5
AS
271 if self.notifications:
272 for n, notification in enumerate(self.notifications):
82f0d747
AS
273 if notification.unread:
274 print(self.header("%2d. %s %s") % (n+1, notification.when(), notification))
275 else:
276 print("%2d. %s %s" % (n+1, notification.when(), notification))
bb8316e5 277 print("Enter a number to select the notification.")
bec46251 278 self.numbers_refer_to = 'notifications'
bb8316e5
AS
279 else:
280 print("There are no notifications. 😢")
149bc23e
AS
281
282 ### The end!
283 def do_quit(self, *args):
284 """Exit jan-pona-mute."""
285 print("Be safe!")
286 sys.exit()
287
288 def default(self, line):
289 if line.strip() == "EOF":
290 return self.onecmd("quit")
291
292 # Expand abbreviated commands
293 first_word = line.split()[0].strip()
82f0d747
AS
294 if first_word in shortcuts:
295 full_cmd = shortcuts[first_word]
149bc23e
AS
296 expanded = line.replace(first_word, full_cmd, 1)
297 return self.onecmd(expanded)
298
bb8316e5
AS
299 # Finally, see if it's a notification and show it
300 self.do_show(line)
1882b5bc 301
bb8316e5 302 def do_show(self, line):
18e74209 303 """Show the post given by the index number.
bec46251 304The index number must refer to the current list of notifications
1ff8fa2c
AS
305or the home stream. If no index number is given, show the current
306post again."""
bec46251
AS
307 if not self.notifications and not self.home:
308 print("Use the 'login' command to load notifications.")
bb8316e5 309 return
1ff8fa2c 310 if line == "" and self.post == None:
bec46251 311 print("Please specify a number.")
bb8316e5 312 return
1ff8fa2c
AS
313 if line != "":
314 try:
315 n = int(line.strip())
316 if self.numbers_refer_to == 'notifications':
317 notification = self.notifications[n-1]
318 self.show(notification)
319 self.load(notification.about())
320 elif self.numbers_refer_to == 'home':
321 self.post = self.home[n-1]
322 else:
323 print("Internal error: not sure what numbers '%s' refer to." % self.numbers_refer_to)
324 return
325 except ValueError:
326 print("The 'show' command takes a notification number but '%s' is not a number" % line)
327 return
328 except IndexError:
329 print("Index too high!")
bec46251 330 return
1882b5bc 331
18e74209
AS
332 print()
333 self.show(self.post)
1882b5bc 334
bb8316e5
AS
335 if(self.post.comments):
336 print()
88c1a828
AS
337 if len(self.post.comments) == 1:
338 print("There is 1 comment.")
339 else:
340 print("There are %d comments." % len(self.post.comments))
bb8316e5
AS
341 print("Use the 'comments' command to list the latest comments.")
342
343 def load(self, id):
344 """Load the post belonging to the id (from a notification),
345or get it from the cache."""
346 if id in self.post_cache:
347 self.post = self.post_cache[id]
ebc699fd 348 print("Retrieved post from the cache.")
bb8316e5
AS
349 else:
350 print("Loading...")
351 self.post = diaspy.models.Post(connection = self.connection, id = id)
352 self.post_cache[id] = self.post
353 return self.post
354
355 def do_reload(self, line):
356 """Reload the current post."""
357 if self.post == None:
358 print("Use the 'show' command to show a post, first.")
359 return
360 print("Reloading...")
361 self.post = diaspy.models.Post(connection = self.connection, id = self.post.id)
362 self.post_cache[id] = self.post
363
1882b5bc
AS
364 def show(self, item):
365 """Show the current item."""
6328099f 366 if self.pager:
88c1a828 367 subprocess.run(self.pager, input = str(item), text = True)
6328099f 368 else:
88c1a828 369 print(str(item))
6328099f 370
18e74209 371 def do_comments(self, line):
bb8316e5
AS
372 """Show the comments for the current post.
373Use the 'all' argument to show them all. Use a numerical argument to
374show that many. The default is to load the last five."""
18e74209 375 if self.post == None:
bb8316e5 376 print("Use the 'show' command to show a post, first.")
18e74209
AS
377 return
378 if self.post.comments == None:
379 print("The current post has no comments.")
380 return
381
382 n = 5
383 comments = self.post.comments
384
385 if line == "all":
386 n = None
387 elif line != "":
6328099f
AS
388 try:
389 n = int(line.strip())
390 except ValueError:
ebc699fd
AS
391 print("The 'comments' command takes a number as its argument, or 'all'.")
392 print("The default is to show the last 5 comments.")
6328099f 393 return
6328099f 394
ebc699fd
AS
395 if n == None:
396 start = 0
397 else:
398 # n is from the back
399 start = max(len(comments) - n, 0)
18e74209 400
42716c49 401 if comments:
ebc699fd 402 for n, comment in enumerate(comments[start:], start):
42716c49
AS
403 print()
404 print(self.header("%2d. %s %s") % (n+1, comment.when(), comment.author()))
405 print()
406 self.show(comment)
407 else:
408 print("There are no comments on the selected post.")
1882b5bc 409
714a2f67 410 def do_comment(self, line):
ebc699fd
AS
411 """Leave a comment on the current post.
412If you just use a number as your comment, it will refer to a note.
413Use the 'edit' command to edit notes."""
714a2f67 414 if self.post == None:
bb8316e5 415 print("Use the 'show' command to show a post, first.")
714a2f67 416 return
ebc699fd 417 try:
9200e1e1 418 # if the comment is just a number, use a note to post
ebc699fd
AS
419 n = int(line.strip())
420 notes = self.get_notes()
421 if notes:
422 try:
7e2b259d 423 line = self.read_note(notes[n-1])
ebc699fd
AS
424 print("Using note #%d: %s" % (n, notes[n-1]))
425 except IndexError:
426 print("Use the 'list notes' command to list valid numbers.")
427 return
428 else:
429 print("There are no notes to use.")
430 return
431 except ValueError:
9200e1e1
AS
432 # in which case we'll simply comment with the line
433 pass
714a2f67
AS
434 comment = self.post.comment(line)
435 self.post.comments.add(comment)
ebc699fd 436 self.undo.append("delete comment %s from %s" % (comment.id, self.post.id))
714a2f67
AS
437 print("Comment posted.")
438
1ff8fa2c
AS
439 def do_post(self, line):
440 """Write a post on the current stream.
441If you just use a number as your post, it will refer to a note.
442Use the 'edit' command to edit notes."""
443 if self.home == None:
444 self.home = diaspy.streams.Stream(self.connection)
445 try:
446 # if the post is just a number, use a note to post
447 n = int(line.strip())
448 notes = self.get_notes()
449 if notes:
450 try:
451 line = self.read_note(notes[n-1])
452 print("Using note #%d: %s" % (n, notes[n-1]))
453 except IndexError:
454 print("Use the 'list notes' command to list valid numbers.")
455 return
456 else:
457 print("There are no notes to use.")
458 return
459 except ValueError:
460 # in which case we'll simply post the line
461 pass
462 self.post = self.home.post(text = line)
463 self.post_cache[self.post.id] = self.post
464 self.undo.append("delete post %s" % self.post.id)
465 print("Posted. Use the 'show' command to show it.")
466
f1bfb7bc
AS
467 def do_delete(self, line):
468 """Delete a comment."""
469 words = line.strip().split()
470 if words:
471 if words[0] == "comment":
ebc699fd 472 if len(words) == 4:
b5d7f34e
AS
473 post = self.post_cache[words[3]]
474 post.delete_comment(words[1])
475 comments = [c.id for c in post.comments if c.id != id]
476 post.comments = diaspy.models.Comments(comments)
ebc699fd
AS
477 print("Comment deleted.")
478 return
b5d7f34e
AS
479 if self.post == None:
480 print("Use the 'show' command to show a post, first.")
481 return
ebc699fd
AS
482 if len(words) == 2:
483 try:
484 n = int(words[1])
485 comment = self.post.comments[n-1]
486 id = comment.id
487 except ValueError:
488 print("Deleting a comment requires an integer.")
489 return
490 except IndexError:
491 print("Use the 'comments' command to find valid comment numbers.")
492 return
493 # not catching any errors from diaspy
494 self.post.delete_comment(id)
495 # there is no self.post.comments.remove(id)
496 comments = [c.id for c in self.post.comments if c.id != id]
497 self.post.comments = diaspy.models.Comments(comments)
498 print("Comment deleted.")
499 return
500 else:
501 print("Deleting a comment requires a comment id and a post id, or a number.")
bb8316e5 502 print("delete comment <comment id> from <post id>")
ebc699fd
AS
503 print("delete comment 5")
504 return
505 if words[0] == "note":
506 if len(words) != 2:
507 print("Deleting a note requires a number.")
508 return
509 try:
510 n = int(words[1])
511 except ValueError:
512 print("Deleting a note requires an integer.")
f1bfb7bc 513 return
ebc699fd
AS
514 notes = self.get_notes()
515 if notes:
516 try:
517 os.unlink(self.get_note_path(notes[n-1]))
518 print("Deleted note #%d: %s" % (n, notes[n-1]))
519 except IndexError:
520 print("Use the 'list notes' command to list valid numbers.")
521 else:
522 print("There are no notes to delete.")
f1bfb7bc 523 else:
ebc699fd 524 print("Things to delete: comment, note.")
f1bfb7bc
AS
525 return
526 else:
527 print("Delete what?")
528
529 def do_undo(self, line):
530 """Undo an action."""
531 if line != "":
ebc699fd 532 print("The 'undo' command does not take an argument.")
f1bfb7bc
AS
533 return
534 if not self.undo:
535 print("There is nothing to undo.")
536 return
537 return self.onecmd(self.undo.pop())
538
ebc699fd
AS
539 def do_edit(self, line):
540 """Edit a note with a given name."""
541 if line == "":
542 print("Edit takes the name of a note as an argument.")
543 return
544 file = self.get_note_path(line)
545 if self.editor:
546 subprocess.run([self.editor, file])
cc59946e 547 self.onecmd("notes")
ebc699fd
AS
548 else:
549 print("Use the 'editor' command to set an editor.")
550
551 def do_notes(self, line):
552 """List notes"""
553 if line != "":
554 print("The 'notes' command does not take an argument.")
555 return
556 notes = self.get_notes()
557 if notes:
558 for n, note in enumerate(notes):
559 print(self.header("%2d. %s") % (n+1, note))
1ff8fa2c
AS
560 print("Use the 'edit' command to edit a note.")
561 print("Use the 'preview' command to look at a note.")
562 print("Use the 'post' command to post a note.")
563 print("Use the 'comment' command to post a comment.")
ebc699fd 564 else:
1ff8fa2c 565 print("Use 'edit' to create a note.")
ebc699fd
AS
566
567 def get_notes(self):
568 """Get the list of notes."""
569 return os.listdir(get_notes_dir())
570
571 def get_note_path(self, filename):
572 """Get the correct path for a note."""
573 return os.path.join(get_notes_dir(), filename)
574
7e2b259d
AS
575 def read_note(self, filename):
576 """Get text of a note."""
577 with open(self.get_note_path(filename), mode = 'r', encoding = 'utf-8') as fp:
578 return fp.read()
579
f38b656c
AS
580 def do_preview(self, line):
581 """Preview a note using your pager.
582Use the 'pager' command to set your pager to something like 'mdcat'."""
583 if line == "":
584 print("The 'preview' command the number of a note as an argument.")
585 print("Use the 'notes' command to list all your notes.")
586 return
587 try:
588 n = int(line.strip())
589 notes = self.get_notes()
590 if notes:
591 try:
592 self.show(self.read_note(notes[n-1]))
593 except IndexError:
594 print("Use the 'list notes' command to list valid numbers.")
595 return
596 else:
597 print("There are no notes to preview.")
598 return
599 except ValueError:
600 print("The 'preview' command takes a number as its argument.")
601 return
602
bec46251
AS
603 def do_home(self, line):
604 """Show the main stream containing the combined posts of the
605followed users and tags and the community spotlights posts if
606the user enabled those."""
607 if line == "":
608 if self.home:
609 print("Redisplaying the cached statuses of the home stream.")
610 print("Use the 'reload' argument to reload them.")
611 print("Use the 'all' argument to show them all.")
612 print("Use a number to show only that many.")
613 print("The default is 5.")
614 else:
615 print("Loading...")
616 self.home = diaspy.streams.Stream(self.connection)
617 self.home.fill()
618 for post in self.home:
619 if post.id not in self.post_cache:
620 self.post_cache[post.id] = post
621 elif line == "reload":
622 if self.connection == None:
623 print("Use the 'login' command, first.")
624 return
625 if self.home:
626 print("Reloading...")
627 self.home.update()
628 line = ""
629 else:
630 self.home = diaspy.streams.Stream(self.connection)
631 self.home.fill()
632
633 n = 5
1ff8fa2c 634 posts = sorted(self.home, key=lambda x: x.data()["created_at"])
bec46251
AS
635
636 if line == "all":
637 n = None
638 elif line != "":
639 try:
640 n = int(line.strip())
641 except ValueError:
642 print("The 'home' command takes a number as its argument, or 'reload' or 'all'.")
643 print("The default is to show the last 5 posts.")
644 return
645
646 if n == None:
647 start = 0
648 else:
649 # n is from the back
650 start = max(len(posts) - n, 0)
651
652 if posts:
653 for n, post in enumerate(posts[start:], start):
654 print()
655 print(self.header("%2d. %s %s") % (n+1, post.data()["created_at"], post.author()))
656 print()
657 self.show(post)
658
659 print("Enter a number to select the post.")
660 self.numbers_refer_to = 'home'
661 else:
662 print("The people you follow have nothing to say.")
663 print("The tags you follow are empty. 😢")
664
82f0d747
AS
665 def do_shortcuts(self, line):
666 """List all shortcuts."""
667 if line != "":
668 print("The 'shortcuts' command does not take an argument.")
669 return
670 print(self.header("Shortcuts"))
671 for shortcut in sorted(shortcuts):
672 print("%s\t%s" % (shortcut, shortcuts[shortcut]))
673 print("Use the 'shortcut' command to change or add shortcuts.")
674
675 def do_shortcut(self, line):
676 """Define a new shortcut."""
677 words = line.strip().split(maxsplit = 1)
678 if len(words) == 0:
679 return self.onecmd("shortcuts")
680 elif len(words) == 1:
681 shortcut = words[0]
682 if shortcut in shortcuts:
683 print("%s\t%s" % (shortcut, shortcuts[shortcut]))
684 else:
685 print("%s is not a shortcut" % shortcut)
686 else:
687 shortcuts[words[0]] = words[1]
688
149bc23e
AS
689# Main function
690def main():
691
692 # Parse args
693 parser = argparse.ArgumentParser(description='A command line Diaspora client.')
1882b5bc
AS
694 parser.add_argument('--no-init-file', dest='init_file', action='store_const',
695 const=False, default=True, help='Do not load a init file')
149bc23e
AS
696 args = parser.parse_args()
697
698 # Instantiate client
1882b5bc
AS
699 c = DiasporaClient()
700
701 # Process init file
6328099f 702 seen_pager = False
ebc699fd 703 seen_editor = False
1882b5bc
AS
704 if args.init_file:
705 rcfile = get_rcfile()
706 if rcfile:
707 print("Using init file %s" % rcfile)
708 with open(rcfile, "r") as fp:
709 for line in fp:
710 line = line.strip()
6328099f
AS
711 if line != "":
712 c.cmdqueue.append(line)
713 if not seen_pager:
714 seen_pager = line.startswith("pager ");
ebc699fd
AS
715 if not seen_editor:
716 seen_editor = line.startswith("editor ");
1882b5bc 717 else:
ebc699fd 718 print("Use the 'save' command to save your login sequence to an init file.")
149bc23e 719
6328099f
AS
720 if not seen_pager:
721 # prepend
722 c.cmdqueue.insert(0, "pager %s" % get_pager())
ebc699fd
AS
723 if not seen_editor:
724 # prepend
725 c.cmdqueue.insert(0, "editor %s" % get_editor())
6328099f 726
149bc23e
AS
727 # Endless interpret loop
728 while True:
729 try:
730 c.cmdloop()
731 except KeyboardInterrupt:
732 print("")
733
734if __name__ == '__main__':
735 main()