Package updates checker implemented
Notify administrators when new versions are available for PyPi packages in `bot.packages`.
This commit is contained in:
		@@ -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.4.25"
 | 
					__version__ = "2.5.0"
 | 
				
			||||||
__maintainer__ = "Davide Testa"
 | 
					__maintainer__ = "Davide Testa"
 | 
				
			||||||
__contact__ = "t.me/davte"
 | 
					__contact__ = "t.me/davte"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,17 +12,19 @@ davtelepot.admin_tools.init(my_bot)
 | 
				
			|||||||
import asyncio
 | 
					import asyncio
 | 
				
			||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
import json
 | 
					import json
 | 
				
			||||||
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Third party modules
 | 
					# Third party modules
 | 
				
			||||||
import davtelepot
 | 
					from sqlalchemy.exc import ResourceClosedError
 | 
				
			||||||
from davtelepot import messages
 | 
					
 | 
				
			||||||
from davtelepot.utilities import (
 | 
					# Project modules
 | 
				
			||||||
    async_wrapper, Confirmator, extract, get_cleaned_text, get_user,
 | 
					from . import bot as davtelepot_bot, messages, __version__
 | 
				
			||||||
    escape_html_chars, line_drawing_unordered_list, make_button,
 | 
					from .utilities import (
 | 
				
			||||||
 | 
					    async_wrapper, CachedPage, Confirmator, extract, get_cleaned_text,
 | 
				
			||||||
 | 
					    get_user, escape_html_chars, line_drawing_unordered_list, make_button,
 | 
				
			||||||
    make_inline_keyboard, remove_html_tags, send_part_of_text_file,
 | 
					    make_inline_keyboard, remove_html_tags, send_part_of_text_file,
 | 
				
			||||||
    send_csv_file
 | 
					    send_csv_file
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
from sqlalchemy.exc import ResourceClosedError
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def _forward_to(update, bot, sender, addressee, is_admin=False):
 | 
					async def _forward_to(update, bot, sender, addressee, is_admin=False):
 | 
				
			||||||
@@ -179,11 +181,9 @@ def get_talk_panel(bot, update, user_record=None, text=''):
 | 
				
			|||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                key: val
 | 
					                                key: val
 | 
				
			||||||
                                for key, val in user.items()
 | 
					                                for key, val in user.items()
 | 
				
			||||||
                                if key in (
 | 
					                                if key in ('first_name',
 | 
				
			||||||
                                    'first_name',
 | 
					 | 
				
			||||||
                                           'last_name',
 | 
					                                           'last_name',
 | 
				
			||||||
                                    'username'
 | 
					                                           'username')
 | 
				
			||||||
                                )
 | 
					 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
                    ),
 | 
					                    ),
 | 
				
			||||||
