Пример #1
0
	def on_close(self):
		for channel in self.channels:
			channels[channel].remove(self)
			if len(channels[channel]) == 0:
				del channels[channel]
		handlers.remove(self)
		console('websocket', "Closed")
Пример #2
0
	def onGameEnd(self, game):
		console('event thread', "Game over: %s" % self.gameCon.logFilename)
		game.out()
		db['games'][self.gameCon.logFilename] = game
		self.gameCon = None
		# After this tick, immediately check again for a new game. This is mostly used when populating the database for the first time
		self.tickWait = False
Пример #3
0
 def __init__(self, host, port):
     if StrictRedis:
         self.conn = StrictRedis(host=host, port=port)
         self.conn.pubsub()
     else:
         console("events", "Unable to load redis python module; event publishing disabled")
         self.conn = None
Пример #4
0
	def load(self, method, url, *, cacheRead = False, cacheWrite = None, data = None, **params):
		if cacheWrite is None:
			cacheWrite = (method == 'get')
		if self.cache is None:
			cacheRead = cacheWrite = False

		if cacheRead:
			rtn = self.cache[(url, params, data)]
			if rtn is not None:
				return rtn

		console('jira api', f"API request: {url} {params} {data}")
		req = requests.request(
			method,
			url,
			params = params,
			data = json.dumps(data) if data else None,
			auth = self.auth,
			headers = {
				'Accept': 'application/json',
				'Content-Type': 'application/json',
			},
		)
		if req.status_code == 204:
			return None
		if req.status_code not in (200, 201):
			messageExtractors = [
				lambda json: json['message'],
				lambda json: json['errorMessages'][0],
				lambda json: list(json['errors'].values())[0],
			]
			message = None
			for fn in messageExtractors:
				try:
					message = fn(req.json())
				except:
					pass
			console('jira api', f"{req}: {req.text}")
			raise APIError(req.status_code, message, url)

		rtn = req.json()
		if method == 'get' and 'startAt' in rtn:
			# Result is paginated
			if 'startAt' in params:
				# This is one page of a paginated request being called from paginate(), just return it
				return rtn
			else:
				# This is the beginning of a paginated request
				return self.paginate(url, params, rtn, cacheWrite)
		else:
			# Result is not paginated, just return the whole thing
			if cacheWrite:
				self.cache[(url, params, data)] = rtn
			return rtn
Пример #5
0
	def on_message(self, message):
		console('websocket', "Message received: %s" % message)
		try:
			data = fromJS(message)
		except:
			return

		if 'subscribe' in data and isinstance(data['subscribe'], list):
			addChannels = (set(data['subscribe']) - self.channels)
			self.channels |= addChannels
			for channel in addChannels:
				if channel not in channels:
					channels[channel] = set()
				channels[channel].add(self)

		if 'unsubscribe' in data and isinstance(data['unsubscribe'], list):
			rmChannels = (self.channels & set(data['unsubscribe']))
			self.channels -= rmChannels
			for channel in rmChannels:
				channels[channel].remove(self)
				if len(channels[channel]) == 0:
					del channels[channel]
Пример #6
0
		def wrap():
			while True:
				console('websocket client', "Connecting to %s" % wsURL)
				ws = yield websocket_connect(wsURL)
				console('websocket client', 'Connected')
				# Trigger once immediately to fetch the current game
				while True:
					self.tickWrap()
					if self.tickWait:
						break
					else:
						self.tickWait = True
				while True:
					# Wait for a message, then poll the logs (we could theoretically just use the message directly, but this is easier to fit into the existing setup that expects offsets and timestamps)
					msg = yield ws.read_message()
					if msg is None:
						break
					console('websocket client', "Message: %s" % msg)
					self.tickWrap()
				console('websocket client', 'Disconnected')
Пример #7
0
	def open(self):
		handlers.append(self)
		console('websocket', "Opened")
Пример #8
0
	def run(self):
		app = tornado.web.Application([('/', WSSpadesHandler), ('/ws', WSSpadesHandler)])
		app.listen(self.port, '0.0.0.0')
		console('websocket', "Listening")
		tornado.ioloop.IOLoop.instance().start()
Пример #9
0
def brick(msg):
	global brickMessage
	brickMessage = msg or True
	console('brick', "Bricked: %s", brickMessage)
Пример #10
0
	def log_message(self, fmt, *args):
		console('rorn', "%s - %s", self.address_string(), fmt % args)
Пример #11
0
	def tick(self):
		if self.gameCon is None:
			# Get list of logs (['YYYY-mm-dd_HHMMSS.log'])
			# Unfortunately the server doesn't return an etag for this page; instead we cache based on the number of logs displayed, on the assumption that logs will never be removed
			req = requests.get(logURL)
			if req.status_code != 200:
				raise RuntimeError("Server returned %d looking up log list" % req.status_code)
			logs = map(str, lxml.html.fromstring(req.text).xpath('//a[substring-after(@href, ".")="log"]/@href'))
			logs = filter(None, map(Shim.onLogLoad, logs))

			numGames = len(getGames())
			if len(logs) < numGames:
				raise RuntimeError("Only got %d %s from server (have %d in database)" % (len(logs), 'log' if len(logs) == 1 else 'logs', numGames))
			elif len(logs) == numGames: # No new logs
				return

			# Find the first one we don't have a game for
			for log in logs:
				if log not in db['games']:
					console('event thread', "Starting new game for %s" % log)
					self.gameCon = Shim.onGameCon(GameConstructor(log, self.onGameEnd))
					break
			else:
				console('event thread', "No games in progress")
				return

		# Look for new events in the current log
		data = self.cachedGet(logURL + self.gameCon.logFilename)
		if data is None: # Nothing new
			console('event thread', "No new data in %s" % self.gameCon.logFilename)
			return
		elif data is False: # Server is down. Hopefully temporarily; try again next tick
			console('event thread', "Failed to fetch data from %s" % self.gameCon.logFilename)
			return
		elif len(data) < self.gameCon.logOffset:
			raise RuntimeError("Fetched %d-byte log file %s, but next event expected at %d" % (len(data), self.gameCon.logFilename, self.gameCon.logOffset))
		while self.gameCon and self.gameCon.logOffset < len(data) and self.test != 0:
			line = data[self.gameCon.logOffset:data.index('\n', self.gameCon.logOffset)+1]
			print "%8d %s" % (self.gameCon.logOffset, line)
			originalLen = len(line)
			line = Shim.onLine(self.gameCon, self.gameCon.logOffset, unpretty(line))
			if line is None:
				self.gameCon.logOffset += originalLen
				continue
			# print "Searching for pattern at %s offset %d: %s" % (self.gameCon.logFilename, self.gameCon.logOffset, line)
			for pattern, fns in eventPatterns:
				match = pattern.match(line)
				if match:
					for fn in fns:
						g = match.groupdict() # Copy; the original match groupdict is not changed below
						# Bit of a hack. We want to rewrite any group that contains a PLAY, but there's no way to tell now. Currently all those groups are named 'play', so we only rewrite those
						if 'play' in g:
							g['play'] = unpretty(g['play'])
						# Same hack with USER
						for k in ('user', 'user1', 'user2'):
							if k in g:
								shimmed = Shim.onUsername(g[k])
								if shimmed != g[k]:
									self.gameCon.usernameShims[shimmed] = g[k]
									g[k] = shimmed
						tz = int(round((datetime.now() - datetime.utcnow()).total_seconds() / 3600))
						event = {'ts': datetime.strptime(g['ts'], '%Y-%m-%d %H:%M:%S') + timedelta(hours = tz), 'off': self.gameCon.logOffset}
						del g['ts']
						event.update(fn(**g))
						if self.test > 0:
							self.test -= 1
						event = Shim.onEvent(self.gameCon, self.gameCon.logOffset, event)
						if event is not None:
							self.gameCon.pump(event)
					# pump() may have triggered onGameEnd and killed the current gameCon
					if self.gameCon is not None:
						self.gameCon.logOffset += originalLen
					break
			else:
				raise RuntimeError("Unrecognized log line at %s:%d: %s" % (self.gameCon.logFilename, self.gameCon.logOffset, line))
		if hasattr(self.gameCon, 'game'):
			self.gameCon.game.out()
			WSSpadesHandler.on_game_change(self.gameCon.game)
Пример #12
0

# When python is started in the background it ignores SIGINT instead of throwing a KeyboardInterrupt
def signal_die(signum, frame):
    signals = dict((getattr(signal, name), name) for name in dir(signal)
                   if name.startswith('SIG') and '_' not in name)
    raise SystemExit("Caught %s; exiting" %
                     signals.get(signum, "signal %d" % signum))


signal.signal(signal.SIGINT, signal.default_int_handler)
signal.signal(signal.SIGTERM, signal_die)

server = HTTPServer(('', config.localBindPort), HTTPHandler)
HTTPHandler.enableStaticHandler('static')
HTTPHandler.enableVue('components', 'views')
try:
    console('main', 'Listening for connections')
    server.serve_forever()
except KeyboardInterrupt:
    sys.__stdout__.write("\n\n")
    console('main', 'Exiting at user request')
except (Exception, SystemExit) as e:
    sys.__stdout__.write("\n\n")
    console('main', '%s', e)

console('main', 'Closing server sockets')
server.server_close()

console('main', 'Done')
Пример #13
0
if settings.redis:
	host, port = settings.redis.split(':', 1)
	port = int(port)
	addEventHandler(EventPublisher.EventPublisher(host, port))

# When python is started in the background it ignores SIGINT instead of throwing a KeyboardInterrupt
def signal_die(signum, frame):
	signals = dict((getattr(signal, name), name) for name in dir(signal) if name.startswith('SIG') and '_' not in name)
	raise SystemExit("Caught %s; exiting" % signals.get(signum, "signal %d" % signum))
signal.signal(signal.SIGINT, signal.default_int_handler)
signal.signal(signal.SIGTERM, signal_die)

# Daemonize
if option('daemon'):
	logFile = datetime.now().strftime('log-%Y%m%d-%H%M%S.log')
	console('main', "Switching to daemon mode. Output logged to %s" % logFile)

	# Double-fork
	if os.fork() != 0:
		os._exit(0)
	os.setsid()
	if os.fork() != 0:
		os._exit(0)

	# Point the standard file descriptors at a log file
	log = os.open(logFile, os.O_CREAT | os.O_RDWR)
	os.dup2(log, 0)
	os.dup2(log, 1)
	os.dup2(log, 2)

# Writing the pidfile
Пример #14
0
	def log_message(self, fmt, *args):
		user = self.session['user'].username if self.session and self.session['user'] else self.address_string()
		console('rorn', "%s(%s) %s", user or 'logged out', self.address_string(), fmt % args)
Пример #15
0
 def log_message(self, fmt, *args):
     console("rorn", "%s - %s", self.address_string(), fmt % args)