forked from kaylee/uvgotmail
		
	broken idle version
This commit is contained in:
		
							parent
							
								
									0a56d15a83
								
							
						
					
					
						commit
						b5cce2a049
					
				
							
								
								
									
										303
									
								
								uvgotmail_buggy_idle.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										303
									
								
								uvgotmail_buggy_idle.py
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,303 @@
 | 
			
		||||
#!/usr/bin/env python3
 | 
			
		||||
import argparse
 | 
			
		||||
import sys
 | 
			
		||||
import os
 | 
			
		||||
import atexit
 | 
			
		||||
 | 
			
		||||
def parseYN(s, default = False):
 | 
			
		||||
    if default == False:
 | 
			
		||||
        return s.lower() in ['true', '1', 't', 'y', 'yes', 'yeah', 'yup', 'certainly', 'uh-huh', 'go for it']
 | 
			
		||||
    else: return not s.lower() in ['false', '0', 'f', 'n', 'no', 'nah', 'nope', 'certainly not', 'nuh-huh', 'bugger that']
 | 
			
		||||
 | 
			
		||||
def getYN(b):
 | 
			
		||||
    return "yes" if b else "no"
 | 
			
		||||
 | 
			
		||||
def parseConfig(args=None,config_path=None):
 | 
			
		||||
    # default settings
 | 
			
		||||
    if args:
 | 
			
		||||
        config = { "no_count": args.no_count, "mailbox_symbol": "📬", "mutt": args.mutt, "ssl": True, "server": "", "port": 0, "user": "", "password": "" }
 | 
			
		||||
    if not config_path:
 | 
			
		||||
        config_path = args.config
 | 
			
		||||
    filename = os.path.expanduser(config_path)
 | 
			
		||||
    # if we have a config file, parse it
 | 
			
		||||
    if os.path.exists(filename):
 | 
			
		||||
        with open(filename) as f:
 | 
			
		||||
            for line in f:
 | 
			
		||||
                line = line.strip()
 | 
			
		||||
                if len(line) == 0: continue
 | 
			
		||||
                if line[0] == "#": continue # skip commented lines
 | 
			
		||||
                kv = line.strip().split("=",1);
 | 
			
		||||
                kv[0] = kv[0].replace("-","_");
 | 
			
		||||
                if len(kv)==1:
 | 
			
		||||
                    continue
 | 
			
		||||
                try:
 | 
			
		||||
                    # load types appropriately
 | 
			
		||||
                    if type(config[kv[0]]) == type(True):
 | 
			
		||||
                        config[kv[0]] = (kv[1] == 'True') # cast to boolean
 | 
			
		||||
                    elif type(config[kv[0]]) == type(1):
 | 
			
		||||
                        config[kv[0]] = int(kv[1])
 | 
			
		||||
                    else:
 | 
			
		||||
                        config[kv[0]] = kv[1]
 | 
			
		||||
                except:
 | 
			
		||||
                    print("uvgotmail: unknown option: "+kv[0])
 | 
			
		||||
            # sort booleans
 | 
			
		||||
    # otherwise, prompt the user to create one
 | 
			
		||||
    elif(args.daemon and not args.mutt):
 | 
			
		||||
        if parseYN(input("uvgotmail: no config file exists. create now? [Y/n] "),default=True):
 | 
			
		||||
            createConfig(config_path, config,args)
 | 
			
		||||
            return
 | 
			
		||||
        else:
 | 
			
		||||
            print("uvgotmail: failed to start.")
 | 
			
		||||
            exit(1)
 | 
			
		||||
 | 
			
		||||
    # if no port is specified, use default
 | 
			
		||||
    if config["port"] == 0:
 | 
			
		||||
        config["port"] = 993 if config["ssl"] else 143
 | 
			
		||||
 | 
			
		||||
    # read muttrc if we should do that
 | 
			
		||||
    if config["mutt"] == True:
 | 
			
		||||
        try: # try both locations of dotfile
 | 
			
		||||
            with open(os.path.expanduser("~/.muttrc")) as f:
 | 
			
		||||
                muttrc = f.readlines();
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            try:
 | 
			
		||||
                with open(os.path.expanduser("~/.mutt/muttrc")) as f:
 | 
			
		||||
                    muttrc = f.readlines();
 | 
			
		||||
            except Exception as f:
 | 
			
		||||
                print(e)
 | 
			
		||||
                print(f)
 | 
			
		||||
                print("uvgotmail: could not read your muttrc. does it exist?")
 | 
			
		||||
                exit(1)
 | 
			
		||||
        for line in muttrc:
 | 
			
		||||
            line = line.strip()
 | 
			
		||||
            if len(line) == 0:
 | 
			
		||||
                continue
 | 
			
		||||
            if line[0] == "#": continue # skip commented lines
 | 
			
		||||
            if "set imap_user" in line:
 | 
			
		||||
                config["user"]=line.split("=",1)[1].strip().replace('"','')
 | 
			
		||||
            if "set imap_pass" in line:
 | 
			
		||||
                # voodoo to remove "s only from the start and end of password, if present
 | 
			
		||||
                config["password"]="".join(line.split("=",1)[1].strip().replace('"','',1).rsplit('"',1))
 | 
			
		||||
            if "set folder" in line:
 | 
			
		||||
                folder = line.split("=",1)[1].strip().replace('"','')
 | 
			
		||||
                if folder.startswith("imaps"):
 | 
			
		||||
                    config["ssl"] = True
 | 
			
		||||
                server_port = folder.split("//")[1].split(":")
 | 
			
		||||
                config["server"] = server_port[0]
 | 
			
		||||
                config["port"] = server_port[1]
 | 
			
		||||
 | 
			
		||||
    # make sure at least a server and a username are set
 | 
			
		||||
    if config["server"] == "" or config["user"] == "":
 | 
			
		||||
        print("uvgotmail: failed to start. no imap server or username specified.")
 | 
			
		||||
    return config
 | 
			
		||||
 | 
			
		||||
