├── Procfile ├── travis.yml ├── requirements.txt ├── README.md ├── Bevco.py ├── run.py ├── .gitignore └── layer.py /Procfile: -------------------------------------------------------------------------------- 1 | worker:python run.py -------------------------------------------------------------------------------- /travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.4" 4 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests==2.20.0 2 | yowsup2==2.5.7 3 | python-dotenv==0.7.1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # whatsapp-Bot 2 | A simple whatsapp bot build using yowsup2(https://github.com/tgalal/yowsup) 3 | 4 | Refer https://letsprogramit.com/posts/whatsapp-hackernews-bot/ for details 5 | -------------------------------------------------------------------------------- /Bevco.py: -------------------------------------------------------------------------------- 1 | import json 2 | from os.path import expanduser 3 | class Bevco(object): 4 | dir = str(expanduser("~"))+"/bevco.json" 5 | bottles = [] 6 | searchIndex = {} 7 | def __init__(self): 8 | with open(self.dir) as jsonfh: 9 | self.bottles = json.load(jsonfh) 10 | 11 | 12 | def getBottlesByName(self,name): 13 | searchBottles = [] 14 | indexB = self.searchIndex.get(name) 15 | if indexB is not None: 16 | return indexB 17 | for bottle in self.bottles: 18 | brand = bottle.get('brand') 19 | # print(brand) 20 | if name in brand: 21 | searchBottles.append(bottle) 22 | self.searchIndex[name] = searchBottles 23 | return searchBottles 24 | 25 | 26 | 27 | # print(jstr) 28 | -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from yowsup.stacks import YowStackBuilder 2 | from layer import EchoLayer 3 | from yowsup.layers.auth import AuthError 4 | from yowsup.layers import YowLayerEvent 5 | from yowsup.layers.network import YowNetworkLayer 6 | from dotenv import load_dotenv 7 | import os 8 | from os.path import expanduser 9 | envPath = str(expanduser("~")) + '/whatsappbot.env' 10 | print(envPath) 11 | load_dotenv(envPath) 12 | 13 | 14 | UNAME = str(os.environ.get("uname")) 15 | PASS = str(os.environ.get("pass")) 16 | 17 | credentials = (UNAME, PASS) # replace with your phone and password 18 | 19 | if __name__== "__main__": 20 | stackBuilder = YowStackBuilder() 21 | 22 | stack = stackBuilder\ 23 | .pushDefaultLayers(True)\ 24 | .push(EchoLayer)\ 25 | .build() 26 | print("setting credentials") 27 | stack.setCredentials(credentials) 28 | stack.broadcastEvent(YowLayerEvent(YowNetworkLayer.EVENT_STATE_CONNECT)) #sending the connect signal 29 | try: 30 | stack.loop() #this is the program mainloop 31 | except AuthError as e: 32 | print("Authentication Error: %s" % e.message) 33 | 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | .static_storage/ 57 | .media/ 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # Environments 86 | .env 87 | .venv 88 | env/ 89 | venv/ 90 | ENV/ 91 | env.bak/ 92 | venv.bak/ 93 | 94 | # Spyder project settings 95 | .spyderproject 96 | .spyproject 97 | 98 | # Rope project settings 99 | .ropeproject 100 | 101 | # mkdocs documentation 102 | /site 103 | 104 | # mypy 105 | .mypy_cache/ 106 | 107 | .vscode -------------------------------------------------------------------------------- /layer.py: -------------------------------------------------------------------------------- 1 | from yowsup.layers.interface import YowInterfaceLayer, ProtocolEntityCallback 2 | from yowsup.layers.protocol_messages.protocolentities import TextMessageProtocolEntity 3 | from yowsup.layers.protocol_receipts.protocolentities import OutgoingReceiptProtocolEntity 4 | from yowsup.layers.protocol_acks.protocolentities import OutgoingAckProtocolEntity 5 | from Bevco import Bevco 6 | import requests 7 | class EchoLayer(YowInterfaceLayer): 8 | hnApi = "http://hn.algolia.com/api/v1/search" 9 | bevco = Bevco() 10 | 11 | @ProtocolEntityCallback("message") 12 | def onMessage(self, messageProtocolEntity): 13 | #send receipt otherwise we keep receiving the same message over and over 14 | if messageProtocolEntity.getType() == 'media': 15 | self.sendMsg(messageProtocolEntity,"") 16 | else: 17 | incomingMsg = messageProtocolEntity.getBody().strip().lower() 18 | sender = messageProtocolEntity.getFrom() 19 | print(sender+" says "+incomingMsg) 20 | if incomingMsg== "hn": 21 | payload = {'tags':'front_page'} 22 | r = requests.get(self.hnApi,params=payload) 23 | msg = self.getMsgFromPosts(r) 24 | self.sendMsg(messageProtocolEntity,msg) 25 | elif incomingMsg.startswith("hn ") and len(incomingMsg)>3: 26 | searchTerm = incomingMsg.split("hn ",1)[1] 27 | payload= {"query":searchTerm,"tags":"story"} 28 | r = requests.get(self.hnApi,params=payload) 29 | msg = self.getMsgFromPosts(r,searchTerm) 30 | self.sendMsg(messageProtocolEntity,msg) 31 | elif incomingMsg.startswith("bevco") and len(incomingMsg) > 6: 32 | searchTerm = incomingMsg.split("bevco ", 1)[1] 33 | msg = self.getMsgForBottles(searchTerm) 34 | self.sendMsg(messageProtocolEntity, msg) 35 | else: 36 | self.sendMsg(messageProtocolEntity,"") 37 | 38 | 39 | @ProtocolEntityCallback("receipt") 40 | def onReceipt(self, entity): 41 | ack = OutgoingAckProtocolEntity(entity.getId(), "receipt", entity.getType(), entity.getFrom()) 42 | self.toLower(ack) 43 | 44 | def sendMsg(self,messageProtocolEntity,message): 45 | receipt = OutgoingReceiptProtocolEntity(messageProtocolEntity.getId(), 46 | messageProtocolEntity.getFrom(), 'read', messageProtocolEntity.getParticipant()) 47 | if message=="": 48 | outgoingMessageProtocolEntity = TextMessageProtocolEntity( 49 | "Welcome to hacker news bot, try sending *_hn_* for *hacker-news for the day*\n To search hackernews try sending *_hn_* \n\nOn another note, search Kerala beverages for all available brands and prices \n *bevco* 😉🍻🍻 \n ex:\n 1. *hn* \n 2. *hn javascript* \n 3. *bevco beer* \n 4. *bevco 8 pm*", 50 | to = messageProtocolEntity.getFrom()) 51 | else: 52 | outgoingMessageProtocolEntity = TextMessageProtocolEntity( 53 | message, 54 | to = messageProtocolEntity.getFrom()) 55 | self.toLower(receipt) 56 | self.toLower(outgoingMessageProtocolEntity) 57 | 58 | def getMsgFromPosts(self,r,type="normal"): 59 | response = r.json() 60 | if r.status_code == 500: 61 | msg = response.get('error') 62 | else: 63 | posts = response.get('hits') 64 | msg='*Hacker News For the day* \n\n' 65 | if type!="normal": 66 | msg='*Hacker News search results for _'+type+'_:* \n\n' 67 | for post in posts: 68 | rTitle = post.get('_highlightResult').get('title').get("value").replace("","*").replace("","*") 69 | #print(rTitle) 70 | msg+=str(rTitle)+'\n\n*points :* '+str(post.get('points'))+'\n\n Url : '+str(post.get('url'))+'\n ------------------------------------------------- \n\n' 71 | return msg 72 | 73 | def getMsgForBottles(self,term): 74 | term = term.upper() 75 | searchBottles = self.bevco.getBottlesByName(term) 76 | msg = "🍺 *Bevco Price List for _"+term+"_:* \n\n" 77 | if len(searchBottles) is 0: 78 | msg = "No bottles found.." 79 | return msg 80 | for bottle in searchBottles: 81 | msg += "🍻 *"+bottle.get("brand")+"* vol : *"+bottle.get("size") + \ 82 | "* Rs *"+bottle.get("amount")+"*\n\n" 83 | return msg 84 | 85 | 86 | 87 | --------------------------------------------------------------------------------