Skip to content

Referral-system#9

Merged
nialine merged 5 commits intomainfrom
referral-system
Dec 4, 2025
Merged

Referral-system#9
nialine merged 5 commits intomainfrom
referral-system

Conversation

@nialine
Copy link
Collaborator

@nialine nialine commented Dec 4, 2025

No description provided.

Copilot AI review requested due to automatic review settings December 4, 2025 13:14
@nialine nialine merged commit 9e93ea7 into main Dec 4, 2025
5 of 6 checks passed
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a referral system for a Telegram bot that manages user connections. Users can generate referral links with QR codes to invite others, earning points when invitees establish at least 5 connections.

Key changes:

  • Added referral tracking with invited and invited_by fields in the User model
  • Implemented QR code generation for shareable referral links
  • Refactored database field naming from _links to links with migration logic

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/userdb.py Added referral tracking methods (add_invited, add_invited_by), renamed _links field to links, improved get_user to return User objects, enhanced get_users with flexible filtering
src/models.py Added invited and invited_by fields to User model, implemented username length validation (minimum 5 characters), removed unused set_link method
src/bot.py Implemented referral system handler with QR code generation, added deep linking support in start command, reorganized message handlers, added referral state to registration flow
requirements.txt Added qrcode[pil] dependency (note: pymongo duplicated)
pyproject.toml Added qrcode[pil] dependency to project configuration

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

self,
username: Username | Iterable[Username] | None = None,
links_less_than: Optional[int] = None,
) -> AsyncCursor:
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The return type annotation is AsyncCursor, but the function now returns a list of User objects (line 74). Update the return type annotation to list[User] to match the actual return value.

Suggested change
) -> AsyncCursor:
) -> List[User]:

Copilot uses AI. Check for mistakes.
from typing import List, Optional
from typing import Iterable, List, Optional

from aiogram.types import user
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Unused import: from aiogram.types import user on line 4 doesn't appear to be used anywhere in the code. Remove this import to keep the codebase clean.

Suggested change
from aiogram.types import user

Copilot uses AI. Check for mistakes.
Comment on lines +467 to +471

if not message:
message += "Пока что никто не переходил по ссылке"
return message

Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The generate_message function parameters str_list and points are provided but never used in the returned message string. Either incorporate these parameters into the message or remove them from the function signature.

Suggested change
if not message:
message += "Пока что никто не переходил по ссылке"
return message
# Add info about invited users and points if provided
if str_list is not None and len(str_list) > 0:
message += (
"\n\nДавай посмотрим, кто перешёл по твоей ссылке:\n"
+ "\n".join(str_list)
)
if points is not None:
message += f"\nВсего баллов: {points}"
return message

Copilot uses AI. Check for mistakes.
def check_tg_username(username: str) -> str:
username = username.strip().strip("@")
if len(username) < 5:
raise ValueError(f"{username} is not valid username")
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Grammar error: missing article "a" before "valid username".

Copilot uses AI. Check for mistakes.
for i in all_friends["links"]:
try:
links_list.append(Link.model_validate(i))
except:
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Bare except clause catches all exceptions including system exits and keyboard interrupts. Specify the exception type (e.g., ValidationError from pydantic) to avoid catching unexpected errors.

Copilot uses AI. Check for mistakes.
Comment on lines +468 to +469
if not message:
message += "Пока что никто не переходил по ссылке"
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The condition if not message: on line 468 will always be False since message is assigned a non-empty string on line 460. This makes the code on line 469 unreachable. If you intended to check whether str_list or points are provided, update the condition accordingly (e.g., if str_list is None or not str_list:).

Suggested change
if not message:
message += "Пока что никто не переходил по ссылке"
if str_list is None or not str_list:
message += "\nПока что никто не переходил по ссылке"

Copilot uses AI. Check for mistakes.
Comment on lines +113 to +123
async def add_invited(self, username: Username, username_invited: Username) -> None:
await self.collection.update_one(
{"username": username}, {"$addToSet": {"invited": username_invited}}
)

async def add_invited_by(
self, username: Username, username_invited_by: Username
) -> None:
await self.collection.update_one(
{"username": username}, {"$set": {"invited_by": username_invited_by}}
)
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Missing input validation: the function doesn't validate whether username_invited and username_invited_by are valid usernames or if the users exist in the database. Consider adding validation to prevent adding invalid or non-existent users to the referral lists.

Copilot uses AI. Check for mistakes.
)
main_user = await userdb.get_user(message.from_user.username)
if len(main_user.links) < 5:
await message.answer("Для доступа к реферальной программе отметь 5\\+ связей")
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

The message uses ParseMode.MARKDOWN_V2 on line 493, which requires special characters to be escaped. However, this message on line 478 contains "5\+" which won't be parsed correctly. The backslash should not be used for escaping the plus sign in raw strings. Use proper MarkdownV2 escaping or consider using a different parse mode.

Suggested change
await message.answer("Для доступа к реферальной программе отметь 5\\+ связей")
await message.answer(
"Для доступа к реферальной программе отметь 5+ связей",
parse_mode=ParseMode.MARKDOWN_V2,
)

Copilot uses AI. Check for mistakes.
from aiogram.utils.formatting import as_list
from dotenv import load_dotenv
from pymongo import AsyncMongoClient
from qrcode import QRCode
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Unused import: from qrcode import QRCode on line 27 doesn't appear to be used (the code uses qrcode.make() instead). Remove this import to keep the codebase clean.

Suggested change
from qrcode import QRCode

Copilot uses AI. Check for mistakes.
if len(ratings) < 5:
await message.answer(
"К сожалению, ты написал слишком мало для полноценного отчёта. Давай постараемся добавить всех друзей!"
)
Copy link

Copilot AI Dec 4, 2025

Choose a reason for hiding this comment

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

Potential division by zero: if len(ratings) is 0, the calculations on lines 365-367 will raise a ZeroDivisionError. Although the check on line 361 guards against len(ratings) < 5, it doesn't protect against the case where len(ratings) == 0. Consider returning early or moving the calculations inside a block that ensures len(ratings) > 0.

Suggested change
)
)
return

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant