-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from ijwfly/feature/allowed-list
Access control
- Loading branch information
Showing
11 changed files
with
224 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,46 @@ | ||
import settings | ||
from app.bot.user_role_manager import UserRoleManager | ||
from app.storage.db import DB | ||
from app.storage.user_role import check_access_conditions | ||
|
||
from aiogram import types | ||
from aiogram.dispatcher.handler import CancelHandler | ||
from aiogram.dispatcher.middlewares import BaseMiddleware | ||
|
||
from app.storage.db import DB | ||
|
||
|
||
class UserMiddleware(BaseMiddleware): | ||
def __init__(self, db: DB): | ||
super().__init__() | ||
self.db = db | ||
|
||
async def on_pre_process_message(self, message: types.Message, data: dict): | ||
is_new_user = False | ||
|
||
user_id = message.from_user.id | ||
# Здесь вы можете получить пользователя из базы данных | ||
user = await self.db.get_or_create_user(user_id) | ||
user = await self.db.get_user(user_id) | ||
if user is None: | ||
user = await self.db.create_user(user_id, settings.USER_ROLE_DEFAULT) | ||
is_new_user = True | ||
|
||
if user.role is None: | ||
user.role = settings.USER_ROLE_DEFAULT | ||
await self.db.update_user(user) | ||
|
||
full_name = message.from_user.full_name | ||
username = message.from_user.username | ||
if user.full_name != full_name or user.username != username: | ||
user.full_name = full_name | ||
user.username = username | ||
await self.db.update_user(user) | ||
|
||
if settings.ENABLE_USER_ROLE_MANAGER_CHAT and is_new_user: | ||
await UserRoleManager.send_new_user_to_admin(message, user) | ||
|
||
user_have_access = check_access_conditions(settings.USER_ROLE_BOT_ACCESS, user.role) | ||
if not user_have_access: | ||
await message.answer( | ||
"You currently don't have access to this bot. You will be notified once the admin grants you access." | ||
) | ||
raise CancelHandler() | ||
|
||
data['user'] = user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from aiogram import types, Bot, Dispatcher | ||
|
||
import settings | ||
from app.storage.db import User, DB | ||
from app.storage.user_role import UserRole, check_access_conditions | ||
|
||
|
||
SET_ROLE_COMMAND = 'setrole' | ||
UPDATE_INFO_COMMAND = 'updinfo' | ||
|
||
|
||
class UserRoleManager: | ||
def __init__(self, bot: Bot, dispatcher: Dispatcher, db: DB): | ||
self.bot = bot | ||
self.dispatcher = dispatcher | ||
self.db = db | ||
self.dispatcher.register_callback_query_handler( | ||
self.setrole_callback, lambda c: SET_ROLE_COMMAND in c.data, | ||
) | ||
self.dispatcher.register_callback_query_handler( | ||
self.updaterole_callback, lambda c: UPDATE_INFO_COMMAND in c.data, | ||
) | ||
|
||
@staticmethod | ||
def get_keyboard(user: User): | ||
keyboard = types.InlineKeyboardMarkup() | ||
|
||
for role in UserRole: | ||
callback_data = f'{SET_ROLE_COMMAND}.{user.telegram_id}.{role.value}' | ||
if role == user.role: | ||
keyboard.add(types.InlineKeyboardButton(text=f'<{role.value}>', callback_data=callback_data)) | ||
else: | ||
keyboard.add(types.InlineKeyboardButton(text=role.value, callback_data=callback_data)) | ||
keyboard.add(types.InlineKeyboardButton(text='🔄', callback_data=f'{UPDATE_INFO_COMMAND}.{user.telegram_id}')) | ||
return keyboard | ||
|
||
@staticmethod | ||
def user_to_string(user): | ||
result = [f'*User Id*: {user.id}', f'*Telegram Id*: {user.telegram_id}'] | ||
if user.full_name: | ||
result.append(f'*Full name*: {user.full_name}') | ||
if user.username: | ||
result.append(f'*Username*: @{user.username}') | ||
result.append(f'*Role*: {user.role.value}') | ||
return '\n'.join(result) | ||
|
||
@classmethod | ||
async def send_new_user_to_admin(cls, message: types.Message, user: User): | ||
bot = message.bot | ||
text = cls.user_to_string(user) | ||
await bot.send_message( | ||
settings.USER_ROLE_MANAGER_CHAT_ID, text, reply_markup=cls.get_keyboard(user), parse_mode=types.ParseMode.MARKDOWN | ||
) | ||
|
||
async def update_message(self, message: types.Message, user: User): | ||
text = self.user_to_string(user) | ||
await message.edit_text(text, reply_markup=self.get_keyboard(user), parse_mode=types.ParseMode.MARKDOWN) | ||
|
||
async def setrole_callback(self, callback_query: types.CallbackQuery): | ||
command, tg_user_id, role_value = callback_query.data.split('.') | ||
tg_user_id = int(tg_user_id) | ||
user = await self.db.get_user(tg_user_id) | ||
user_had_access = check_access_conditions(settings.USER_ROLE_BOT_ACCESS, user.role) | ||
user.role = UserRole(role_value) | ||
await self.db.update_user(user) | ||
await self.bot.answer_callback_query(callback_query.id) | ||
await self.update_message(callback_query.message, user) | ||
if check_access_conditions(settings.USER_ROLE_BOT_ACCESS, user.role) and not user_had_access: | ||
await self.bot.send_message(tg_user_id, f'You have been granted access to the bot.') | ||
|
||
async def updaterole_callback(self, callback_query: types.CallbackQuery): | ||
command, tg_user_id = callback_query.data.split('.') | ||
tg_user_id = int(tg_user_id) | ||
user = await self.db.get_user(tg_user_id) | ||
await self.bot.answer_callback_query(callback_query.id) | ||
await self.update_message(callback_query.message, user) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
from enum import Enum | ||
|
||
|
||
class UserRole(Enum): | ||
ADMIN = 'admin' | ||
ADVANCED = 'advanced' | ||
BASIC = 'basic' | ||
STRANGER = 'stranger' | ||
|
||
|
||
ROLE_ORDER = [UserRole.STRANGER, UserRole.BASIC, UserRole.ADVANCED, UserRole.ADMIN] | ||
|
||
|
||
def check_access_conditions(required_role: UserRole, user_role: UserRole) -> bool: | ||
return ROLE_ORDER.index(user_role) >= ROLE_ORDER.index(required_role) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
ALTER TABLE chatgpttg.user ADD COLUMN IF NOT EXISTS full_name text; | ||
ALTER TABLE chatgpttg.user ADD COLUMN IF NOT EXISTS username text; | ||
|
||
-- create user_role field | ||
DO $$ | ||
BEGIN | ||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'user_roles') THEN | ||
CREATE TYPE chatgpttg.user_roles AS ENUM ('admin', 'advanced', 'basic', 'stranger'); | ||
END IF; | ||
END | ||
$$; | ||
ALTER TABLE chatgpttg.user ADD COLUMN IF NOT EXISTS role chatgpttg.user_roles; | ||
ALTER TABLE chatgpttg.user ADD COLUMN IF NOT EXISTS cdate timestamp WITH TIME ZONE NOT NULL default NOW(); | ||
|
Oops, something went wrong.