Belote/belote.py

423 lines
16 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import flask
from flask_socketio import SocketIO
import flask_login as fll
import datetime
from db import db, User, Game, Player, levels_users
from belote_jeu import N_TURN, N_PLAYERS
import belote_ws
import settings
login_manager = fll.LoginManager()
app = flask.Flask(__name__)
app.config.from_pyfile('settings.cfg')
#######################################################################
# Plugin management #
#######################################################################
# SocketIO
socketio = SocketIO(app, cors_allowed_origins=settings.PUBLIC_URL)
login_manager.init_app(app)
# Database management
db.app = app
db.init_app(app)
db.create_all()
db.session.commit()
# Insert admin if not exist
if User.query.limit(1).first() is None:
admin = User(login='admin', name='Admin', level=levels_users['Admin'])
admin.set_password('admin')
db.session.add(admin)
db.session.commit()
# Login management
@login_manager.user_loader
def load_user(user_id):
if user_id is None :
return None
return User.query.get(user_id)
@login_manager.unauthorized_handler
def unauthorized_callback():
return flask.redirect('/login?next=' + flask.request.path)
#######################################################################
# Global variables or fuctions #
#######################################################################
URL_DEFAULT = '/games'
namespaces_join = {}
namespaces = {}
def create_namespace(gid, join=False):
if(join):
namespace = belote_ws.CustomNamespaceJoin(gid)
namespaces_join[gid] = True
socketio.on_namespace(namespace)
else:
namespace = belote_ws.CustomNamespacePlay(gid)
namespaces[gid] = True
socketio.on_namespace(namespace)
@app.route('/')
def home():
user = fll.current_user
if not hasattr(user, 'level'):
user = None
return flask.render_template("index.html", user=user)
#######################################################################
# Login, logout #
#######################################################################
@app.route('/login', methods=['GET', 'POST'])
def login():
if fll.current_user.is_authenticated:
return flask.redirect(URL_DEFAULT)
if flask.request.method=="POST":
username = flask.request.form['username']
password = flask.request.form['password']
user = User.query.get(username)
if user and user.level > levels_users['Inactive'] and user.check_password(password):
fll.login_user(user)
user.last_login = datetime.datetime.utcnow()
next = flask.request.args.get('next')
if next is not None and next[0]=='/':
return flask.redirect(next)
else:
return flask.redirect(URL_DEFAULT)
else:
app.logger.warning('Login fail for user {} from {}'.format(user, flask.request.remote_addr))
flask.flash('Nom d\'utilisateur ou mot de passe incorrect')
return flask.render_template('login.html', user=None)
@app.route('/logout')
def logout():
fll.logout_user()
return flask.redirect('/')
#######################################################################
# Games : list #
#######################################################################
@app.route('/games')
@fll.login_required
def games():
games = Game.query.all()
mesjeux = []
joinable = []
for g in games:
if g.admin==fll.current_user.login:
mesjeux.append(g)
elif g.can_join(fll.current_user):
joinable.append(g)
cancreategames = fll.current_user.level >= levels_users['Can create games']
candeletegames = fll.current_user.level >= levels_users['Admin']
return flask.render_template('games.html', mesjeux=mesjeux, joinable=joinable, user=fll.current_user, \
cancreategames=cancreategames, candeletegames=candeletegames)
#######################################################################
# Create, join and play to a game #
#######################################################################
@app.route('/add_game', methods=['GET', 'POST'])
@fll.login_required
def game_add():
player = fll.current_user
if fll.current_user.level >= levels_users['Can create games']:
if flask.request.method=="POST":
name = flask.request.form['name']
if name is None or name == '':
return flask.redirect('/add_game')
game = Game(name=name, admin=player.login)
db.session.add(game)
db.session.commit()
game.join(fll.current_user)
db.session.commit()
return flask.redirect('/game/{}'.format(game.id))
return flask.render_template('game_add.html', user=fll.current_user)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/join')
@fll.login_required
def game_page_join(roomid):
game = Game.query.get(roomid)
if game:
player = fll.current_user
if game.start:
return flask.redirect('/game/{}'.format(roomid))
if game.can_join(player):
if game.id not in namespaces_join:
create_namespace(game.id, join=True)
return flask.render_template('game_join.html', game=game, user=player, admin=game.admin)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/start')
@fll.login_required
def game_page_start(roomid):
game = Game.query.get(roomid)
if game:
player = fll.current_user
if game.isadmin(player) and game.fixplayers and not game.start:
return flask.render_template('game_start.html', game=game, user=player)
return flask.redirect('/game/{}'.format(roomid))
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>')
@fll.login_required
def game_page(roomid):
game = Game.query.get(roomid)
if game:
player = fll.current_user
if not game.start:
return flask.redirect('/game/{}/join'.format(roomid))
if game.authorized(player) :
if game.id not in namespaces:
create_namespace(game.id)
return flask.render_template('game.html', game=game, user=player)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/del')
@fll.login_required
def delgame(roomid):
game = Game.query.get(roomid)
if game is not None and fll.current_user.login == game.admin:
confirm = flask.request.args.get('confirm')
if confirm is not None:
db.session.delete(game)
db.session.commit()
else:
return flask.render_template('game_del.html', game=game, user=fll.current_user)
return flask.redirect(URL_DEFAULT)
#######################################################################
# Users management #
#######################################################################
@app.route('/admin/users')
@fll.login_required
def admin_users():
if fll.current_user.level < levels_users['Can manage simple users']:
return flask.redirect(URL_DEFAULT)
users = User.query.all()
return flask.render_template('admin_users.html', user=fll.current_user, users=users)
@app.route('/admin/users/<user>', methods=['GET', 'POST'])
@fll.login_required
def edituser(user):
new = user=='new'
user = User.query.get(user)
if not new and user is None:
return flask.redirect('/admin/users')
if fll.current_user.level >= levels_users['Admin'] or \
fll.current_user.level >= levels_users['Can manage simple users'] and \
fll.current_user.level > user.level:
if flask.request.method=="POST":
username = flask.request.form['username']
name = flask.request.form['name']
password = flask.request.form['password']
level = int(flask.request.form['level'])
if level not in levels_users.values():
flask.flash('Level unknown')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if fll.current_user.level < levels_users['Admin']:
if not level < fll.current_user.level:
flask.flash('Invalid level')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if len(password)<2 and new:
flask.flash('Mot de passe trop court')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if new and User.query.get(username) is not None:
flask.flash('Login déjà utilisé ! Choisissez un autre !')
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
if new:
usertoadd = User(login=username, name=name, level=level)
usertoadd.set_password(password)
db.session.add(usertoadd)
else:
user.name = name
if len(password)>=2:
user.set_password(password)
user.level = level
db.session.commit()
return flask.redirect('/admin/users')
else:
return flask.render_template("admin_user_edit.html", user=fll.current_user, useredit=user, levels_users=levels_users)
return flask.redirect(URL_DEFAULT)
@app.route('/admin/users/<user>/del')
@fll.login_required
def deluser(user):
user = User.query.get(user)
if user is None:
return flask.redirect('/admin/users')
if fll.current_user.level >= levels_users['Admin'] or \
fll.current_user.level >= levels_users['Can manage simple users'] and \
fll.current_user.level > user.level:
confirm = flask.request.args.get('confirm')
if confirm is not None:
delete_game(user=user)
db.session.delete(user)
db.session.commit()
return flask.redirect('/admin/users')
return flask.render_template("admin_user_del.html", user=fll.current_user, useredit=user)
@app.route('/password', methods=['GET', 'POST'])
@fll.login_required
def change_password():
if flask.request.method=="POST":
password = flask.request.form['password']
if len(password) < 5:
flask.flash('Mot de passe trop court !')
else:
fll.current_user.set_password(password)
db.session.commit()
return flask.redirect(URL_DEFAULT)
return flask.render_template("password.html", user=fll.current_user)
#######################################################################
# Game managament #
#######################################################################
@app.route('/admin/games')
@fll.login_required
def admin_games():
if fll.current_user.level < levels_users['Can manage games']:
return flask.redirect(URL_DEFAULT)
games = Game.query.all()
return flask.render_template('admin_games.html', user=fll.current_user, games=games)
@app.route('/admin/games/<roomid>/del')
@fll.login_required
def admin_game_del(roomid):
game = Game.query.get(roomid)
if game is None:
return flask.redirect('/admin/games')
if fll.current_user.level >= levels_users['Can manage games']:
confirm = flask.request.args.get('confirm')
if confirm is not None:
delete_game(game=game)
return flask.redirect('/admin/games')
else:
return flask.render_template('game_del.html', user=fll.current_user, game=game)
return flask.redirect(URL_DEFAULT)
@app.route('/game/<roomid>/see')
@fll.login_required
def see_game(roomid):
game = Game.query.get(roomid)
if game is None:
return flask.redirect('/games')
if fll.current_user.level >= levels_users['Can manage games'] or game.authorized(fll.current_user) and game.turn==N_TURN*N_PLAYERS:
#return game.serialize_state_anonymous()
return flask.render_template('game_see.html', user=fll.current_user, game=game, serialized=game.serialize_state_anonymous())
return flask.redirect(URL_DEFAULT)
@app.route('/admin/games/new', methods=['GET', 'POST'])
@fll.login_required
def admin_games_new():
if fll.current_user.level < levels_users['Can manage games']:
return flask.redirect(URL_DEFAULT)
if flask.request.method=="POST":
name = flask.request.form['name']
login0 = flask.request.form['player0']
login1 = flask.request.form['player1']
login2 = flask.request.form['player2']
login3 = flask.request.form['player3']
admin = flask.request.form['admin']
if 'fixplayers' in flask.request.form:
start = True
else:
start = False
try:
admin = int(admin)
except:
return 'Error'
if admin<0 or admin>3:
return 'Error'
adminn = -1
lusers = []
if login0 != '':
user0 = User.query.get(login0)
if user0 is None:
flask.flash('Utilisateur inconnu {}'.format(login0))
else:
lusers.append(user0)
if admin==0: adminn = 0
if login1 != '':
user1 = User.query.get(login1)
if user1 is None:
flask.flash('Utilisateur inconnu {}'.format(login1))
else:
if admin==1: adminn = len(lusers)
lusers.append(user1)
if login2 != '':
user2 = User.query.get(login2)
if user2 is None:
flask.flash('Utilisateur inconnu {}'.format(login2))
else:
if admin==2: adminn = len(lusers)
lusers.append(user2)
if login3 != '':
user3 = User.query.get(login3)
if user3 is None:
flask.flash('Utilisateur inconnu {}'.format(login3))
else:
if admin==3: adminn = len(lusers)
lusers.append(user3)
if adminn <0:
flask.flash('Vous devez définir un maitre du jeu (admin).')
elif len(name)<2:
flask.flash('Nom trop court !')
else:
game = Game(name=name, admin=lusers[adminn].login)
db.session.add(game)
db.session.commit()
ordered_players = []
for u in lusers:
ordered_players.append(u.login)
game.join(u)
db.session.commit()
if start and game.can_start():
game.fix_players()
game.start_game(ordered_players)
return flask.redirect('/admin/games')
return flask.render_template('admin_game_add.html', user=fll.current_user)
def delete_game(user=None, game=None):
if user is not None:
games = Game.query.filter_by(admin=user.login).all()
for g in games:
for p in g.players:
db.session.delete(p)
db.session.delete(g)
db.session.commit()
if isinstance(game, int):
g = Game.query.get(game)
if g is not None:
for p in g.players:
db.session.delete(p)
db.session.delete(g)
db.session.commit()
elif game is not None:
for p in game.players:
db.session.delete(p)
db.session.delete(game)
db.session.commit()
if __name__ == '__main__':
socketio.run(app, debug=True, host='127.0.0.1', port=8080 )