Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,25 @@
| ✔️ | top | Вывести информацию о участниках беседы в порядке уменьшения кармы. |
| ✔️ | top [ЯЗЫКИ] | Вывести информацию о участниках беседы с указанными языками в порядке уменьшения кармы. |
| ✔️ | top [ЧИСЛО] | Вывести информацию об указанном числе участников беседы в порядке уменьшения кармы. |
| ✔️ | local top | Вывести информацию о участниках беседы в порядке уменьшения локальной кармы в текущем чате. |
| ✔️ | local top [ЧИСЛО] | Вывести информацию об указанном числе участников беседы в порядке уменьшения локальной кармы в текущем чате. |
| ✔️ | bottom | Вывести информацию о участниках беседы в порядке увеличения кармы. |
| ✔️ | bottom [ЯЗЫКИ] | Вывести информацию о участниках беседы с указанными языками в порядке увеличения кармы. |
| ✔️ | bottom [ЧИСЛО] | Вывести информацию об указанном числе участников беседы беседы в порядке увеличения кармы. |
| ✔️ | local bottom | Вывести информацию о участниках беседы в порядке увеличения локальной кармы в текущем чате. |
| ✔️ | local bottom [ЧИСЛО] | Вывести информацию об указанном числе участников беседы в порядке увеличения локальной кармы в текущем чате. |
| ✔️ | karma | Вывод своей кармы или кармы участника беседы из пересланного сообщения. |
| ✔️ | local karma | Вывод своей локальной кармы в текущем чате или локальной кармы участника беседы из пересланного сообщения. |
| ⭐ | info | Вывести общую информацию (карма (только для бесед с кармой), добавленные языки, ссылка на профиль github) о себе или участнике беседы из пересланного сообщения. |
| ⭐ | update | Обновить информацию о вас (имя). Эта команда так же выводит информацию о вас как это делает команда info. |
| ✔️ | + | Проголосовать за повышение кармы участника беседы из пересланного сообщения. |
| ✔️ | - | Проголосовать за понижение кармы участника беседы из пересланного сообщения. |
| ✔️ | +[ЧИСЛО] | Повысить карму участника беседы из пересланного сообщения на указанное число, потратив свою. |
| ✔️ | -[ЧИСЛО] | Понизить карму участника беседы из пересланного сообщения на указанное число, потратив свою. |
| ✔️ | local + | Повысить локальную карму участника беседы из пересланного сообщения в текущем чате. |
| ✔️ | local - | Понизить локальную карму участника беседы из пересланного сообщения в текущем чате. |
| ✔️ | local +[ЧИСЛО] | Повысить локальную карму участника беседы из пересланного сообщения на указанное число в текущем чате. |
| ✔️ | local -[ЧИСЛО] | Понизить локальную карму участника беседы из пересланного сообщения на указанное число в текущем чате. |
| ⭐ | += [ЯЗЫК] | Добавить язык программирования в свой профиль. |
| ⭐ | -= [ЯЗЫК] | Убрать язык программирования из своего профиля. |
| ⭐ | += [ССЫЛКА] | Добавить ссылку на профиль github в свой профиль. |
Expand All @@ -42,6 +51,9 @@
| top | топ | верх |
| bottom | дно | низ |
| karma | карма|
| local karma | локальная карма | местная карма | лкарма |
| local top | локальный топ | местный топ | лтоп |
| local bottom | локальный низ | местный низ | лдно |
| info | инфо |
| update | обновить |
| what is | что такое |
Expand Down
5 changes: 5 additions & 0 deletions python/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,22 @@ def __init__(
(patterns.REMOVE_GITHUB_PROFILE,
lambda: self.commands.change_github_profile(False)),
(patterns.KARMA, self.commands.karma_message),
(patterns.LOCAL_KARMA, self.commands.local_karma_message),
(patterns.TOP, self.commands.top),
(patterns.LOCAL_TOP, self.commands.local_top),
(patterns.PEOPLE, self.commands.top),
(patterns.BOTTOM,
lambda: self.commands.top(True)),
(patterns.LOCAL_BOTTOM,
lambda: self.commands.local_top(True)),
(patterns.TOP_LANGUAGES, self.commands.top_langs),
(patterns.PEOPLE_LANGUAGES, self.commands.top_langs),
(patterns.BOTTOM_LANGUAGES,
lambda: self.commands.top_langs(True)),
(patterns.WHAT_IS, self.commands.what_is),
(patterns.WHAT_MEAN, self.commands.what_is),
(patterns.APPLY_KARMA, self.commands.apply_karma),
(patterns.APPLY_LOCAL_KARMA, self.commands.apply_local_karma),
(patterns.GITHUB_COPILOT, self.commands.github_copilot)
)

Expand Down
96 changes: 96 additions & 0 deletions python/modules/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,102 @@ def apply_user_karma(
user.karma = new_karma
return (user.uid, user.name, initial_karma, new_karma)

def local_karma_message(self) -> NoReturn:
"""Shows user's local karma for current chat."""
if self.peer_id < 2e9 and not self.karma_enabled:
return
is_self = self.user.uid == self.from_id
self.vk_instance.send_msg(
CommandsBuilder.build_local_karma(self.user, self.data_service, is_self, self.peer_id),
self.peer_id)

def apply_local_karma(self) -> NoReturn:
"""Changes user local karma for current chat."""
if self.peer_id < 2e9 or not self.karma_enabled or not self.matched or self.is_bot_selected:
return

operator = self.matched.group("operator")
amount_string = self.matched.group("amount")
amount = int(amount_string) if amount_string else 1

if amount > 10:
return

selected_user_id = self.matched.group("selectedUserId")
selected_user = self.data_service.get_user(
int(selected_user_id), self.vk_instance) if selected_user_id else self.user

if selected_user.uid == self.from_id:
return

if selected_user.uid == config.BOT_GROUP_ID:
self.is_bot_selected = True
return

if operator == "-":
# Downvotes disabled for users with negative local karma
if self.data_service.get_local_karma(self.current_user, self.peer_id) < 0:
self.vk_instance.send_msg(
CommandsBuilder.build_not_enough_local_karma(self.current_user, self.data_service, self.peer_id),
self.peer_id)
return

current_time = datetime.now()
last_vote_time = self.current_user.last_collective_vote

if last_vote_time > 0:
time_diff = (current_time - datetime.fromtimestamp(last_vote_time)).total_seconds() / 3600
hours_limit = karma_limit(
self.data_service.get_local_karma(self.current_user, self.peer_id))

if time_diff < hours_limit:
return

# Apply local karma change
local_karma_change = self.apply_user_local_karma(selected_user, amount if operator == "+" else -amount)

if local_karma_change:
self.current_user.last_collective_vote = current_time.timestamp()
self.data_service.save_user(self.current_user)
self.data_service.save_user(selected_user)
self.vk_instance.send_msg(
CommandsBuilder.build_local_karma_change(local_karma_change, self.peer_id),
self.peer_id)

def apply_user_local_karma(
self,
user: BetterUser,
amount: int
) -> Optional[Tuple[int, str, int, int]]:
"""Changes user local karma for current chat

:param user: user object
:param amount: karma amount to change
:return: tuple of (user_id, username, initial_karma, new_karma) or None
"""
initial_karma = self.data_service.get_local_karma(user, self.peer_id)
new_karma = initial_karma + amount
self.data_service.set_local_karma(user, self.peer_id, new_karma)
return (user.uid, user.name, initial_karma, new_karma)

def local_top(self, reverse: bool = False) -> NoReturn:
"""Sends users local karma top for current chat."""
if self.peer_id < 2e9:
return
maximum_users = self.matched.group("maximum_users")
maximum_users = int(maximum_users) if maximum_users else -1
users = DataBuilder.get_users_sorted_by_local_karma(
self.vk_instance, self.data_service, self.peer_id, self.peer_id)
users = [user for user in users if user["local_karma"] != 0 or not reverse]
if maximum_users != -1:
users = users[:maximum_users]
if reverse:
users.reverse()
self.vk_instance.send_msg(
CommandsBuilder.build_local_top_users(
users, self.data_service, reverse, self.karma_enabled, maximum_users),
self.peer_id)

def what_is(self) -> NoReturn:
"""Search on wikipedia and sends if available"""
question = self.matched.groups()
Expand Down
78 changes: 78 additions & 0 deletions python/modules/commands_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ def build_help_message(
elif peer_id > 2e9:
if karma:
return ("Вы находитесь в беседе с включённой кармой.\n"
"Доступные команды для локальной кармы:\n"
"• 'local karma' / 'локальная карма' - показать локальную карму\n"
"• 'local +/-' - изменить локальную карму пользователя\n"
"• 'local top' / 'локальный топ' - топ по локальной карме\n"
"• 'local bottom' / 'локальный низ' - низ по локальной карме\n"
f"Документация — {documentation_link}")
else:
return (f"Вы находитесь в беседе (#{peer_id}) с выключенной кармой.\n"
Expand Down Expand Up @@ -185,3 +190,76 @@ def build_karma_change(
return ("Карма изменена: [id%s|%s] [%s]->[%s]. Голосовали: (%s)" %
(selected_user_karma_change + (", ".join([f"@id{voter}" for voter in voters]),)))
return None

@staticmethod
def build_local_karma(
user: BetterUser,
data: BetterBotBaseDataService,
is_self: bool,
chat_id: int
) -> str:
"""Sends user local karma amount for specific chat.
"""
if is_self:
return (f"Ваша локальная карма в этом чате — "
f"{DataBuilder.build_local_karma(user, data, chat_id)}.")
else:
mention = f"[id{user.uid}|{user.name}]"
return (f"Локальная карма {mention} в этом чате — "
f"{DataBuilder.build_local_karma(user, data, chat_id)}.")

@staticmethod
def build_not_enough_local_karma(
user: BetterUser,
data: BetterBotBaseDataService,
chat_id: int
) -> str:
"""Builds message about insufficient local karma."""
return (f"Вы не можете минусовать карму, "
f"но Вашей локальной кармы [{data.get_local_karma(user, chat_id)}] "
f"в этом чате недостаточно.")

@staticmethod
def build_local_karma_change(
local_karma_change: Tuple[int, str, int, int],
chat_id: int
) -> str:
"""Builds local karma changing message."""
return ("Локальная карма в этом чате изменена: [id%s|%s] [%s]->[%s]." %
local_karma_change)

@staticmethod
def build_local_top_users(
users: List[Dict[str, Any]],
data: BetterBotBaseDataService,
reverse: bool = False,
has_karma: bool = True,
maximum_users: int = -1
) -> Optional[str]:
"""Builds local karma top users list."""
if not users:
return None
if reverse:
users = list(reversed(users))
user_strings = []
for user in users:
karma_str = f"[{user['local_karma']}]" if has_karma else ""
user_string = (f"{karma_str} "
f"[id{user['uid']}|{user['name']}]"
f"{DataBuilder.build_github_profile_from_dict(user, ' - ')}"
f"{DataBuilder.build_programming_languages_from_dict(user, '')}")
user_strings.append(user_string)

total_symbols = 0
i = 0
for user_string in user_strings:
user_string_length = len(user_string)
if (total_symbols + user_string_length + 2) >= 4096: # Maximum message size for VK API (messages.send)
user_strings = user_strings[:i]
break
else:
total_symbols += user_string_length + 2
i += 1
if maximum_users > 0:
return '\n'.join(user_strings[:maximum_users])
return '\n'.join(user_strings)
63 changes: 63 additions & 0 deletions python/modules/data_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,66 @@ def calculate_real_karma(
up_votes = len(user["supporters"])/config.POSITIVE_VOTES_PER_KARMA
down_votes = len(user["opponents"])/config.NEGATIVE_VOTES_PER_KARMA
return base_karma + up_votes - down_votes

@staticmethod
def build_local_karma(
user: BetterUser,
data: BetterBotBaseDataService,
chat_id: int
) -> str:
"""Builds the user's local karma for specific chat and returns its string representation.
"""
local_karma = data.get_local_karma(user, chat_id)
plus_string = ""
minus_string = ""
up_votes = len(user["supporters"])
down_votes = len(user["opponents"])
if up_votes > 0:
plus_string = "+%.1f" % (up_votes / config.POSITIVE_VOTES_PER_KARMA)
if down_votes > 0:
minus_string = "-%.1f" % (down_votes / config.NEGATIVE_VOTES_PER_KARMA)
if up_votes > 0 or down_votes > 0:
return f"[{local_karma}][{plus_string}{minus_string}]"
else:
return f"[{local_karma}]"

@staticmethod
def get_users_sorted_by_local_karma(
vk_instance: Vk,
data: BetterBotBaseDataService,
peer_id: int,
chat_id: int
) -> List[Dict[str, Any]]:
"""Returns users from the chat sorted by local karma."""
members = vk_instance.get_members_ids(peer_id)
users = []
for member_id in members:
user = data.get_user(member_id)
local_karma = data.get_local_karma(user, chat_id)
users.append({
"uid": member_id,
"local_karma": local_karma,
"name": user.name,
"programming_languages": user.programming_languages,
"github_profile": user.github_profile
})
return sorted(users, key=lambda u: u["local_karma"], reverse=True)

@staticmethod
def build_programming_languages_from_dict(
user_dict: Dict[str, Any],
default: str = "отсутствуют"
) -> str:
"""Builds programming languages from user dictionary."""
languages = user_dict.get("programming_languages", [])
languages = languages if isinstance(languages, list) else []
return ", ".join(sorted(languages)) if len(languages) > 0 else default

@staticmethod
def build_github_profile_from_dict(
user_dict: Dict[str, Any],
prefix: str = ""
) -> str:
"""Builds github profile from user dictionary."""
profile = user_dict.get("github_profile", "")
return f"{prefix}github.com/{profile}" if profile else ""
33 changes: 33 additions & 0 deletions python/modules/data_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ def __init__(self, db_name: str = "users"):
self.base.addPattern("supporters", [])
self.base.addPattern("opponents", [])
self.base.addPattern("karma", 0)
self.base.addPattern("local_karma", {})

def get_or_create_user(
self,
Expand Down Expand Up @@ -115,3 +116,35 @@ def save_user(
user: BetterUser
) -> NoReturn:
self.base.save(user)

@staticmethod
def get_local_karma(
user: Union[Dict[str, Any], BetterUser],
chat_id: int
) -> int:
"""Get user's karma for specific chat.

:param user: dict or BetterUser
:param chat_id: chat identifier
:return: karma value for the chat
"""
local_karma_dict = BetterBotBaseDataService.get_user_property(user, "local_karma")
return local_karma_dict.get(str(chat_id), 0)

@staticmethod
def set_local_karma(
user: Union[Dict[str, Any], BetterUser],
chat_id: int,
karma_value: int
) -> NoReturn:
"""Set user's karma for specific chat.

:param user: dict or BetterUser
:param chat_id: chat identifier
:param karma_value: new karma value
"""
local_karma_dict = BetterBotBaseDataService.get_user_property(user, "local_karma")
if local_karma_dict is None:
local_karma_dict = {}
local_karma_dict[str(chat_id)] = karma_value
BetterBotBaseDataService.set_user_property(user, "local_karma", local_karma_dict)
Loading
Loading