#!/usr/bin/python __version__ = "2.0.15" __author__ = "Tarek Galal" import sys, argparse, yowsup, logging from yowsup.env import YowsupEnv HELP_CONFIG = """ ############# Yowsup Configuration Sample ########### # # ==================== # The file contains info about your WhatsApp account. This is used during registration and login. # You can define or override all fields in the command line args as well. # # Country code. See http://www.ipipi.com/help/telephone-country-codes.htm. This is now required. cc=49 # # Your full phone number including the country code you defined in 'cc', without preceding '+' or '00' phone=491234567890 # # You obtain this password when you register using Yowsup. password=NDkxNTIyNTI1NjAyMkBzLndoYXRzYXBwLm5ldA== ####################################################### """ CR_TEXT = """yowsup-cli v{cliVersion} yowsup v{yowsupVersion} Copyright (c) 2012-2016 Tarek Galal http://www.openwhatsapp.org This software is provided free of charge. Copying and redistribution is encouraged. If you appreciate this software and you would like to support future development please consider donating: http://openwhatsapp.org/yowsup/donate """ logger = logging.getLogger("yowsup-cli") class YowArgParser(argparse.ArgumentParser): def __init__(self, *args, **kwargs): super(YowArgParser, self).__init__(*args, **kwargs) self.add_argument("-v", "--version", action = "store_true", help = "Print version info and exit" ) self.add_argument("-d", "--debug", action = "store_true", help = "Show debug messages" ) self.add_argument("--help-config", help = "Prints a config file sample", action = "store_true") self.args = {} def getArgs(self): return self.parse_args() def getConfig(self, config): try: f = open(config) out = {} for l in f: line = l.strip() if len(line) and line[0] not in ('#',';'): prep = line.split('#', 1)[0].split(';', 1)[0].split('=', 1) varname = prep[0].strip() val = prep[1].strip() out[varname.replace('-', '_')] = val return out except IOError: print("Invalid config path: %s" % config) sys.exit(1) def process(self): self.args = vars(self.parse_args()) if self.args["debug"]: logging.basicConfig(level = logging.DEBUG) else: logging.basicConfig(level = logging.INFO) if self.args["version"]: print("yowsup-cli v%s\nUsing yowsup v%s" % (__version__, yowsup.__version__)) sys.exit(0) if self.args["help_config"]: print(HELP_CONFIG) sys.exit(0) def printInfoText(self): print(CR_TEXT.format(cliVersion=__version__, yowsupVersion=yowsup.__version__)) class RegistrationArgParser(YowArgParser): def __init__(self, *args, **kwargs): super(RegistrationArgParser, self).__init__(*args, **kwargs) self.description = "WhatsApp Registration options" configGroup = self.add_argument_group("Configuration options", description = "Config file is optional. Other configuration arguments have higher priority if given, and will override those specified in the config file") configGroup.add_argument("-c", '--config', action = "store", help = 'Path to config file. If this is not set then you must set at least --phone and --cc arguments') configGroup.add_argument('-E', '--env', action = "store", help = "Set the environment yowsup simulates", choices = YowsupEnv.getRegisteredEnvs()) configGroup.add_argument("-m", '--mcc', action = "store", help = "Mobile Country Code. Check your mcc here: https://en.wikipedia.org/wiki/Mobile_country_code") configGroup.add_argument("-n", '--mnc', action = "store", help = "Mobile Network Code. Check your mnc from https://en.wikipedia.org/wiki/Mobile_country_code") # configGroup.add_argument("-M", '--sim-mcc', # action = "store", # help = "Mobile Country Code. Check your mcc here: https://en.wikipedia.org/wiki/Mobile_country_code" # ) # configGroup.add_argument("-N", '--sim-mnc', # action= "store", # help = "SIM MNC" # ) configGroup.add_argument("-p", '--phone', action= "store", help = " Your full phone number including the country code you defined in 'cc', without preceeding '+' or '00'") configGroup.add_argument("-C", '--cc', action = "store", help = "Country code. See http://www.ipipi.com/networkList.do. This is now required") # configGroup.add_argument("-i", '--id', # action="store", # help = "Identity" # ) regSteps = self.add_argument_group("Modes") regSteps = regSteps.add_mutually_exclusive_group() regSteps.add_argument("-r", '--requestcode', help='Request the digit registration code from Whatsapp.', action="store", required=False, metavar="(sms|voice)") regSteps.add_argument("-R", '--register', help='Register account on Whatsapp using the code you previously received', action="store", required=False, metavar="code") def process(self): super(RegistrationArgParser, self).process() config = self.getConfig(self.args["config"]) if self.args["config"] else {} if self.args["env"]: YowsupEnv.setEnv(self.args["env"]) if self.args["mcc"] : config["mcc"] = self.args["mcc"] if self.args["mnc"] : config["mnc"] = self.args["mnc"] if self.args["phone"] : config["phone"] = self.args["phone"] if self.args["cc" ] : config["cc"] = self.args["cc"] #if self.args["sim_mnc"] : config["sim_mnc"] = self.args["sim_mnc"] #if self.args["sim_mcc"] : config["sim_mcc"] = self.args["sim_mcc"] if not "mcc" in config: config["mcc"] = "000" if not "mnc" in config: config["mnc"] = "000" if not "sim_mcc" in config: config["sim_mcc"] = "000" if not "sim_mnc" in config: config["sim_mnc"] = "000" try: assert self.args["requestcode"] or self.args["register"], "Must specify one of the modes -r/-R" assert "cc" in config, "Must specify cc (country code)" assert "phone" in config, "Must specify phone number" except AssertionError as e: print(e) print("\n") return False if not config["phone"].startswith(config["cc"]): print("Error, phone number does not start with the specified country code\n") return False config["phone"] = config["phone"][len(config["cc"]):] if self.args["requestcode"]: self.handleRequestCode(self.args["requestcode"], config) elif self.args["register"]: self.handleRegister(self.args["register"], config) else: return False return True def handleRequestCode(self, method, config): from yowsup.registration import WACodeRequest self.printInfoText() codeReq = WACodeRequest(config["cc"], config["phone"], config["mcc"], config["mnc"], config["mcc"], config["mnc"], method ) result = codeReq.send() print(self.resultToString(result)) def handleRegister(self, code, config): from yowsup.registration import WARegRequest self.printInfoText() code = code.replace('-', '') req = WARegRequest(config["cc"], config["phone"], code) result = req.send() print(self.resultToString(result)) def resultToString(self, result): unistr = str if sys.version_info >= (3, 0) else unicode out = [] for k, v in result.items(): if v is None: continue out.append("%s: %s" %(k, v.encode("utf-8") if type(v) is unistr else v)) return "\n".join(out) class DemosArgParser(YowArgParser): def __init__(self, *args, **kwargs): super(DemosArgParser, self).__init__(*args, **kwargs) self.description = "Run a yowsup demo" configGroup = self.add_argument_group("Configuration options for demos") credentialsOpts = configGroup.add_mutually_exclusive_group() credentialsOpts.add_argument("-l", "--login", action="store", metavar="phone:b64password", help = "WhatsApp login credentials, in the format phonenumber:password, where password is base64 encoded.") credentialsOpts.add_argument("-c", "--config", action="store", help = "Path to config file containing authentication info. For more info about config format use --help-config") configGroup.add_argument('-E', '--env', action = "store", help = "Set the environment yowsup simulates", choices = YowsupEnv.getRegisteredEnvs()) configGroup.add_argument("-M", "--unmoxie", action="store_true", help="Disable E2E Encryption") cmdopts = self.add_argument_group("Command line interface demo") cmdopts.add_argument('-y', '--yowsup', action = "store_true", help = "Start the Yowsup command line client") echoOpts = self.add_argument_group("Echo client demo") echoOpts.add_argument('-e', '--echo', action = "store_true", help = "Start the Yowsup Echo client") sendOpts = self.add_argument_group("Send client demo") sendOpts.add_argument('-s', '--send', action="store", help = "Send a message to specified phone number, " "wait for server receipt and exit", metavar=("phone", "message"), nargs = 2) syncContacts = self.add_argument_group("Sync contacts") syncContacts.add_argument('-S','--sync', action = "store" , help = "Sync ( check valid ) whatsapp contacts",metavar =("contacts")) def process(self): super(DemosArgParser, self).process() if self.args["env"]: YowsupEnv.setEnv(self.args["env"]) if self.args["yowsup"]: self.startCmdline() elif self.args["echo"]: self.startEcho() elif self.args["send"]: self.startSendClient() elif self.args["sync"]: self.startSyncContacts() else: return False return True def _getCredentials(self): if self.args["login"]: return tuple(self.args["login"].split(":")) elif self.args["config"]: config = self.getConfig(self.args["config"]) assert "password" in config and "phone" in config, "Must specify at least phone number and password in config file" return config["phone"], config["password"] else: return None def startCmdline(self): from yowsup.demos import cli credentials = self._getCredentials() if not credentials: print("Error: You must specify a configuration method") sys.exit(1) self.printInfoText() stack = cli.YowsupCliStack(credentials, not self.args["unmoxie"]) stack.start() def startEcho(self): from yowsup.demos import echoclient credentials = self._getCredentials() if not credentials: print("Error: You must specify a configuration method") sys.exit(1) try: self.printInfoText() stack = echoclient.YowsupEchoStack(credentials, not self.args["unmoxie"]) stack.start() except KeyboardInterrupt: print("\nYowsdown") sys.exit(0) def startSendClient(self): from yowsup.demos import sendclient credentials = self._getCredentials() if not credentials: print("Error: You must specify a configuration method") sys.exit(1) try: self.printInfoText() stack = sendclient.YowsupSendStack(credentials, [([self.args["send"][0], self.args["send"][1]])], not self.args["unmoxie"]) stack.start() except KeyboardInterrupt: print("\nYowsdown") sys.exit(0) def startSyncContacts(self): from yowsup.demos import contacts credentials = self._getCredentials() if not credentials: print("Error: You must specify a configuration method") sys.exit(1) try: self.printInfoText() stack = contacts.YowsupSyncStack(credentials,self.args["sync"].split(','), not self.args["unmoxie"]) stack.start() except KeyboardInterrupt: print("\nYowsdown") sys.exit(0) if __name__ == "__main__": args = sys.argv if(len(args) > 1): del args[0] modeDict = { "demos": DemosArgParser, "registration": RegistrationArgParser, "version": None } if(len(args) == 0 or args[0] not in modeDict): print("Available commands:\n===================") print(", ".join(modeDict.keys())) sys.exit(1) mode = args[0] if mode == "version": print("yowsup-cli v%s\nUsing yowsup v%s" % (__version__, yowsup.__version__)) sys.exit(0) else: parser = modeDict[mode]() if not parser.process(): parser.print_help()