Created modules and organized code

This commit is contained in:
2025-05-23 00:06:26 +02:00
parent 020b5e0193
commit 3088a75a7d
17 changed files with 340 additions and 248 deletions

18
handlers/__init__.py Normal file
View File

@@ -0,0 +1,18 @@
from .access import requestaccess, grantaccess
from .credentials import setcredentials
from .gates import opengate, open_gate_menu, handle_gate_open_callback
from .main import handle_main_menu_callback, post_init, start
from .users import updateuser
__all__ = [
"grantaccess",
"handle_main_menu_callback",
"handle_gate_open_callback",
"opengate",
"open_gate_menu",
"post_init",
"requestaccess",
"setcredentials",
"start",
"updateuser"
]

38
handlers/access.py Normal file
View File

@@ -0,0 +1,38 @@
from telegram import Update
from telegram.ext import ContextTypes
from datetime import datetime
from models import Users, Role
async def requestaccess(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users):
user_id = str(update.effective_user.id)
role = users.get_role(user_id)
if role != Role.GUEST:
return await update.message.reply_text("Only guests can request access.")
if not context.args:
return await update.message.reply_text("Usage: `/requestaccess`", parse_mode="Markdown")
requester = users.get_fullname(user_id) or users.get_username(user_id)
text = (f"Access request: {requester} ({user_id}) requests access.\nUse `/approve {user_id} <gate|all> YYYY-MM-DDTHH:MM:SSZ` to grant access.")
await update.message.reply_text("Your request has been submitted.")
admins = users.get_admins()
for admin_id in admins:
try:
await context.bot.send_message(chat_id=admin_id, text=text, parse_mode="Markdown")
except Exception as e:
print(f"Failed to notify {admin_id} that guest {user_id} requested access: {e}")
async def grantaccess(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users):
grantor_id = str(update.effective_user.id)
if users.get_role(grantor_id) != Role.ADMIN:
return await update.message.reply_text("Only admins can grant access.")
try:
user_id = context.args[0]
gate = context.args[1]
expires_at = context.args[2]
users.grant_access(user_id, gate, datetime.fromisoformat(expires_at.replace("Z", "+00:00")), grantor_id=grantor_id)
await update.message.reply_text(f"Access to {gate} granted to user {user_id} until {expires_at}")
try:
await context.bot.send_message(chat_id=user_id, text=f"Access granted to gate {gate} up to {expires_at}")
except Exception as e:
print(f"Failed to notify {user_id} that admin {grantor_id} granted access for {gate} up to {expires_at}: {e}")
except Exception:
await update.message.reply_text("Usage: `/approve <user_id> <gate|all> <expires_at:YYYY-MM-DDTHH:MM:SSZ>`")

16
handlers/credentials.py Normal file
View File

@@ -0,0 +1,16 @@
from telegram import Update
from telegram.ext import ContextTypes
from models import Users, Credential, Role
async def setcredentials(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users):
user_id = str(update.effective_user.id)
args = context.args
if len(args) != 2:
return await update.message.reply_text("Usage: `/setcredentials <username> <password>`")
role = users.get_role(user_id)
if role not in (Role.ADMIN, Role.MEMBER):
return await update.message.reply_text("Only members or admins can set credentials")
if users.set_credentials(user_id, Credential(args[0], args[1])):
await update.message.reply_text("Credentials saved")
else:
await update.message.reply_text("Error saving credentials")

85
handlers/gates.py Normal file
View File

