diff --git a/db.py b/db.py
index b8ab01b6..742dd22e 100644
--- a/db.py
+++ b/db.py
@@ -1215,31 +1215,90 @@ mini_list = [Royal, Telegram, Steam, Dota, LeagueOfLegends, Osu, Discord, Overwa
Terraria13]
-class Matchmaker(Base):
- __tablename__ = "matchmakers"
+class Match(Base):
+ __tablename__ = "matches"
id = Column(Integer, primary_key=True)
+ timestamp = Column(DateTime)
+ creator_id = Column(BigInteger, ForeignKey("telegram.telegram_id"))
+ creator = relationship("Telegram", backref="matches_created", lazy="joined")
- matchmaking_name = Column(String)
- matchmaking_desc = Column(Text)
-
+ match_title = Column(String)
+ match_desc = Column(Text)
min_players = Column(Integer)
max_players = Column(Integer)
+ closed = Column(Boolean, default=False)
- timestamp = Column(DateTime)
- expires_in = Column(DateTime)
+ message_id = Column(BigInteger)
- players = relationship("MatchmakingEntry", lazy="joined")
+ def active_players_count(self):
+ count = 0
+ for player in self.players:
+ if player.status == MatchmakingStatus.READY or player.status == MatchmakingStatus.WAIT_FOR_ME:
+ count += 1
+ return count
+
+ def generate_text(self, session):
+ player_list = session.query(MatchPartecipation).filter_by(match=self).all()
+ title = f"{self.match_title}"
+ description = self.match_desc if self.match_desc else ""
+ if self.min_players:
+ minimum = f" (minimo {self.min_players})"
+ else:
+ minimum = ""
+ plist = f"Giocatori{minimum}:\n"
+ ignore_count = 0
+ for player in player_list:
+ if player.status == MatchmakingStatus.WAIT_FOR_ME:
+ icon = "๐"
+ elif player.status == MatchmakingStatus.READY:
+ icon = "๐ต"
+ elif player.status == MatchmakingStatus.IGNORED:
+ ignore_count += 1
+ continue
+ else:
+ continue
+ plist += f"{icon} {player.user.royal.username}\n"
+ if ignore_count:
+ ignored = f"โ {ignore_count} persone non ne hanno voglia.\n"
+ else:
+ ignored = ""
+ if self.max_players:
+ players = f"[{self.active_players_count()}/{self.max_players}]"
+ else:
+ players = f"[{self.active_players_count()}]"
+ close = f"[matchmaking concluso]\n" if self.closed else ""
+ message = f"{title} {players}\n" \
+ f"{description}\n" \
+ f"{plist}\n" \
+ f"{ignored}" \
+ f"{close}"
+ return message
+
+ def __repr__(self):
+ return f""
-class MatchmakingEntry(Base):
- __tablename__ = "matchmakingentry"
-
- royal_id = Column(Integer, ForeignKey("royals.id"), primary_key=True)
- royal = relationship("Royal", backref="matchmades", lazy="joined")
+class MatchmakingStatus(enum.IntEnum):
+ WAIT_FOR_ME = 1
+ READY = 2
+ IGNORED = -1
+class MatchPartecipation(Base):
+ __tablename__ = "matchpartecipations"
+ __table_args__ = (PrimaryKeyConstraint("user_id", "match_id"),)
+ user_id = Column(BigInteger, ForeignKey("telegram.telegram_id"))
+ user = relationship("Telegram", backref="match_partecipations", lazy="joined")
+
+ match_id = Column(Integer, ForeignKey("matches.id"))
+ match = relationship("Match", backref="players", lazy="joined")
+
+ status = Column(Integer)
+
+ def __repr__(self):
+ return f""
# If run as script, create all the tables in the db
diff --git a/telegrambot.py b/telegrambot.py
index 37d67d3d..787d15d6 100644
--- a/telegrambot.py
+++ b/telegrambot.py
@@ -64,19 +64,20 @@ def catch_and_report(func: "function"):
f"```", parse_mode="Markdown")
except Exception:
logger.error(f"Double critical error: {sys.exc_info()}")
- if not __debug__:
- sentry.user_context({
- "id": update.effective_user.id,
- "telegram": {
- "username": update.effective_user.username,
- "first_name": update.effective_user.first_name,
- "last_name": update.effective_user.last_name
- }
- })
- sentry.extra_context({
- "update": update.to_dict()
- })
- sentry.captureException()
+ if __debug__:
+ raise
+ sentry.user_context({
+ "id": update.effective_user.id,
+ "telegram": {
+ "username": update.effective_user.username,
+ "first_name": update.effective_user.first_name,
+ "last_name": update.effective_user.last_name
+ }
+ })
+ sentry.extra_context({
+ "update": update.to_dict()
+ })
+ sentry.captureException()
return new_func
@@ -282,32 +283,69 @@ def cmd_vote(bot: Bot, update: Update):
parse_mode="HTML")
vote.message_id = message.message_id
session.commit()
- except Exception:
- raise
+ finally:
+ session.close()
+
+
+@catch_and_report
+def cmd_mm(bot: Bot, update: Update):
+ session = db.Session()
+ try:
+ user = session.query(db.Telegram).filter_by(telegram_id=update.message.from_user.id).one_or_none()
+ if user is None:
+ bot.send_message(update.message.chat.id,
+ "โ Il tuo account Telegram non รจ registrato a Royalnet!"
+ " Registrati con `/register@royalgamesbot `.", parse_mode="Markdown")
+ return
+ match = re.match(r"/(?:mm|matchmaking)(?:@royalgamesbot)?(?: (?:([0-9]+)-)?([0-9]+))? (?:per )?([A-Za-z0-9!\-_\. ]+)(?:.*\n(.+))?",
+ update.message.text)
+ if match is None:
+ bot.send_message(update.message.chat.id,
+ "โ Sintassi del comando errata.\n"
+ "Sintassi: `/matchmaking@royalgamesbot [minplayers]-[maxplayers] per \\n [descrizione]`")
+ return
+ min_players, max_players, match_name, match_desc = match.group(1, 2, 3, 4)
+ db_match = db.Match(timestamp=datetime.datetime.now(),
+ match_title=match_name,
+ match_desc=match_desc,
+ min_players=min_players,
+ max_players=max_players,
+ creator=user)
+ session.add(db_match)
+ session.flush()
+ inline_keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("๐ต Ci sono!", callback_data="match_ready")],
+ [InlineKeyboardButton("๐ Sto arrivando, aspettatemi!", callback_data="match_wait_for_me")],
+ [InlineKeyboardButton("โ Non vengo.", callback_data="match_ignore")],
+ [InlineKeyboardButton("๐ฉ [termina la ricerca]", callback_data="match_close")]])
+ message = bot.send_message(update.message.chat.id, db_match.generate_text(session=session),
+ parse_mode="HTML",
+ reply_markup=inline_keyboard)
+ db_match.message_id = message.message_id
+ session.commit()
finally:
session.close()
@catch_and_report
def on_callback_query(bot: Bot, update: Update):
- if update.callback_query.data == "vote_yes":
- choice = db.VoteChoices.YES
- emoji = "๐ต"
- elif update.callback_query.data == "vote_no":
- choice = db.VoteChoices.NO
- emoji = "๐ด"
- elif update.callback_query.data == "vote_abstain":
- choice = db.VoteChoices.ABSTAIN
- emoji = "โซ๏ธ"
- else:
- raise NotImplementedError()
if update.callback_query.data.startswith("vote_"):
+ if update.callback_query.data == "vote_yes":
+ status = db.VoteChoices.YES
+ emoji = "๐ต"
+ elif update.callback_query.data == "vote_no":
+ status = db.VoteChoices.NO
+ emoji = "๐ด"
+ elif update.callback_query.data == "vote_abstain":
+ status = db.VoteChoices.ABSTAIN
+ emoji = "โซ๏ธ"
+ else:
+ raise NotImplementedError()
session = db.Session()
try:
user = session.query(db.Telegram).filter_by(telegram_id=update.callback_query.from_user.id).one_or_none()
if user is None:
bot.answer_callback_query(update.callback_query.id, show_alert=True,
- text="โ Il tuo account Telegram non รจ registrato al RYGdb!"
+ text="โ Il tuo account Telegram non รจ registrato a Royalnet!"
" Registrati con `/register@royalgamesbot `.",
parse_mode="Markdown")
return
@@ -316,14 +354,14 @@ def on_callback_query(bot: Bot, update: Update):
.one()
answer = session.query(db.VoteAnswer).filter_by(question=question, user=user).one_or_none()
if answer is None:
- answer = db.VoteAnswer(question=question, choice=choice, user=user)
+ answer = db.VoteAnswer(question=question, choice=status, user=user)
session.add(answer)
bot.answer_callback_query(update.callback_query.id, text=f"Hai votato {emoji}.", cache_time=1)
- elif answer.choice == choice:
+ elif answer.choice == status:
session.delete(answer)
bot.answer_callback_query(update.callback_query.id, text=f"Hai ritratto il tuo voto.", cache_time=1)
else:
- answer.choice = choice
+ answer.choice = status
bot.answer_callback_query(update.callback_query.id, text=f"Hai cambiato il tuo voto in {emoji}.",
cache_time=1)
session.commit()
@@ -338,8 +376,79 @@ def on_callback_query(bot: Bot, update: Update):
text=question.generate_text(session),
reply_markup=inline_keyboard,
parse_mode="HTML")
- except Exception:
- raise
+ finally:
+ session.close()
+ elif update.callback_query.data.startswith("match_"):
+ session = db.Session()
+ try:
+ user = session.query(db.Telegram).filter_by(telegram_id=update.callback_query.from_user.id).one_or_none()
+ if user is None:
+ bot.answer_callback_query(update.callback_query.id, show_alert=True,
+ text="โ Il tuo account Telegram non รจ registrato a Royalnet!"
+ " Registrati con `/register@royalgamesbot `.",
+ parse_mode="Markdown")
+ return
+ match = session.query(db.Match).filter_by(message_id=update.callback_query.message.message_id).one()
+ if update.callback_query.data == "match_ready":
+ 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_close":
+ status = None
+ if match.creator == user:
+ match.closed = True
+ text = "๐ฉ Matchmaking chiuso!"
+ 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? {match.match_title} 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
+ else:
+ raise NotImplementedError()
+ if status:
+ if match.closed:
+ bot.answer_callback_query(update.callback_query.id, show_alert=True,
+ text="โ Il matchmaking รจ terminato!")
+ return
+ 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
+ player = session.query(db.MatchPartecipation).filter_by(match=match, user=user).one_or_none()
+ if player is None:
+ player = db.MatchPartecipation(match=match, status=status.value, user=user)
+ session.add(player)
+ else:
+ player.status = status.value
+ session.commit()
+ bot.answer_callback_query(update.callback_query.id, text=text, cache_time=1)
+ if not match.closed:
+ inline_keyboard = InlineKeyboardMarkup([[InlineKeyboardButton("๐ต Ci sono!", callback_data="match_ready")],
+ [InlineKeyboardButton("๐ Sto arrivando, aspettatemi!",
+ callback_data="match_wait_for_me")],
+ [InlineKeyboardButton("โ Non vengo.",
+ callback_data="match_ignore")],
+ [InlineKeyboardButton("๐ฉ [termina la ricerca]",
+ callback_data="match_close")]])
+ else:
+ inline_keyboard = None
+ bot.edit_message_text(message_id=update.callback_query.message.message_id,
+ chat_id=update.callback_query.message.chat.id,
+ text=match.generate_text(session),
+ reply_markup=inline_keyboard,
+ parse_mode="HTML")
finally:
session.close()
@@ -601,6 +710,8 @@ def process(arg_discord_connection):
u.dispatcher.add_handler(CommandHandler("markov", cmd_markov))
u.dispatcher.add_handler(CommandHandler("roll", cmd_roll))
u.dispatcher.add_handler(CommandHandler("r", cmd_roll))
+ u.dispatcher.add_handler(CommandHandler("mm", cmd_mm))
+ u.dispatcher.add_handler(CommandHandler("matchmaking", cmd_mm))
u.dispatcher.add_handler(CallbackQueryHandler(on_callback_query))
logger.info("Handlers registered.")
u.start_polling()