def createConfig(config_path, config,args):
 | 
			
		||||
    # helper function to deal with setting each item
 | 
			
		||||
    def getInput(prompt, existing):
 | 
			
		||||
        boolean = type(existing) == type(True)
 | 
			
		||||
        existing_str = getYN(existing) if boolean else str(existing)
 | 
			
		||||
        inp = input(prompt + " [" + existing_str + "]: ")
 | 
			
		||||
        if inp=="":
 | 
			
		||||
            return existing
 | 
			
		||||
        if boolean:
 | 
			
		||||
            inp = parseYN(inp)
 | 
			
		||||
        return inp
 | 
			
		||||
 | 
			
		||||
    import datetime
 | 
			
		||||
    config_path = getInput("where do you want to put your config file? (directory tree will be created if necessary)",config_path)
 | 
			
		||||
    config["mailbox_symbol"] = getInput("what symbol do you want to display when there are unread messages?",config["mailbox_symbol"])
 | 
			
		||||
    config["no_count"] = not getInput("do you want to display the number of unread messages?",not config["no_count"])
 | 
			
		||||
    config["mutt"] = getInput("do you want to use your muttrc for the server settings?",config["mutt"])
 | 
			
		||||
    if not config["mutt"]:
 | 
			
		||||
        config["server"] = getInput("what's your IMAP server?",config["server"])
 | 
			
		||||
        config["ssl"] = getInput("are you using ssl? it's " + str(datetime.date.today().year) +" so you really should be",config["ssl"])
 | 
			
		||||
        if(config["port"] == ""):
 | 
			
		||||
            config["port"] = 993 if config["ssl"] else 143
 | 
			
		||||
        config["port"] = getInput("what's the IMAP port?",config["port"])
 | 
			
		||||
        config["user"]= getInput("what's your IMAP username?",config["user"])
 | 
			
		||||
        config["password"] = getInput("what's your IMAP password?",config["password"])
 | 
			
		||||
    # confirm all this is correct; if not go round again
 | 
			
		||||
    if(not parseYN(input("is this correct? [y/N] "))):
 | 
			
		||||
        createConfig(config_path,config)
 | 
			
		||||
        return
 | 
			
		||||
    else:
 | 
			
		||||
        from pathlib import Path
 | 
			
		||||
        while True:
 | 
			
		||||
            path =  Path(os.path.expanduser(config_path))
 | 
			
		||||
            try:
 | 
			
		||||
                path.parent.mkdir(parents=True, exist_ok=True,mode=0o700)
 | 
			
		||||
                path.touch(mode=0o600,exist_ok=False)
 | 
			
		||||
                with path.open(mode="w") as f:
 | 
			
		||||
                    for i,key in enumerate(config):
 | 
			
		||||
                        f.write(key+"="+str(config[key])+("\n" if i < len(config)-1 else ""))
 | 
			
		||||
                print("config file created; starting daemon")
 | 
			
		||||
                parseConfig(config_path = config_path,args=args)
 | 
			
		||||
                break
 | 
			
		||||
            except Exception as e:
 | 
			
		||||
                if not parseYN(input("failed to create config file: " + str(e) +"... retry? [Y/n] ")):
 | 
			
		||||
                    exit(1)
 | 
			
		||||
                config_path = getInput("where do you want to put your config file? (directory tree will be created if necessary)",config_path)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main():
 | 
			
		||||
    parser = argparse.ArgumentParser()
 | 
			
		||||
    parser.add_argument("--daemon", help="run in daemon mode (put this at end of .bashrc)", action="store_true")
 | 
			
		||||
    parser.add_argument("--config", help="specify config file (default: ~/.uvgotmail/config)", default="~/.uvgotmail/config")
 | 
			
		||||
    parser.add_argument("--mutt", help="use existing mutt configuration (experimental)", action="store_true")
 | 
			
		||||
    parser.add_argument("--check", help="run in check mode (put this in PS1)", action="store_true")
 | 
			
		||||
    parser.add_argument("--no-count", help="don't display how many unread messages there are", action="store_true")
 | 
			
		||||
    parser.add_argument("--mailbox-symbol", help="specify a string instead of the mailbox emoji",default="")
 | 
			
		||||
    parser.add_argument("--debug", help="print errors",action="store_true")
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    global config
 | 
			
		||||
    config = parseConfig(args);
 | 
			
		||||
 | 
			
		||||
    config["debug"] = args.debug
 | 
			
		||||
 | 
			
		||||
    if(args.daemon == args.check):
 | 
			
		||||
        parser.print_help()
 | 
			
		||||
        sys.exit(0)
 | 
			
		||||
    if(args.daemon):
 | 
			
		||||
        daemon()
 | 
			
		||||
    else:
 | 
			
		||||
        check()
 | 
			
		||||
 | 
			
		||||
