This commit is contained in:
m 2020-12-20 17:49:35 +01:00
parent 997986d892
commit b674928043
8 changed files with 9695 additions and 79 deletions

2
.gitignore vendored
View File

@ -2,3 +2,5 @@ app/main_temp.py
database/people*
database/task*
.mypy_cache
logs/**
secrets/**

View File

@ -41,7 +41,6 @@ def insert_task_in_db(*, db: sqlite3.Connection = task_db,
#TODO check existing
insert_tuple = (task_type, place, message.text, message.message_id)
task_insertion = f"""INSERT INTO {task_table}
(type, place, creation_message, creation_message_id, creation_chat_id
)
@ -140,3 +139,7 @@ def list_places(db=task_db) -> list[str]:
FROM places_group_id
""").fetchall()
return [place[0] for place in place_tuples]
def dump(dbs) -> str:
raise NotImplementedError

View File

@ -1,33 +1,32 @@
"""business logic and database"""
from typing import Union, Optional
from collections import defaultdict #, namedtuple
from telegram import Update, Message, MessageEntity, User, Chat, ChatMember
from telegram.ext import CallbackContext
import database_interface as db
from collections import defaultdict # , namedtuple
from sqlite3 import Error as DBError
from typing import Optional, Union
from telegram import Chat, ChatMember, Message, MessageEntity, Update, User
from telegram.ext import CallbackContext
import database_interface as db
InternaldictType = dict[MessageEntity, str]
ClassifiedEntitiesdictType = dict[str, list[InternaldictType]]
def classify_entities(entities: InternaldictType) -> ClassifiedEntitiesdictType:
"""classify entities by type and returns a dictionary with types as keys"""
internaldict: InternaldictType = InternaldictType()
internaldictkeys = ("entity", "text")
# def classify_entities(entities: InternaldictType) -> ClassifiedEntitiesdictType:
# """classify entities by type and returns a dictionary with types as keys"""
# internaldict: InternaldictType = InternaldictType()
# internaldictkeys = ("entity", "text")
entities_bytype: ClassifiedEntitiesdictType = defaultdict(list)
# entities_bytype: ClassifiedEntitiesdictType = defaultdict(list)
for entity, text in entities.items():
internaldict = dict(zip(internaldictkeys,
[entity, text]))
entities_bytype[entity.type].append(internaldict)
# for entity, text in entities.items():
# internaldict = dict(zip(internaldictkeys,
# [entity, text]))
# entities_bytype[entity.type].append(internaldict)
return entities_bytype
# return entities_bytype
def is_group(chat: Chat) -> bool:
@ -63,7 +62,7 @@ def person_id_of(telegram_id: int) -> int:
def insert_task(update: Update) -> int:
def insert_task(update: Update) -> int: # TODO refactor for gettin externally the command, the message, the place and the participants?
message = update.message
if not is_group(update.effective_chat):
@ -71,6 +70,7 @@ def insert_task(update: Update) -> int:
# split command and determine participants, task type and place
entities_bytype = classify_entities(message.parse_entities()) # TODO include captions to images etc"or message.parse_caption_entities())"
task_kind: str = entities_bytype[MessageEntity.BOT_COMMAND][0]["text"][1:]
chat_id: int = update.effective_chat.id #NOTE TBD permit use of a h/cashtag for signaling that the task is in either place?
@ -83,9 +83,11 @@ def insert_task(update: Update) -> int:
# except AttributeError: # the user does not exists
# pass
participants: list[User] = get_participants(entities_bytype)
print(entities_bytype)
participants_id: list[int] = get_ids_of(participants)
participants_id_unique: list[int] = remove_duplicates(participants_id)
print(f"participants: {[participant.first_name for participant in participants]}")
print(f"id: {participants_id}")
# insert in db
inserted_task_id: int = db.insert_task_in_db(task_type=task_kind, chat_id=chat_id,
participants=participants_id_unique, message=message)
@ -97,19 +99,43 @@ def remove_duplicates(in_list: Union[list, tuple]) -> list: # to export
def get_participants(entities_bytype: ClassifiedEntitiesdictType) -> list[User]:
"""get unique users form a list of classified"""
"""get real, unique users form a list of classified"""
participants: list = []
participants.append(entities_bytype[MessageEntity.MENTION])
participants.append(entities_bytype[MessageEntity.TEXT_MENTION])
users = map(lambda d_entity_text: d_entity_text["entity"].user , participants)
unique_users = remove_duplicates(users)
participants.extend(entities_bytype[MessageEntity.MENTION])
participants.extend(entities_bytype[MessageEntity.TEXT_MENTION])
print(f"mentions: {entities_bytype[MessageEntity.MENTION]}")
print(f"text_mentions: {entities_bytype[MessageEntity.TEXT_MENTION]}")
users = [ participant["entity"].user for participant in participants]
#users = map(lambda d_entity_text: d_entity_text["entity"].user , participants)
real_users = [user for user in users if user and not user.is_bot] # remove inexistent and bot users
unique_users = remove_duplicates([user for user in real_users if user])
return unique_users
def get_ids_of(users: list[User]) -> list[int]:
return list(map(lambda user: user.id))
def get_mentions(message: Message) -> dict[MessageEntity, str]:
"""get all mentions and textmentions in the text and the caption.
returns a dictionary of the same type of Message.parse_entities() type"""
# mix of parse_caption_entities, parse_entities and MENTION, TEXT_MENTION
# done like this and not with [MessageEntity.MENTION, MessageEntity.TEXT_MENTION] because I no longer trust the parse function
# caption
mentions_caption_at = message.parse_caption_entities(MessageEntity.MENTION)
mentions_caption_textlike = message.parse_caption_entities(MessageEntity.TEXT_MENTION)
mentions_caption = mentions_caption_at | mentions_caption_textlike
# text message
mentions_textmessage_at = message.parse_entities(MessageEntity.MENTION)
mentions_textmessage_textlike = message.parse_entities(MessageEntity.TEXT_MENTION)
mentions_textmessage = mentions_textmessage_at | mentions_textmessage_textlike
mentions = mentions_text | mentions_at
return mentions
def get_ids_of(user_list: list[User]) -> list[int]:
return list(map(lambda user: user.id, user_list))
def get_administrators_id(chat: Chat) -> list[int]: #util
@ -124,7 +150,8 @@ def is_admin(user: Union[User, int], chat: Chat) -> bool:
test = user in get_administrators_id(chat)
return test
def dump_db():
def dump_db(dbs: list[str]) -> str:
#return db.dump(dbs)
raise NotImplementedError

View File

@ -1,8 +1,10 @@
"""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
@ -18,6 +20,8 @@ import helpers as h
# TODO make a conf file
DEVELOPMENT = True
# decorator functions
__command_dict__ = dict()
@ -35,22 +39,6 @@ def command(func, command_name: str = ""):
return func
# def command(command_name: str = ""):
# def inner(function):
# if not command_name:
# command_name = function.__name__
# global __command_dict__
# __command_dict__[command_name] = function
# @wraps(function)
# def wrapper(*args, **kwargs):
# function(*args, **kwargs)
# return wrapper
# return inner
def get_commands() -> dict:
return __command_dict__
@ -173,11 +161,21 @@ def test(update: Update, context: CallbackContext) -> None:
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"""
reply_text = h.dump_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)
@ -232,35 +230,40 @@ def help(update: Update, context: CallbackContext) -> None:
## group task commands
@command
def insert_task(update: Update, context: CallbackContext, task_type: str) -> None:
task_id: int = 0
reply_text = ""
try:
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)
reply_text = f"OK, {task_type} task n.{task_id} registered."
except Exception as e:
reply_text = "Task not inserted. Nothing done." + str(e)
update.message.reply_text(reply_text)
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:
insert_task(update, context, "assistance")
register_task(update, context, "assistance")
@command
def construction(update: Update, context: CallbackContext) -> None:
insert_task(update, context, "construction")
register_task(update, context, "construction")
@command
def use(update: Update, context: CallbackContext) -> None:
insert_task(update, context, "use")
register_task(update, context, "use")
@command
def cleaning(update: Update, context: CallbackContext) -> None:
insert_task(update, context, "cleaning")
register_task(update, context, "cleaning")
@command #dev
@ -278,29 +281,13 @@ def register_group(update: Update, context: CallbackContext) -> None:
message = update.message
place = h.place_name_from_message(messagedef 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?
)
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 = dedent(f"""Warning: the place name you inserted, '{place}', does not exists yet.
The places currently registered were {existing_places}.
Inserting it anyway... \n""" )
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)
@ -308,7 +295,7 @@ def register_group(update: Update, context: CallbackContext) -> None:
if success:
reply_text = f"Current group registered as {place} group "
else:
reply_text = "Failed to register. Nothing done"
reply_text = "Not registered. Nothing done"
reply_text = warning + reply_text
message.reply_to_message(reply_text)

File diff suppressed because it is too large Load Diff