DotaNoobs main site.
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.

326 lines
12 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. import simplejson as json
  2. from datetime import datetime
  3. from random import choice
  4. from flask.ext.sqlalchemy import SQLAlchemy
  5. from sqlalchemy.ext.mutable import Mutable
  6. import board
  7. from app import db, app
  8. from utils import parse_valve_heropedia, complete_hero_data, API_DATA
  9. # Model independant get_or_create
  10. def get_or_create_instance(session, model, **kwargs):
  11. instance = session.query(model).filter_by(**kwargs).first()
  12. if instance:
  13. return instance
  14. else:
  15. instance = model(**kwargs)
  16. session.add(instance)
  17. return instance
  18. # Get a little of that Mongoness back
  19. class Json(db.TypeDecorator):
  20. impl = db.Text
  21. def process_bind_param(self, value, dialect):
  22. if value is not None:
  23. value = json.dumps(value)
  24. return value
  25. def process_result_value(self, value, dialect):
  26. try:
  27. if value is not None:
  28. value = json.loads(value)
  29. except ValueError:
  30. return {}
  31. return value
  32. # Mongoness factor - phase 2
  33. class MutableDict(Mutable, dict):
  34. @classmethod
  35. def coerce(cls, key, value):
  36. if not isinstance(value, MutableDict):
  37. if isinstance(value, dict):
  38. return MutableDict(value)
  39. return Mutable.coerce(key,value)
  40. else:
  41. return value
  42. def __delitem__(self, key):
  43. dict.__delitem__(self, key)
  44. self.changed()
  45. def __setitem__(self, key, value):
  46. dict.__setitem__(self, key, value)
  47. self.changed()
  48. def __getstate__(self):
  49. return dict(self)
  50. def __setstate__(self, state):
  51. self.update(self)
  52. class User(db.Model):
  53. id = db.Column(db.Integer, primary_key=True)
  54. steam_id = db.Column(db.String(40), unique=True)
  55. forum_id = db.Column(db.Integer)
  56. teamspeak_id = db.Column(db.String(200), unique=True)
  57. nickname = db.Column(db.String(80))
  58. avatar = db.Column(db.String(255))
  59. admin = db.Column(db.Boolean)
  60. bio_text = db.Column(db.String(4096))
  61. created = db.Column(db.DateTime)
  62. last_seen = db.Column(db.DateTime)
  63. twitch = db.Column(db.String(60))
  64. hitbox = db.Column(db.String(60))
  65. random_heroes = db.Column(MutableDict.as_mutable(Json))
  66. az_completions = db.Column(db.Integer)
  67. public = db.Column(db.Boolean)
  68. logo = db.Column(db.Boolean)
  69. points_from_events = db.Column(db.Integer)
  70. points_from_ts3 = db.Column(db.Integer)
  71. points_from_forum = db.Column(db.Integer)
  72. ts3_starttime = db.Column(db.DateTime)
  73. ts3_endtime = db.Column(db.DateTime)
  74. ts3_rewardtime = db.Column(db.DateTime)
  75. ts3_connections = db.Column(MutableDict.as_mutable(Json))
  76. last_post_reward = db.Column(db.Integer)
  77. winrate_data = db.Column(MutableDict.as_mutable(Json))
  78. @classmethod
  79. def get_or_create(self, steam_id):
  80. return get_or_create_instance(db.session, User, steam_id=steam_id)
  81. @classmethod
  82. def get_streaming_users(self):
  83. twitch_streams = []
  84. hitbox_streams = []
  85. for user in User.query.all():
  86. if user.points_from_events + user.points_from_ts3 + user.points_from_forum < 5: continue
  87. if user.twitch:
  88. twitch_streams.append(user.twitch)
  89. if user.hitbox:
  90. hitbox_streams.append(user.hitbox)
  91. return {'twitch': twitch_streams, 'hitbox': hitbox_streams}
  92. def __init__(self, steam_id):
  93. self.steam_id = steam_id
  94. self.random_heroes = {'current':None, 'completed':[]}
  95. self.az_completions = 0
  96. self.ts3_connections = {'list':[]}
  97. self.ts3_rewardtime = datetime.utcnow()
  98. self.created = datetime.utcnow()
  99. self.last_seen = datetime.utcnow()
  100. self.bio_text = None
  101. self.points_from_events = 0
  102. self.points_from_ts3 = 0
  103. self.points_from_forum = 0
  104. self.admin = False
  105. self.public = True
  106. self.biglogo = True
  107. @property
  108. def random_hero(self):
  109. if not self.random_heroes['current']:
  110. heroes = []
  111. for (tavern_name, tavern) in parse_valve_heropedia():
  112. heroes.extend([complete_hero_data('name', entry['name']) for entry in tavern if entry['name'] not in self.random_heroes['completed']])
  113. if heroes:
  114. self.random_heroes['current'] = choice(heroes)
  115. self.random_heroes = self.random_heroes
  116. db.session.commit()
  117. return self.random_heroes['current']
  118. @random_hero.setter
  119. def random_hero(self, herodata):
  120. self.random_heroes['current'] = herodata
  121. self.random_heroes = self.random_heroes
  122. db.session.commit()
  123. @property
  124. def random_completed(self):
  125. return self.random_heroes['completed']
  126. def random_success(self):
  127. self.random_heroes['completed'].append(self.random_heroes['current']['name'])
  128. if len(API_DATA['result']['heroes']) - len(self.random_heroes['completed']) <= 0:
  129. self.az_completions = self.az_completions + 1
  130. del self.random_heroes['completed'][:]
  131. self.random_heroes['current'] = None
  132. self.random_heroes = self.random_heroes
  133. db.session.commit()
  134. return self.random_hero
  135. def random_skip(self):
  136. self.random_heroes['current'] = None
  137. self.random_heroes = self.random_heroes
  138. db.session.commit()
  139. return self.random_hero
  140. def update_connection(self, reward_threshold=30):
  141. now = datetime.utcnow()
  142. self.ts3_starttime = self.ts3_starttime or now
  143. self.ts3_endtime = now
  144. # Add general TS3 points here
  145. if self.ts3_endtime and self.ts3_rewardtime:
  146. delta = (self.ts3_endtime - self.ts3_rewardtime)
  147. duration = (delta.seconds % 3600) // 60
  148. if duration > reward_threshold:
  149. self.ts3_rewardtime = datetime.utcnow()
  150. self.points_from_ts3 += 1
  151. else:
  152. self.ts3_rewardtime = datetime.utcnow()
  153. self.last_seen = datetime.utcnow()
  154. db.session.commit();
  155. def finalize_connection(self):
  156. self.ts3_connections['list'].append({'starttime': self.ts3_starttime, 'endtime': self.ts3_endtime})
  157. self.ts3_startime = None
  158. self.ts3_endtime = None
  159. db.session.commit();
  160. def update_forum_posts(self, reward_threshold=5):
  161. if self.forum_id:
  162. posts = board.Users.select().where(board.Users.id == int(self.forum_id))[0].posts
  163. if self.last_post_reward:
  164. num_points = (posts - self.last_post_reward) / reward_threshold
  165. if num_points > 0:
  166. self.points_from_forum += num_points
  167. self.last_post_reward += num_points * reward_threshold
  168. else:
  169. # Initialize if this is the first reward
  170. self.last_post_reward = posts
  171. db.session.commit()
  172. @property
  173. def is_active(self):
  174. return self.ts3_starttime and True or False
  175. def __repr__(self):
  176. return '<User {}>'.format(self.id)
  177. class TeamspeakData(db.Model):
  178. id = db.Column(db.Integer, primary_key=True)
  179. time = db.Column(db.DateTime())
  180. clients = db.Column(Json())
  181. def __init__(self, clientlist):
  182. self.time = datetime.utcnow()
  183. self.clients = clientlist
  184. class Event(db.Model):
  185. id = db.Column(db.Integer, primary_key=True)
  186. name = db.Column(db.String(200))
  187. desc = db.Column(db.String(4096))
  188. type = db.Column(db.String(20))
  189. start_time = db.Column(db.DateTime)
  190. end_time = db.Column(db.DateTime)
  191. points = db.Column(db.Integer)
  192. reward_threshold = db.Column(db.Integer)
  193. total_subchans = db.Column(db.Integer)
  194. channels = db.Column(MutableDict.as_mutable(Json))
  195. participants = db.Column(MutableDict.as_mutable(Json))
  196. def __init__(self, id):
  197. self.channels = {'event_cid':None, 'cids':[]}
  198. @classmethod
  199. def get_or_create(self, event_id):
  200. return get_or_create_instance(db.session, Event, id=event_id)
  201. @property
  202. def cids(self):
  203. return self.channels['cids']
  204. @property
  205. def event_cid(self):
  206. return self.channels['event_cid']
  207. @property
  208. def channel_ids(self):
  209. cids = self.channels['cids']
  210. if self.channels['event_cid']:
  211. cids.append(self.channels['event_cid'])
  212. return cids
  213. def create_channels(self):
  214. import ts3
  215. server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
  216. server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
  217. server.use(1)
  218. # Create the parent channel
  219. if not self.event_cid:
  220. # Find the LFG channel and place this one after
  221. response = server.send_command('channelfind', keys={'pattern': 'Looking for Group'})
  222. if response.is_successful:
  223. cid = response.data[0]['cid']
  224. response = server.send_command('channelcreate', keys={'channel_name':self.name.encode('utf-8'), 'channel_flag_semi_permanent':'1', 'channel_order':cid})
  225. if response.is_successful:
  226. self.channels['event_cid'] = response.data[0]['cid']
  227. # Create the subchannels
  228. if not self.cids:
  229. cids = []
  230. keys = {'channel_name':'Event Room #{}'.format(len(self.cids) + 1), 'channel_flag_semi_permanent':'1', 'cpid':self.event_cid.encode('utf-8')}
  231. response = server.send_command('channelcreate', keys=keys)
  232. if response.is_successful:
  233. parent_cid = response.data[0]['cid']
  234. cids.append(parent_cid)
  235. else:
  236. raise UserWarning("channelcreate failed")
  237. response = server.send_command('channelcreate', keys={'channel_name':'Radiant Team', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
  238. if response.is_successful:
  239. cids.append(response.data[0]['cid'])
  240. response = server.send_command('channelcreate', keys={'channel_name':'Dire Team', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
  241. if response.is_successful:
  242. cids.append(response.data[0]['cid'])
  243. response = server.send_command('channelcreate', keys={'channel_name':'Spectators', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
  244. if response.is_successful:
  245. cids.append(response.data[0]['cid'])
  246. self.channels['cids'] = cids
  247. db.session.commit()
  248. def remove_channels(self):
  249. import ts3
  250. server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
  251. server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
  252. server.use(1)
  253. response = server.send_command('channeldelete', keys={'cid':self.event_cid.encode('utf-8'), 'force':'1'})
  254. if response.is_successful:
  255. self.channels = {'event_cid': None, 'cids':[]}
  256. db.session.commit()
  257. @property
  258. def active(self):
  259. current_time = datetime.utcnow()
  260. return self.start_time < current_time and current_time < self.end_time
  261. @property
  262. def expired(self):
  263. current_time = datetime.utcnow()
  264. return self.end_time < curent_time
  265. def add_participant(self, user):
  266. entry = self.participants.setdefault(user, {'start_time': datetime.utcnow() })
  267. entry['end_time'] = datetime.utcnow()
  268. if 'points' not in entry and (entry['end_time'] - entry['start_time']) > self.reward_threshold:
  269. user.points_from_events += self.points
  270. entry['points'] = self.points
  271. db.session.commit()
  272. @property
  273. def participants(self):
  274. return tuple(self.participants)
  275. def __repr__(self):
  276. return '<Event {}>'.format(self.id)