-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
executable file
·286 lines (230 loc) · 12.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
#!/usr/bin/env python
""" HoN Server monitor (HoNMonitor). """
import sys, os, threading, signal, re, time, urllib
from os import getpid, uname, path
from setproctitle import setproctitle
from daemon import Daemon
try:
from honcore.client import HoNClient
from honcore.constants import *
from honcore.exceptions import *
except ImportError:
print "Could not find the honcore library, please ensure it is available."
from core import log, events, db
import config
acc_config = {"username" : "", "password" : ""}
basic_config = {"masterserver" : None, "honver" : None}
down_count = 0
class HoNStatus(HoNClient):
def __init__(self):
super(HoNStatus, self).__init__()
self.logged_in = False
self.setup_events()
def setup_events(self):
self.connect_event(HON_SC_TOTAL_ONLINE, events.on_total_online)
self.connect_event(HON_SC_PING, events.on_ping)
@property
def is_logged_in(self):
return self.logged_in
def configure(self):
"""
Sets up the data from the database so it can be used for setting...settings.
The DB structure sucks, so some extra processing to get the correct datatypes.
TODO: Clean up, I'm writing this at 2AM..
"""
log.info("Loading configuration from the database...")
settings = dict(db.query("""SELECT `key`, `value` FROM settings"""))
log.info("Config loaded")
log.info("HoN Version: %s Chat Port: %s Protocol: %s" % (settings['honver'], settings['chatport'], settings['chatver']))
if 'username' in settings:
acc_config['username'] = settings['username']
if 'password' in settings:
acc_config['password'] = settings['password']
if 'invis' in settings:
settings['invis'] = True if settings['invis'] == "True" else False
if 'chatport' in settings:
settings['chatport'] = int(settings['chatport'])
if 'chatver' in settings:
settings['chatver'] = int(settings['chatver'])
for key in settings:
if key in basic_config:
basic_config[key] = settings[key]
self._configure(chatport=settings['chatport'], protocol=settings['chatver'], invis=settings['invis'],
masterserver=settings['masterserver'], basicserver=settings['basicserver'], honver=settings['honver'])
def login_test(self):
""" The aim of this test is to retrieve the cookie and auth hash from the master server.
First the master server should be checked for basic connectivity.
A basic ping is likely not possible because the server would drop ICMP requests and can be seen
in the results below.
PING masterserver.hon.s2games.com (199.7.76.170) 56(84) bytes of data.
--- masterserver.hon.s2games.com ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2008ms
"""
log.info("Testing login server...")
status = ""
note = ""
try:
response = self._login(acc_config['username'], acc_config['password'])
status = "Up"
note = "Login server is OK."
except MasterServerError, e:
status = "Down"
note = e.error
db.execute("""UPDATE servers SET status = %s, time = %s, note = %s WHERE id = 1 """, [1 if status == "Up" else 0, time.time(), note])
return status, note
def chat_test(self):
log.info("Testing chat server...")
status = ""
note = ""
global down_count
try:
response = self._chat_connect()
status = "Up"
note = "Chat server is OK."
down_count = 0
except ChatServerError, e:
status = "Down"
note = e.error
down_count += 1
db.execute("""UPDATE servers SET status = %s, time = %s, note = %s WHERE id = 2 """, [1 if status == "Up" else 0, time.time(), note])
return status, note
def disconnect_logout(self):
log.info("Disconnecting from chat server")
self._chat_disconnect()
log.info("Logging out")
self._logout()
def motd_parser(self):
""" Retrieves the dictionary of message of the day(s(?))
and replaces S2's colour formatting with html classes.
Then places any new items into the database.
"""
colour_map = ["00","1C","38","54","70","8C","A8","C4","E0","FF"]
s2colours = lambda m: '<span style="color: #' + ''.join([colour_map[int(x)] for x in m.group(1)]) + '">'
def urlfix(x):
""" Replaces urls which only contain a 'www' with a 'http://wwww'. """
if x.group(2) in ['http://', 'www']:
colour = "y"
url = x.group(1)
else:
colour = x.group(1)
url = x.group(2)
r = re.compile(r"(?<!http://)www")
if r.match(url):
url = 'http://' + ''.join(url)
return '<a href=' + url + ' class=' + colour + ' target="_blank">' + url + '</a>'
motd_data = self.motd_get()
motd_list = motd_data['motd_list']
## NOTE: This entire thing is fairly broken due to glows, and when retards use rainbows in their text.
# Iterate over the list in reverse because entries are retrieved in order newest -> oldest
# and must be entered into the database oldest -> newest.
for motd in motd_list[::-1]:
# First find any un-coloured hyperlinks and fill them with html tags.
# This regex matches any hyperlink which is not preceeded by a ^g formatter for EG.
# It is not very accurate at the moment as it will match a http which has a ^* at the end.
# http://gskinner.com/RegExr/?2u79l
r = re.compile(r"(?<=[^\^a-zA-Z])((http://|(?<!http://)www)[-a-zA-Z0-9@:%_\+.~#?&//=]+)[^\^\*]")
motd['body'] = r.sub(urlfix, motd['body'])
# Then find all hyperlinks that contain colour formatting and replace with HTML tags.
r = re.compile(r"\^([a-zA-Z])((http://|(?<!http://)www)[-a-zA-Z0-9@:%_\+.~#?&//=]+)(\^\*)")
motd['body'] = r.sub(urlfix, motd['body'])
# Find all coded colours eg ^428 and replace with inline html styling
# ''.join([color_map[int(x)] for x in r.search(msg).group(1)])
r = re.compile(r"\^([0-9]{3})")
motd['body'] = r.sub(s2colours, motd['body'])
# Replace the colours with HTML classes
# Replace ^* with </span>
motd['body'] = motd['body'].replace("^*", "</span>")
# Find all basic colour codes eg ^y or ^r or ^o and replace with inline html
r = re.compile(r"\^([a-z]{1})")
motd['body'] = r.sub(r"<span class='\1'>", motd['body'])
# Replace \r\n with <br />
motd['body'] = motd['body'].replace("\r\n", "<br />")
title_exists = db.query("""SELECT id FROM motds WHERE title = %s AND date = %s""", [motd['title'], motd['date']])
msg_exists = db.query("""SELECT id, title FROM motds WHERE body = %s AND date = %s""", [motd['body'], motd['date']])
if not title_exists:
# Check if the message was simply updated by the staff.
# If it's been changed then update it in the database automatically.
# TODO: Is this level of comparison okay?
if msg_exists:
# Title doesn't exist, but message body does, title changed.
db.execute("""UPDATE motds SET title=%s, author=%s, date=%s, body=%s WHERE id = %s""", [motd['title'], motd['author'], motd['date'], motd['body'], msg_exists[0][0]])
log.info("Updated motd #%s - %s. Title updated to %s" % (msg_exists[0][0], msg_exists[0][1], motd['title']))
elif title_exists:
log.debug("Duplicate title for motd id %s with title %s" % (title_exists[0][0], motd['title']))
# This entry is already here, possibly it could have been updated.
# Note: Seems they like to change the titles after publishing them.
if not msg_exists:
# Title exists but the msg body doesn't, so it was likely updated.
db.execute("""UPDATE motds SET title=%s, author=%s, date=%s, body=%s WHERE id = %s""", [motd['title'], motd['author'], motd['date'], motd['body'], title_exists[0][0]])
log.info("Updated motd #%s - %s. Message updated" % (title_exists[0][0], motd['title']))
if not msg_exists and not title_exists:
# Neither the title or message are there, either both are changed or this is a brand new motd
# Treat it as new for now.
# Add this motd to the database
db.execute("""INSERT INTO motds (title, author, date, body) VALUES(%s, %s, %s, %s)""", [motd['title'], motd['author'], motd['date'], motd['body']])
log.info("Added new message of the day - %s - %s" % (motd['title'], motd['date']))
# Get the image from S2 for the motd.
# Save it to static/img/motd/ if it does not exist.
image_file = motd_data['image'].split("`")[0]
image_name = re.search(r'\/([a-f0-9]+.jpg)', image_file).group(1)
if not os.path.isfile(os.path.join(config.motd_img_dir, image_name)):
urllib.urlretrieve(image_file, os.path.join(config.motd_img_dir, image_name))
# Set the image name in the database so it can be retrieved.
db.execute("""UPDATE `motd_extra` SET `value`=%s WHERE `key`=%s""", [image_name, 'image'])
log.info("New MOTD image.")
def main():
db.connect(config.dbhost, config.dbuser, config.dbpass, config.dbname)
hon_monitor = HoNStatus()
test_count = 1
while True:
log.info("Running test #" + str(test_count))
# Reconfigure the monitor.
hon_monitor.configure()
login_status, login_reason = hon_monitor.login_test()
log.info("Login server: %s - %s" % (login_status, login_reason))
chat_status, chat_reason = hon_monitor.chat_test()
log.info("Chat Server: %s - %s" % (chat_status, chat_reason))
# MotD data can be checked each test, regardless of the server statuses.
try:
hon_monitor.motd_parser()
except MasterServerError, e:
if e.code == 108:
log.error('Could not obtain MotD data from the Master server')
# Check that all tests returned good, otherwise the test fails and should
# be re-attempted in 90 seconds
if login_status is "Up" and chat_status is "Up":
hon_monitor.logged_in = True
timer = 0
while hon_monitor.is_logged_in:
timer += 1
if timer >= 300:
hon_monitor.disconnect_logout()
break
else:
time.sleep(1)
# Client disconnected, cool down for a moment
log.debug("Client disconnected, cooling..")
time.sleep(2)
else:
# Start dropping the players online to zero once it's been determined that
# the servers are down.
if down_count > 5:
db.execute("""INSERT INTO players (time, value) VALUES (%s, %s)""", [str(int(time.time())), 0])
time.sleep(90)
# Loop increment
test_count += 1
# And log back out again
hon_monitor.logged_in = False
def sigint_handler(signal, frame):
""" Basic SIGINT quit """
log.info("Quitting...")
sys.exit(0)
if __name__ == "__main__":
# Set up some global stuff.
signal.signal(signal.SIGINT, sigint_handler)
log.add_logger('/var/python/HoNStatus/honstatus_mon.log', 'ERROR', False)
pname = "hon_monitor:%s:%s" % (uname()[1], getpid())
setproctitle(pname)
print "HoN Monitor started"
main()
print "HoN Monitor stopped"