You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
11 KiB
293 lines
11 KiB
import simplejson as json
|
|
from datetime import datetime, timedelta
|
|
from flask.ext.sqlalchemy import SQLAlchemy
|
|
from sqlalchemy.ext.mutable import Mutable
|
|
|
|
import board
|
|
from app import db, app
|
|
from utils import parse_valve_heropedia, complete_hero_data, API_DATA
|
|
|
|
# Model independant get_or_create
|
|
def get_or_create_instance(session, model, **kwargs):
|
|
instance = session.query(model).filter_by(**kwargs).first()
|
|
if instance:
|
|
return instance
|
|
else:
|
|
instance = model(**kwargs)
|
|
session.add(instance)
|
|
return instance
|
|
|
|
# Get a little of that Mongoness back
|
|
class Json(db.TypeDecorator):
|
|
impl = db.Text
|
|
|
|
def process_bind_param(self, value, dialect):
|
|
if value is not None:
|
|
value = json.dumps(value)
|
|
return value
|
|
|
|
def process_result_value(self, value, dialect):
|
|
try:
|
|
if value is not None:
|
|
value = json.loads(value)
|
|
except ValueError:
|
|
return {}
|
|
return value
|
|
|
|
# Mongoness factor - phase 2
|
|
class MutableDict(Mutable, dict):
|
|
@classmethod
|
|
def coerce(cls, key, value):
|
|
if not isinstance(value, MutableDict):
|
|
if isinstance(value, dict):
|
|
return MutableDict(value)
|
|
return Mutable.coerce(key,value)
|
|
else:
|
|
return value
|
|
|
|
def __delitem__(self, key):
|
|
dict.__delitem__(self, key)
|
|
self.changed()
|
|
|
|
def __setitem__(self, key, value):
|
|
dict.__setitem__(self, key, value)
|
|
self.changed()
|
|
|
|
def __getstate__(self):
|
|
return dict(self)
|
|
|
|
def __setstate__(self, state):
|
|
self.update(self)
|
|
|
|
|
|
class User(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
steam_id = db.Column(db.String(40), unique=True)
|
|
forum_id = db.Column(db.Integer)
|
|
teamspeak_id = db.Column(db.String(200), unique=True)
|
|
|
|
nickname = db.Column(db.String(80))
|
|
avatar = db.Column(db.String(255))
|
|
|
|
bio_text = db.Column(db.String(4096))
|
|
created = db.Column(db.DateTime)
|
|
last_seen = db.Column(db.DateTime)
|
|
twitch = db.Column(db.String(60))
|
|
hitbox = db.Column(db.String(60))
|
|
|
|
admin = db.Column(db.Boolean)
|
|
public = db.Column(db.Boolean)
|
|
logo = db.Column(db.Boolean)
|
|
|
|
points_from_events = db.Column(db.Integer)
|
|
points_from_ts3 = db.Column(db.Integer)
|
|
points_from_forum = db.Column(db.Integer)
|
|
|
|
ts3_starttime = db.Column(db.DateTime)
|
|
ts3_endtime = db.Column(db.DateTime)
|
|
ts3_rewardtime = db.Column(db.DateTime)
|
|
ts3_stretch_award_time = db.Column(db.DateTime)
|
|
ts3_longest_stretch = db.Column(db.Interval)
|
|
|
|
last_post_reward = db.Column(db.Integer)
|
|
winrate_data = db.Column(MutableDict.as_mutable(Json))
|
|
|
|
|
|
@classmethod
|
|
def get_or_create(self, steam_id):
|
|
return get_or_create_instance(db.session, User, steam_id=steam_id)
|
|
|
|
@classmethod
|
|
def get_streaming_users(self):
|
|
twitch_streams = []
|
|
hitbox_streams = []
|
|
for user in User.query.all():
|
|
if user.points_from_events + user.points_from_ts3 + user.points_from_forum < 5: continue
|
|
if user.twitch:
|
|
twitch_streams.append(user.twitch)
|
|
if user.hitbox:
|
|
hitbox_streams.append(user.hitbox)
|
|
|
|
return {'twitch': twitch_streams, 'hitbox': hitbox_streams}
|
|
|
|
def __init__(self, steam_id):
|
|
self.steam_id = steam_id
|
|
self.az_completions = 0
|
|
self.ts3_rewardtime = datetime.utcnow()
|
|
self.ts3_longest_stretch = timedelta()
|
|
self.created = datetime.utcnow()
|
|
self.last_seen = datetime.utcnow()
|
|
self.bio_text = None
|
|
self.points_from_events = 0
|
|
self.points_from_ts3 = 0
|
|
self.points_from_forum = 0
|
|
self.admin = False
|
|
self.public = True
|
|
self.biglogo = True
|
|
|
|
def update_connection(self, reward_threshold=30):
|
|
now = datetime.utcnow()
|
|
self.ts3_starttime = self.ts3_starttime or now
|
|
self.ts3_endtime = now
|
|
|
|
# Add general TS3 points here
|
|
delta = (self.ts3_endtime - self.ts3_rewardtime)
|
|
duration = (delta.seconds % 3600) // 60
|
|
if duration > reward_threshold:
|
|
self.ts3_rewardtime = datetime.utcnow()
|
|
self.points_from_ts3 += 1
|
|
|
|
# Update last_seen for web profile
|
|
self.last_seen = datetime.utcnow()
|
|
db.session.commit();
|
|
|
|
def finalize_connection(self):
|
|
# Check for longest!
|
|
if self.ts3_endtime and self.ts3_starttime:
|
|
current_stretch = self.ts3_endtime - self.ts3_starttime
|
|
if current_stretch > self.ts3_longest_stretch:
|
|
self.ts3_longest_stretch = current_stretch
|
|
self.ts3_stretch_award_time = datetime.utcnow()
|
|
|
|
# Reset values
|
|
self.ts3_starttime = None
|
|
self.ts3_endtime = None
|
|
db.session.commit();
|
|
|
|
def update_forum_posts(self, reward_threshold=5):
|
|
if self.forum_id:
|
|
posts = board.Users.select().where(board.Users.id == int(self.forum_id))[0].posts
|
|
if self.last_post_reward:
|
|
num_points = (posts - self.last_post_reward) / reward_threshold
|
|
print("Old: {0}, New: {1}, ({0} - {1}) / {2}, {3}, {4}".format(self.last_post_reward, posts, reward_threshold, num_points, self.nickname))
|
|
if num_points > 0:
|
|
self.points_from_forum += num_points
|
|
self.last_post_reward += num_points * reward_threshold
|
|
else:
|
|
# Initialize if this is the first reward
|
|
self.last_post_reward = posts
|
|
db.session.commit()
|
|
|
|
@property
|
|
def is_active(self):
|
|
return self.ts3_starttime and True or False
|
|
|
|
def __repr__(self):
|
|
return '<User {}>'.format(self.id)
|
|
|
|
class TeamspeakData(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
time = db.Column(db.DateTime())
|
|
clients = db.Column(Json())
|
|
|
|
def __init__(self, clientlist):
|
|
self.time = datetime.utcnow()
|
|
self.clients = clientlist
|
|
|
|
class Event(db.Model):
|
|
id = db.Column(db.Integer, primary_key=True)
|
|
name = db.Column(db.String(200))
|
|
desc = db.Column(db.String(4096))
|
|
type = db.Column(db.String(20))
|
|
|
|
start_time = db.Column(db.DateTime)
|
|
end_time = db.Column(db.DateTime)
|
|
points = db.Column(db.Integer)
|
|
reward_threshold = db.Column(db.Integer)
|
|
|
|
total_subchans = db.Column(db.Integer)
|
|
channels = db.Column(MutableDict.as_mutable(Json))
|
|
participants = db.Column(MutableDict.as_mutable(Json))
|
|
|
|
def __init__(self, id):
|
|
self.channels = {'event_cid':None, 'cids':[]}
|
|
|
|
@classmethod
|
|
def get_or_create(self, event_id):
|
|
return get_or_create_instance(db.session, Event, id=event_id)
|
|
|
|
@property
|
|
def cids(self):
|
|
return self.channels['cids']
|
|
|
|
@property
|
|
def event_cid(self):
|
|
return self.channels['event_cid']
|
|
|
|
@property
|
|
def channel_ids(self):
|
|
cids = self.channels['cids']
|
|
if self.channels['event_cid']:
|
|
cids.append(self.channels['event_cid'])
|
|
return cids
|
|
|
|
def create_channels(self):
|
|
import ts3
|
|
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
|
|
server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
|
|
server.use(1)
|
|
# Create the parent channel
|
|
if not self.event_cid:
|
|
# Find the LFG channel and place this one after
|
|
response = server.send_command('channelfind', keys={'pattern': 'Looking for Group'})
|
|
if response.is_successful:
|
|
cid = response.data[0]['cid']
|
|
response = server.send_command('channelcreate', keys={'channel_name':self.name.encode('utf-8'), 'channel_flag_semi_permanent':'1', 'channel_order':cid})
|
|
if response.is_successful:
|
|
self.channels['event_cid'] = response.data[0]['cid']
|
|
# Create the subchannels
|
|
if not self.cids:
|
|
cids = []
|
|
keys = {'channel_name':'Event Room #{}'.format(len(self.cids) + 1), 'channel_flag_semi_permanent':'1', 'cpid':self.event_cid.encode('utf-8')}
|
|
response = server.send_command('channelcreate', keys=keys)
|
|
if response.is_successful:
|
|
parent_cid = response.data[0]['cid']
|
|
cids.append(parent_cid)
|
|
else:
|
|
raise UserWarning("channelcreate failed")
|
|
response = server.send_command('channelcreate', keys={'channel_name':'Radiant Team', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
|
|
if response.is_successful:
|
|
cids.append(response.data[0]['cid'])
|
|
response = server.send_command('channelcreate', keys={'channel_name':'Dire Team', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
|
|
if response.is_successful:
|
|
cids.append(response.data[0]['cid'])
|
|
response = server.send_command('channelcreate', keys={'channel_name':'Spectators', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
|
|
if response.is_successful:
|
|
cids.append(response.data[0]['cid'])
|
|
self.channels['cids'] = cids
|
|
db.session.commit()
|
|
|
|
def remove_channels(self):
|
|
import ts3
|
|
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
|
|
server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
|
|
server.use(1)
|
|
response = server.send_command('channeldelete', keys={'cid':self.event_cid.encode('utf-8'), 'force':'1'})
|
|
if response.is_successful:
|
|
self.channels = {'event_cid': None, 'cids':[]}
|
|
db.session.commit()
|
|
|
|
@property
|
|
def active(self):
|
|
current_time = datetime.utcnow()
|
|
return self.start_time < current_time and current_time < self.end_time
|
|
|
|
@property
|
|
def expired(self):
|
|
current_time = datetime.utcnow()
|
|
return self.end_time < current_time
|
|
|
|
def add_participant(self, user):
|
|
entry = self.participants.setdefault(user, {'start_time': datetime.utcnow() })
|
|
entry['end_time'] = datetime.utcnow()
|
|
if 'points' not in entry and (entry['end_time'] - entry['start_time']) > self.reward_threshold:
|
|
user.points_from_events += self.points
|
|
entry['points'] = self.points
|
|
db.session.commit()
|
|
|
|
@property
|
|
def participants(self):
|
|
return tuple(self.participants)
|
|
|
|
def __repr__(self):
|
|
return '<Event {}>'.format(self.id)
|