def check():
 | 
			
		||||
    filename = os.path.expanduser('~/.uvgotmail/unread')
 | 
			
		||||
    if os.path.exists(filename):
 | 
			
		||||
        with open(filename) as f:
 | 
			
		||||
            unread = f.read();
 | 
			
		||||
            if unread != '':
 | 
			
		||||
                unread = int(unread)
 | 
			
		||||
            else:
 | 
			
		||||
                unread = 0
 | 
			
		||||
        if unread == 0:
 | 
			
		||||
            sys.exit(0)
 | 
			
		||||
        pre = ""
 | 
			
		||||
        if(unread > 1 and config["no_count"] == False):
 | 
			
		||||
            pre = "("+str(unread)+") "
 | 
			
		||||
        print(pre+config["mailbox_symbol"],end=' ')
 | 
			
		||||
    # if there's no unread count file, just exit quietly
 | 
			
		||||
 | 
			
		||||
def writeUnreadFile(unseen):
 | 
			
		||||
    try:
 | 
			
		||||
        with open(os.path.expanduser('~/.uvgotmail/unread'),'w') as f:
 | 
			
		||||
            f.write(str(len(unseen)))
 | 
			
		||||
    except Exception as e:
 | 
			
		||||
        if config["debug"]: print(e)
 | 
			
		||||
        print("uvgotmail: could not write to unread file")
 | 
			
		||||
 | 
			
		||||
# we're implementing this in a disgusting way. i'm sorry.
 | 
			
		||||
import time
 | 
			
		||||
import socket
 | 
			
		||||
import re
 | 
			
		||||
 | 
			
		||||
def idleFunction(s,unseen):
 | 
			
		||||
    s._startIdle = time.time()
 | 
			
		||||
    if 'IDLE' not in s.capabilities:
 | 
			
		||||
        raise s.error('Server does not support IDLE')
 | 
			
		||||
    idle_tag = s._command('IDLE')  # start idling
 | 
			
		||||
    s._get_response()
 | 
			
		||||
    while line := s._get_line():
 | 
			
		||||
        if b'FETCH' in line:
 | 
			
		||||
            l = str(line)
 | 
			
		||||
            msg = re.findall(r'\d+',l)[0]
 | 
			
		||||
            if config["debug"]: print(msg, unseen)
 | 
			
		||||
            if "seen" in l.lower():
 | 
			
		||||
                unseen.remove(msg)
 | 
			
		||||
            else:
 | 
			
		||||
                unseen.append(msg)
 | 
			
		||||
            print(unseen)
 | 
			
		||||
            writeUnreadFile(unseen)
 | 
			
		||||
        else:
 | 
			
		||||
            # we only want to idle for ten minutes maximum. we will use the keep-alive messages to keep track in a single thread.
 | 
			
		||||
            if(time.time() - s.start > 600):
 | 
			
		||||
                s.send(b'DONE' + imaplib.CRLF)
 | 
			
		||||
                return s._command_complete('IDLE', idle_tag)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# thanks to trentbuck on github for this awful idea
 | 
			
		||||
 | 
			
		||||
