Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement database to store users email #8

Open
wants to merge 21 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion data/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
"""Data"""
"""
Data
"""
20 changes: 20 additions & 0 deletions data/db/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""
Initialize the database engine + base model
"""
import os

from sqlalchemy import create_engine
from sqlalchemy.orm import DeclarativeBase, sessionmaker

db_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "db.sqlite3")
engine = create_engine("sqlite:///" + db_path)
Session = sessionmaker(engine)
Comment on lines +12 to +14
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Evita sempre di mettere direttive a questo livello, in quanto verranno eseguite nel momento in cui il modulo viene importato. Senza saperlo sto provocando un side-effect (creando un file).
Inoltre rendi piu' delicati i test (cosa succede se voglio usare un altro path invece di db.sqlite?)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per cui come dovrei fare?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Essenzialmente l'ideale sarebbe rinchiudere tutto dentro una funzione da chiamare al momento del bisogno, possibilmente anche dal main.
Non c'è bisogno di avere una sessione globale: puoi crearne una nuova ogni volta che ti serve, è praticamente equivalente. Leggi questa risposta da uno degli sviluppatori per approfondire.
Se proprio pensi di aver bisogno di uno stato globale, un Singleton potrebbe fare al caso tuo. Almeno puoi controllare il momento in cui viene inizializzato, piuttosto che fare tutto nel momento il cui il modulo viene importato


# pylint: disable=too-few-public-methods
class Base(DeclarativeBase):
pass

# pylint: disable=wrong-import-position,cyclic-import
from .models import User
R1D3R175 marked this conversation as resolved.
Show resolved Hide resolved
R1D3R175 marked this conversation as resolved.
Show resolved Hide resolved

Base.metadata.create_all(engine)
26 changes: 26 additions & 0 deletions data/db/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
Definition of database tables
"""
import hashlib

from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy import String
from . import Base

# pylint: disable=too-few-public-methods
class User(Base):
"""
User table, maps the following fields:
- id (int): primary key, autoincrement
- email (str): hexdigest of salted user's email hashed with sha256
- chat_id (int): id of the chat the user is in
"""
__tablename__ = "user"

id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
email: Mapped[str] = mapped_column(String(64), unique=True)
chat_id: Mapped[int] = mapped_column(unique=True)

def __init__(self, email: str, chat_id: int):
self.email = hashlib.sha256(email.encode()).hexdigest()
self.chat_id = chat_id
70 changes: 41 additions & 29 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,47 +1,59 @@
"""main module"""
from telegram import BotCommand
from telegram.ext import CommandHandler, MessageHandler, Updater, Dispatcher, Filters

from module.commands import start, report, help_cmd
"""
main module
"""
from module.commands import start, report, help, login
from module.data import HELP, REPORT

def add_commands(up: Updater) -> None:
"""Adds list of commands with their description to the boy
from telegram import BotCommand, Update
from telegram.ext import filters, Application, ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes

async def add_commands(app: Application) -> None:
"""
Adds a list of commands with their description to the bot

Args:
up(Updater): supplied Updater
Args:
app (Application): the built application
"""
commands = [
BotCommand("start", "messaggio di benvenuto"),
BotCommand("help", "ricevi aiuto sui comandi"),
BotCommand("report", "segnala un problema")
BotCommand("report", "segnala un problema"),
BotCommand("login", "procedura di autenticazione")
]
up.bot.set_my_commands(commands=commands)

def add_handlers(dp:Dispatcher) -> None:
"""Adds all the handlers the bot will react to
await app.bot.set_my_commands(commands)

Args:
dp:suppplied Dispatcher
def add_handlers(app: Application) -> None:
"""
Adds all the handlers to the bot

dp.add_handler(CommandHandler("start", start, Filters.chat_type.private))
dp.add_handler(CommandHandler("chatid", lambda u, c: u.message.reply_text(str(u.message.chat_id))))
dp.add_handler(CommandHandler("help", help_cmd, Filters.chat_type.private))
dp.add_handler(MessageHandler(Filters.regex(HELP) & Filters.chat_type.private, help_cmd))
dp.add_handler(CommandHandler("report", report))
dp.add_handler(MessageHandler(Filters.regex(REPORT) & Filters.chat_type.private, report))
dp.add_handler(CommandHandler("chatid", lambda u, c: u.message.reply_text(str(u.message.chat_id))))
Args:
app (Application): the built application
"""
async def chatid(update: Update, context: ContextTypes.DEFAULT_TYPE):
await context.bot.send_message(
chat_id=update.effective_chat.id,
text=str(update.effective_chat.id)
)

handlers = [
CommandHandler("start", start, filters.ChatType.PRIVATE),
CommandHandler("chatid", chatid),
CommandHandler("help", help, filters.ChatType.PRIVATE),
MessageHandler(filters.Regex(HELP) & filters.ChatType.PRIVATE, help),
CommandHandler("report", report),
MessageHandler(filters.Regex(REPORT) & filters.ChatType.PRIVATE, report),
CommandHandler("login", login)
]

def main() -> None:
"""Main function"""
updater = Updater()
add_commands(updater)
add_handlers(updater.dispatcher)
app.add_handlers(handlers)

updater.start_polling()
updater.idle()
def main():
app = ApplicationBuilder().token("TOKEN").build()
add_commands(app)
add_handlers(app)

app.run_polling()

if __name__ == "__main__":
main()
8 changes: 7 additions & 1 deletion module/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
"""Commands"""
"""
Commands
"""
from .start import start
from .help import help
from .report import report
from .login import login
19 changes: 19 additions & 0 deletions module/commands/help.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""/help command"""
from telegram import Update
from telegram.ext import ContextTypes

from module.data.constants import HELP_CMD_TEXT

async def help(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""
Called by the /help command
Sends a list of the avaible bot's commands

Args:
update: update event
context: context passed by the handler
"""
await context.bot.send_message(
chat_id=update.effective_chat.id,
text=HELP_CMD_TEXT
)
16 changes: 0 additions & 16 deletions module/commands/help_cmd.py

This file was deleted.

88 changes: 88 additions & 0 deletions module/commands/login.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"""
/login command
"""
import hashlib

from telegram import Update
from telegram.ext import ContextTypes

from sqlalchemy import select
from data.db import Session
from data.db.models import User

async def login(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
R1D3R175 marked this conversation as resolved.
Show resolved Hide resolved
"""
Called by the /login command.

Theoretically, it should send an OTP to the student's email address
that must be validated.
If this check is successfull the user is then logged in and registered
in the database.

Args:
update: update event
context: context passed by the handler
"""
args = update.message.text.strip().split()[1:]

if len(args) != 1:
await context.bot.send_message(
chat_id=update.effective_chat.id,
text="Utilizzo sbagliato. /login <email>"
)

return

email = args[0]
if not email.endswith("@studium.unict.it"):
R1D3R175 marked this conversation as resolved.
Show resolved Hide resolved
await context.bot.send_message(
chat_id=update.effective_chat.id,
text=(
"Questo bot e' solo per gli studenti di UNICT.\n"
"Se sei uno studente controlla di aver scritto bene l'email "
"(deve finire con @studium.unict.it)"
)
)

return

with Session() as session:
stmt = select(User).where(User.chat_id == update.effective_chat.id)
result = session.scalars(stmt).first()
if result is None:
await context.bot.send_message(
chat_id=update.effective_chat.id,
text="Non sei registrato, procedo alla registrazione.."
)

session.add(User(email=email, chat_id=update.effective_chat.id))
session.commit()

return

await context.bot.send_message(
chat_id=update.effective_chat.id,
text="Sei gia' registrato! Controllo se i dati corrispondono..."
)

if result.chat_id != update.effective_chat.id:
await context.bot.send_message(
chat_id=update.effective_chat.id,
text="Il chat_id non corrisponde..."
)

return

email_digest = hashlib.sha256(email.encode()).hexdigest()
if result.email != email_digest:
await context.bot.send_message(
chat_id=update.effective_chat.id,
text="L'email non corrisponde..."
)

return

await context.bot.send_message(
chat_id=update.effective_chat.id,
text="Bentornato!"
)
20 changes: 11 additions & 9 deletions module/commands/report.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
"""/report command"""
"""
/report command
"""
from telegram import Update
from telegram.ext import CallbackContext
from telegram.ext import ContextTypes

async def report(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""
Called by the /report command
Sends a report to the admin group

def report(update: Update, context: CallbackContext) -> None:
"""Called by the /report command
Sends a report to the admin group

Args:
update: update event
context: context passed by the handler
Args:
update: update event
context: context passed by the handler
"""
# post(update, context)
print(update, context)
Expand Down
20 changes: 11 additions & 9 deletions module/commands/start.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
"""/start command"""
from telegram import Update
from telegram.ext import CallbackContext
from telegram.ext import ContextTypes

from module.data import START_CMD_TEXT

def start(update: Update, context: CallbackContext) -> None:
"""Called by the /start command
Sends a welcome message
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE) -> None:
"""
Called by the /start command
Sends a welcome message

Args:
update: update event
context: context passed by the handler
Args:
update: update event
context: context passed by the handler
"""
context.bot.sendMessage(
chat_id=update.message.chat_id, text=START_CMD_TEXT
await context.bot.send_message(
chat_id=update.effective_chat.id,
text=START_CMD_TEXT
)
2 changes: 1 addition & 1 deletion module/data/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
REPORT,
HELP,
HELP_CMD_TEXT,
START_CMD_TEXT
START_CMD_TEXT,
)
5 changes: 4 additions & 1 deletion module/data/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

START_CMD_TEXT = "Benvenuto! Questo bot è stato realizzato dagli studenti del Corso di Laurea in Informatica"

HELP_CMD_TEXT = """📬 /report Fornisce la possibilità di poter inviare una segnalazione agli sviluppatori riguardante qualsiasi disservizio"""
HELP_CMD_TEXT = '\n'.join((
"📬 /report Fornisce la possibilità di poter inviare una segnalazione agli sviluppatori riguardante qualsiasi disservizio",
"🔑 /login Permette di effettuare il login al sistema"
))

REPORT = "Segnalazioni Rappresentanti 📬"
HELP = "Help ❔"
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
python-telegram-bot==13.5
python-telegram-bot
R1D3R175 marked this conversation as resolved.
Show resolved Hide resolved
pyyaml
SQLAlchemy