Provide a default multilanguage suggestions module

This commit is contained in:
Davte 2020-02-17 12:14:52 +01:00
parent ceef3103f4
commit f17333ca42
5 changed files with 446 additions and 6 deletions

View File

@ -14,12 +14,12 @@ __author__ = "Davide Testa"
__email__ = "davide@davte.it"
__credits__ = ["Marco Origlia", "Nick Lee @Nickoala"]
__license__ = "GNU General Public License v3.0"
__version__ = "2.3.30"
__version__ = "2.4.0"
__maintainer__ = "Davide Testa"
__contact__ = "t.me/davte"
# Legacy module; please use `from davtelepot.bot import Bot` from now on
from .custombot import Bot
from . import administration_tools, authorization, bot, helper, utilities
from . import administration_tools, authorization, bot, helper, suggestions, utilities
__all__ = [administration_tools, authorization, Bot, bot, helper, utilities]
__all__ = [administration_tools, authorization, Bot, bot, helper, suggestions, utilities]

View File

@ -4,6 +4,7 @@
from collections import OrderedDict
# Project modules
from .bot import Bot
from .utilities import (
Confirmator, get_cleaned_text, get_user, make_button, make_inline_keyboard
)
@ -132,12 +133,20 @@ class Role():
@classmethod
def get_by_role_id(cls, role_id=100):
"""Give a `role_id`, return the corresponding `Role` instance."""
"""Given a `role_id`, return the corresponding `Role` instance."""
for code, role in cls.roles.items():
if code == role_id:
return role
raise IndexError(f"Unknown role id: {role_id}")
@classmethod
def get_role_by_name(cls, name='everybody'):
"""Given a `name`, return the corresponding `Role` instance."""
for role in cls.roles.values():
if role.name == name:
return role
raise IndexError(f"Unknown role name: {name}")
@classmethod
def get_user_role(cls, user_record=None, user_role_id=None):
"""Given a `user_record`, return its `Role`.
@ -488,7 +497,7 @@ async def _ban_command(bot, update, user_record):
return
def init(bot, roles=None, authorization_messages=None):
def init(bot: Bot, roles=None, authorization_messages=None):
"""Set bot roles and assign role-related commands.
Pass an OrderedDict of `roles` to get them set.
@ -496,7 +505,7 @@ def init(bot, roles=None, authorization_messages=None):
class _Role(Role):
roles = OrderedDict()
bot.Role = _Role
bot.set_role_class(_Role)
if roles is None:
roles = DEFAULT_ROLES
# Cast roles to OrderedDict

View File