@@ -0,0 +1,85 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import ContextTypes
from models import Gates, Users, Role
async def opengate(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users, gates: Gates):
user_id = str(update.effective_user.id)
args = context.args
if not args:
return await update.message.reply_text("Usage: `/opengate <gate>`")
gate = args[0]
gate_name = gates.get_name(gate)
role = users.get_role(user_id)
if role in (Role.ADMIN, Role.MEMBER):
creds = users.get_credentials(user_id)
if not creds:
return await update.message.reply_text("Please set your credentials with `/setcredentials` first")
elif role == Role.GUEST and users.can_open_gate(user_id, gate):
grantor = users.get_grantor(user_id, gate)
creds = users.get_credentials(grantor)
if not grantor:
return await update.message.reply_text("No valid grantor available.")
if not creds:
return await update.message.reply_text("No valid grantor credentials available.")
users.update_grant_last_used(user_id, gate)
try:
await context.bot.send_message(chat_id=grantor, text=f"Guest {user_id} opened {gate_name}")
except Exception as e:
print(f"Failed to notify {grantor} that guest {user_id} opened {gate_name}: {e}")
else:
return await update.message.reply_text("Access denied.")
if gates.open_gate(gate, creds):
return await update.message.reply_text(f"Gate {gate_name} opened!")
await update.message.reply_text(f"ERROR: Cannot open gate {gate_name}")
async def open_gate_menu(update: Update, context: ContextTypes.DEFAULT_TYPE, gates: Gates):
# Show a list of available gates as buttons
keyboard = [
[InlineKeyboardButton(gate.name, callback_data=f"opengate_{gate_id}")]
for gate_id, gate in gates._gates.items()
]
reply_markup = InlineKeyboardMarkup(keyboard)
if update.callback_query:
await update.callback_query.answer()
await update.callback_query.edit_message_text(
"Select a gate to open:", reply_markup=reply_markup
)
else:
await update.message.reply_text("Select a gate to open:", reply_markup=reply_markup)
async def handle_gate_open_callback(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users, gates: Gates):
query = update.callback_query
user_id = str(query.from_user.id)
data = query.data
if data.startswith("opengate_"):
gate_id = data[len("opengate_") :]
gate_name = gates.get_name(gate_id)
role = users.get_role(user_id)
if role in (Role.ADMIN, Role.MEMBER):
creds = users.get_credentials(user_id)
if not creds:
await query.answer("Please set your credentials with /setcredentials first", show_alert=True)
return
elif role == Role.GUEST and users.can_open_gate(user_id, gate_id):
grantor = users.get_grantor(user_id, gate_id)
creds = users.get_credentials(grantor)
if not grantor or not creds:
await query.answer("No valid grantor credentials available.", show_alert=True)
return
users.update_grant_last_used(user_id, gate_id)
try:
await context.bot.send_message(chat_id=grantor, text=f"Guest {user_id} opened {gate_name}")
except Exception as e:
print(f"Failed to notify {grantor} that guest {user_id} opened {gate_name}: {e}")
else:
# TODO: guest not working
await query.answer("Access denied.", show_alert=True)
return
if gates.open_gate(gate_id, creds):
await query.answer(f"Gate {gate_name} opened!", show_alert=True)
await query.edit_message_text(f"Gate {gate_name} opened!")
else:
await query.answer(f"ERROR: Cannot open gate {gate_name}", show_alert=True)
await query.edit_message_text(f"ERROR: Cannot open gate {gate_name}")

46
handlers/main.py Normal file
View File

@@ -0,0 +1,46 @@
from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import Application, ContextTypes
from .gates import open_gate_menu
from .access import requestaccess
from models import Gates, Users, Role
from config import BotConfig
async def post_init(application: Application, bot_config: BotConfig) -> None:
await application.bot.set_my_name(bot_config.name)
await application.bot.set_my_description(bot_config.description)
await application.bot.set_my_short_description(bot_config.short_description)
await application.bot.set_my_commands(bot_config.commands)
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users):
user_id = str(update.effective_user.id)
role = users.get_role(user_id)
keyboard = []
if role == Role.GUEST:
# Guests: can request access, or open a gate if granted
if users.has_grants(user_id):
keyboard.append([InlineKeyboardButton("Open Gate", callback_data="open_gate_menu")])
keyboard.append([InlineKeyboardButton("Request Access", callback_data="request_access")])
elif role == Role.MEMBER:
# Members: can open gates and set credentials
keyboard.append([InlineKeyboardButton("Open Gate", callback_data="open_gate_menu")])
keyboard.append([InlineKeyboardButton("Set Credentials", callback_data="set_credentials")])
elif role == Role.ADMIN:
# Admins: can open gates, grant access, and set credentials
keyboard.append([InlineKeyboardButton("Open Gate", callback_data="open_gate_menu")])
keyboard.append([InlineKeyboardButton("Grant Access", callback_data="grant_access")])
keyboard.append([InlineKeyboardButton("Set Credentials", callback_data="set_credentials")])
reply_markup = InlineKeyboardMarkup(keyboard)
await update.message.reply_text(
"Welcome to GatekeeperBot! Use the buttons below or commands.",
reply_markup=reply_markup
)
async def handle_main_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users, gates: Gates):
query = update.callback_query
data = query.data
if data == "open_gate_menu":
await open_gate_menu(update, context, gates=gates)
elif data == "request_access":
await requestaccess(update, context, users=users)

9
handlers/users.py Normal file
View File

@@ -0,0 +1,9 @@
from telegram import Update
from telegram.ext import ContextTypes
from models import Users
async def updateuser(update: Update, context: ContextTypes.DEFAULT_TYPE, users: Users):
user_id = str(update.effective_user.id)
username = update.effective_user.username
fullname = update.effective_user.full_name
users.update_user(user_id, username, fullname)