@@ -779,7 +779,7 @@ def get_maintenance_exception_criterion(bot, allowed_command):
 | 
				
			|||||||
    return criterion
 | 
					    return criterion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def get_version():
 | 
					async def get_last_commit():
 | 
				
			||||||
    """Get last commit hash and davtelepot version."""
 | 
					    """Get last commit hash and davtelepot version."""
 | 
				
			||||||
    try:
 | 
					    try:
 | 
				
			||||||
        _subprocess = await asyncio.create_subprocess_exec(
 | 
					        _subprocess = await asyncio.create_subprocess_exec(
 | 
				
			||||||
@@ -793,70 +793,132 @@ async def get_version():
 | 
				
			|||||||
        last_commit = f"{e}"
 | 
					        last_commit = f"{e}"
 | 
				
			||||||
    if last_commit.startswith("fatal: not a git repository"):
 | 
					    if last_commit.startswith("fatal: not a git repository"):
 | 
				
			||||||
        last_commit = "-"
 | 
					        last_commit = "-"
 | 
				
			||||||
    davtelepot_version = davtelepot.__version__
 | 
					    return last_commit
 | 
				
			||||||
    return last_commit, davtelepot_version
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def _version_command(bot, update, user_record):
 | 
					async def _version_command(bot, update, user_record):
 | 
				
			||||||
    last_commit, davtelepot_version = await get_version()
 | 
					    last_commit = await get_last_commit()
 | 
				
			||||||
    return bot.get_message(
 | 
					    return bot.get_message(
 | 
				
			||||||
        'admin', 'version_command', 'result',
 | 
					        'admin', 'version_command', 'result',
 | 
				
			||||||
        last_commit=last_commit,
 | 
					        last_commit=last_commit,
 | 
				
			||||||
        davtelepot_version=davtelepot_version,
 | 
					        davtelepot_version=__version__,
 | 
				
			||||||
        update=update, user_record=user_record
 | 
					        update=update, user_record=user_record
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async def notify_new_version(bot):
 | 
					async def notify_new_version(bot: davtelepot_bot):
 | 
				
			||||||
    """Notify `bot` administrators about new versions.
 | 
					    """Notify `bot` administrators about new versions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Notify admins when last commit and/or davtelepot version change.
 | 
					    Notify admins when last commit and/or davtelepot version change.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    last_commit, davtelepot_version = await get_version()
 | 
					    last_commit = await get_last_commit()
 | 
				
			||||||
    old_record = bot.db['version_history'].find_one(
 | 
					    old_record = bot.db['version_history'].find_one(
 | 
				
			||||||
        order_by=['-id']
 | 
					        order_by=['-id']
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    current_versions = {
 | 
				
			||||||
 | 
					        f"{package.__name__}_version": package.__version__
 | 
				
			||||||
 | 
					        for package in bot.packages
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    current_versions['last_commit'] = last_commit
 | 
				
			||||||
    if old_record is None:
 | 
					    if old_record is None:
 | 
				
			||||||
        old_record = dict(
 | 
					        old_record = dict(
 | 
				
			||||||
            updated_at=datetime.datetime.min,
 | 
					            updated_at=datetime.datetime.min,
 | 
				
			||||||
            last_commit=None,
 | 
					 | 
				
			||||||
            davtelepot_version=None
 | 
					 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    if (
 | 
					    for name in current_versions.keys():
 | 
				
			||||||
            old_record['last_commit'] != last_commit
 | 
					        if name not in old_record:
 | 
				
			||||||
            or old_record['davtelepot_version'] != davtelepot_version
 | 
					            old_record[name] = None
 | 
				
			||||||
 | 
					    if any(
 | 
				
			||||||
 | 
					            old_record[name] != current_version
 | 
				
			||||||
 | 
					            for name, current_version in current_versions.items()
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        new_record = dict(
 | 
					 | 
				
			||||||
                updated_at=datetime.datetime.now(),
 | 
					 | 
				
			||||||
                last_commit=last_commit,
 | 
					 | 
				
			||||||
                davtelepot_version=davtelepot_version
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        bot.db['version_history'].insert(
 | 
					        bot.db['version_history'].insert(
 | 
				
			||||||
            new_record
 | 
					            dict(
 | 
				
			||||||
 | 
					                updated_at=datetime.datetime.now(),
 | 
				
			||||||
 | 
					                **current_versions
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        for admin in bot.administrators:
 | 
				
			||||||
 | 
					            text = bot.get_message(
 | 
				
			||||||
 | 
					                'admin', 'new_version', 'title',
 | 
				
			||||||
 | 
					                user_record=admin
 | 
				
			||||||
 | 
					            ) + '\n\n'
 | 
				
			||||||
 | 
					            if last_commit != old_record['last_commit']:
 | 
				
			||||||
 | 
					                text += bot.get_message(
 | 
				
			||||||
 | 
					                    'admin', 'new_version', 'last_commit',
 | 
				
			||||||
 | 
					                    old_record=old_record,
 | 
				
			||||||
 | 
					                    new_record=current_versions,
 | 
				
			||||||
 | 
					                    user_record=admin
 | 
				
			||||||
 | 
					                ) + '\n\n'
 | 
				
			||||||
 | 
					            text += '\n'.join(
 | 
				
			||||||
 | 
					                f"<b>{name[:-len('_version')]}</b>: "
 | 
				
			||||||
 | 
					                f"<code>{old_record[name]}</code> —> "
 | 
				
			||||||
 | 
					                f"<code>{current_version}</code>"
 | 
				
			||||||
 | 
					                for name, current_version in current_versions.items()
 | 
				
			||||||
 | 
					                if name not in ('last_commit', )
 | 
				
			||||||
 | 
					                and current_version != old_record[name]
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        for admin in bot.db['users'].find(privileges=[1, 2]):
 | 
					 | 
				
			||||||
            await bot.send_message(
 | 
					            await bot.send_message(
 | 
				
			||||||
                chat_id=admin['telegram_id'],
 | 
					                chat_id=admin['telegram_id'],
 | 
				
			||||||
                disable_notification=True,
 | 
					                disable_notification=True,
 | 
				
			||||||
                text='\n\n'.join(
 | 
					                text=text
 | 
				
			||||||
                    bot.get_message(
 | 
					 | 
				
			||||||
                        'admin', 'new_version', field,
 | 
					 | 
				
			||||||
                        old_record=old_record,
 | 
					 | 
				
			||||||
                        new_record=new_record,
 | 
					 | 
				
			||||||
                        user_record=admin
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                    for field in filter(
 | 
					 | 
				
			||||||
                        lambda x: (x not in old_record
 | 
					 | 
				
			||||||
                                   or old_record[x] != new_record[x]),
 | 
					 | 
				
			||||||
                        ('title', 'last_commit', 'davtelepot_version')
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
    return
 | 
					    return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def init(telegram_bot, talk_messages=None, admin_messages=None):
 | 
					async def get_package_updates(bot: davtelepot_bot,
 | 
				
			||||||
 | 
					                              monitoring_interval: int = 60 * 60):
 | 
				
			||||||
 | 
					    while 1:
 | 
				
			||||||
 | 
					        news = dict()
 | 
				
			||||||
 | 
					        for package in bot.packages:
 | 
				
			||||||
 | 
					            package_web_page = CachedPage.get(
 | 
				
			||||||
 | 
					                f'https://pypi.python.org/pypi/{package.__name__}/json',
 | 
				
			||||||
 | 
					                cache_time=2,
 | 
				
			||||||
 | 
					                mode='json'
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            web_page = await package_web_page.get_page()
 | 
				
			||||||
 | 
					            if web_page is None or isinstance(web_page, Exception):
 | 
				
			||||||
 | 
					                logging.error(f"Cannot get updates for {package.__name__}, "
 | 
				
			||||||
 | 
					                              "skipping...")
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            new_version = web_page['info']['version']
 | 
				
			||||||
 | 
					            current_version = package.__version__
 | 
				
			||||||
 | 
					            if new_version != current_version:
 | 
				
			||||||
 | 
					                news[package.__name__] = {
 | 
				
			||||||
 | 
					                    'current': current_version,
 | 
				
			||||||
 | 
					                    'new': new_version
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					        if news:
 | 
				
			||||||
 | 
					            for admin in bot.administrators:
 | 
				
			||||||
 | 
					                text = bot.get_message(
 | 
				
			||||||
 | 
					                    'admin', 'updates_available', 'header',
 | 
				
			||||||
 | 
					                    user_record=admin
 | 
				
			||||||
 | 
					                ) + '\n\n'
 | 
				
			||||||
 | 
					                text += '\n'.join(
 | 
				
			||||||
 | 
					                    f"<b>{package}</b>: "
 | 
				
			||||||
 | 
					                    f"<code>{versions['current']}</code> —> "
 | 
				
			||||||
 | 
					                    f"<code>{versions['new']}</code>"
 | 
				
			||||||
 | 
					                    for package, versions in news.items()
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                await bot.send_message(
 | 
				
			||||||
 | 
					                    chat_id=admin['telegram_id'],
 | 
				
			||||||
 | 
					                    disable_notification=True,
 | 
				
			||||||
 | 
					                    text=text
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        await asyncio.sleep(monitoring_interval)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init(telegram_bot,
 | 
				
			||||||
 | 
					         talk_messages=None,
 | 
				
			||||||
 | 
					         admin_messages=None,
 | 
				
			||||||
 | 
					         packages=None):
 | 
				
			||||||
    """Assign parsers, commands, buttons and queries to given `bot`."""
 | 
					    """Assign parsers, commands, buttons and queries to given `bot`."""
 | 
				
			||||||
 | 
					    if packages is None:
 | 
				
			||||||
 | 
					        packages = []
 | 
				
			||||||
 | 
					    telegram_bot.packages.extend(
 | 
				
			||||||
 | 
					        filter(lambda package: package not in telegram_bot.packages,
 | 
				
			||||||
 | 
					               packages)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    asyncio.ensure_future(get_package_updates(telegram_bot))
 | 
				
			||||||
    if talk_messages is None:
 | 
					    if talk_messages is None:
 | 
				
			||||||
        talk_messages = messages.default_talk_messages
 | 
					        talk_messages = messages.default_talk_messages
 | 
				
			||||||
    telegram_bot.messages['talk'] = talk_messages
 | 
					    telegram_bot.messages['talk'] = talk_messages
 | 
				
			||||||
@@ -1045,7 +1107,7 @@ def init(telegram_bot, talk_messages=None, admin_messages=None):
 | 
				
			|||||||
                                         'help_section',)
 | 
					                                         'help_section',)
 | 
				
			||||||
                             },
 | 
					                             },
 | 
				
			||||||
                          show_in_keyboard=False,
 | 
					                          show_in_keyboard=False,
 | 
				
			||||||
                          authorization_level='admin',)
 | 
					                          authorization_level='admin')
 | 
				
			||||||
    async def version_command(bot, update, user_record):
 | 
					    async def version_command(bot, update, user_record):
 | 
				
			||||||
        return await _version_command(bot=bot,
 | 
					        return await _version_command(bot=bot,
 | 
				
			||||||
                                      update=update,
 | 
					                                      update=update,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
"""This module provides a glow-like middleware for Telegram bot API.
 | 
					"""This module provides a python mirror for Telegram bot API.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
All methods and parameters are the same as the original json API.
 | 
					All methods and parameters are the same as the original json API.
 | 
				
			||||||
A simple aiohttp asynchronous web client is used to make requests.
 | 
					A simple aiohttp asynchronous web client is used to make requests.
 | 
				
			||||||
@@ -10,11 +10,11 @@ import datetime
 | 
				
			|||||||
import json
 | 
					import json
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Third party modules
 | 
					 | 
				
			||||||
from typing import Union, List
 | 
					from typing import Union, List
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Third party modules
 | 
				
			||||||
import aiohttp
 | 
					import aiohttp
 | 
				
			||||||
from aiohttp import web
 | 
					import aiohttp.web
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TelegramError(Exception):
 | 
					class TelegramError(Exception):
 | 
				
			||||||
@@ -82,7 +82,7 @@ class TelegramBot:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    loop = asyncio.get_event_loop()
 | 
					    loop = asyncio.get_event_loop()
 | 
				
			||||||
    app = web.Application()
 | 
					    app = aiohttp.web.Application()
 | 
				
			||||||
    sessions_timeouts = {
 | 
					    sessions_timeouts = {
 | 
				
			||||||
        'getUpdates': dict(
 | 
					        'getUpdates': dict(
 | 
				
			||||||
            timeout=35,
 | 
					            timeout=35,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Standard library modules
 | 
					# Standard library modules
 | 
				
			||||||
from collections import OrderedDict
 | 
					from collections import OrderedDict
 | 
				
			||||||
 | 
					from typing import Callable, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Project modules
 | 
					# Project modules
 | 
				
			||||||
from .bot import Bot
 | 
					from .bot import Bot
 | 
				
			||||||
 | 
					from .messages import default_authorization_messages
 | 
				
			||||||
from .utilities import (
 | 
					from .utilities import (
 | 
				
			||||||
    Confirmator, get_cleaned_text, get_user, make_button, make_inline_keyboard
 | 
					    Confirmator, get_cleaned_text, get_user, make_button, make_inline_keyboard
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -256,85 +258,10 @@ def get_authorization_function(bot):
 | 
				
			|||||||
    return is_authorized
 | 
					    return is_authorized
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
deafult_authorization_messages = {
 | 
					 | 
				
			||||||
    'auth_command': {
 | 
					 | 
				
			||||||
        'description': {
 | 
					 | 
				
			||||||
            'en': "Edit user permissions. To select a user, reply to "
 | 
					 | 
				
			||||||
                  "a message of theirs or write their username",
 | 
					 | 
				
			||||||
            'it': "Cambia il grado di autorizzazione di un utente "
 | 
					 | 
				
			||||||
                  "(in risposta o scrivendone lo username)"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'unhandled_case': {
 | 
					 | 
				
			||||||
            'en': "<code>Unhandled case :/</code>",
 | 
					 | 
				
			||||||
            'it': "<code>Caso non previsto :/</code>"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'instructions': {
 | 
					 | 
				
			||||||
            'en': "Reply with this command to a user or write "
 | 
					 | 
				
			||||||
                  "<code>/auth username</code> to edit their permissions.",
 | 
					 | 
				
			||||||
            'it': "Usa questo comando in risposta a un utente "
 | 
					 | 
				
			||||||
                  "oppure scrivi <code>/auth username</code> per "
 | 
					 | 
				
			||||||
                  "cambiarne il grado di autorizzazione."
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'unknown_user': {
 | 
					 | 
				
			||||||
            'en': "Unknown user.",
 | 
					 | 
				
			||||||
            'it': "Utente sconosciuto."
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'choose_user': {
 | 
					 | 
				
			||||||
            'en': "{n} users match your query. Please select one.",
 | 
					 | 
				
			||||||
            'it': "Ho trovato {n} utenti che soddisfano questi criteri.\n"
 | 
					 | 
				
			||||||
                  "Per procedere selezionane uno."
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'no_match': {
 | 
					 | 
				
			||||||
            'en': "No user matches your query. Please try again.",
 | 
					 | 
				
			||||||
            'it': "Non ho trovato utenti che soddisfino questi criteri.\n"
 | 
					 | 
				
			||||||
                  "Prova di nuovo."
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    'ban_command': {
 | 
					 | 
				
			||||||
        'description': {
 | 
					 | 
				
			||||||
            'en': "Reply to a user with /ban to ban them",
 | 
					 | 
				
			||||||
            'it': "Banna l'utente (da usare in risposta)"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    'auth_button': {
 | 
					 | 
				
			||||||
        'description': {
 | 
					 | 
				
			||||||
            'en': "Edit user permissions",
 | 
					 | 
				
			||||||
            'it': "Cambia il grado di autorizzazione di un utente"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'confirm': {
 | 
					 | 
				
			||||||
            'en': "Are you sure?",
 | 
					 | 
				
			||||||
            'it': "Sicuro sicuro?"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'back_to_user': {
 | 
					 | 
				
			||||||
            'en': "Back to user",
 | 
					 | 
				
			||||||
            'it': "Torna all'utente"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'permission_denied': {
 | 
					 | 
				
			||||||
            'user': {
 | 
					 | 
				
			||||||
                'en': "You cannot appoint this user!",
 | 
					 | 
				
			||||||
                'it': "Non hai l'autorità di modificare i permessi di questo "
 | 
					 | 
				
			||||||
                      "utente!"
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            'role': {
 | 
					 | 
				
			||||||
                'en': "You're not allowed to appoint someone to this role!",
 | 
					 | 
				
			||||||
                'it': "Non hai l'autorità di conferire questo permesso!"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'no_change': {
 | 
					 | 
				
			||||||
            'en': "No change suggested!",
 | 
					 | 
				
			||||||
            'it': "È già così!"
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        'appointed': {
 | 
					 | 
				
			||||||
            'en': "Permission granted",
 | 
					 | 
				
			||||||
            'it': "Permesso conferito"
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async def _authorization_command(bot, update, user_record):
 | 
					async def _authorization_command(bot, update, user_record):
 | 
				
			||||||
    text = get_cleaned_text(bot=bot, update=update, replace=['auth'])
 | 
					    text = get_cleaned_text(bot=bot, update=update, replace=['auth'])
 | 
				
			||||||
    reply_markup = None
 | 
					    reply_markup = None
 | 
				
			||||||
 | 
					    # noinspection PyUnusedLocal
 | 
				
			||||||
    result = bot.get_message(
 | 
					    result = bot.get_message(
 | 
				
			||||||
        'authorization', 'auth_command', 'unhandled_case',
 | 
					        'authorization', 'auth_command', 'unhandled_case',
 | 
				
			||||||
        update=update, user_record=user_record
 | 
					        update=update, user_record=user_record
 | 
				
			||||||
@@ -509,7 +436,17 @@ async def _ban_command(bot, update, user_record):
 | 
				
			|||||||
    return
 | 
					    return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def init(telegram_bot: Bot, roles=None, authorization_messages=None):
 | 
					def default_get_administrators_function(bot: Bot):
 | 
				
			||||||
 | 
					    return list(
 | 
				
			||||||
 | 
					        bot.db['users'].find(privileges=[1,2])
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def init(telegram_bot: Bot,
 | 
				
			||||||
 | 
					         roles: Union[list, OrderedDict] = None,
 | 
				
			||||||
 | 
					         authorization_messages=None,
 | 
				
			||||||
 | 
					         get_administrators_function: Callable[[object],
 | 
				
			||||||
 | 
					                                               list] = None):
 | 
				
			||||||
    """Set bot roles and assign role-related commands.
 | 
					    """Set bot roles and assign role-related commands.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Pass an OrderedDict of `roles` to get them set.
 | 
					    Pass an OrderedDict of `roles` to get them set.
 | 
				
			||||||
@@ -537,8 +474,11 @@ def init(telegram_bot: Bot, roles=None, authorization_messages=None):
 | 
				
			|||||||
    telegram_bot.set_authorization_function(
 | 
					    telegram_bot.set_authorization_function(
 | 
				
			||||||
        get_authorization_function(telegram_bot)
 | 
					        get_authorization_function(telegram_bot)
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    if authorization_messages is None:
 | 
					    get_administrators_function = (get_administrators_function
 | 
				
			||||||
        authorization_messages = deafult_authorization_messages
 | 
					                                   or default_get_administrators_function)
 | 
				
			||||||
 | 
					    telegram_bot.set_get_administrator_function(get_administrators_function)
 | 
				
			||||||
 | 
					    authorization_messages = (authorization_messages
 | 
				
			||||||
 | 
					                              or default_authorization_messages)
 | 
				
			||||||
    telegram_bot.messages['authorization'] = authorization_messages
 | 
					    telegram_bot.messages['authorization'] = authorization_messages
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @telegram_bot.command(command='/auth', aliases=[], show_in_keyboard=False,
 | 
					    @telegram_bot.command(command='/auth', aliases=[], show_in_keyboard=False,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,13 +34,16 @@ Usage
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Standard library modules
 | 
					# Standard library modules
 | 
				
			||||||
import asyncio
 | 
					import asyncio
 | 
				
			||||||
from collections import OrderedDict
 | 
					 | 
				
			||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
import io
 | 
					import io
 | 
				
			||||||
import inspect
 | 
					import inspect
 | 
				
			||||||
import logging
 | 
					import logging
 | 
				
			||||||
import os
 | 
					import os
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from collections import OrderedDict
 | 
				
			||||||
 | 
					from typing import Callable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Third party modules
 | 
					# Third party modules
 | 
				
			||||||
from aiohttp import web
 | 
					from aiohttp import web
 | 
				
			||||||
@@ -210,6 +213,8 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
 | 
				
			|||||||
            if 'chat' in update
 | 
					            if 'chat' in update
 | 
				
			||||||
            else None
 | 
					            else None
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        # Function to get updated list of bot administrators
 | 
				
			||||||
 | 
					        self._get_administrators = lambda bot: []
 | 
				
			||||||
        # Message to be returned if user is not allowed to call method
 | 
					        # Message to be returned if user is not allowed to call method
 | 
				
			||||||
        self._authorization_denied_message = None
 | 
					        self._authorization_denied_message = None
 | 
				
			||||||
        # Default authorization function (always return True)
 | 
					        # Default authorization function (always return True)
 | 
				
			||||||
@@ -223,6 +228,7 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
 | 
				
			|||||||
        self.placeholder_requests = dict()
 | 
					        self.placeholder_requests = dict()
 | 
				
			||||||
        self.shared_data = dict()
 | 
					        self.shared_data = dict()
 | 
				
			||||||
        self.Role = None
 | 
					        self.Role = None
 | 
				
			||||||
 | 
					        self.packages = [sys.modules['davtelepot']]
 | 
				
			||||||
        # Add `users` table with its fields if missing
 | 
					        # Add `users` table with its fields if missing
 | 
				
			||||||
        if 'users' not in self.db.tables:
 | 
					        if 'users' not in self.db.tables:
 | 
				
			||||||
            table = self.db.create_table(
 | 
					            table = self.db.create_table(
 | 
				
			||||||
@@ -553,6 +559,26 @@ class Bot(TelegramBot, ObjectWithDatabase, MultiLanguageObject):
 | 
				
			|||||||
            default_inline_query_answer
 | 
					            default_inline_query_answer
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def set_get_administrator_function(self,
 | 
				
			||||||
 | 
					                                       new_function: Callable[[object],
 | 
				
			||||||
 | 
					                                                              list]):
 | 
				
			||||||
 | 
					        """Set a new get_administrators function.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        This function should take bot as argument and return an updated list
 | 
				
			||||||
 | 
					            of its administrators.
 | 
				
			||||||
 | 
					        Example:
 | 
				
			||||||
 | 
					        ```python
 | 
				
			||||||
 | 
					        def get_administrators(bot):
 | 
				
			||||||
 | 
					            admins = bot.db['users'].find(privileges=2)
 | 
				
			||||||
 | 
					            return list(admins)
 | 
				
			||||||
 | 
					        ```
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        self._get_administrators = new_function
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def administrators(self):
 | 
				
			||||||
 | 
					        return self._get_administrators(self)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def message_router(self, update, user_record):
 | 
					    async def message_router(self, update, user_record):
 | 
				
			||||||
        """Route Telegram `message` update to appropriate message handler."""
 | 
					        """Route Telegram `message` update to appropriate message handler."""
 | 
				
			||||||
        for key, value in update.items():
 | 
					        for key, value in update.items():
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -134,14 +134,6 @@ default_admin_messages = {
 | 
				
			|||||||
            'it': "Vecchio commit: <code>{old_record[last_commit]}</code>\n"
 | 
					            'it': "Vecchio commit: <code>{old_record[last_commit]}</code>\n"
 | 
				
			||||||
                  "Nuovo commit: <code>{new_record[last_commit]}</code>",
 | 
					                  "Nuovo commit: <code>{new_record[last_commit]}</code>",
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
        'davtelepot_version': {
 | 
					 | 
				
			||||||
            'en': "davtelepot version: "
 | 
					 | 
				
			||||||
                  "<code>{old_record[davtelepot_version]}</code> —> "
 | 
					 | 
				
			||||||
                  "<code>{new_record[davtelepot_version]}</code>",
 | 
					 | 
				
			||||||
            'it': "Versione di davtelepot: "
 | 
					 | 
				
			||||||
                  "<code>{old_record[davtelepot_version]}</code> —> "
 | 
					 | 
				
			||||||
                  "<code>{new_record[davtelepot_version]}</code>",
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    'query_button': {
 | 
					    'query_button': {
 | 
				
			||||||
        'error': {
 | 
					        'error': {
 | 
				
			||||||
@@ -256,6 +248,14 @@ default_admin_messages = {
 | 
				
			|||||||
                  "sessione"
 | 
					                  "sessione"
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    'updates_available': {
 | 
				
			||||||
 | 
					        'header': {
 | 
				
			||||||
 | 
					            'en': "🔔 Updates available! ⬇️\n\n"
 | 
				
			||||||
 | 
					                  "Click to /restart bot",
 | 
				
			||||||
 | 
					            'it': "🔔 Aggiornamenti disponibili! ⬇\n\n"
 | 
				
			||||||
 | 
					                  "Clicka qui per fare il /restart",
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    'version_command': {
 | 
					    'version_command': {
 | 
				
			||||||
        'reply_keyboard_button': {
 | 
					        'reply_keyboard_button': {
 | 
				
			||||||
            'en': "Version #️⃣",
 | 
					            'en': "Version #️⃣",
 | 
				
			||||||
@@ -275,6 +275,81 @@ default_admin_messages = {
 | 
				
			|||||||
    },
 | 
					    },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					default_authorization_messages = {
 | 
				
			||||||
 | 
					    'auth_command': {
 | 
				
			||||||
 | 
					        'description': {
 | 
				
			||||||
 | 
					            'en': "Edit user permissions. To select a user, reply to "
 | 
				
			||||||
 | 
					                  "a message of theirs or write their username",
 | 
				
			||||||
 | 
					            'it': "Cambia il grado di autorizzazione di un utente "
 | 
				
			||||||
 | 
					                  "(in risposta o scrivendone lo username)"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'unhandled_case': {
 | 
				
			||||||
 | 
					            'en': "<code>Unhandled case :/</code>",
 | 
				
			||||||
 | 
					            'it': "<code>Caso non previsto :/</code>"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'instructions': {
 | 
				
			||||||
 | 
					            'en': "Reply with this command to a user or write "
 | 
				
			||||||
 | 
					                  "<code>/auth username</code> to edit their permissions.",
 | 
				
			||||||
 | 
					            'it': "Usa questo comando in risposta a un utente "
 | 
				
			||||||
 | 
					                  "oppure scrivi <code>/auth username</code> per "
 | 
				
			||||||
 | 
					                  "cambiarne il grado di autorizzazione."
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'unknown_user': {
 | 
				
			||||||
 | 
					            'en': "Unknown user.",
 | 
				
			||||||
 | 
					            'it': "Utente sconosciuto."
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'choose_user': {
 | 
				
			||||||
 | 
					            'en': "{n} users match your query. Please select one.",
 | 
				
			||||||
 | 
					            'it': "Ho trovato {n} utenti che soddisfano questi criteri.\n"
 | 
				
			||||||
 | 
					                  "Per procedere selezionane uno."
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'no_match': {
 | 
				
			||||||
 | 
					            'en': "No user matches your query. Please try again.",
 | 
				
			||||||
 | 
					            'it': "Non ho trovato utenti che soddisfino questi criteri.\n"
 | 
				
			||||||
 | 
					                  "Prova di nuovo."
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    'ban_command': {
 | 
				
			||||||
 | 
					        'description': {
 | 
				
			||||||
 | 
					            'en': "Reply to a user with /ban to ban them",
 | 
				
			||||||
 | 
					            'it': "Banna l'utente (da usare in risposta)"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    'auth_button': {
 | 
				
			||||||
 | 
					        'description': {
 | 
				
			||||||
 | 
					            'en': "Edit user permissions",
 | 
				
			||||||
 | 
					            'it': "Cambia il grado di autorizzazione di un utente"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'confirm': {
 | 
				
			||||||
 | 
					            'en': "Are you sure?",
 | 
				
			||||||
 | 
					            'it': "Sicuro sicuro?"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'back_to_user': {
 | 
				
			||||||
 | 
					            'en': "Back to user",
 | 
				
			||||||
 | 
					            'it': "Torna all'utente"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'permission_denied': {
 | 
				
			||||||
 | 
					            'user': {
 | 
				
			||||||
 | 
					                'en': "You cannot appoint this user!",
 | 
				
			||||||
 | 
					                'it': "Non hai l'autorità di modificare i permessi di questo "
 | 
				
			||||||
 | 
					                      "utente!"
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            'role': {
 | 
				
			||||||
 | 
					                'en': "You're not allowed to appoint someone to this role!",
 | 
				
			||||||
 | 
					                'it': "Non hai l'autorità di conferire questo permesso!"
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'no_change': {
 | 
				
			||||||
 | 
					            'en': "No change suggested!",
 | 
				
			||||||
 | 
					            'it': "È già così!"
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        'appointed': {
 | 
				
			||||||
 | 
					            'en': "Permission granted",
 | 
				
			||||||
 | 
					            'it': "Permesso conferito"
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
default_authorization_denied_message = {
 | 
					default_authorization_denied_message = {
 | 
				
			||||||
    'en': "You are not allowed to use this command, sorry.",
 | 
					    'en': "You are not allowed to use this command, sorry.",
 | 
				
			||||||
    'it': "Non disponi di autorizzazioni sufficienti per questa richiesta, spiacente.",
 | 
					    'it': "Non disponi di autorizzazioni sufficienti per questa richiesta, spiacente.",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -149,15 +149,6 @@ async def _suggestions_button(bot: davtelepot.bot.Bot, update, user_record, data
 | 
				
			|||||||
        when = datetime.datetime.now()
 | 
					        when = datetime.datetime.now()
 | 
				
			||||||
        with bot.db as db:
 | 
					        with bot.db as db:
 | 
				
			||||||
            registered_user = db['users'].find_one(telegram_id=user_id)
 | 
					            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(
 | 
					            db['suggestions'].update(
 | 
				
			||||||
                dict(
 | 
					                dict(
 | 
				
			||||||
                    id=suggestion_id,
 | 
					                    id=suggestion_id,
 | 
				
			||||||
@@ -176,11 +167,11 @@ async def _suggestions_button(bot: davtelepot.bot.Bot, update, user_record, data
 | 
				
			|||||||
            bot=bot,
 | 
					            bot=bot,
 | 
				
			||||||
            update=update, user_record=user_record,
 | 
					            update=update, user_record=user_record,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        for admin in admins:
 | 
					        for admin in bot.administrators:
 | 
				
			||||||
            when += datetime.timedelta(seconds=1)
 | 
					            when += datetime.timedelta(seconds=1)
 | 
				
			||||||
            asyncio.ensure_future(
 | 
					            asyncio.ensure_future(
 | 
				
			||||||
                bot.send_message(
 | 
					                bot.send_message(
 | 
				
			||||||
                    chat_id=admin,
 | 
					                    chat_id=admin['telegram_id'],
 | 
				
			||||||
                    text=suggestion_message,
 | 
					                    text=suggestion_message,
 | 
				
			||||||
                    parse_mode='HTML'
 | 
					                    parse_mode='HTML'
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
@@ -248,8 +239,10 @@ async def _see_suggestions(bot: davtelepot.bot.Bot, update, user_record):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def init(telegram_bot: davtelepot.bot.Bot, suggestion_messages=default_suggestion_messages):
 | 
					def init(telegram_bot: davtelepot.bot.Bot, suggestion_messages=None):
 | 
				
			||||||
    """Set suggestion handling for `bot`."""
 | 
					    """Set suggestion handling for `bot`."""
 | 
				
			||||||
 | 
					    if suggestion_messages is None:
 | 
				
			||||||
 | 
					        suggestion_messages = default_suggestion_messages
 | 
				
			||||||
    telegram_bot.messages['suggestions'] = suggestion_messages
 | 
					    telegram_bot.messages['suggestions'] = suggestion_messages
 | 
				
			||||||
    suggestion_prefixes = (
 | 
					    suggestion_prefixes = (
 | 
				
			||||||
        list(suggestion_messages['suggestions_command']['reply_keyboard_button'].values())
 | 
					        list(suggestion_messages['suggestions_command']['reply_keyboard_button'].values())
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user