@ -219,6 +219,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
self._errors_file_name = None
self.placeholder_requests = dict()
self.shared_data = dict()
self.Role = None
# Add `users` table with its fields if missing
self.db['users'].upsert(
dict(
@ -2814,3 +2815,10 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
finally:
cls.loop.run_until_complete(cls.stop_app())
return cls.final_state
def set_role_class(self, role):
"""Set a Role class for bot.
`role` must be an instance of `authorization.Role`.
"""
self.Role = role

View File

@ -37,3 +37,140 @@ default_help_messages = {
'it': "Comandi 🤖",
},
}
default_suggestion_messages = {
'suggestions_command': {
'command': "/suggestion",
'aliases': [
"/suggestions", "/ideas",
"/suggerimento", "/suggerimenti", "idee"
],
'reply_keyboard_button': {
'en': "Ideas 💡",
'it': "Idee 💡"
},
'description': {
'en': "Send a suggestion to help improve the bot",
'it': "Invia un suggerimento per aiutare a migliorare il bot"
},
'prompt_text': {
'en': (
"Send a suggestion to bot administrator.\n\n"
"Maximum 1500 characters (extra ones will be ignored).\n"
"If you need more space, you may create a telegra.ph topic and link it here.\n\n"
"/cancel if you misclicked."
),
'it': (
"Inserisci un suggerimento da inviare agli amministratori.\n\n"
"Massimo 1500 caratteri (quelli in più non verranno registrati).\n"
"Se ti serve maggiore libertà, puoi per esempio creare un topic "
"su telegra.ph e linkarlo qui!\n\n"
"/annulla se hai clickato per errore."
),
},
'prompt_popup': {
'en': (
"Send a suggestion"
),
'it': (
"Inserisci un suggerimento"
),
},
'entered_suggestion': {
'text': {
'en': (
"Entered suggestions:\n\n"
"<code>{suggestion}</code>\n\n"
"Do you want to send it to bot administrators?"
),
'it': (
"Suggerimento inserito:\n\n"
"<code>{suggestion}</code>\n\n"
"Vuoi inviarlo agli amministratori?"
),
},
'buttons': {
'send': {
'en': "Send it! 📧",
'it': "Invia! 📧",
},
'cancel': {
'en': "Cancel ❌",
'it': "Annulla ❌",
},
}
},
'received_suggestion': {
'text': {
'en': (
"💡 We received a new suggestion! 💡\n\n"
"{user}\n\n"
"<code>{suggestion}</code>\n\n"
"#suggestions #{bot.name}"
),
'it': (
"💡 Abbiamo ricevuto un nuovo suggerimento! 💡\n\n"
"{user}\n\n"
"<code>{suggestion}</code>\n\n"
"#suggestions #{bot.name}"
),
},
'buttons': {
'new': {
'en': "New suggestion 💡",
'it': "Nuovo suggerimento 💡",
},
},
},
'invalid_suggestion': {
'en': "Invalid suggestion.",
'it': "Suggerimento non valido."
},
'cancel_messages': {
'en': ['cancel'],
'it': ['annulla', 'cancella'],
},
'operation_cancelled': {
'en': "Operation cancelled.",
'it': "Operazione annullata con successo.",
},
'suggestion_sent': {
'popup': {
'en': "Thanks!",
'it': "Grazie!",
},
'text': {
'en': (
"💡 Suggestion sent, thank you! 💡\n\n"
"<code>{suggestion}</code>\n\n"
"#suggestions #{bot.name}"
),
'it': (
"💡 Suggerimento inviato, grazie! 💡\n\n"
"<code>{suggestion}</code>\n\n"
"#suggerimenti #{bot.name}"
),
},
}
},
'suggestions_button': {
'file_name': {
'en': "Suggestions.csv",
'it': "Suggerimenti.csv",
},
'file_caption': {
'en': "Here is the suggestions file.",
'it': "Ecco il file dei suggerimenti.",
}
},
'see_suggestions': {
'command': "/getsuggestions",
'aliases': [
"/vedisuggerimenti",
],
'description': {
'en': "Get a file containing all suggestions",
'it': "Richiedi un file con tutti i suggerimenti"
},
}
}

286
davtelepot/suggestions.py Normal file
View File

@ -0,0 +1,286 @@
"""Receive structured suggestions from bot users."""
# Standard library modules
import asyncio
import datetime
# Third party modules
import davtelepot
# Project modules
from .messages import default_suggestion_messages
from .utilities import (
async_wrapper, get_cleaned_text, make_button,
make_inline_keyboard, send_csv_file
)
async def _handle_suggestion_message(bot: davtelepot.bot.Bot, update, user_record, try_no=1,
suggestion_prefixes=None):
if suggestion_prefixes is None:
suggestion_prefixes = []
suggestion_prefixes = [prefix.strip('/') for prefix in suggestion_prefixes]
user_id = user_record['id']
telegram_id = user_record['telegram_id']
text = get_cleaned_text(
update,
bot,
suggestion_prefixes
)
text = text.strip(' /')[:1500]
if not text:
if try_no < 2:
bot.set_individual_text_message_handler(
await async_wrapper(
_handle_suggestion_message,
bot=bot,
update=update,
user_record=user_record,
try_no=(try_no + 1),
suggestion_prefixes=suggestion_prefixes
),
user_id=telegram_id
)
return dict(
chat_id=telegram_id,
reply_markup=dict(
force_reply=True
),
text=bot.get_message(
'suggestions', 'suggestions_command', 'prompt_text',
update=update, user_record=user_record
)
)
return bot.get_message(
'suggestions', 'suggestions_command', 'invalid_suggestion',
update=update, user_record=user_record
)
if text.lower() in bot.messages['suggestions']['suggestions_command']['cancel_messages']:
return bot.get_message(
'suggestions', 'suggestions_command', 'operation_cancelled',
update=update, user_record=user_record
)
created = datetime.datetime.now()
with bot.db as db:
db['suggestions'].insert(
dict(
user_id=user_id,
suggestion=text,
created=created
),
ensure=True
)
suggestion_id = db['suggestions'].find_one(
user_id=user_id,
created=created
)['id']
text = bot.get_message(
'suggestions', 'suggestions_command', 'entered_suggestion', 'text',
suggestion=text,
update=update, user_record=user_record
)
reply_markup = make_inline_keyboard(
[
make_button(
bot.get_message(
'suggestions', 'suggestions_command', 'entered_suggestion', 'buttons', 'send',
update=update, user_record=user_record
),
prefix='suggest:///',
delimiter='|',
data=['confirm', suggestion_id]
),
make_button(
bot.get_message(
'suggestions', 'suggestions_command', 'entered_suggestion', 'buttons', 'cancel',
update=update, user_record=user_record
),
prefix='suggest:///',
delimiter='|',
data=['cancel']
)
]
)
return dict(
chat_id=telegram_id,
text=text,
parse_mode='HTML',
reply_markup=reply_markup
)
async def _suggestions_button(bot: davtelepot.bot.Bot, update, user_record, data):
command = data[0]
user_id = update['from']['id']
result, text, reply_markup = '', '', None
if command in ['new']:
bot.set_individual_text_message_handler(
_handle_suggestion_message,
user_id=user_id
)
asyncio.ensure_future(
bot.send_message(
chat_id=user_id,
reply_markup=dict(
force_reply=True
),
text=bot.get_message(
'suggestions', 'suggestions_command', 'prompt_text',
update=update, user_record=user_record
)
)
)
result = bot.get_message(
'suggestions', 'suggestions_command', 'prompt_popup',
update=update, user_record=user_record
)
elif command in ['cancel']:
result = 'Operazione annullata'
text = 'Operazione annullata con successo.'
reply_markup = None
elif command in ['confirm'] and len(data) > 1:
suggestion_id = data[1]
when = datetime.datetime.now()
with bot.db as db:
registered_user = db['users'].find_one(telegram_id=user_id)
admins = [
x['telegram_id']
for x in db['users'].find(
privileges=[
bot.Role.get_role_by_name('admin').code,
bot.Role.get_role_by_name('founder').code
]
)
]
db['suggestions'].update(
dict(
id=suggestion_id,
sent=when
),
['id'],
ensure=True
)
suggestion_text = db['suggestions'].find_one(
id=suggestion_id
)['suggestion']
suggestion_message = bot.get_message(
'suggestions', 'suggestions_command', 'received_suggestion', 'text',
user=bot.Role.get_user_role_panel(registered_user)[0],
suggestion=suggestion_text,
bot=bot,
update=update, user_record=user_record,
)
for admin in admins:
when += datetime.timedelta(seconds=1)
asyncio.ensure_future(
bot.send_message(
chat_id=admin,
text=suggestion_message,
parse_mode='HTML'
)
)
reply_markup = make_inline_keyboard(
[
make_button(
text=bot.get_message(
'suggestions', 'suggestions_command', 'received_suggestion', 'buttons', 'new',
bot=bot,
update=update, user_record=user_record,
),
prefix='suggest:///',
delimiter='|',
data=['new']
)
],
1
)
result = bot.get_message(
'suggestions', 'suggestions_command', 'suggestion_sent', 'popup',
suggestion=suggestion_text, bot=bot,
update=update, user_record=user_record,
)
text = bot.get_message(
'suggestions', 'suggestions_command', 'suggestion_sent', 'text',
suggestion=suggestion_text, bot=bot,
update=update, user_record=user_record,
)
if text:
return dict(
text=result,
edit=dict(
text=text,
reply_markup=reply_markup,
parse_mode='HTML'
)
)
return result
async def _see_suggestions(bot: davtelepot.bot.Bot, update, user_record):
chat_id = update['from']['id']
query = (
"SELECT u.username, u.privileges, s.created, s.sent, s.suggestion "
"FROM suggestions s "
"LEFT JOIN users u "
"ON u.id = s.user_id "
"ORDER BY s.created"
)
await send_csv_file(
bot=bot,
chat_id=chat_id,
query=query,
caption=bot.get_message(
'suggestions', 'suggestions_button', 'file_caption',
user_record=user_record, update=update
),
file_name=bot.get_message(
'suggestions', 'suggestions_button', 'file_name',
user_record=user_record, update=update
),
update=update,
user_record=user_record
)
def init(telegram_bot: davtelepot.bot.Bot, suggestion_messages=default_suggestion_messages):
"""Set suggestion handling for `bot`."""
telegram_bot.messages['suggestions'] = suggestion_messages
suggestion_prefixes = (
list(suggestion_messages['suggestions_command']['reply_keyboard_button'].values())
+ [suggestion_messages['suggestions_command']['command']]
+ suggestion_messages['suggestions_command']['aliases']
)
@telegram_bot.command(command=suggestion_messages['suggestions_command']['command'],
aliases=suggestion_messages['suggestions_command']['aliases'],
reply_keyboard_button=(
suggestion_messages['suggestions_command']['reply_keyboard_button']
),
show_in_keyboard=True,
description=suggestion_messages['suggestions_command']['description'],
authorization_level='everybody')
async def suggestions_command(bot, update, user_record):
return await _handle_suggestion_message(
bot=bot,
update=update,
user_record=user_record,
try_no=1,
suggestion_prefixes=suggestion_prefixes
)
@telegram_bot.button(prefix='suggest:///', separator='|',
authorization_level='everybody')
async def suggestions_button(bot, update, user_record, data):
return await _suggestions_button(
bot=bot, update=update,
user_record=user_record, data=data
)
@telegram_bot.command(command=suggestion_messages['see_suggestions']['command'],
aliases=suggestion_messages['see_suggestions']['aliases'],
description=(
suggestion_messages['see_suggestions']['description']
),
authorization_level='admin')
async def see_suggestions(bot, update, user_record):
return await _see_suggestions(bot, update, user_record)