-
Notifications
You must be signed in to change notification settings - Fork 6.6k
xmpp wikibot for GCE #618
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
xmpp wikibot for GCE #618
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Uncyclobot example that can be run on Google Compute Engine | ||
|
||
This sample shows how to use the [SleekXMPP](http://sleekxmpp.com/index.html) | ||
client and [Flask](http://flask.pocoo.org/) to build a simple chatbot that can | ||
be run on [Google Compute Engine](https://cloud.google.com/compute/). The | ||
chatbot does two things: | ||
|
||
1. Sends messages to XMPP users via http get: | ||
* The server is running on port 5000 | ||
* if running on virtual machine use: | ||
`http://<MACHINE IP>:5000/send_message?recipient=<RECIPIENT ADDRESS>&message=<MSG>` | ||
* If running locally use: | ||
`http://localhost:5000/send_message?recipient=<RECIPIENT ADDRESS>&message=<MSG>` | ||
|
||
2. Responds to incoming messages with a Uncyclopedia page on the topic: | ||
* Send a message with a topic (e.g., 'Hawaii') to the XMPP account the | ||
server is using | ||
* It should respond with a Uncyclopedia page (when one exists) | ||
|
||
## Setup | ||
|
||
Follow the instructions at the | ||
[Compute Engine Quickstart Guide](https://cloud.google.com/compute/docs/quickstart-linux) | ||
on how to create a project, create a virtual machine, and connect to your | ||
instance via SSH. Once you have done this, you may jump to | ||
[Installing files and dependencies](#installing-files-and-dependencies). | ||
|
||
You should also download the [Google Cloud SDK](https://cloud.google.com/sdk/). | ||
It will allow you to access many of the features of Google Compute Engine via | ||
your local machine. | ||
|
||
**IMPORTANT** You must enable tcp traffic on port 5000 to send messages to the | ||
XMPP server. This can be done by running the following SDK commands: | ||
|
||
gcloud config set project <YOUR PROJECT NAME> | ||
|
||
gcloud compute firewall-rules create wikibot-server-rule --allow tcp:5000 --source-ranges=0.0.0.0/0 | ||
|
||
Or you can create a new firewall rule via the UI in the | ||
[Networks](https://console.cloud.google.com/networking/networks/list) section of | ||
the Google Cloud Console. | ||
|
||
### Installing files and dependencies | ||
|
||
First, install the `wikibot.py` and `requirements.txt` files onto your remote | ||
instance. See the guide on | ||
[Transferring Files](https://cloud.google.com/compute/docs/instances/transfer-files) | ||
for more information on how to do this using the Mac file browser, `scp`, or | ||
the Google Cloud SDK. | ||
|
||
Before running or deploying this application, you must install the dependencies | ||
using [pip](http://pip.readthedocs.io/en/stable/): | ||
|
||
pip install -r requirements.txt | ||
|
||
|
||
## Running the sample | ||
|
||
You'll need to have an XMPP account prior to actually running the sample. | ||
If you do not have one, you can easily create an account at one of the many | ||
XMPP servers such as [Jabber.at](https://jabber.at/account/register/). | ||
Once you have an account, run the following command: | ||
|
||
python wikibot.py '<YOUR XMPP USERNAME>' '<PASSWORD>' | ||
|
||
Where the username (e.g., '[email protected]') and password for the account that | ||
you'd like to use for your chatbot are passed in as arguments. | ||
|
||
Enter control-C to stop the server | ||
|
||
|
||
### Running on your local machine | ||
|
||
You may also run the sample locally by simply copying `wikibot.py` to a project | ||
directory and installing all python dependencies there. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Flask==0.11.1 | ||
requests==2.11.1 | ||
sleekxmpp==1.3.1 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
# Copyright 2016 Google Inc. All Rights Reserved. | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# http://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
|
||
"""Uncyclobot server example using SleekXMPP client library""" | ||
|
||
import argparse | ||
import getpass | ||
import json | ||
import logging | ||
import threading | ||
import urllib | ||
|
||
from flask import Flask, request | ||
import requests | ||
import sleekxmpp | ||
|
||
|
||
app = Flask(__name__) | ||
|
||
@app.route('/send_message', methods=['GET']) | ||
def send_message(): | ||
recipient = request.args.get('recipient') | ||
message = request.args.get('message') | ||
|
||
if chat_client and recipient and message: | ||
chat_client.send_message(mto=recipient, mbody=message) | ||
return 'message sent to {} with body: {}'.format(recipient, message) | ||
else: | ||
logging.info('chat client or recipient or message does not exist!') | ||
return 'message failed to send', 400 | ||
|
||
|
||
def run_server(host='0.0.0.0'): | ||
app.run(threaded=False, use_reloader=False, host=host) | ||
|
||
|
||
class UncycloBot(sleekxmpp.ClientXMPP): | ||
"""A simple SleekXMPP bot that will take messages, look up their content on | ||
wikipedia and provide a link to the page if it exists. | ||
""" | ||
|
||
def __init__(self, jid, password): | ||
sleekxmpp.ClientXMPP.__init__(self, jid, password) | ||
|
||
# The session_start event will be triggered when | ||
# the bot establishes its connection with the server | ||
# and the XML streams are ready for use. We want to | ||
# listen for this event so that we we can initialize | ||
# our roster. | ||
self.add_event_handler('session_start', self.start) | ||
|
||
# The message event is triggered whenever a message | ||
# stanza is received. Be aware that that includes | ||
# MUC messages and error messages. | ||
self.add_event_handler('message', self.message) | ||
|
||
def start(self, event): | ||
"""Process the session_start event. | ||
|
||
Typical actions for the session_start event are requesting the roster | ||
and broadcasting an initial presence stanza. | ||
|
||
Arguments: | ||
event -- An empty dictionary. The session_start event does not | ||
provide any additional data. | ||
""" | ||
self.send_presence() | ||
self.get_roster() | ||
|
||
def message(self, msg): | ||
"""Process incoming message stanzas. | ||
|
||
Be aware that this also includes MUC messages and error messages. It is | ||
usually a good idea to check the messages's type before processing or | ||
sending replies. If the message is the appropriate type, then the bot | ||
checks wikipedia to see if the message string exists as a page on the | ||
site. If so, it sends this link back to the sender in the reply. | ||
|
||
Arguments: | ||
msg -- The received message stanza. See the SleekXMPP documentation | ||
for stanza objects and the Message stanza to see how it may be | ||
used. | ||
""" | ||
if msg['type'] in ('chat', 'normal'): | ||
msg_body = msg['body'] | ||
logging.info('Message sent was: {}'.format(msg_body)) | ||
encoded_body = urllib.quote_plus(msg_body) | ||
svrResponse = requests.get( | ||
'https://en.wikipedia.org/w/api.php?' | ||
'action=parse&prop=sections&format=json&page={}'.format( | ||
encoded_body)) | ||
doc = json.loads(svrResponse.content) | ||
try: | ||
page_id = str(doc['parse']['pageid']) | ||
defn_url = 'https://en.wikipedia.org/?curid={}'.format(page_id) | ||
msg.reply('find out more about: "{}" here: {}'.format( | ||
msg_body, defn_url)).send() | ||
except KeyError as e: | ||
logging.info('key error: {0}'.format(e)) | ||
msg.reply('I wasn\'t able to locate info on "{}" Sorry'.format( | ||
msg_body)).send() | ||
|
||
|
||
if __name__ == '__main__': | ||
# Setup the command line arguments. | ||
parser = argparse.ArgumentParser( | ||
description=__doc__, | ||
formatter_class=argparse.RawDescriptionHelpFormatter) | ||
|
||
# Output verbosity options. | ||
parser.add_argument( | ||
'-q', '--quiet', help='set logging to ERROR', action='store_const', | ||
dest='loglevel', const=logging.ERROR, default=logging.INFO) | ||
parser.add_argument( | ||
'-d', '--debug', help='set logging to DEBUG', action='store_const', | ||
dest='loglevel', const=logging.DEBUG, default=logging.INFO) | ||
parser.add_argument( | ||
'-v', '--verbose', help='set logging to COMM', action='store_const', | ||
dest='loglevel', const=5, default=logging.INFO) | ||
|
||
# JID and password options. | ||
parser.add_argument('-j', '--jid', help='JID to use', required=True) | ||
parser.add_argument('-p', '--password', help='password to use') | ||
|
||
args = parser.parse_args() | ||
|
||
# Setup logging. | ||
logging.basicConfig(level=args.loglevel, | ||
format='%(levelname)-8s %(message)s') | ||
|
||
if args.password is None: | ||
args.password = getpass.getpass('Password: ') | ||
|
||
# Setup the UncycloBot and register plugins. Note that while plugins may | ||
# have interdependencies, the order in which you register them does | ||
# not matter. | ||
xmpp = UncycloBot(args.jid, args.password) | ||
xmpp.register_plugin('xep_0030') # Service Discovery | ||
xmpp.register_plugin('xep_0004') # Data Forms | ||
xmpp.register_plugin('xep_0060') # PubSub | ||
xmpp.register_plugin('xep_0199') # XMPP Ping | ||
|
||
chat_client = xmpp # set the global variable | ||
|
||
# start the app server and run it as a thread so that the XMPP server may | ||
# also start | ||
threading.Thread(target=run_server).start() | ||
|
||
# Connect to the XMPP server and start processing XMPP stanzas. | ||
if xmpp.connect(): | ||
xmpp.process(block=True) | ||
print('Done') | ||
else: | ||
print('Unable to connect.') |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
debug=False here, please. We shouldn't be using Flask's dev server to serve public traffic at all, but at least disable the debug flag.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I know . The setup gets increasingly more complex if I try to do the right thing with a real web server. Jerjou and I discussed possibly removing the server component altogether as the chat interactions are more interesting anyway. Will refine tomorrow. Thanks for all the help!