from telegram import Update from telegram.ext import Application, CommandHandler, MessageHandler, ContextTypes from datetime import datetime from config import BotConfig from gates import Gates from users import Users, Credential, Role BOT_USERNAME = "lagomareGateKeeperBot" bot_config = BotConfig(BOT_USERNAME) gates = Gates() users = Users() async def post_init(application: Application) -> 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 updateuser(update: Update, context: ContextTypes.DEFAULT_TYPE): 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) async def start(update: Update, context: ContextTypes.DEFAULT_TYPE): await update.message.reply_text("Welcome to GatekeeperBot! Use `/setcredentials` to configure your access") async def setcredentials(update: Update, context: ContextTypes.DEFAULT_TYPE): user_id = str(update.effective_user.id) args = context.args if len(args) != 2: return await update.message.reply_text("Usage: `/setcredentials `") role = users.get_role(user_id) if role not in ("admin", "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") async def opengate(update: Update, context: ContextTypes.DEFAULT_TYPE): user_id = str(update.effective_user.id) args = context.args if not args: return await update.message.reply_text("Usage: `/opengate `") 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.") #TODO: update guest last_used_at 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 requestaccess(update: Update, context: ContextTypes.DEFAULT_TYPE): user_id = str(update.effective_user.id) role = users.get_role(user_id) if role not in ("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} 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 approve(update: Update, context: ContextTypes.DEFAULT_TYPE): approver_id = str(update.effective_user.id) if users.get_role(approver_id) != "admin": return await update.message.reply_text("Only admins can approve 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=approver_id) await update.message.reply_text(f"Access to {gate} granted to user {user_id}.") 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 {approver_id} approved access for {gate} up to {expires_at}: {e}") except: await update.message.reply_text("Usage: `/approve `") def main(): app = Application.builder().token(bot_config.token).post_init(post_init).build() app.add_handler(MessageHandler(None, updateuser), group=1) app.add_handler(CommandHandler("start", start)) app.add_handler(CommandHandler("setcredentials", setcredentials)) app.add_handler(CommandHandler("opengate", opengate)) app.add_handler(CommandHandler("requestaccess", requestaccess)) app.add_handler(CommandHandler("approve", approve)) app.run_polling() if __name__ == "__main__": main()