/log and /errors commands implemented
Receive bot general log and errors log via bot itself
This commit is contained in:
parent
7a994ca957
commit
417dcb63e4
@ -14,7 +14,7 @@ __author__ = "Davide Testa"
|
|||||||
__email__ = "davide@davte.it"
|
__email__ = "davide@davte.it"
|
||||||
__credits__ = ["Marco Origlia", "Nick Lee @Nickoala"]
|
__credits__ = ["Marco Origlia", "Nick Lee @Nickoala"]
|
||||||
__license__ = "GNU General Public License v3.0"
|
__license__ = "GNU General Public License v3.0"
|
||||||
__version__ = "2.1.34"
|
__version__ = "2.1.35"
|
||||||
__maintainer__ = "Davide Testa"
|
__maintainer__ = "Davide Testa"
|
||||||
__contact__ = "t.me/davte"
|
__contact__ = "t.me/davte"
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ import json
|
|||||||
from davtelepot.utilities import (
|
from davtelepot.utilities import (
|
||||||
async_wrapper, Confirmator, extract, get_cleaned_text, get_user,
|
async_wrapper, Confirmator, extract, get_cleaned_text, get_user,
|
||||||
escape_html_chars, line_drawing_unordered_list, make_button,
|
escape_html_chars, line_drawing_unordered_list, make_button,
|
||||||
make_inline_keyboard, remove_html_tags, send_csv_file
|
make_inline_keyboard, remove_html_tags, send_part_of_text_file,
|
||||||
|
send_csv_file
|
||||||
)
|
)
|
||||||
from sqlalchemy.exc import ResourceClosedError
|
from sqlalchemy.exc import ResourceClosedError
|
||||||
|
|
||||||
@ -624,6 +625,86 @@ default_admin_messages = {
|
|||||||
'en': "No result to show.",
|
'en': "No result to show.",
|
||||||
'it': "Nessun risultato da mostrare."
|
'it': "Nessun risultato da mostrare."
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
'log_command': {
|
||||||
|
'description': {
|
||||||
|
'en': "Receive bot log file, if set",
|
||||||
|
'it': "Ricevi il file di log del bot, se impostato"
|
||||||
|
},
|
||||||
|
'no_log': {
|
||||||
|
'en': "Sorry but no log file is set.\n"
|
||||||
|
"To set it, use `bot.set_log_file_name` instance method or "
|
||||||
|
"`Bot.set_class_log_file_name` class method.",
|
||||||
|
'it': "Spiacente ma il file di log non è stato impostato.\n"
|
||||||
|
"Per impostarlo, usa il metodo d'istanza "
|
||||||
|
"`bot.set_log_file_name` o il metodo di classe"
|
||||||
|
"`Bot.set_class_log_file_name`."
|
||||||
|
},
|
||||||
|
'sending_failure': {
|
||||||
|
'en': "Sending log file failed!\n\n"
|
||||||
|
"<b>Error:</b>\n"
|
||||||
|
"<code>{e}</code>",
|
||||||
|
'it': "Inviio del messaggio di log fallito!\n\n"
|
||||||
|
"<b>Errore:</b>\n"
|
||||||
|
"<code>{e}</code>"
|
||||||
|
},
|
||||||
|
'here_is_log_file': {
|
||||||
|
'en': "Here is the complete log file.",
|
||||||
|
'it': "Ecco il file di log completo."
|
||||||
|
},
|
||||||
|
'log_file_first_lines': {
|
||||||
|
'en': "Here are the first {lines} lines of the log file.",
|
||||||
|
'it': "Ecco le prime {lines} righe del file di log."
|
||||||
|
},
|
||||||
|
'log_file_last_lines': {
|
||||||
|
'en': "Here are the last {lines} lines of the log file.\n"
|
||||||
|
"Newer lines are at the top of the file.",
|
||||||
|
'it': "Ecco le ultime {lines} righe del file di log.\n"
|
||||||
|
"L'ordine è cronologico, con i messaggi nuovi in alto."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'errors_command': {
|
||||||
|
'description': {
|
||||||
|
'en': "Receive bot error log file, if set",
|
||||||
|
'it': "Ricevi il file di log degli errori del bot, se impostato"
|
||||||
|
},
|
||||||
|
'no_log': {
|
||||||
|
'en': "Sorry but no errors log file is set.\n"
|
||||||
|
"To set it, use `bot.set_errors_file_name` instance method"
|
||||||
|
"or `Bot.set_class_errors_file_name` class method.",
|
||||||
|
'it': "Spiacente ma il file di log degli errori non è stato "
|
||||||
|
"impostato.\n"
|
||||||
|
"Per impostarlo, usa il metodo d'istanza "
|
||||||
|
"`bot.set_errors_file_name` o il metodo di classe"
|
||||||
|
"`Bot.set_class_errors_file_name`."
|
||||||
|
},
|
||||||
|
'empty_log': {
|
||||||
|
'en': "Congratulations! Errors log is empty!",
|
||||||
|
'it': "Congratulazioni! Il log degli errori è vuoto!"
|
||||||
|
},
|
||||||
|
'sending_failure': {
|
||||||
|
'en': "Sending errors log file failed!\n\n"
|
||||||
|
"<b>Error:</b>\n"
|
||||||
|
"<code>{e}</code>",
|
||||||
|
'it': "Inviio del messaggio di log degli errori fallito!\n\n"
|
||||||
|
"<b>Errore:</b>\n"
|
||||||
|
"<code>{e}</code>"
|
||||||
|
},
|
||||||
|
'here_is_log_file': {
|
||||||
|
'en': "Here is the complete errors log file.",
|
||||||
|
'it': "Ecco il file di log degli errori completo."
|
||||||
|
},
|
||||||
|
'log_file_first_lines': {
|
||||||
|
'en': "Here are the first {lines} lines of the errors log file.",
|
||||||
|
'it': "Ecco le prime {lines} righe del file di log degli errori."
|
||||||
|
},
|
||||||
|
'log_file_last_lines': {
|
||||||
|
'en': "Here are the last {lines} lines of the errors log file.\n"
|
||||||
|
"Newer lines are at the top of the file.",
|
||||||
|
'it': "Ecco le ultime {lines} righe del file di log degli "
|
||||||
|
"errori.\n"
|
||||||
|
"L'ordine è cronologico, con i messaggi nuovi in alto."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -867,6 +948,103 @@ async def _query_button(bot, update, user_record, data):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def _log_command(bot, update, user_record):
|
||||||
|
if bot.log_file_path is None:
|
||||||
|
return bot.get_message(
|
||||||
|
'admin', 'log_command', 'no_log',
|
||||||
|
update=update, user_record=user_record
|
||||||
|
)
|
||||||
|
# Always send log file in private chat
|
||||||
|
chat_id = update['from']['id']
|
||||||
|
text = get_cleaned_text(update, bot, ['log'])
|
||||||
|
reversed_ = 'r' not in text
|
||||||
|
text = text.strip('r')
|
||||||
|
if text.isnumeric():
|
||||||
|
limit = int(text)
|
||||||
|
else:
|
||||||
|
limit = 100
|
||||||
|
if limit is None:
|
||||||
|
sent = await bot.send_document(
|
||||||
|
chat_id=chat_id,
|
||||||
|
document_path=bot.log_file_path,
|
||||||
|
caption=bot.get_message(
|
||||||
|
'admin', 'log_command', 'here_is_log_file',
|
||||||
|
update=update, user_record=user_record
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sent = await send_part_of_text_file(
|
||||||
|
bot=bot,
|
||||||
|
update=update,
|
||||||
|
user_record=user_record,
|
||||||
|
chat_id=chat_id,
|
||||||
|
file_path=bot.log_file_path,
|
||||||
|
file_name=bot.log_file_name,
|
||||||
|
caption=bot.get_message(
|
||||||
|
'admin', 'log_command', (
|
||||||
|
'log_file_last_lines'
|
||||||
|
if reversed_
|
||||||
|
else 'log_file_first_lines'
|
||||||
|
),
|
||||||
|
update=update, user_record=user_record,
|
||||||
|
lines=limit
|
||||||
|
),
|
||||||
|
reversed_=reversed_,
|
||||||
|
limit=limit
|
||||||
|
)
|
||||||
|
if isinstance(sent, Exception):
|
||||||
|
return bot.get_message(
|
||||||
|
'admin', 'log_command', 'sending_failure',
|
||||||
|
update=update, user_record=user_record,
|
||||||
|
e=sent
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
async def _errors_command(bot, update, user_record):
|
||||||
|
# Always send errors log file in private chat
|
||||||
|
chat_id = update['from']['id']
|
||||||
|
if bot.errors_file_path is None:
|
||||||
|
return bot.get_message(
|
||||||
|
'admin', 'errors_command', 'no_log',
|
||||||
|
update=update, user_record=user_record
|
||||||
|
)
|
||||||
|
await bot.sendChatAction(chat_id=chat_id, action='upload_document')
|
||||||
|
try:
|
||||||
|
# Check that error log is not empty
|
||||||
|
with open(bot.errors_file_path, 'r') as errors_file:
|
||||||
|
for line in errors_file:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
return bot.get_message(
|
||||||
|
'admin', 'errors_command', 'empty_log',
|
||||||
|
update=update, user_record=user_record
|
||||||
|
)
|
||||||
|
# Send error log
|
||||||
|
sent = await bot.send_document(
|
||||||
|
# Always send log file in private chat
|
||||||
|
chat_id=chat_id,
|
||||||
|
document_path=bot.errors_file_path,
|
||||||
|
caption=bot.get_message(
|
||||||
|
'admin', 'errors_command', 'here_is_log_file',
|
||||||
|
update=update, user_record=user_record
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Reset error log
|
||||||
|
with open(bot.errors_file_path, 'w') as errors_file:
|
||||||
|
errors_file.write('')
|
||||||
|
except Exception as e:
|
||||||
|
sent = e
|
||||||
|
# Notify failure
|
||||||
|
if isinstance(sent, Exception):
|
||||||
|
return bot.get_message(
|
||||||
|
'admin', 'errors_command', 'sending_failure',
|
||||||
|
update=update, user_record=user_record,
|
||||||
|
e=sent
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def init(bot, talk_messages=None, admin_messages=None):
|
def init(bot, talk_messages=None, admin_messages=None):
|
||||||
"""Assign parsers, commands, buttons and queries to given `bot`."""
|
"""Assign parsers, commands, buttons and queries to given `bot`."""
|
||||||
if talk_messages is None:
|
if talk_messages is None:
|
||||||
@ -992,3 +1170,15 @@ def init(bot, talk_messages=None, admin_messages=None):
|
|||||||
authorization_level='admin')
|
authorization_level='admin')
|
||||||
async def query_button(bot, update, user_record, data):
|
async def query_button(bot, update, user_record, data):
|
||||||
return await _query_button(bot, update, user_record, data)
|
return await _query_button(bot, update, user_record, data)
|
||||||
|
|
||||||
|
@bot.command(command='/log', aliases=[], show_in_keyboard=False,
|
||||||
|
description=admin_messages['log_command']['description'],
|
||||||
|
authorization_level='admin')
|
||||||
|
async def log_command(bot, update, user_record):
|
||||||
|
return await _log_command(bot, update, user_record)
|
||||||
|
|
||||||
|
@bot.command(command='/errors', aliases=[], show_in_keyboard=False,
|
||||||
|
description=admin_messages['errors_command']['description'],
|
||||||
|
authorization_level='admin')
|
||||||
|
async def errors_command(bot, update, user_record):
|
||||||
|
return await _errors_command(bot, update, user_record)
|
||||||
|
@ -86,6 +86,8 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
_log_file_name = None
|
||||||
|
_errors_file_name = None
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self, token, hostname='', certificate=None, max_connections=40,
|
self, token, hostname='', certificate=None, max_connections=40,
|
||||||
@ -206,6 +208,8 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
|
|||||||
self.default_reply_keyboard_elements = []
|
self.default_reply_keyboard_elements = []
|
||||||
self._default_keyboard = dict()
|
self._default_keyboard = dict()
|
||||||
self.recent_users = OrderedDict()
|
self.recent_users = OrderedDict()
|
||||||
|
self._log_file_name = None
|
||||||
|
self._errors_file_name = None
|
||||||
return
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -225,6 +229,57 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
|
|||||||
"""Set instance path attribute."""
|
"""Set instance path attribute."""
|
||||||
self._path = path
|
self._path = path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def log_file_name(self):
|
||||||
|
"""Return log file name.
|
||||||
|
|
||||||
|
Fallback to class file name if set, otherwise return None.
|
||||||
|
"""
|
||||||
|
return self._log_file_name or self.__class__._log_file_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def log_file_path(self):
|
||||||
|
"""Return log file path basing on self.path and `_log_file_name`.
|
||||||
|
|
||||||
|
Fallback to class file if set, otherwise return None.
|
||||||
|
"""
|
||||||
|
return f"{self.path}/data/{self.log_file_name}"
|
||||||
|
|
||||||
|
def set_log_file_name(self, file_name):
|
||||||
|
"""Set log file name."""
|
||||||
|
self._log_file_name = file_name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_class_log_file_name(cls, file_name):
|
||||||
|
"""Set class log file name."""
|
||||||
|
cls._log_file_name = file_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors_file_name(self):
|
||||||
|
"""Return errors file name.
|
||||||
|
|
||||||
|
Fallback to class file name if set, otherwise return None.
|
||||||
|
"""
|
||||||
|
return self._errors_file_name or self.__class__._errors_file_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def errors_file_path(self):
|
||||||
|
"""Return errors file path basing on self.path and `_errors_file_name`.
|
||||||
|
|
||||||
|
Fallback to class file if set, otherwise return None.
|
||||||
|
"""
|
||||||
|
if self.errors_file_name:
|
||||||
|
return f"{self.path}/data/{self.errors_file_name}"
|
||||||
|
|
||||||
|
def set_errors_file_name(self, file_name):
|
||||||
|
"""Set errors file name."""
|
||||||
|
self._errors_file_name = file_name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def set_class_errors_file_name(cls, file_name):
|
||||||
|
"""Set class errors file name."""
|
||||||
|
cls._errors_file_name = file_name
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get(cls, token, *args, **kwargs):
|
def get(cls, token, *args, **kwargs):
|
||||||
"""Given a `token`, return class instance with that token.
|
"""Given a `token`, return class instance with that token.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user