Implement webchat and badge notifications
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.cache import cache
|
||||||
from minecraft.models import Server
|
from minecraft.models import Server
|
||||||
from json import dumps, JSONEncoder
|
from json import dumps, JSONEncoder
|
||||||
import beanstalkc
|
import beanstalkc
|
||||||
@@ -16,6 +17,19 @@ class Event(object):
|
|||||||
self.type = type
|
self.type = type
|
||||||
self.data = data
|
self.data = data
|
||||||
|
|
||||||
|
class ChatEvent(Event):
|
||||||
|
def __init__(self, sender, message):
|
||||||
|
super(ChatEvent, self).__init__(type='chat', data={'sender': sender,
|
||||||
|
'message': message})
|
||||||
|
|
||||||
|
class QuitEvent(Event):
|
||||||
|
def __init__(self, player):
|
||||||
|
super(QuitEvent, self).__init__(type='quit', data={'player': player})
|
||||||
|
|
||||||
|
class JoinEvent(Event):
|
||||||
|
def __init__(self, player):
|
||||||
|
super(JoinEvent, self).__init__(type='join', data={'player': player})
|
||||||
|
|
||||||
class BroadcastEvent(Event):
|
class BroadcastEvent(Event):
|
||||||
def __init__(self, message):
|
def __init__(self, message):
|
||||||
super(BroadcastEvent, self).__init__(type='broadcast', data={'message':
|
super(BroadcastEvent, self).__init__(type='broadcast', data={'message':
|
||||||
@@ -58,3 +72,33 @@ def player_message(playername, message, *args):
|
|||||||
for server in Server.objects.all():
|
for server in Server.objects.all():
|
||||||
event = PlayerMessageEvent(playername, message)
|
event = PlayerMessageEvent(playername, message)
|
||||||
send_server_event(server, event)
|
send_server_event(server, event)
|
||||||
|
|
||||||
|
def web_queue(id):
|
||||||
|
queueName = 'caminus-web-%s'%id
|
||||||
|
queue = beanstalkc.Connection(host=settings.CAMINUS_BEANSTALKD_HOST,
|
||||||
|
port = settings.CAMINUS_BEANSTALKD_PORT)
|
||||||
|
queue.use(queueName)
|
||||||
|
queue.watch(queueName)
|
||||||
|
return queue
|
||||||
|
|
||||||
|
def send_web_event(event):
|
||||||
|
latest = cache.get('minecraft-web-events')
|
||||||
|
if latest is None:
|
||||||
|
latest = []
|
||||||
|
latest.append(dumps(event, cls=EventEncoder))
|
||||||
|
while len(latest) > 10:
|
||||||
|
latest.pop(0)
|
||||||
|
cache.set('minecraft-web-events', latest, 86400);
|
||||||
|
print 'cache:', latest
|
||||||
|
if settings.CAMINUS_USE_BEANSTALKD:
|
||||||
|
queue = beanstalkc.Connection(host=settings.CAMINUS_BEANSTALKD_HOST,
|
||||||
|
port = settings.CAMINUS_BEANSTALKD_PORT)
|
||||||
|
json = dumps(event, cls=EventEncoder)
|
||||||
|
for tube in queue.tubes():
|
||||||
|
if tube.startswith("caminus-web-"):
|
||||||
|
queue.use(tube)
|
||||||
|
queue.put(json)
|
||||||
|
|
||||||
|
def chat(playername, message):
|
||||||
|
evt = ChatEvent(playername, message)
|
||||||
|
send_web_event(evt)
|
||||||
|
@@ -1,4 +1,7 @@
|
|||||||
from piston.handler import AnonymousBaseHandler, BaseHandler
|
from piston.handler import AnonymousBaseHandler, BaseHandler
|
||||||
|
import time
|
||||||
|
from django.core.cache import cache
|
||||||
|
from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
import appversion
|
import appversion
|
||||||
from minecraft.models import MinecraftProfile
|
from minecraft.models import MinecraftProfile
|
||||||
@@ -10,7 +13,7 @@ from urllib2 import urlopen
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from models import cachePlayerList
|
from models import cachePlayerList
|
||||||
from events import server_queue
|
from events import server_queue, web_queue, chat, server_broadcast, send_web_event, QuitEvent, JoinEvent
|
||||||
|
|
||||||
class MOTDHandler(AnonymousBaseHandler):
|
class MOTDHandler(AnonymousBaseHandler):
|
||||||
allowed_methods = ('GET',)
|
allowed_methods = ('GET',)
|
||||||
@@ -40,6 +43,7 @@ class NewPlayerSessionHandler(BaseHandler):
|
|||||||
server = request.server
|
server = request.server
|
||||||
profile = MinecraftProfile.objects.get(mc_username__exact=playername)
|
profile = MinecraftProfile.objects.get(mc_username__exact=playername)
|
||||||
session = PlayerSession.objects.create(server=server, player=profile, ip=ip)
|
session = PlayerSession.objects.create(server=server, player=profile, ip=ip)
|
||||||
|
send_web_event(JoinEvent(playername))
|
||||||
return {'success': True, 'error': '', 'permissions': profile.serverPermissions(), 'sessionId': session.id}
|
return {'success': True, 'error': '', 'permissions': profile.serverPermissions(), 'sessionId': session.id}
|
||||||
else:
|
else:
|
||||||
return {'success': False, 'error': 'Your account is inactive.', 'permissions': []}
|
return {'success': False, 'error': 'Your account is inactive.', 'permissions': []}
|
||||||
@@ -52,6 +56,7 @@ class ClosePlayerSessionHandler(BaseHandler):
|
|||||||
for session in sessions:
|
for session in sessions:
|
||||||
session.end = datetime.now()
|
session.end = datetime.now()
|
||||||
session.save()
|
session.save()
|
||||||
|
send_web_event(QuitEvent(playername))
|
||||||
return {'valid': True}
|
return {'valid': True}
|
||||||
|
|
||||||
class EconomyHandler(BaseHandler):
|
class EconomyHandler(BaseHandler):
|
||||||
@@ -79,7 +84,7 @@ class ServerPingHandler(BaseHandler):
|
|||||||
return {'identity': request.server, 'api-version': 2, 'server-version': appversion.version()}
|
return {'identity': request.server, 'api-version': 2, 'server-version': appversion.version()}
|
||||||
|
|
||||||
class ServerEventHandler(BaseHandler):
|
class ServerEventHandler(BaseHandler):
|
||||||
allowed_methods = ('GET', 'POST')
|
allowed_methods = ('GET', 'POST', 'PUT')
|
||||||
|
|
||||||
def read(self, request):
|
def read(self, request):
|
||||||
queue = server_queue(request.server)
|
queue = server_queue(request.server)
|
||||||
@@ -95,6 +100,22 @@ class ServerEventHandler(BaseHandler):
|
|||||||
queue.delete(int(request.POST['job']))
|
queue.delete(int(request.POST['job']))
|
||||||
return {'result': 'success'}
|
return {'result': 'success'}
|
||||||
|
|
||||||
|
def update(self, request):
|
||||||
|
events = json.loads(request.POST['events'])['events']
|
||||||
|
for evt in events:
|
||||||
|
print repr(evt)
|
||||||
|
if evt['type'] == 'chat':
|
||||||
|
chat(evt['payload']['sender'], evt['payload']['message'])
|
||||||
|
return {'result': 'success'}
|
||||||
|
|
||||||
|
class ChatHandler(BaseHandler):
|
||||||
|
allowed_methods = ('POST',)
|
||||||
|
|
||||||
|
def create(self, request):
|
||||||
|
chat(request.user.minecraftprofile.mc_username, request.POST['message'])
|
||||||
|
server_broadcast("<%s> %s"%(request.user.minecraftprofile.mc_username,
|
||||||
|
request.POST['message']))
|
||||||
|
|
||||||
class PollHandler(BaseHandler):
|
class PollHandler(BaseHandler):
|
||||||
allowed_methods = ('GET',)
|
allowed_methods = ('GET',)
|
||||||
|
|
||||||
@@ -106,4 +127,19 @@ class PollHandler(BaseHandler):
|
|||||||
pollData['server-info'] = cache.get('caminus-server-info')
|
pollData['server-info'] = cache.get('caminus-server-info')
|
||||||
if not request.user.is_anonymous():
|
if not request.user.is_anonymous():
|
||||||
pollData['user-info']['balance'] = request.user.minecraftprofile.currencyaccount.balance
|
pollData['user-info']['balance'] = request.user.minecraftprofile.currencyaccount.balance
|
||||||
|
pollData['events'] = []
|
||||||
|
pollData['poll-id'] = timestamp
|
||||||
|
if timestamp == "0" and settings.CAMINUS_USE_BEANSTALKD:
|
||||||
|
pollData['poll-id'] = time.time()
|
||||||
|
latestEvents = cache.get('minecraft-web-events')
|
||||||
|
if not latestEvents:
|
||||||
|
latestEvents = []
|
||||||
|
for e in latestEvents:
|
||||||
|
pollData['events'].append(json.loads(e))
|
||||||
|
else:
|
||||||
|
eventQueue = web_queue(timestamp)
|
||||||
|
event = eventQueue.reserve(timeout=30)
|
||||||
|
if event:
|
||||||
|
pollData['events'].append(json.loads(event.body))
|
||||||
|
event.delete()
|
||||||
return pollData
|
return pollData
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
from django.db.models.signals import post_save
|
from django.db.models.signals import post_save
|
||||||
from minecraft.models import PlayerSession, Server
|
from minecraft.models import PlayerSession, Server
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
|
import badges.api
|
||||||
|
import events
|
||||||
|
|
||||||
def cachePlayerList():
|
def cachePlayerList():
|
||||||
serverInfo = {}
|
serverInfo = {}
|
||||||
@@ -15,3 +17,9 @@ def update_player_lists(sender, instance, created, **kwargs):
|
|||||||
cachePlayerList()
|
cachePlayerList()
|
||||||
|
|
||||||
post_save.connect(update_player_lists, sender=PlayerSession)
|
post_save.connect(update_player_lists, sender=PlayerSession)
|
||||||
|
|
||||||
|
def notify_badge(sender, award, *args, **kwargs):
|
||||||
|
player = award.user.minecraftprofile.mc_username
|
||||||
|
events.server_broadcast("%s was awarded the %s badge!"%(player, award.badge.name))
|
||||||
|
|
||||||
|
badges.api.badge_awarded.connect(notify_badge)
|
||||||
|
@@ -38,6 +38,11 @@ class ServerResource(Resource):
|
|||||||
super(ServerResource, self).__init__(handler, ServerAuther())
|
super(ServerResource, self).__init__(handler, ServerAuther())
|
||||||
self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)
|
self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)
|
||||||
|
|
||||||
|
class SimpleResource(Resource):
|
||||||
|
def __init__(self, handler):
|
||||||
|
super(SimpleResource, self).__init__(handler)
|
||||||
|
self.csrf_exempt = getattr(self.handler, 'csrf_exempt', True)
|
||||||
|
|
||||||
urlpatterns = patterns('api',
|
urlpatterns = patterns('api',
|
||||||
url(r'^motd/(?P<username>.*)$', motdHandler),
|
url(r'^motd/(?P<username>.*)$', motdHandler),
|
||||||
url(r'^server/whoami$', ServerResource(handlers.ServerPingHandler)),
|
url(r'^server/whoami$', ServerResource(handlers.ServerPingHandler)),
|
||||||
@@ -45,5 +50,6 @@ urlpatterns = patterns('api',
|
|||||||
url(r'^server/economy/(?P<playername>.*)$', ServerResource(handlers.EconomyHandler)),
|
url(r'^server/economy/(?P<playername>.*)$', ServerResource(handlers.EconomyHandler)),
|
||||||
url(r'^server/session/(?P<playername>.*)/new$', ServerResource(handlers.NewPlayerSessionHandler)),
|
url(r'^server/session/(?P<playername>.*)/new$', ServerResource(handlers.NewPlayerSessionHandler)),
|
||||||
url(r'^server/session/(?P<playername>.*)/close$', ServerResource(handlers.ClosePlayerSessionHandler)),
|
url(r'^server/session/(?P<playername>.*)/close$', ServerResource(handlers.ClosePlayerSessionHandler)),
|
||||||
url(r'^poll/(?P<timestamp>[0-9]+)$', Resource(handlers.PollHandler)),
|
url(r'^poll/(?P<timestamp>.+)$', SimpleResource(handlers.PollHandler)),
|
||||||
|
url(r'^chat$', SimpleResource(handlers.ChatHandler)),
|
||||||
)
|
)
|
||||||
|
@@ -1,8 +1,21 @@
|
|||||||
|
function sendChat(message) {
|
||||||
|
$.post('/api/chat', {'message': message}, function(data) {
|
||||||
|
$('#chat-line').val('');
|
||||||
|
$('#chat-line').disabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#server-interaction .drawer').each(function() {
|
$('#server-interaction .drawer').each(function() {
|
||||||
var canvas = $(this).children('.canvas');
|
var canvas = $(this).children('.canvas');
|
||||||
$(this, '.drawer-label').click(function() {
|
$(this).children('.drawer-label').click(function() {
|
||||||
canvas.slideToggle("blind");
|
canvas.slideToggle("blind");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
$('#chat-line').keypress(function(evt) {
|
||||||
|
if (evt.charCode == 13) {
|
||||||
|
$('#chat-line').disabled = true;
|
||||||
|
sendChat($('#chat-line').val());
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -1,12 +1,23 @@
|
|||||||
function pollMessages() {
|
function pollMessages(id) {
|
||||||
$.get('/api/poll/0', function(data) {
|
$.get('/api/poll/'+id, function(data) {
|
||||||
|
if (id == 0)
|
||||||
|
$('#chat-display').html('');
|
||||||
$('#balance-display').html(data['user-info']['balance']);
|
$('#balance-display').html(data['user-info']['balance']);
|
||||||
|
$(data['events']).each(function(idx, evt) {
|
||||||
|
if (evt['type'] == "chat") {
|
||||||
|
$('#chat-display').append("<li>"+evt['payload']['sender']+": "+evt['payload']['message']);
|
||||||
|
} else if (evt['type'] == 'join') {
|
||||||
|
$('#chat-display').append("<li><em>"+evt['payload']['player']+" has joined</em></li>");
|
||||||
|
} else if (evt['type'] == 'quit') {
|
||||||
|
$('#chat-display').append("<li><em>"+evt['payload']['player']+" has quit</em></li>");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
window.setTimeout(function() {pollMessages(data['poll-id'])}, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function poll() {
|
function poll() {
|
||||||
pollMessages();
|
pollMessages(0);
|
||||||
window.setTimeout(pollMessages, 3000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
@@ -169,6 +169,17 @@ s.parentNode.insertBefore(po, s);
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div id="server-interaction">
|
<div id="server-interaction">
|
||||||
|
<div class="drawer">
|
||||||
|
<div class="drawer-label">
|
||||||
|
Chat
|
||||||
|
</div>
|
||||||
|
<div class="canvas">
|
||||||
|
<ul id="chat-display">
|
||||||
|
<li>Loading...</li>
|
||||||
|
</ul>
|
||||||
|
<input type="text" id="chat-line"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="drawer">
|
<div class="drawer">
|
||||||
<div class="drawer-label">
|
<div class="drawer-label">
|
||||||
Online Players
|
Online Players
|
||||||
|
Reference in New Issue
Block a user