diff --git a/LICENSE b/LICENSE index 472ac23..22df25f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ MIT License -Copyright (c) +Copyright (c) 2025 Ettore Dreucci Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index 6170586..bc67c1f 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,86 @@ -# lagomareGateKeeperBot +# Lagomare GateKeeper Bot +A Telegram bot for managing access to gates, with user roles, credential management, and integration with AVConnect. + +--- + +## Features + +- **Role-based access:** Admins, members, and guests with different permissions. +- **Gate control:** Open gates via Telegram commands or inline keyboards. +- **Credential management:** Users can set and update their own credentials. +- **Access requests:** Guests can request access; admins can grant or revoke it. +- **Grant tracking:** Usage of guest grants is tracked. +- **Integration:** Connects to AVConnect for actual gate operations. + +--- + +## Project Structure + +``` +lagomareGateKeeperBot/ +│ +├── bot.py # Main entry point +├── config.py # Bot configuration loader +├── handlers/ # Telegram command and callback handlers +├── models/ # Data models (users, gates, etc.) +├── services/ # External integrations (e.g., AVConnect) +├── utils/ # Helper functions +├── data/ # Persistent data (users.json, gates.json, etc.) +└── README.md +``` + +--- + +## Setup + +1. **Clone the repository:** + ```sh + git clone https://git.dreucci.it/noettore/lagomareGateKeeperBot.git + cd lagomareGateKeeperBot + ``` + +2. **Install dependencies:** + ```sh + pip install -r requirements.txt + ``` + +3. **Configure the bot:** + - Edit `data/config.json` with your bot token and settings. + - Add gate and user data to `data/gates.json` and `data/users.json`. + +4. **Run the bot:** + ```sh + python bot.py + ``` + +--- + +## Usage + +- `/start` — Show main menu (with inline keyboard based on user role) +- `/setcredentials ` — Set your AVConnect credentials +- `/opengate ` — Open a gate +- `/requestaccess` — Guests can request access +- `/grantaccess ` — Admins grant access + +--- + +## Development + +- Handlers are in the `handlers/` directory, grouped by feature. +- Data models are in `models/`. +- External services (like AVConnect) are in `services/`. +- Utilities and keyboard builders are in `utils/`. + +--- + +## License + +MIT License + +--- + +## Authors + +- [Ettore Dreucci](https://ettore.dreucci.it) \ No newline at end of file diff --git a/access_control.py b/access_control.py deleted file mode 100644 index ec2b89f..0000000 --- a/access_control.py +++ /dev/null @@ -1,45 +0,0 @@ -import json -from datetime import datetime, timezone - -class AccessControl: - _ACCESS_FILE = "./data/access.json" - - def __init__(self): - self._access = self._load_access() - - def _load_access(self) -> dict: - try: - with open(self._ACCESS_FILE, "r") as f: - return json.load(f) - except FileNotFoundError: - return {} - - def _save_access(self): - with open(self._ACCESS_FILE, "w") as f: - json.dump(self._access, f) - - def get_grantor(self, user_id: str, gate: str) -> str: - access = self._access.get(user_id, {}) - entry = access.get(gate) or access.get("all") - return entry.get("grantor", "") if entry else "" - - def can_open_gate(self, user_id: str, gate: str) -> bool: - access = self._access.get(user_id, {}) - entry = access.get(gate) or access.get("all") - if not entry or entry.get("type") != "timed": - return False - if datetime.now(timezone.utc) < datetime.fromisoformat(entry["expires_at"].replace("Z", "+00:00")): - return True - return False - - def grant_access(self, user_id: str, gate: str, expires_at: str, grantor_id: str): - user_access = self._access.get(user_id, {}) - user_access[gate] = { - "type": "timed", - "granted_at": datetime.now(timezone.utc).isoformat(), - "expires_at": expires_at, - "grantor": grantor_id, - "last_used_at": None - } - self._access[user_id] = user_access - self._save_access() diff --git a/bot.py b/bot.py index 2420a12..6184e1d 100644 --- a/bot.py +++ b/bot.py @@ -1,200 +1,23 @@ -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, MessageHandler, ContextTypes, CallbackQueryHandler -from datetime import datetime +from telegram.ext import Application, CommandHandler, MessageHandler, CallbackQueryHandler +from functools import partial from config import BotConfig -from gates import Gates -from users import Users, Credential, Role +from models import Gates, Users +from handlers import * -BOT_USERNAME = "lagomareGateKeeperBot" -bot_config = BotConfig(BOT_USERNAME) +bot_config = BotConfig("lagomareGateKeeperBot") 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): - 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 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 (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") - -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.") - 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): - # 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): - 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}") - -async def requestaccess(update: Update, context: ContextTypes.DEFAULT_TYPE): - 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} 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 handle_main_menu_callback(update: Update, context: ContextTypes.DEFAULT_TYPE): - query = update.callback_query - data = query.data - if data == "open_gate_menu": - await open_gate_menu(update, context) - elif data == "request_access": - await requestaccess(update, context) - -async def grantaccess(update: Update, context: ContextTypes.DEFAULT_TYPE): - 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 `") - 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("grantaccess", grantaccess)) - app.add_handler(CallbackQueryHandler(handle_main_menu_callback, pattern="^(open_gate_menu|request_access)$")) - app.add_handler(CallbackQueryHandler(handle_gate_open_callback, pattern="^opengate_")) + app = Application.builder().token(bot_config.token).post_init(partial(post_init, bot_config=bot_config)).build() + app.add_handler(MessageHandler(None, partial(updateuser, users=users)), group=1) + app.add_handler(CommandHandler("start", partial(start, users=users))) + app.add_handler(CommandHandler("setcredentials", partial(setcredentials, users=users))) + app.add_handler(CommandHandler("opengate", partial(opengate, users=users, gates=gates))) + app.add_handler(CommandHandler("requestaccess", partial(requestaccess, users=users))) + app.add_handler(CommandHandler("grantaccess", partial(grantaccess, users=users))) + app.add_handler(CallbackQueryHandler(partial(handle_main_menu_callback, users=users, gates=gates), pattern="^(open_gate_menu|request_access)$")) + app.add_handler(CallbackQueryHandler(partial(handle_gate_open_callback, users=users, gates=gates), pattern="^opengate_")) app.run_polling() if __name__ == "__main__": diff --git a/handlers/__init__.py b/handlers/__init__.py new file mode 100644 index 0000000..25fa59e --- /dev/null +++ b/handlers/__init__.py @@ -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" +] \ No newline at end of file diff --git a/handlers/access.py b/handlers/access.py new file mode 100644 index 0000000..19de332 --- /dev/null +++ b/handlers/access.py @@ -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} 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 `") \ No newline at end of file diff --git a/handlers/credentials.py b/handlers/credentials.py new file mode 100644 index 0000000..f27c7c2 --- /dev/null +++ b/handlers/credentials.py @@ -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 `") + 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") \ No newline at end of file diff --git a/handlers/gates.py b/handlers/gates.py new file mode 100644 index 0000000..cedfa4d --- /dev/null +++ b/handlers/gates.py @@ -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 = 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}") \ No newline at end of file diff --git a/handlers/main.py b/handlers/main.py new file mode 100644 index 0000000..644e1f0 --- /dev/null +++ b/handlers/main.py @@ -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) \ No newline at end of file diff --git a/handlers/users.py b/handlers/users.py new file mode 100644 index 0000000..eaee2a8 --- /dev/null +++ b/handlers/users.py @@ -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) \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..63a7c78 --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,12 @@ +from .credential import Credential +from .gates import Gates +from .status import Status +from .users import Users, Role + +__all__ = [ + "Credential", + "Gates", + "Status", + "Users", + "Role" +] \ No newline at end of file diff --git a/commons.py b/models/credential.py similarity index 81% rename from commons.py rename to models/credential.py index 0488556..5ff9e18 100644 --- a/commons.py +++ b/models/credential.py @@ -1,9 +1,3 @@ -from enum import Enum - -class Status(Enum): - ENABLED = 1 - DISABLED = 0 - class Credential: def __init__(self, username: str, password: str): self.username = username diff --git a/gates.py b/models/gates.py similarity index 94% rename from gates.py rename to models/gates.py index 813d1e4..a2b396a 100644 --- a/gates.py +++ b/models/gates.py @@ -1,6 +1,7 @@ import json -from avconnect import AVConnectAPI -from commons import Status, Credential +from services import AVConnectAPI +from .status import Status +from .credential import Credential class Gate: def __init__(self, id: str, name: str, status: Status = Status.ENABLED): diff --git a/models/status.py b/models/status.py new file mode 100644 index 0000000..5282ab6 --- /dev/null +++ b/models/status.py @@ -0,0 +1,5 @@ +from enum import Enum + +class Status(Enum): + ENABLED = 1 + DISABLED = 0 \ No newline at end of file diff --git a/users.py b/models/users.py similarity index 99% rename from users.py rename to models/users.py index 5799623..b18a36e 100644 --- a/users.py +++ b/models/users.py @@ -1,7 +1,8 @@ import json from enum import Enum from datetime import datetime, timezone -from commons import Status, Credential +from .status import Status +from .credential import Credential class Role(Enum): ADMIN = "admin" diff --git a/services/__init__.py b/services/__init__.py new file mode 100644 index 0000000..583106e --- /dev/null +++ b/services/__init__.py @@ -0,0 +1,5 @@ +from .avconnect import AVConnectAPI + +__all__ = [ + "AVConnectAPI" +] \ No newline at end of file diff --git a/avconnect.py b/services/avconnect.py similarity index 95% rename from avconnect.py rename to services/avconnect.py index f5d4dbd..9f06182 100644 --- a/avconnect.py +++ b/services/avconnect.py @@ -1,6 +1,6 @@ import requests from fake_useragent import UserAgent -from commons import Credential +from models import Credential class AVConnectAPI: _BASE_URL = "https://www.avconnect.it"