def daemon():
 | 
			
		||||
    import imaplib
 | 
			
		||||
    imaplib.Commands['IDLE'] = ('AUTH', 'SELECTED')
 | 
			
		||||
    class IMAP4_SSL_plus_IDLE(imaplib.IMAP4_SSL):
 | 
			
		||||
        def idle(self, unseen):
 | 
			
		||||
            idleFunction(self,unseen)
 | 
			
		||||
    class IMAP4_plus_IDLE(imaplib.IMAP4):
 | 
			
		||||
        def idle(self, unseen):
 | 
			
		||||
            idleFunction(self,unseen)
 | 
			
		||||
 | 
			
		||||
    IMAP4_SSL = IMAP4_SSL_plus_IDLE
 | 
			
		||||
    IMAP4 = IMAP4_plus_IDLE
 | 
			
		||||
    socket.setdefaulttimeout(120)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    # first check if daemon is already running
 | 
			
		||||
    pidfile = os.path.expanduser('~/.uvgotmail/.run.pid')
 | 
			
		||||
    if os.path.exists(pidfile):
 | 
			
		||||
        with open(pidfile) as f:
 | 
			
		||||
            pid = f.read()
 | 
			
		||||
            if pid != '':
 | 
			
		||||
                pid = int(pid)
 | 
			
		||||
            else:
 | 
			
		||||
                pid = 0
 | 
			
		||||
            running_cmd = os.popen("ps -p" + str(pid) +" -o command").read()
 | 
			
		||||
            current_cmd = os.popen("ps -p" + str(os.getpid()) +" -o command").read()
 | 
			
		||||
            if running_cmd == current_cmd or os.path.basename(sys.argv[0]) in running_cmd:
 | 
			
		||||
                # the process is running; we don't need to start up at all
 | 
			
		||||
                exit(0)
 | 
			
		||||
    try:
 | 
			
		||||
        with open(pidfile,'w') as f:
 | 
			
		||||
            f.write(str(os.getpid()))
 | 
			
		||||
        def delPid(pidfile):
 | 
			
		||||
            os.remove(pidfile)
 | 
			
		||||
        atexit.register(delPid,pidfile)
 | 
			
		||||
    except:
 | 
			
		||||
        print("uvgotmail: failed to write to PID file")
 | 
			
		||||
        exit(1)
 | 
			
		||||
 | 
			
		||||
    # main loop
 | 
			
		||||
    import time
 | 
			
		||||
    start_time = time.time();
 | 
			
		||||
    IMAP4_func = IMAP4_SSL if config["ssl"] else IMAP4
 | 
			
		||||
    while True:
 | 
			
		||||
        try:
 | 
			
		||||
            with IMAP4_func(config["server"],config["port"]) as conn:
 | 
			
		||||
                conn.login(user=config["user"],password=config["password"])
 | 
			
		||||
                conn.select(mailbox='INBOX',readonly=True)
 | 
			
		||||
                typ,msgnums = conn.search(None, "UNSEEN");
 | 
			
		||||
                unseen = [n.decode() for n in msgnums if n != b'']
 | 
			
		||||
                if len(unseen)>0:
 | 
			
		||||
                    unseen=unseen[0].split(' ')
 | 
			
		||||
                else:
 | 
			
		||||
                    unseen=[]
 | 
			
		||||
                if config["debug"]: print(unseen)
 | 
			
		||||
                writeUnreadFile(unseen)
 | 
			
		||||
                #try:
 | 
			
		||||
                resp = conn.idle(unseen)
 | 
			
		||||
                #except Exception as e:
 | 
			
		||||
                if config["debug"]: print(e)
 | 
			
		||||
                if time.time() - start_time <= 10:
 | 
			
		||||
                    print("uvgotmail: too many imap errors. exiting.")
 | 
			
		||||
                    sys.exit(1)
 | 
			
		||||
                start_time = time.time();
 | 
			
		||||
 | 
			
		||||
        except Exception as e:
 | 
			
		||||
            if config["debug"]:
 | 
			
		||||
                print(e)
 | 
			
		||||
            print("uvgotmail: failed to log in to server. dumping config.")
 | 
			
		||||
            print(config);
 | 
			
		||||
            exit(1)
 | 
			
		||||
 | 
			
		||||
# handle ctrl+c gracefully
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    try:
 | 
			
		||||
        main()
 | 
			
		||||
    except KeyboardInterrupt:
 | 
			
		||||
        print('Interrupted')
 | 
			
		||||
        exit(130)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user