301 lines
8.8 KiB
Python
301 lines
8.8 KiB
Python
"""Collection of all text commands (commands that starts with / ).
|
|
This module is the only message interface: other modules should not communicate with the telegram user.
|
|
Decorate a function with @command for marking it as a text command.
|
|
Functions here should be only for user interaction (messages etc).
|
|
"""
|
|
|
|
from pprint import pprint
|
|
|
|
from sqlite3 import Error as DBError
|
|
from typing import Union, Optional
|
|
from functools import wraps
|
|
from textwrap import dedent
|
|
|
|
from telegram import Update, Message, MessageEntity, User, Chat, ChatMember
|
|
from telegram.ext import CallbackContext
|
|
|
|
import helpers as h
|
|
|
|
# flags and conf
|
|
# TODO make a conf file
|
|
DEVELOPMENT = True
|
|
|
|
|
|
|
|
# decorator functions
|
|
|
|
__command_dict__ = dict()
|
|
|
|
def command(func, command_name: str = ""):
|
|
"""functions decorated will be counted as '/' commands"""
|
|
if not command_name:
|
|
command_name = func.__name__
|
|
global __command_dict__
|
|
__command_dict__[command_name] = func
|
|
|
|
#@wraps(func)
|
|
#def inner(func):
|
|
# return func
|
|
|
|
return func
|
|
|
|
|
|
def get_commands() -> dict:
|
|
return __command_dict__
|
|
|
|
|
|
def nullfunc(*, phony):
|
|
return
|
|
|
|
|
|
def admin_only(func): # decorator
|
|
"""decorator: the command is only to be used by admins. it implies it is only in groups"""
|
|
@wraps(func)
|
|
def inner(update: Update, context: CallbackContext) -> None:
|
|
user: User = update.effective_user
|
|
chat: Chat = update.effective_chat
|
|
if h.is_admin(user, chat) and h.is_group(chat):
|
|
return func
|
|
else:
|
|
return nullfunc
|
|
|
|
return inner
|
|
|
|
|
|
def group_only(func): # decorator
|
|
"""decorator: the command is only to be used in groups"""
|
|
@wraps(func)
|
|
def inner(update: Update, context: CallbackContext) -> None:
|
|
if h.is_group(update.effective_chat):
|
|
return func
|
|
else:
|
|
return nullfunc
|
|
|
|
return inner
|
|
|
|
|
|
def private_only(func): # decorator
|
|
"""decorator: the command is only to be used in private chats"""
|
|
@wraps(func)
|
|
def inner(update: Update, context: CallbackContext) -> None:
|
|
if h.is_private(update.effective_chat):
|
|
return func
|
|
else:
|
|
return nullfunc
|
|
|
|
return inner
|
|
|
|
|
|
def dev_only(func): # decorator
|
|
"""decorator: the command will be available only durong development"""
|
|
if DEVELOPMENT:
|
|
return func
|
|
else:
|
|
return nullfunc
|
|
|
|
# common strings
|
|
reply_text_to_not_admins = "This command is for administrators only. You're not an administrator. Nothing done."
|
|
|
|
|
|
# commands
|
|
|
|
# def insert_task_command_wrapper(message: Message, command_name: str) -> None:
|
|
# """creates the reply text for the insertion command"""
|
|
# task_type: str = command_name
|
|
|
|
# try: #TODO logging
|
|
# h.insert_task(message)
|
|
# except ValueError: #not a group
|
|
# reply_text = "This command must be used only in group chats. Nothing done."
|
|
# except DBError: # db error
|
|
# reply_text = "Database error, please retry."
|
|
# except: # other
|
|
# reply_text = "Unknown error, please retry."
|
|
# else: # success
|
|
# reply_text = f"{task_type.title()} task inserted." #TODO private message to users?
|
|
# #TODO user mentions?
|
|
|
|
|
|
@command
|
|
def delete(update: Update, context: CallbackContext) -> None:
|
|
"""mark as deleted the task in the message replied_to"""
|
|
# TODO add syntax for deletion comment (i.e. "/delete because reasons")
|
|
# TODO add syntax for deletion of task number without responding (and comment, like "/delete 56 because reasons")
|
|
|
|
success = True
|
|
deleted_task_id = 0
|
|
message = update.message
|
|
if h.is_from_admin(message):
|
|
original_message: Message = message.reply_to_message
|
|
deleted_task_id = h.delete_task(deleting_message=message, original_message=original_message)
|
|
else:
|
|
success = False
|
|
|
|
update.message.reply_text(f"Task {deleted_task_id} deleted" if success else "Task not deleted")
|
|
|
|
return
|
|
|
|
|
|
@command
|
|
def start(update: Update, context: CallbackContext) -> None:
|
|
context.bot.send_message(chat_id=update.effective_chat.id, text="I'm a bot, please talk to me!")
|
|
|
|
|
|
@command
|
|
def test(update: Update, context: CallbackContext) -> None:
|
|
#update.message.reply_text("ok")
|
|
reply = ""
|
|
ent = None
|
|
try :
|
|
ent = h.classify_entities(update.message)
|
|
reply += str(ent)
|
|
except Exception as e1:
|
|
reply += str(e1)
|
|
|
|
try:
|
|
with open("app/.tmp/update", "tw") as f:
|
|
f.write(str(update))
|
|
except Exception as e2:
|
|
reply += str(e2)
|
|
|
|
print(reply or "None")
|
|
update.message.reply_text(reply or "None")
|
|
|
|
|
|
@command
|
|
@admin_only
|
|
@dev_only
|
|
@private_only
|
|
def dump(update: Update, context: CallbackContext) -> None:
|
|
"""dumps db"""
|
|
msg_text = update.message.text
|
|
dbs_to_dump = []
|
|
if "task" in msg_text:
|
|
dbs_to_dump.append("task")
|
|
if "people" in msg_text:
|
|
dbs_to_dump.append("people")
|
|
|
|
reply_text = h.dump_db(dbs_to_dump)
|
|
update.message.reply_text(reply_text)
|
|
|
|
|
|
|
|
|
|
@command
|
|
def admins(update: Update, context: CallbackContext) -> None:
|
|
admins: list[ChatMember] = update.message.chat.get_administrators()
|
|
admins_names = [admin.user.username or admin.user.firstname for admin in admins]
|
|
reply_text = "Admins: \n" + ", ".join("@"+name for name in admins_names)
|
|
|
|
update.message.reply_text(reply_text)
|
|
|
|
|
|
@command
|
|
def register_person(update: Update, context: CallbackContext) -> None:
|
|
"""associate a person id with the telegram id.
|
|
multiple telegram accounts for a single person are allowed,
|
|
while multiple people with a single shared account is not supported.
|
|
admin only"""
|
|
#TODO
|
|
reply_text = reply_text_to_not_admins or "Not implemented. Nothing done."
|
|
|
|
update.message.reply_text(reply_text)
|
|
|
|
raise NotImplementedError
|
|
|
|
|
|
@command
|
|
def help(update: Update, context: CallbackContext) -> None:
|
|
usage = """Usage: use a command of (/use, /cleaning, /construction, /assistance, /meeting) for inserting a task.
|
|
|
|
The command must be followed by @mentions of all the participants.
|
|
Except for that rule, you can write what you want in the message."""
|
|
|
|
if "admin" in update.message.text:
|
|
usage += """
|
|
|
|
Admin-only commands:
|
|
/register_group name: register a group chat as the official work chat for the place.
|
|
/delete: respond with that command to a task for deleting it"""
|
|
|
|
if "dev" in update.message.text:
|
|
usage += """
|
|
|
|
Dev-only usage:
|
|
/chat_number
|
|
/test"""
|
|
|
|
update.message.reply_text(dedent(usage))
|
|
|
|
## group task commands
|
|
|
|
@command
|
|
def register_task(update: Update, context: CallbackContext, task_type: str) -> None:
|
|
message = update.message
|
|
mentions = h.get_mentions(message)
|
|
participants = h.get_participants(h.classify_entities(mentions))
|
|
print(f"{participants=}")
|
|
thanks = "\nThanks to " + ", ".join(user.mention_html() for user in participants)
|
|
|
|
if participants:
|
|
task_id = h.insert_task(update)
|
|
success_text = f"OK, {task_type} task n.{task_id} registered."
|
|
reply_text = success_text + thanks
|
|
else:
|
|
reply_text = "Task not inserted. Nothing Done."
|
|
update.message.reply_html(reply_text)
|
|
|
|
|
|
@command
|
|
def assistance(update: Update, context: CallbackContext) -> None:
|
|
register_task(update, context, "assistance")
|
|
|
|
|
|
@command
|
|
def construction(update: Update, context: CallbackContext) -> None:
|
|
register_task(update, context, "construction")
|
|
|
|
|
|
@command
|
|
def use(update: Update, context: CallbackContext) -> None:
|
|
register_task(update, context, "use")
|
|
|
|
|
|
@command
|
|
def cleaning(update: Update, context: CallbackContext) -> None:
|
|
register_task(update, context, "cleaning")
|
|
|
|
|
|
@command #dev
|
|
def chat_number(update: Update, context: CallbackContext) -> None:
|
|
chat_id = update.effective_chat.id
|
|
context.bot.send_message(chat_id=chat_id, text=f"{chat_id}")
|
|
|
|
|
|
@command
|
|
def register_group(update: Update, context: CallbackContext) -> None:
|
|
"""Admin can associate a group to a place name.
|
|
Substituting the precedent group:place pair or creating another place.
|
|
type this command followed by the name that you want to assign to the place.
|
|
Spaces and capitalization are ignored"""
|
|
|
|
|
|
message = update.message
|
|
place = h.place_name_from_message(message)
|
|
# check if it is a new place, in this case put a wanring #TBD
|
|
existing_places = h.list_places()
|
|
if place not in existing_places:
|
|
warning = f"Warning: the place name you inserted, '{place}', does not exists yet. \
|
|
The places currently registered were {existing_places}. \
|
|
Inserting it anyway... \n"
|
|
|
|
success = h.register_group_as_place(message)
|
|
|
|
|
|
if success:
|
|
reply_text = f"Current group registered as {place} group "
|
|
else:
|
|
reply_text = "Not registered. Nothing done"
|
|
|
|
reply_text = warning + reply_text
|
|
message.reply_to_message(reply_text) |