moved to crossbarexamples repo
This commit is contained in:
@@ -36,15 +36,6 @@ For examples using RPC, you need to run the backend first, so that procedures ar
|
||||
* [Options](pubsub/options): use of PublishOptions and SubscribeOptions
|
||||
* [Unsubscribe](pubsub/unsubscribe): listen to events for a limited time
|
||||
|
||||
### WAMPlet Examples
|
||||
|
||||
These are some larger examples, implemented as pluggable "WAMPlets". These can also serve as skeletons to base your own WAMPlets from, should you wish to package components like this.
|
||||
|
||||
* [votegame](wamplet/votegame): a collaborative voting "game" to decide the most amazing fruit. Updates votes amongst all clients in realtime
|
||||
* [wampirc](wamplet/wampirc): shows some simple bridging between IRC and WAMP, exporting private messages to the bot as WAMP publish()-es
|
||||
* [wamplet1](wamplet/wamplet1): just a minimal skeleton
|
||||
|
||||
|
||||
### App Examples
|
||||
|
||||
_We still need to explain these. For starters, here's the list:_
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
*.pyc
|
||||
*.egg-info
|
||||
build
|
||||
dist
|
||||
*.db
|
||||
@@ -1 +0,0 @@
|
||||
recursive-include votegame/web *
|
||||
@@ -1,11 +0,0 @@
|
||||
test:
|
||||
python votegame/backend.py
|
||||
|
||||
install:
|
||||
python setup.py install
|
||||
|
||||
clean:
|
||||
find . -name "*.pyc" -exec rm -f {} \;
|
||||
rm -rf dist
|
||||
rm -rf build
|
||||
rm -rf *.egg-info
|
||||
@@ -1,31 +0,0 @@
|
||||
This example demonstrates the use of databases (e.g. SQLite or PostgreSQL) from WAMP application components. The example is a simple voting app packaged up as a **WAMPlet** application component.
|
||||
|
||||
A **WAMPlet** can be thought of a reusable application component that can be deployed dynamically as needed.
|
||||
|
||||
Get started by copying this folder and it's contents and begin by modifying a working base line.
|
||||
|
||||
## Try it
|
||||
|
||||
All the interesting bits with our application component are in [here](votegame/backend.py).
|
||||
|
||||
For development, start a locally running WAMP router, e.g. **Crossbar**.io:
|
||||
|
||||
```shell
|
||||
cd $HOME
|
||||
crossbar init
|
||||
crossbar start
|
||||
```
|
||||
|
||||
and in a second terminal run the file containing the application component:
|
||||
|
||||
```shell
|
||||
python votegame/backend.py
|
||||
```
|
||||
|
||||
In your browser, open the file `votegame/web/index.html`.
|
||||
|
||||
## Deploying
|
||||
|
||||
You can deploy your WAMPlet to a WAMPlet container for production.
|
||||
|
||||
A configuration for [**Crossbar**.io](http://crossbar.io) which runs a WAMP router, loads the VoteGame backend component and serves static Web content from the VoteGame package can be found [here](config.json).
|
||||
@@ -1,60 +0,0 @@
|
||||
{
|
||||
"processes": [
|
||||
{
|
||||
"type": "worker",
|
||||
"modules": [
|
||||
{
|
||||
"type": "router",
|
||||
"realms": {
|
||||
"realm1": {
|
||||
"permissions": {
|
||||
"anonymous": {
|
||||
"create": true,
|
||||
"join": true,
|
||||
"access": {
|
||||
"*": {
|
||||
"publish": true,
|
||||
"subscribe": true,
|
||||
"call": true,
|
||||
"register": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"type": "wamplet",
|
||||
"dist": "votegame",
|
||||
"entry": "backend",
|
||||
"extra": {
|
||||
"dbfile": "votegame.db",
|
||||
"items": ["banana", "lemon", "grapefruit"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"transports": [
|
||||
{
|
||||
"type": "web",
|
||||
"endpoint": {
|
||||
"type": "tcp",
|
||||
"port": 8080
|
||||
},
|
||||
"paths": {
|
||||
"/": {
|
||||
"type": "static",
|
||||
"module": "votegame",
|
||||
"resource": "web"
|
||||
},
|
||||
"ws": {
|
||||
"type": "websocket"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='votegame',
|
||||
version='0.0.2',
|
||||
description='VoteGame Service WAMPlet',
|
||||
platforms=['Any'],
|
||||
packages=find_packages(),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
entry_points={
|
||||
'autobahn.twisted.wamplet': [
|
||||
'backend = votegame.backend:make'
|
||||
],
|
||||
}
|
||||
)
|
||||
@@ -1,160 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import sqlite3
|
||||
|
||||
from twisted.python import log
|
||||
from twisted.enterprise import adbapi
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
|
||||
from autobahn import wamp
|
||||
from autobahn.twisted.wamp import ApplicationSession
|
||||
from autobahn.wamp.exception import ApplicationError
|
||||
from autobahn.wamp.types import PublishOptions
|
||||
|
||||
|
||||
# WAMP application component with our app code.
|
||||
##
|
||||
class VoteGameBackend(ApplicationSession):
|
||||
|
||||
def __init__(self, config):
|
||||
ApplicationSession.__init__(self)
|
||||
self.config = config
|
||||
self.init_db()
|
||||
|
||||
def init_db(self):
|
||||
if not os.path.isfile(self.config.extra['dbfile']):
|
||||
log.msg("Initializing database ..")
|
||||
|
||||
db = sqlite3.connect(self.config.extra['dbfile'])
|
||||
cur = db.cursor()
|
||||
|
||||
cur.execute("""
|
||||
CREATE TABLE votes (
|
||||
item TEXT NOT NULL,
|
||||
count NUMBER NOT NULL,
|
||||
PRIMARY KEY (item))
|
||||
""")
|
||||
|
||||
for item in self.config.extra['items']:
|
||||
cur.execute("INSERT INTO votes (item, count) VALUES (?, ?)", [item, 0])
|
||||
db.commit()
|
||||
|
||||
db.close()
|
||||
log.msg("Database initialized.")
|
||||
|
||||
else:
|
||||
log.msg("Database already exists.")
|
||||
|
||||
self.db = adbapi.ConnectionPool('sqlite3', self.config.extra['dbfile'], check_same_thread=False)
|
||||
log.msg("Database opened.")
|
||||
|
||||
@wamp.register("com.votegame.get_votes")
|
||||
def get_votes(self):
|
||||
def run(txn):
|
||||
txn.execute("SELECT item, count FROM votes")
|
||||
res = {}
|
||||
for row in txn.fetchall():
|
||||
res[row[0]] = row[1]
|
||||
return res
|
||||
return self.db.runInteraction(run)
|
||||
|
||||
@wamp.register("com.votegame.vote")
|
||||
def vote(self, item):
|
||||
if item not in self.config.extra['items']:
|
||||
raise ApplicationError("com.votegame.error.no_such_item", "no item '{}' to vote on".format(item))
|
||||
|
||||
def run(txn):
|
||||
# FIXME: make the following into 1 (atomic) SQL statement
|
||||
# => does SQLite feature "UPDATE .. RETURNING"?
|
||||
txn.execute("UPDATE votes SET count = count + 1 WHERE item = ?", [item])
|
||||
txn.execute("SELECT count FROM votes WHERE item = ?", [item])
|
||||
count = int(txn.fetchone()[0])
|
||||
|
||||
self.publish("com.votegame.onvote", item, count,
|
||||
options=PublishOptions(excludeMe=False))
|
||||
|
||||
return count
|
||||
|
||||
return self.db.runInteraction(run)
|
||||
|
||||
@inlineCallbacks
|
||||
def onJoin(self, details):
|
||||
|
||||
def onvote(item, count):
|
||||
print("New vote on '{}': {}".format(item, count))
|
||||
|
||||
yield self.subscribe(onvote, 'com.votegame.onvote')
|
||||
|
||||
try:
|
||||
regs = yield self.register(self)
|
||||
print("Ok, registered {} procedures.".format(len(regs)))
|
||||
except Exception as e:
|
||||
print("Failed to register procedures: {}".format(e))
|
||||
|
||||
print("VoteGame Backend ready!")
|
||||
|
||||
def onDisconnect(self):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def make(config):
|
||||
##
|
||||
# This component factory creates instances of the
|
||||
# application component to run.
|
||||
##
|
||||
# The function will get called either during development
|
||||
# using the ApplicationRunner below, or as a plugin running
|
||||
# hosted in a WAMPlet container such as a Crossbar.io worker.
|
||||
##
|
||||
if config:
|
||||
return VoteGameBackend(config)
|
||||
else:
|
||||
# if no config given, return a description of this WAMPlet ..
|
||||
return {'label': 'VoteGame Service WAMPlet',
|
||||
'description': 'This is the backend WAMP application component of VoteGame.'}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from autobahn.twisted.wamp import ApplicationRunner
|
||||
|
||||
extra = {
|
||||
"dbfile": "votegame.db",
|
||||
"items": ["banana", "lemon", "grapefruit"]
|
||||
}
|
||||
|
||||
# test drive the component during development ..
|
||||
runner = ApplicationRunner(
|
||||
url="ws://127.0.0.1:8080/ws",
|
||||
realm="realm1",
|
||||
extra=extra,
|
||||
debug=False, # low-level WebSocket debugging
|
||||
debug_wamp=False, # WAMP protocol-level debugging
|
||||
debug_app=True) # app-level debugging
|
||||
|
||||
runner.run(make)
|
||||
@@ -1,60 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>Votes Frontend</h1>
|
||||
<p>Open JavaScript console to watch output.</p>
|
||||
<script>AUTOBAHN_DEBUG = true;</script>
|
||||
<script src="https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz"></script>
|
||||
|
||||
<button onclick="vote('banana')">Banana</button>
|
||||
<button onclick="vote('lemon')">Lemon</button>
|
||||
<button onclick="vote('grapefruit')">Grapefruit</button>
|
||||
|
||||
<script>
|
||||
// WebSocket (WAMP) server address
|
||||
var wsuri;
|
||||
|
||||
if (document.location.origin == "file://") {
|
||||
wsuri = "ws://127.0.0.1:8080/ws";
|
||||
} else {
|
||||
wsuri = (window.location.protocol === "http:" ? "ws:" : "wss:") + "//" + document.location.host + "/ws";
|
||||
}
|
||||
|
||||
var connection = new autobahn.Connection({
|
||||
url: wsuri,
|
||||
realm: 'realm1'
|
||||
});
|
||||
|
||||
connection.onopen = function (session) {
|
||||
|
||||
console.log("Connected to", wsuri);
|
||||
|
||||
session.subscribe('com.votegame.onvote', function (args) {
|
||||
console.log("new votes for " + args[0] + " : " + args[1]);
|
||||
});
|
||||
|
||||
session.call('com.votegame.get_votes').then(
|
||||
function (votes) {
|
||||
console.log("Current votes:", votes);
|
||||
},
|
||||
session.log
|
||||
);
|
||||
};
|
||||
|
||||
connection.open();
|
||||
|
||||
function vote(item) {
|
||||
if (connection.session) {
|
||||
connection.session.call('com.votegame.vote', [item]).then(
|
||||
function (count) {
|
||||
console.log("Ok, voted for " + item + " : " + count);
|
||||
},
|
||||
connection.session.log
|
||||
);
|
||||
} else {
|
||||
console.log("can't vote: no connection");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
4
examples/twisted/wamp/app/wampirc/.gitignore
vendored
4
examples/twisted/wamp/app/wampirc/.gitignore
vendored
@@ -1,4 +0,0 @@
|
||||
*.egg-info
|
||||
build
|
||||
dist
|
||||
*.pyc
|
||||
@@ -1,14 +0,0 @@
|
||||
all:
|
||||
@echo "Targets: run, install, clean"
|
||||
|
||||
run:
|
||||
PYTHONPATH="." python wampirc/service.py
|
||||
|
||||
install:
|
||||
python setup.py install
|
||||
|
||||
clean:
|
||||
find . -name "*.pyc" -exec rm -f {} \;
|
||||
rm -rf dist
|
||||
rm -rf build
|
||||
rm -rf *.egg-info
|
||||
@@ -1,24 +0,0 @@
|
||||
# WAMP IRC
|
||||
|
||||
WAMPlet that provides IRC bot services to applications.
|
||||
|
||||
The component bridges IRC and [WAMP](http://wamp.ws). It exposes IRC to WAMP, e.g. there are RPC endpoints for starting a bot, joining channels, listening for activity, and publishing IRC activity as WAMP PubSub events.
|
||||
|
||||
It is written as a WAMPlet, a reusable WAMP-based application component, that can be run connecting to any WAMP router (e.g. [**Crossbar**.io](https://github.com/crossbario/crossbar/wiki)). The component can be started directly, or WAMP routers capable of *hosting* WAMPlets can run the component under supervision.
|
||||
|
||||
## Try it
|
||||
|
||||
Start up a WAMP router in a first terminal - e.g. **Crossbar**.io:
|
||||
|
||||
```shell
|
||||
crossbar init
|
||||
crossbar start
|
||||
```
|
||||
|
||||
Run the WAMPlet from the local directory:
|
||||
|
||||
```shell
|
||||
PYTHONPATH="." python wampirc/service.py
|
||||
```
|
||||
|
||||
Open the test console `test/index.html` in your browser to control the component.
|
||||
@@ -1,41 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='wampirc',
|
||||
version='0.0.1',
|
||||
description='An IRC bot service component.',
|
||||
platforms=['Any'],
|
||||
packages=find_packages(),
|
||||
entry_points={
|
||||
'autobahn.twisted.wamplet': [
|
||||
'bot = wampirc.service:make'
|
||||
],
|
||||
},
|
||||
zip_safe=False,
|
||||
)
|
||||
@@ -1,50 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>IRC Bot Test Frontend</h1>
|
||||
<p>Open JavaScript console to watch output.</p>
|
||||
<script>AUTOBAHN_DEBUG = true;</script>
|
||||
<script src="https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz"></script>
|
||||
|
||||
<button onclick="start_bot()">Start Bot</button>
|
||||
<button onclick="stop_bot(1)">Stop Bot 1</button>
|
||||
|
||||
<script>
|
||||
var connection = new autobahn.Connection({
|
||||
url: 'ws://127.0.0.1:8080/ws',
|
||||
realm: 'realm1'}
|
||||
);
|
||||
|
||||
connection.onopen = function (session) {
|
||||
|
||||
session.subscribe('com.myapp.on_privmsg', function (args) {
|
||||
console.log(args);
|
||||
});
|
||||
};
|
||||
|
||||
connection.open();
|
||||
|
||||
function start_bot() {
|
||||
if (connection.session) {
|
||||
connection.session.call("com.myapp.start_bot",
|
||||
["bot23", ["autobahn"]]).then(
|
||||
connection.session.log,
|
||||
connection.session.log);
|
||||
} else {
|
||||
console.log("no connection");
|
||||
}
|
||||
}
|
||||
|
||||
function stop_bot(id) {
|
||||
if (connection.session) {
|
||||
connection.session.call("com.myapp.stop_bot",
|
||||
[id]).then(
|
||||
connection.session.log,
|
||||
connection.session.log);
|
||||
} else {
|
||||
console.log("no connection");
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,83 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from twisted.python import log
|
||||
from twisted.internet import protocol
|
||||
from twisted.words.protocols import irc
|
||||
|
||||
|
||||
class IRCClientProtocol(irc.IRCClient):
|
||||
|
||||
def connectionMade(self):
|
||||
irc.IRCClient.connectionMade(self)
|
||||
print "connected"
|
||||
|
||||
def connectionLost(self, reason):
|
||||
irc.IRCClient.connectionLost(self, reason)
|
||||
print "lost", reason
|
||||
|
||||
def signedOn(self):
|
||||
print "signedon"
|
||||
for channel in self.channels:
|
||||
print "joining", channel
|
||||
self.join(channel)
|
||||
|
||||
def joined(self, channel):
|
||||
print "joined", channel
|
||||
|
||||
def privmsg(self, user, channel, msg):
|
||||
# privmsg oberstet!~vanaland@89.204.139.245 #autobahn bot23: test
|
||||
print "privmsg", user, channel, msg
|
||||
self.factory.session.publish('com.myapp.on_privmsg', user, channel, msg)
|
||||
|
||||
def action(self, user, channel, msg):
|
||||
print "action", user, channel, msg
|
||||
|
||||
|
||||
class IRCClientFactory(protocol.ClientFactory):
|
||||
|
||||
def __init__(self, session, nickname, channels):
|
||||
self.session = session
|
||||
self.proto = None
|
||||
self.nickname = str(nickname)
|
||||
self.channels = [str(c) for c in channels]
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
assert(not self.proto)
|
||||
self.proto = IRCClientProtocol()
|
||||
self.proto.factory = self
|
||||
self.proto.nickname = self.nickname
|
||||
self.proto.channels = self.channels
|
||||
return self.proto
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
# connector.connect()
|
||||
self.proto = None
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
# from twisted.internet import reactor
|
||||
# reactor.stop()
|
||||
self.proto = None
|
||||
@@ -1,131 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
from twisted.internet.endpoints import clientFromString
|
||||
from twisted.words.protocols import irc
|
||||
|
||||
from autobahn import wamp
|
||||
from autobahn.twisted.wamp import ApplicationSession
|
||||
from autobahn.wamp.exception import ApplicationError
|
||||
|
||||
from wampirc.client import IRCClientFactory
|
||||
|
||||
|
||||
class Bot:
|
||||
|
||||
"""
|
||||
Tracks currently running bot instances.
|
||||
"""
|
||||
|
||||
def __init__(self, id, factory, client):
|
||||
self.id = id
|
||||
self.factory = factory
|
||||
self.client = client
|
||||
|
||||
|
||||
class IRCComponent(ApplicationSession):
|
||||
|
||||
"""
|
||||
IRC bot services component.
|
||||
"""
|
||||
|
||||
def __init__(self, config):
|
||||
ApplicationSession.__init__(self)
|
||||
self.config = config
|
||||
self._bots = {}
|
||||
self._bot_no = 0
|
||||
|
||||
@wamp.register('com.myapp.start_bot')
|
||||
def start_bot(self, nick, channels):
|
||||
self._bot_no += 1
|
||||
id = self._bot_no
|
||||
factory = IRCClientFactory(self, nick, channels)
|
||||
|
||||
from twisted.internet import reactor
|
||||
client = clientFromString(reactor, self.config.extra['server'])
|
||||
d = client.connect(factory)
|
||||
|
||||
def onconnect(res):
|
||||
self._bots[id] = Bot(id, factory, client)
|
||||
return id
|
||||
d.addCallback(onconnect)
|
||||
|
||||
return d
|
||||
|
||||
@wamp.register('com.myapp.stop_bot')
|
||||
def stop_bot(self, id):
|
||||
if id in self._bots:
|
||||
f = self._bots[id].factory
|
||||
if f.proto:
|
||||
f.proto.transport.loseConnection()
|
||||
f.stopFactory()
|
||||
del self._bots[id]
|
||||
else:
|
||||
raise ApplicationError('com.myapp.error.no_such_bot')
|
||||
|
||||
@inlineCallbacks
|
||||
def onJoin(self, details):
|
||||
try:
|
||||
regs = yield self.register(self)
|
||||
print("Ok, registered {} procedures.".format(len(regs)))
|
||||
except Exception as e:
|
||||
print("Failed to register procedures: {}".format(e))
|
||||
|
||||
print("IRC Bot Backend ready!")
|
||||
|
||||
def onDisconnect(self):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def make(config):
|
||||
if config:
|
||||
return IRCComponent(config)
|
||||
else:
|
||||
# if no config given, return a description of this WAMPlet ..
|
||||
return {'label': 'An IRC bot service component',
|
||||
'description': 'This component provides IRC bot services via WAMP.'}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from autobahn.twisted.wamp import ApplicationRunner
|
||||
|
||||
extra = {
|
||||
"server": "tcp:irc.freenode.net:6667"
|
||||
}
|
||||
|
||||
# test drive the component during development ..
|
||||
runner = ApplicationRunner(
|
||||
url="ws://127.0.0.1:8080/ws",
|
||||
realm="realm1",
|
||||
extra=extra,
|
||||
debug=False, # low-level WebSocket debugging
|
||||
debug_wamp=False, # WAMP protocol-level debugging
|
||||
debug_app=True) # app-level debugging
|
||||
|
||||
runner.run(make)
|
||||
@@ -1,4 +0,0 @@
|
||||
wamplet1.egg-info
|
||||
build
|
||||
dist
|
||||
*.pyc
|
||||
@@ -1,11 +0,0 @@
|
||||
test:
|
||||
python wamplet1/component1.py
|
||||
|
||||
install:
|
||||
python setup.py install
|
||||
|
||||
clean:
|
||||
find . -name "*.pyc" -exec rm -f {} \;
|
||||
rm -rf dist
|
||||
rm -rf build
|
||||
rm -rf *.egg-info
|
||||
@@ -1,52 +0,0 @@
|
||||
This folder contains a minimal skeleton of a **WAMPlet** application component.
|
||||
|
||||
A **WAMPlet** can be thought of a reusable application component that can be deployed dynamically as needed.
|
||||
|
||||
Get started by copying this folder and it's contents and begin by modifying a working base line.
|
||||
|
||||
> This example is using **Twisted**. You can find the **asyncio** variant [here](https://github.com/tavendo/AutobahnPython/tree/master/examples/asyncio/wamp/wamplet/wamplet1)
|
||||
>
|
||||
|
||||
## WAMPlet Development
|
||||
|
||||
All the interesting bits with our application component are in [here](wamplet1/component1.py).
|
||||
|
||||
For development, start a locally running WAMP router, e.g. **Crossbar**.io:
|
||||
|
||||
```shell
|
||||
cd $HOME
|
||||
crossbar init
|
||||
crossbar start
|
||||
```
|
||||
|
||||
and in a second terminal run the file containing the application component:
|
||||
|
||||
```shell
|
||||
python wamplet1/component1.py
|
||||
```
|
||||
|
||||
## WAMPlet Installation and Distribution
|
||||
|
||||
For installation as a local WAMPlet in your Python package directory
|
||||
|
||||
```shell
|
||||
python setup.py install
|
||||
```
|
||||
|
||||
Installation of the WAMPlet allows **Crossbar**.io to find your WAMPlet even if no explicit Python paths are configured.
|
||||
|
||||
Above will also leave you an **.egg** file with your WAMPlet packaged up as a redistributable egg file that can be installed on other hosts. You can even publish your WAMPlet on the [Python Package Index](https://pypi.python.org).
|
||||
|
||||
To make *automatic WAMPlet discovery* work, the [Setup file](setup.py) contains an item
|
||||
|
||||
```python
|
||||
entry_points = {
|
||||
'autobahn.twisted.wamplet': [
|
||||
'component1 = wamplet1.component1:make'
|
||||
],
|
||||
},
|
||||
```
|
||||
|
||||
where `entry_points` must have an entry `autobahn.twisted.wamplet` that lists application components the package exposes.
|
||||
|
||||
Here, the factory function `make()` in the module `component1` in the package `wamplet1` is to be exposed as the WAMPlet `component1`.
|
||||
@@ -1,41 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name='wamplet1',
|
||||
version='0.0.1',
|
||||
description='A demo WAMPlet.',
|
||||
platforms=['Any'],
|
||||
packages=find_packages(),
|
||||
entry_points={
|
||||
'autobahn.twisted.wamplet': [
|
||||
'component1 = wamplet1.component1:make'
|
||||
],
|
||||
},
|
||||
zip_safe=False,
|
||||
)
|
||||
@@ -1,49 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
from autobahn import wamp
|
||||
|
||||
|
||||
class Calculator(object):
|
||||
|
||||
"""
|
||||
An application component registering RPC endpoints using decorators.
|
||||
"""
|
||||
|
||||
@wamp.register('com.mathservice.add2')
|
||||
def add2(self, x, y):
|
||||
return x + y
|
||||
|
||||
@wamp.register('com.mathservice.mul2')
|
||||
def mul2(self, x, y):
|
||||
return x * y
|
||||
|
||||
@wamp.register('com.mathservice.div2')
|
||||
def square(self, x, y):
|
||||
if y:
|
||||
return float(x) / float(y)
|
||||
else:
|
||||
return 0
|
||||
@@ -1,97 +0,0 @@
|
||||
###############################################################################
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) Tavendo GmbH
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
#
|
||||
###############################################################################
|
||||
|
||||
import datetime
|
||||
|
||||
from twisted.internet.defer import inlineCallbacks
|
||||
|
||||
from autobahn.twisted.wamp import ApplicationSession
|
||||
|
||||
from calculator import Calculator
|
||||
|
||||
|
||||
# WAMP application component with our app code.
|
||||
##
|
||||
class Component1(ApplicationSession):
|
||||
|
||||
@inlineCallbacks
|
||||
def onJoin(self, details):
|
||||
|
||||
# register a function that can be called remotely
|
||||
##
|
||||
def utcnow():
|
||||
now = datetime.datetime.utcnow()
|
||||
return now.strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
reg = yield self.register(utcnow, 'com.timeservice.now')
|
||||
print("Procedure registered with ID {}".format(reg.id))
|
||||
|
||||
# create an application object that exposes methods for remoting
|
||||
##
|
||||
self.calculator = Calculator()
|
||||
|
||||
# register all methods on the "calculator" decorated with "@wamp.register"
|
||||
##
|
||||
results = yield self.register(self.calculator)
|
||||
for success, res in results:
|
||||
if success:
|
||||
print("Ok, registered procedure with registration ID {}".format(res.id))
|
||||
else:
|
||||
print("Failed to register procedure: {}".format(res.value))
|
||||
|
||||
def onDisconnect(self):
|
||||
reactor.stop()
|
||||
|
||||
|
||||
def make(config):
|
||||
##
|
||||
# This component factory creates instances of the
|
||||
# application component to run.
|
||||
##
|
||||
# The function will get called either during development
|
||||
# using the ApplicationRunner below, or as a plugin running
|
||||
# hosted in a WAMPlet container such as a Crossbar.io worker.
|
||||
##
|
||||
if config:
|
||||
return Component1(config)
|
||||
else:
|
||||
# if no config given, return a description of this WAMPlet ..
|
||||
return {'label': 'Awesome WAMPlet 1',
|
||||
'description': 'This is just a test WAMPlet that provides some procedures to call.'}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from autobahn.twisted.wamp import ApplicationRunner
|
||||
|
||||
# test drive the component during development ..
|
||||
runner = ApplicationRunner(
|
||||
url="ws://127.0.0.1:8080/ws",
|
||||
realm="realm1",
|
||||
debug=False, # low-level WebSocket debugging
|
||||
debug_wamp=False, # WAMP protocol-level debugging
|
||||
debug_app=True) # app-level debugging
|
||||
|
||||
runner.run(make)
|
||||
Reference in New Issue
Block a user