mirror of
https://github.com/RYGhub/royalnet.git
synced 2024-11-24 03:54:20 +00:00
Many /matchmaking improvements
This commit is contained in:
parent
9ceb8c1c3d
commit
13b91d0a39
3 changed files with 103 additions and 78 deletions
24
db.py
24
db.py
|
@ -1234,14 +1234,16 @@ class Match(Base):
|
||||||
def active_players_count(self):
|
def active_players_count(self):
|
||||||
count = 0
|
count = 0
|
||||||
for player in self.players:
|
for player in self.players:
|
||||||
if player.status == MatchmakingStatus.READY or player.status == MatchmakingStatus.WAIT_FOR_ME:
|
if player.status == MatchmakingStatus.READY \
|
||||||
|
or player.status == MatchmakingStatus.WAIT_FOR_ME \
|
||||||
|
or player.status == MatchmakingStatus.SOMEONE_ELSE:
|
||||||
count += 1
|
count += 1
|
||||||
return count
|
return count
|
||||||
|
|
||||||
def generate_text(self, session):
|
def generate_text(self, session):
|
||||||
player_list = session.query(MatchPartecipation).filter_by(match=self).all()
|
player_list = session.query(MatchPartecipation).filter_by(match=self).all()
|
||||||
title = f"<b>{self.match_title}</b>"
|
title = f"<b>{self.match_title}</b>"
|
||||||
description = self.match_desc if self.match_desc else ""
|
description = f"{self.match_desc}\n" if self.match_desc else ""
|
||||||
if self.min_players:
|
if self.min_players:
|
||||||
minimum = f" <i>(minimo {self.min_players})</i>"
|
minimum = f" <i>(minimo {self.min_players})</i>"
|
||||||
else:
|
else:
|
||||||
|
@ -1264,14 +1266,14 @@ class Match(Base):
|
||||||
continue
|
continue
|
||||||
plist += f"{icon} {player.user.royal.username}\n"
|
plist += f"{icon} {player.user.royal.username}\n"
|
||||||
if ignore_count:
|
if ignore_count:
|
||||||
ignored = f"❌ <i>{ignore_count} persone non ne hanno voglia.</i>\n"
|
ignored = f"❌ <i>{ignore_count} persone non sono interessate.</i>\n"
|
||||||
else:
|
else:
|
||||||
ignored = ""
|
ignored = ""
|
||||||
if self.max_players:
|
if self.max_players:
|
||||||
players = f"[{self.active_players_count()}/{self.max_players}]"
|
players = f"[{self.active_players_count()}/{self.max_players}]"
|
||||||
else:
|
else:
|
||||||
players = f"[{self.active_players_count()}]"
|
players = f"[{self.active_players_count()}]"
|
||||||
close = f"[matchmaking concluso]\n" if self.closed else ""
|
close = f"[matchmaking terminato]\n" if self.closed else ""
|
||||||
message = f"{title} {players}\n" \
|
message = f"{title} {players}\n" \
|
||||||
f"{description}\n" \
|
f"{description}\n" \
|
||||||
f"{plist}\n" \
|
f"{plist}\n" \
|
||||||
|
@ -1282,6 +1284,20 @@ class Match(Base):
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<Match {self.match_title}>"
|
return f"<Match {self.match_title}>"
|
||||||
|
|
||||||
|
def format_dict(self) -> typing.Dict[str, str]:
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"timestamp": self.timestamp.isoformat(),
|
||||||
|
"creator_id": self.creator_id,
|
||||||
|
"creator_name": self.creator.mention(),
|
||||||
|
"match_title": self.match_title,
|
||||||
|
"match_desc": self.match_desc,
|
||||||
|
"min_players": self.min_players,
|
||||||
|
"max_players": self.max_players,
|
||||||
|
"active_players": self.active_players_count(),
|
||||||
|
"players": len(self.active_players_count())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class MatchmakingStatus(enum.IntEnum):
|
class MatchmakingStatus(enum.IntEnum):
|
||||||
WAIT_FOR_ME = 1
|
WAIT_FOR_ME = 1
|
||||||
|
|
50
strings.py
Normal file
50
strings.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from db import MatchmakingStatus
|
||||||
|
|
||||||
|
|
||||||
|
class SafeDict(dict):
|
||||||
|
def __missing__(self, key):
|
||||||
|
return '<code>' + key + '</code>'
|
||||||
|
|
||||||
|
|
||||||
|
def safely_format_string(string, **kwargs):
|
||||||
|
return string.format_map(SafeDict(**kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
class ROYALNET:
|
||||||
|
class ERRORS:
|
||||||
|
TELEGRAM_NOT_LINKED = "⚠ Il tuo account Telegram non è registrato a Royalnet! Registrati con `/register@royalgamesbot <nomeutenteryg>`."
|
||||||
|
|
||||||
|
|
||||||
|
# Matchmaking service strings
|
||||||
|
class MATCHMAKING:
|
||||||
|
TICKER_TEXT = {
|
||||||
|
"match_ready": "🔵 Hai detto che sei pronto per giocare!",
|
||||||
|
"match_wait_for_me": "🕒 Hai chiesto agli altri di aspettarti.",
|
||||||
|
"match_maybe": "❔ Hai detto che forse ci sarai.",
|
||||||
|
"match_someone_else": "💬 Hai detto che vuoi aspettare che venga qualcun altro.",
|
||||||
|
"match_ignore": "❌ Non hai intenzione di partecipare.",
|
||||||
|
"match_close": "🚩 Hai notificato tutti che la partita sta iniziando.",
|
||||||
|
"match_cancel": "🗑 Hai annullato la partita."
|
||||||
|
}
|
||||||
|
|
||||||
|
GAME_START = {
|
||||||
|
MatchmakingStatus.READY: "🔵 Che <b>{match_title}</b> abbia inizio!",
|
||||||
|
MatchmakingStatus.WAIT_FOR_ME: "🕒 Sbrigati! <b>{match_title}</b> sta per iniziare!",
|
||||||
|
MatchmakingStatus.SOMEONE_ELSE: "❔ <b>{match_title}</b> sta iniziando. Se vuoi partecipare, fai in fretta!",
|
||||||
|
MatchmakingStatus.MAYBE: "💬 <b>{match_title}</b> sta per iniziare, e ci sono {active_players} giocatori."
|
||||||
|
}
|
||||||
|
|
||||||
|
BUTTONS = {
|
||||||
|
"match_ready": "🔵 Sono pronto per iniziare!",
|
||||||
|
"match_wait_for_me": "🕒 Ci sarò, aspettatemi!",
|
||||||
|
"match_maybe": "❔ Forse vengo, se non ci sono fate senza di me.",
|
||||||
|
"match_someone_else": "💬 Solo se viene anche qualcun altro...",
|
||||||
|
"match_ignore": "❌ Non ci sarò.",
|
||||||
|
"match_close": "🚩 ADMIN: Avvia la partita",
|
||||||
|
"match_cancel": "🗑 ADMIN: Annulla la partita"
|
||||||
|
}
|
||||||
|
|
||||||
|
class ERRORS:
|
||||||
|
INVALID_SYNTAX = "⚠ Sintassi del comando errata.\n Sintassi: `/mm [minplayers-][maxplayers] per <gamename> \\n [descrizione]`"
|
||||||
|
NOT_ADMIN = "⚠ Non sei il creatore di questo match!"
|
||||||
|
MATCH_CLOSED = "⚠ Il matchmaking per questa partita è terminato!"
|
107
telegrambot.py
107
telegrambot.py
|
@ -9,6 +9,7 @@ import stagismo
|
||||||
from telegram import Bot, Update, InlineKeyboardMarkup, InlineKeyboardButton
|
from telegram import Bot, Update, InlineKeyboardMarkup, InlineKeyboardButton
|
||||||
# noinspection PyPackageRequirements
|
# noinspection PyPackageRequirements
|
||||||
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
|
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
|
||||||
|
from telegram.error import TimedOut
|
||||||
import dice
|
import dice
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
@ -19,6 +20,8 @@ import configparser
|
||||||
import markovify
|
import markovify
|
||||||
import raven
|
import raven
|
||||||
import coloredlogs
|
import coloredlogs
|
||||||
|
import strings
|
||||||
|
s = strings.safely_format_string
|
||||||
|
|
||||||
# Markov model
|
# Markov model
|
||||||
try:
|
try:
|
||||||
|
@ -50,6 +53,8 @@ def catch_and_report(func: "function"):
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
try:
|
try:
|
||||||
return func(bot, update)
|
return func(bot, update)
|
||||||
|
except TimedOut:
|
||||||
|
logger.warning(f"Telegram timed out in {update}")
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.error(f"Critical error: {sys.exc_info()}")
|
logger.error(f"Critical error: {sys.exc_info()}")
|
||||||
# noinspection PyBroadException
|
# noinspection PyBroadException
|
||||||
|
@ -293,16 +298,13 @@ def cmd_mm(bot: Bot, update: Update):
|
||||||
try:
|
try:
|
||||||
user = session.query(db.Telegram).filter_by(telegram_id=update.message.from_user.id).one_or_none()
|
user = session.query(db.Telegram).filter_by(telegram_id=update.message.from_user.id).one_or_none()
|
||||||
if user is None:
|
if user is None:
|
||||||
bot.send_message(update.message.chat.id,
|
bot.send_message(update.message.chat.id, strings.ROYALNET.ERRORS.TELEGRAM_NOT_LINKED, parse_mode="Markdown")
|
||||||
"⚠ Il tuo account Telegram non è registrato a Royalnet!"
|
|
||||||
" Registrati con `/register@royalgamesbot <nomeutenteryg>`.", parse_mode="Markdown")
|
|
||||||
return
|
return
|
||||||
match = re.match(r"/(?:mm|matchmaking)(?:@royalgamesbot)?(?: (?:([0-9]+)-)?([0-9]+))? (?:per )?([A-Za-z0-9!\-_\. ]+)(?:.*\n(.+))?",
|
match = re.match(r"/(?:mm|matchmaking)(?:@royalgamesbot)?(?: (?:([0-9]+)-)?([0-9]+))? (?:per )?([A-Za-z0-9!\-_\. ]+)(?:.*\n(.+))?",
|
||||||
update.message.text)
|
update.message.text)
|
||||||
if match is None:
|
if match is None:
|
||||||
bot.send_message(update.message.chat.id,
|
bot.send_message(update.message.chat.id,
|
||||||
"⚠ Sintassi del comando errata.\n"
|
"")
|
||||||
"Sintassi: `/matchmaking@royalgamesbot [minplayers]-[maxplayers] per <gamename> \\n [descrizione]`")
|
|
||||||
return
|
return
|
||||||
min_players, max_players, match_name, match_desc = match.group(1, 2, 3, 4)
|
min_players, max_players, match_name, match_desc = match.group(1, 2, 3, 4)
|
||||||
db_match = db.Match(timestamp=datetime.datetime.now(),
|
db_match = db.Match(timestamp=datetime.datetime.now(),
|
||||||
|
@ -313,20 +315,9 @@ def cmd_mm(bot: Bot, update: Update):
|
||||||
creator=user)
|
creator=user)
|
||||||
session.add(db_match)
|
session.add(db_match)
|
||||||
session.flush()
|
session.flush()
|
||||||
inline_keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("🔵 Possiamo iniziare!",
|
inline_keyboard = InlineKeyboardMarkup([([InlineKeyboardButton(strings.MATCHMAKING.BUTTONS[key],
|
||||||
callback_data="match_ready")],
|
callback_data=key)])
|
||||||
[InlineKeyboardButton("🕒 Ci sarò, aspettatemi!",
|
for key in strings.MATCHMAKING.BUTTONS])
|
||||||
callback_data="match_wait_for_me")],
|
|
||||||
[InlineKeyboardButton("❔ Forse vengo, se non ci sono fate senza di me.",
|
|
||||||
callback_data="match_maybe")],
|
|
||||||
[InlineKeyboardButton("💬 Solo se viene anche qualcun altro...",
|
|
||||||
callback_data="match_someone_else")],
|
|
||||||
[InlineKeyboardButton("❌ Non ci sarò.",
|
|
||||||
callback_data="match_ignore")],
|
|
||||||
[InlineKeyboardButton("🗑 [annulla la partita]",
|
|
||||||
callback_data="match_delete")],
|
|
||||||
[InlineKeyboardButton("🚩 [avvia la partita]",
|
|
||||||
callback_data="match_close")]])
|
|
||||||
message = bot.send_message(config["Telegram"]["announcement_group"], db_match.generate_text(session=session),
|
message = bot.send_message(config["Telegram"]["announcement_group"], db_match.generate_text(session=session),
|
||||||
parse_mode="HTML",
|
parse_mode="HTML",
|
||||||
reply_markup=inline_keyboard)
|
reply_markup=inline_keyboard)
|
||||||
|
@ -393,55 +384,32 @@ def on_callback_query(bot: Bot, update: Update):
|
||||||
try:
|
try:
|
||||||
user = session.query(db.Telegram).filter_by(telegram_id=update.callback_query.from_user.id).one_or_none()
|
user = session.query(db.Telegram).filter_by(telegram_id=update.callback_query.from_user.id).one_or_none()
|
||||||
if user is None:
|
if user is None:
|
||||||
bot.answer_callback_query(update.callback_query.id, show_alert=True,
|
bot.answer_callback_query(update.callback_query.id,
|
||||||
text="⚠ Il tuo account Telegram non è registrato a Royalnet!"
|
show_alert=True,
|
||||||
" Registrati con `/register@royalgamesbot <nomeutenteryg>`.",
|
text=strings.ROYALNET.ERRORS.TELEGRAM_NOT_LINKED,
|
||||||
parse_mode="Markdown")
|
parse_mode="Markdown")
|
||||||
return
|
return
|
||||||
match = session.query(db.Match).filter_by(message_id=update.callback_query.message.message_id).one()
|
match = session.query(db.Match).filter_by(message_id=update.callback_query.message.message_id).one()
|
||||||
if update.callback_query.data == "match_ready":
|
if update.callback_query.data == "match_close":
|
||||||
status = db.MatchmakingStatus.READY
|
|
||||||
text = "🔵 Hai detto che sei pronto per giocare!"
|
|
||||||
elif update.callback_query.data == "match_wait_for_me":
|
|
||||||
status = db.MatchmakingStatus.WAIT_FOR_ME
|
|
||||||
text = "🕒 Hai chiesto agli altri di aspettarti."
|
|
||||||
elif update.callback_query.data == "match_ignore":
|
|
||||||
status = db.MatchmakingStatus.IGNORED
|
|
||||||
text = "❌ Non ti interessa questa partita."
|
|
||||||
elif update.callback_query.data == "match_maybe":
|
|
||||||
status = db.MatchmakingStatus.MAYBE
|
|
||||||
text = "❔ Hai detto che forse ci sarai."
|
|
||||||
elif update.callback_query.data == "match_someone_else":
|
|
||||||
status = db.MatchmakingStatus.SOMEONE_ELSE
|
|
||||||
text = "💬 Hai detto che vuoi aspettare che venga qualcun altro."
|
|
||||||
elif update.callback_query.data == "match_close" or update.callback_query.data == "match_delete":
|
|
||||||
status = None
|
status = None
|
||||||
if match.creator == user:
|
if match.creator != user:
|
||||||
match.closed = True
|
bot.answer_callback_query(update.callback_query.id,
|
||||||
text = "🚩 Matchmaking chiuso!"
|
show_alert=True,
|
||||||
if update.callback_query.data == "match_close":
|
text=strings.MATCHMAKING.ERRORS.NOT_ADMIN)
|
||||||
for player in match.players:
|
|
||||||
if player.status == db.MatchmakingStatus.READY or player.status == db.MatchmakingStatus.WAIT_FOR_ME:
|
|
||||||
try:
|
|
||||||
bot.send_message(player.user.telegram_id,
|
|
||||||
f"🚩 Sei pronto? <b>{match.match_title}</b> sta iniziando!",
|
|
||||||
parse_mode="HTML")
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"Failed to notify {player.user.username}: {e}")
|
|
||||||
else:
|
|
||||||
bot.answer_callback_query(update.callback_query.id, show_alert=True,
|
|
||||||
text="⚠ Non sei il creatore di questo match!")
|
|
||||||
return
|
return
|
||||||
|
match.closed = True
|
||||||
|
for player in match.players:
|
||||||
|
if player.status >= 1:
|
||||||
|
bot.send_message(player.user.telegram_id,
|
||||||
|
s(strings.MATCHMAKING.GAME_START[player.status],
|
||||||
|
**match.format_dict()))
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
if status:
|
if status:
|
||||||
if match.closed:
|
if match.closed:
|
||||||
bot.answer_callback_query(update.callback_query.id, show_alert=True,
|
bot.answer_callback_query(update.callback_query.id,
|
||||||
text="⚠ Il matchmaking è terminato!")
|
show_alert=True,
|
||||||
return
|
text=strings.MATCHMAKING.ERRORS.MATCH_CLOSED)
|
||||||
if match.max_players and match.active_players_count() >= match.max_players:
|
|
||||||
bot.answer_callback_query(update.callback_query.id, show_alert=True,
|
|
||||||
text="⚠ La partita è piena.")
|
|
||||||
return
|
return
|
||||||
player = session.query(db.MatchPartecipation).filter_by(match=match, user=user).one_or_none()
|
player = session.query(db.MatchPartecipation).filter_by(match=match, user=user).one_or_none()
|
||||||
if player is None:
|
if player is None:
|
||||||
|
@ -450,22 +418,13 @@ def on_callback_query(bot: Bot, update: Update):
|
||||||
else:
|
else:
|
||||||
player.status = status.value
|
player.status = status.value
|
||||||
session.commit()
|
session.commit()
|
||||||
bot.answer_callback_query(update.callback_query.id, text=text, cache_time=1)
|
bot.answer_callback_query(update.callback_query.id,
|
||||||
|
text=s(strings.MATCHMAKING.TICKER_TEXT[update.callback_query.data]),
|
||||||
|
cache_time=1)
|
||||||
if not match.closed:
|
if not match.closed:
|
||||||
inline_keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("🔵 Possiamo iniziare!",
|
inline_keyboard = InlineKeyboardMarkup([([InlineKeyboardButton(strings.MATCHMAKING.BUTTONS[key],
|
||||||
callback_data="match_ready")],
|
callback_data=key)])
|
||||||
[InlineKeyboardButton("🕒 Ci sarò, aspettatemi!",
|
for key in strings.MATCHMAKING.BUTTONS])
|
||||||
callback_data="match_wait_for_me")],
|
|
||||||
[InlineKeyboardButton("❔ Forse vengo, se non ci sono fate senza di me.",
|
|
||||||
callback_data="match_maybe")],
|
|
||||||
[InlineKeyboardButton("💬 Solo se viene anche qualcun altro...",
|
|
||||||
callback_data="match_someone_else")],
|
|
||||||
[InlineKeyboardButton("❌ Non ci sarò.",
|
|
||||||
callback_data="match_ignore")],
|
|
||||||
[InlineKeyboardButton("🗑 [annulla la partita]",
|
|
||||||
callback_data="match_delete")],
|
|
||||||
[InlineKeyboardButton("🚩 [avvia la partita]",
|
|
||||||
callback_data="match_close")]])
|
|
||||||
else:
|
else:
|
||||||
inline_keyboard = None
|
inline_keyboard = None
|
||||||
bot.edit_message_text(message_id=update.callback_query.message.message_id,
|
bot.edit_message_text(message_id=update.callback_query.message.message_id,
|
||||||
|
|
Loading…
Reference in a new issue