{exc_info}" + CRITICAL_ERROR_QUERY = "☢ ERRORE CRITICO!" + UNAUTHORIZED_USER = "⚠ Non sono autorizzato a inviare messaggi a {mention}.\nPer piacere, {mention}, inviami un messaggio in privata!" + UNAUTHORIZED_GROUP = "⚠ Non sono autorizzato a inviare messaggi in {group}.\n@Steffo, aggiungimi al gruppo o concedimi i permessi!" + + +PONG = "🏓 Pong!" +ESCAPE = "{text}" + + +# Ah, non lo so io. +class AHNONLOSOIO: + ONCE = "😐 Ah, non lo so io!" + AGAIN = "😐 Ah, non lo so nemmeno io..." + + +# Bridge commands between Discord and Telegram +class BRIDGE: + SUCCESS = "✅ Comando inoltrato a Discord." + FAILURE = "❎ Errore nell'esecuzione del comando su Discord." + + class ERRORS: + INVALID_SYNTAX = "⚠ Non hai specificato un comando!\nSintassi:
/bridge (comando)
"
+ INACTIVE_BRIDGE = "⚠ Il collegamento tra Telegram e Discord non è attivo al momento."
+
+
+# Random spellslinging
+class CAST:
+ class ERRORS:
+ NOT_YET_AVAILABLE = "⚠ Il nuovo cast non è ancora disponibile! Per un'anteprima sulle nuove funzioni, usa /spell
."
+
+
+# Ciao Ruozi!
+class CIAORUOZI:
+ THE_LEGEND_HIMSELF = "👋 Ciao me!"
+ SOMEBODY_ELSE = "👋 Ciao Ruozi!"
+
+
+# The /color meme, from Octeon
+COLOR = "I am sorry, unknown error occured during working with your request, Admin were notified"
+
+
+# Diario
+class DIARIO:
+ SUCCESS = "✅ Riga aggiunta al diario:\n{diario}"
+ ANONYMOUS = "Anonimo"
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Sintassi del comando errata.\nSintassi: /diario (frase)
, oppure rispondi a un messaggio con /diario
."
+ NO_TEXT = "⚠ Il messaggio a cui hai risposto non contiene testo."
+
+
+# Diario search
+class DIARIOSEARCH:
+ HEADER = "ℹ️ Risultati della ricerca di {term}:\n"
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Non hai specificato un termine da cercare!\nSintassi: /{command} (termine)
"
+ RESULTS_TOO_LONG = "⚠ Sono presenti troppi risultati da visualizzare! Prova a restringere la ricerca."
+
+
+# Eat!
+class EAT:
+ FOODS = {
+ "_default": "🍗 Hai mangiato {food}!\nMa non succede nulla.",
+ "tonnuooooooro": "👻 Il {food} che hai mangiato era posseduto.\nSpooky!",
+ "uranio": "☢️ L'{food} che hai mangiato era radioattivo.\nStai brillando di verde!",
+ "pollo": '🍗 Il {food} che hai appena mangiato proveniva dallo spazio.\nCoccodè?',
+ "ragno": "🕸 Hai mangiato un {food}.\nEwww!",
+ "curry": "🔥 BRUCIAAAAAAAAAA! Il {food} era piccantissimo!\nStai sputando fiamme!",
+ "torta": "⬜️ Non hai mangiato niente.\nLa {food} è una menzogna!",
+ "cake": "⬜️ Non hai mangiato niente.\nThe {food} is a lie!",
+ "biscotto": "🍪 Hai mangiato un {food} di contrabbando.\nL'Inquisizione non lo saprà mai!",
+ "biscotti": "🍪 Hai mangiato tanti {food} di contrabbando.\nAttento! L'Inquisizione è sulle tue tracce!",
+ "tango": "🌳 Hai mangiato un {food}, e un albero insieme ad esso.\nSenti il tuo corpo curare le tue ferite.",
+ "sasso": "🥌 Il {food} che hai mangiato era duro come un {food}\nStai soffrendo di indigestione!",
+ "gnocchetti": "🥘 Ullà, sono duri 'sti {food}!\nFai fatica a digerirli.",
+ "tide pods": "☣️ I {food} che hai mangiato erano buonissimi.\nStai sbiancando!"
+ }
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Non hai specificato cosa mangiare!\nSintassi: /eat (cibo)
"
+
+
+# Emojify a string
+class EMOJIFY:
+ RESPONSE = "{emojified}"
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Non hai specificato una frase!\nSintassi: /emojify (testo)
"
+
+
+# Royalnet linking
+class LINK:
+ SUCCESS = "✅ Collegamento riuscito!"
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Non hai specificato un username!\nSintassi: /link (username)
"
+ NOT_FOUND = "⚠ Non esiste nessun account Royalnet con quel nome.\nNota: gli username sono case-sensitive, e iniziano sempre con una maiuscola!"
+ ALREADY_EXISTING = "⚠ Questo account è già collegato a un account Royalnet."
+ ROYALNET_NOT_LINKED = "⚠ Il tuo account Telegram non è connesso a Royalnet! Connettilo con /link (username)
."
+
+
+# Markov strings
+class MARKOV:
+ class ERRORS:
+ NO_MODEL = "⚠ La catena di Markov non è disponibile."
+ GENERATION_FAILED = "⚠ markovify
non è riuscito a generare una frase. Prova di nuovo?\n E' un'avvenimento sorprendentemente raro..."
+ SPECIFIC_WORD_FAILED = "⚠ markovify
non è riuscito a generare una frase partendo da questa parola. Provane una diversa..."
+ MISSING_WORD = "⚠ La parola specificata non è presente nella catena di Markov. Provane una diversa..."
+
+
+# Matchmaking service strings
+class MATCHMAKING:
+ EMOJIS = {
+ "ready": "🔵",
+ "wait_for_me": "🕒",
+ "maybe": "❓",
+ "ignore": "❌",
+ "close": "🚩",
+ "cancel": "🗑"
+ }
+
+ ENUM_TO_EMOJIS = {
+ utils.MatchmakingStatus.READY: EMOJIS["ready"],
+ utils.MatchmakingStatus.WAIT_FOR_ME: EMOJIS["wait_for_me"],
+ utils.MatchmakingStatus.MAYBE: EMOJIS["maybe"],
+ utils.MatchmakingStatus.IGNORED: EMOJIS["ignore"],
+ }
+
+ BUTTONS = {
+ "match_ready": f"{EMOJIS['ready']} Sono pronto per iniziare!",
+ "match_wait_for_me": f"{EMOJIS['wait_for_me']} Ci sarò, aspettatemi!",
+ "match_maybe": f"{EMOJIS['maybe']} Forse vengo, se non ci sono fate senza di me.",
+ "match_ignore": f"{EMOJIS['ignore']} Non ci sarò.",
+ "match_close": f"{EMOJIS['close']} ADMIN: Avvia la partita",
+ "match_cancel": f"{EMOJIS['cancel']} ADMIN: Annulla la partita"
+ }
+
+ TICKER_TEXT = {
+ "match_ready": f"{EMOJIS['ready']} Hai detto che sei pronto per giocare!",
+ "match_wait_for_me": f"{EMOJIS['wait_for_me']} Hai chiesto agli altri di aspettarti.",
+ "match_maybe": f"{EMOJIS['maybe']} Hai detto che forse ci sarai.",
+ "match_ignore": f"{EMOJIS['ignore']} Non hai intenzione di partecipare.",
+ "match_close": f"{EMOJIS['close']} Hai notificato tutti che la partita sta iniziando.",
+ "match_cancel": f"{EMOJIS['cancel']} Hai annullato la partita."
+ }
+
+ GAME_START = {
+ int(utils.MatchmakingStatus.READY): "🔵 Che {match_title} abbia inizio!",
+ int(utils.MatchmakingStatus.WAIT_FOR_ME): "🕒 Sbrigati! {match_title} sta per iniziare!",
+ int(utils.MatchmakingStatus.MAYBE): "❓ {match_title} sta iniziando. Se vuoi partecipare, fai in fretta!",
+ }
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Sintassi del comando errata.\nSintassi: /mm [minplayers-][maxplayers] ['per'] (gamename) \\n[descrizione]" + NOT_ADMIN = "⚠ Non sei il creatore di questo match!" + MATCH_CLOSED = "⚠ Il matchmaking per questa partita è terminato." + + +# Pug sender +class PUG: + HERE_HAVE_A_PUG = '🐶 Ecco, tieni un carlino.' + + class ERRORS: + PRIVATE_CHAT_ONLY = "⚠ Foto di carlini possono essere inviate esclusivamente in chat privata, in seguito al Disegno di Legge Intergalattico n. 5116." + + +# Dice roller +class ROLL: + SUCCESS = "🎲 {result}" + + SYMBOLS = { + dice.elements.Div: "/", + dice.elements.Mul: "*", + dice.elements.Sub: "-", + dice.elements.Add: "+", + dice.elements.Modulo: "%", + dice.elements.AddEvenSubOdd: "+-", + dice.elements.Highest: "h", + dice.elements.Lowest: "l", + dice.elements.Middle: "m", + dice.elements.Again: "a", + dice.elements.Successes: "e", + dice.elements.SuccessFail: "f", + dice.elements.ArrayAdd: ".+", + dice.elements.ArraySub: ".-", + dice.elements.Array: ",", + dice.elements.Extend: "|", + dice.elements.Reroll: "r", + dice.elements.Explode: "x", + dice.elements.ForceReroll: "rr" + } + + class ERRORS: + INVALID_SYNTAX = "⚠ Sintassi del tiro di dadi non valida." + DICE_ERROR = "⚠ Il tiro di dadi è fallito." + + +# Ship creator +class SHIP: + RESULT = "💕 {one} + {two} = {result}" + + class ERRORS: + INVALID_SYNTAX = "⚠ Non hai specificato correttamente i due nomi!\nSintassi:
/ship (nome) (nome)
"
+ INVALID_NAMES = "⚠ I nomi specificati non sono validi.\nRiprova con dei nomi diversi!"
+
+
+# Get information about a spell
+class SPELL:
+ HEADER = "🔍 La magia {name} ha le seguenti proprietà (v{version}):\n"
+ ACCURACY = "Precisione - {accuracy}%\n"
+ DAMAGE = "Danni - {number}d{type}{constant} (in media {avg})\n"
+ TYPE = "Tipo - {type}\n"
+ REPEAT = "Multiattacco - ×{repeat}\n"
+ HEALING = "Cura - {number}d{type}{constant} (in media {avg})\n"
+ STAT = "Attrib. - {name}{change}\n"
+ STATUS_EFFECT = "Infligge - {effect} ({chance}% di probabilità)"
+ NOTHING = "Chi la usa sguazza nell'acqua, senza ottenere alcun effetto."
+
+ class ERRORS:
+ INVALID_SYNTAX = "⚠ Non hai specificato la magia di cui vuoi conoscere i dettagli!\nSintassi: /spell (nome)
"
+
+
+# Game stats updates
+class STATSUPDATE:
+ class BRAWLHALLA:
+ SOLO = "✳️ {username} ha ora {rating} ({delta}) Elo 1v1 su Brawlhalla!"
+ TEAM = "✳️ {username}+{other} hanno ora {rating} Elo 2v2 su Brawlhalla!"
+
+
+# Secondo me, è colpa delle stringhe.
+SMECDS = "🤔 Secondo me, è colpa {ds}."
+
+
+# Wiki notifications
+class WIKI:
+ PAGE_LOCKED = '🔒 La pagina wiki {key} è stata bloccata da {user}.'
+ PAGE_UNLOCKED = '🔓 La pagina wiki {key} è stata sbloccata da {user}.'
+ PAGE_UPDATED = '📖 La pagina wiki {key} è stata modificata da {user}.\n{reason} [{change}]'
diff --git a/telegrambot.py b/telegrambot.py
index 37d67d3d..131c045f 100644
--- a/telegrambot.py
+++ b/telegrambot.py
@@ -2,30 +2,40 @@ import datetime
import random
import typing
import db
-import errors
-import stagismo
+from utils import smecds, cast, errors, emojify, reply_msg
# python-telegram-bot has a different name
# noinspection PyPackageRequirements
-from telegram import Bot, Update, InlineKeyboardMarkup, InlineKeyboardButton
+import telegram
# noinspection PyPackageRequirements
from telegram.ext import Updater, CommandHandler, CallbackQueryHandler
+# noinspection PyPackageRequirements
+from telegram.error import TimedOut, Unauthorized, BadRequest, TelegramError
import dice
import sys
import os
-import cast
import re
import logging
import configparser
import markovify
import raven
import coloredlogs
+import strings
+import time
+import requests
+IKMarkup = telegram.InlineKeyboardMarkup
+IKButton = telegram.InlineKeyboardButton
-# Markov model
+# Markov models
try:
- with open("markovmodel.json") as file:
- model = markovify.Text.from_json(file.read())
+ with open("markovmodels/default.json") as file:
+ default_model = markovify.Text.from_json(file.read())
except Exception:
- model = None
+ default_model = None
+try:
+ with open("markovmodels/dnd4.json") as file:
+ dnd4_model = markovify.Text.from_json(file.read())
+except Exception:
+ dnd4_model = None
logging.getLogger().disabled = True
logger = logging.getLogger(__name__)
@@ -35,6 +45,7 @@ coloredlogs.install(level="DEBUG", logger=logger)
# Init the config reader
config = configparser.ConfigParser()
config.read("config.ini")
+main_group_id = int(config["Telegram"]["main_group"])
discord_connection = None
@@ -45,75 +56,106 @@ sentry = raven.Client(config["Sentry"]["token"],
hook_libraries=[])
-def catch_and_report(func: "function"):
- def new_func(bot: Bot, update: Update):
+def reply(bot: telegram.Bot, update: telegram.Update, string: str, ignore_escaping=False, disable_web_page_preview=True, **kwargs) -> telegram.Message:
+ while True:
+ try:
+ return reply_msg(bot, update.message.chat.id, string, ignore_escaping=ignore_escaping, disable_web_page_preview=disable_web_page_preview, **kwargs)
+ except Unauthorized:
+ if update.message.chat.type == telegram.Chat.PRIVATE:
+ return reply_msg(bot, main_group_id, strings.TELEGRAM.ERRORS.UNAUTHORIZED_USER,
+ mention=update.message.from_user.mention_html())
+ else:
+ return reply_msg(bot, main_group_id, strings.TELEGRAM.ERRORS.UNAUTHORIZED_GROUP,
+ group=(update.message.chat.title or update.message.chat.id))
+ except TimedOut:
+ time.sleep(1)
+ pass
+
+
+# noinspection PyUnresolvedReferences
+def command(func: "function"):
+ def new_func(bot: telegram.Bot, update: telegram.Update):
# noinspection PyBroadException
try:
+ bot.send_chat_action(update.message.chat.id, telegram.ChatAction.TYPING)
return func(bot, update)
+ except TimedOut:
+ logger.warning(f"Telegram timed out in {update}")
except Exception:
logger.error(f"Critical error: {sys.exc_info()}")
# noinspection PyBroadException
try:
- bot.send_message(int(config["Telegram"]["main_group"]),
- "☢ **ERRORE CRITICO!** \n"
- f"Il bot ha ignorato il comando.\n"
- f"Una segnalazione di errore è stata automaticamente mandata a @Steffo.\n\n"
- f"Dettagli dell'errore:\n"
- f"```\n"
- f"{sys.exc_info()}\n"
- f"```", parse_mode="Markdown")
+ reply_msg(bot, main_group_id, strings.TELEGRAM.ERRORS.CRITICAL_ERROR,
+ exc_info=repr(sys.exc_info()))
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()
+ 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
-@catch_and_report
-def cmd_ping(bot: Bot, update: Update):
- bot.send_message(update.message.chat.id, "🏓 Pong!")
+# noinspection PyUnresolvedReferences
+def database_access(func: "function"):
+ def new_func(bot: telegram.Bot, update: telegram.Update):
+ try:
+ session = db.Session()
+ return func(bot, update, session)
+ except Exception:
+ logger.error(f"Database error: {sys.exc_info()}")
+ sentry.captureException()
+ finally:
+ try:
+ session.close()
+ except Exception:
+ pass
+ return new_func
-@catch_and_report
-def cmd_register(bot: Bot, update: Update):
- session = db.Session()
+@command
+def cmd_ping(bot: telegram.Bot, update: telegram.Update):
+ reply(bot, update, strings.PONG)
+
+
+@command
+@database_access
+def cmd_link(bot: telegram.Bot, update: telegram.Update, session: db.Session):
try:
username = update.message.text.split(" ", 1)[1]
except IndexError:
- bot.send_message(update.message.chat.id, "⚠️ Non hai specificato un username!")
+ reply(bot, update, strings.LINK.ERRORS.INVALID_SYNTAX)
session.close()
return
try:
t = db.Telegram.create(session,
royal_username=username,
telegram_user=update.message.from_user)
+ except errors.NotFoundError:
+ reply(bot, update, strings.LINK.ERRORS.NOT_FOUND)
+ session.close()
+ return
except errors.AlreadyExistingError:
- bot.send_message(update.message.chat.id, "⚠ Il tuo account Telegram è già collegato a un account RYG o"
- " l'account RYG che hai specificato è già collegato a un account"
- " Telegram.")
+ reply(bot, update, strings.LINK.ERRORS.ALREADY_EXISTING)
session.close()
return
session.add(t)
session.commit()
- bot.send_message(update.message.chat.id, "✅ Sincronizzazione completata!")
- session.close()
+ reply(bot, update, strings.LINK.SUCCESS)
-@catch_and_report
-def cmd_discord(bot: Bot, update: Update):
+@command
+def cmd_cv(bot: telegram.Bot, update: telegram.Update):
if discord_connection is None:
- bot.send_message(update.message.chat.id, "⚠ Il bot non è collegato a Discord al momento.")
+ reply(bot, update, strings.BRIDGE.ERRORS.INACTIVE_BRIDGE)
return
# dirty hack as usual
if update.message.text.endswith("full"):
@@ -121,194 +163,249 @@ def cmd_discord(bot: Bot, update: Update):
else:
discord_connection.send("get cv")
message = discord_connection.recv()
- bot.send_message(update.message.chat.id, message, disable_web_page_preview=True, parse_mode="HTML")
+ reply(bot, update, message)
-@catch_and_report
-def cmd_cast(bot: Bot, update: Update):
- try:
- spell: str = update.message.text.split(" ", 1)[1]
- except IndexError:
- bot.send_message(update.message.chat.id, "⚠️ Non hai specificato nessun incantesimo!\n"
- "Sintassi corretta: `/cast {query}
", entries), parse_mode="HTML")
+ except (BadRequest, TelegramError):
+ reply(bot, update, strings.DIARIOSEARCH.ERRORS.RESULTS_TOO_LONG)
+
+
+@command
+@database_access
+def cmd_mm(bot: telegram.Bot, update: telegram.Update, session: db.Session):
+ user = session.query(db.Telegram).filter_by(telegram_id=update.message.from_user.id).one_or_none()
+ if user is None:
+ reply(bot, update, strings.LINK.ERRORS.ROYALNET_NOT_LINKED)
+ 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:
+ reply(bot, update, strings.MATCHMAKING.ERRORS.INVALID_SYNTAX)
+ 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 = IKMarkup([([IKButton(strings.MATCHMAKING.BUTTONS[key], callback_data=key)]) for key in strings.MATCHMAKING.BUTTONS])
+ message = bot.send_message(config["Telegram"]["announcement_group"], db_match.generate_text(session=session),
+ parse_mode="HTML",
+ reply_markup=inline_keyboard)
+ db_match.message_id = message.message_id
+ session.commit()
+
+
+def on_callback_query(bot: telegram.Bot, update: telegram.Update):
+ try:
session = db.Session()
- try:
+ 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()
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!"
- " Registrati con `/register@royalgamesbot Il tuo browser ha inviato una richiesta non valida. Magari non hai riempito qualche campo di un form?
++{% endblock %} \ No newline at end of file diff --git a/templates/403.html b/templates/403.html index bfc94b22..4d2aafea 100644 --- a/templates/403.html +++ b/templates/403.html @@ -9,6 +9,25 @@ 403 - Forbidden+++ Villa di Von Shdfisjz +
++ — Steffo, +
+
- Non puoi accedere a questa pagina. Magari hai sbagliato password? + Non puoi accedere a questa pagina.
+ {% if g.logged_in %} ++ Forse dovresti provare a fare il login... +
+ {% else %} ++ Temo che questa pagina sia riservata agli amministratori... +
+ {% endif %} ++{% endblock %} \ No newline at end of file diff --git a/templates/500.html b/templates/500.html index 34fb14dd..f887d27b 100644 --- a/templates/500.html +++ b/templates/500.html @@ -11,14 +11,18 @@+++ Io sono il padrone, questo è champagne, buon Natale! +
++ —
+ , +
Il server è crashato mentre cercava di generare questa pagina. Oops.
--- I am sorry, unknown error occured during working with your request, Admin were notified -
++++ I am sorry, unknown error occured during working with your request, Admin were notified +
++ — OcteonRygBot, +
+L'errore dovrebbe essere stato segnalato a Steffo. - Se riesci e ne hai voglia, spiegagli anche cosa ha provocato l'errore. + Se riesci e ne hai voglia, spiegagli cosa ha provocato l'errore.
- @OcteonRygBot, 2017 {% endblock %} \ No newline at end of file diff --git a/templates/activity.html b/templates/activity.html index 1fdbf3ab..7b4054d1 100644 --- a/templates/activity.html +++ b/templates/activity.html @@ -18,7 +18,7 @@Attività su Discord negli ultimi 7 giorni
- + - + - + ++ Attività per ogni ora nell'ultimo mese +
+ + ++ Confronto cv con il mese scorso +
+ + diff --git a/templates/base.html b/templates/base.html index cfa63c9f..9c943137 100644 --- a/templates/base.html +++ b/templates/base.html @@ -14,7 +14,7 @@ - {% block body %}{% endblock %} ++ {% block body %}{% endblock %} +